From f5cdab1569b26df0c7ffc3df1529f095116fd13a Mon Sep 17 00:00:00 2001 From: Parker Berberian Date: Thu, 6 Feb 2020 12:59:51 -0500 Subject: Modifies Resource Models for ongoing refactor Change-Id: Ice88f53135f57aca8e2de4d69274e7d490f981a4 Signed-off-by: Parker Berberian --- src/resource_inventory/models.py | 465 +++++++++++++++++++++++---------------- 1 file changed, 270 insertions(+), 195 deletions(-) (limited to 'src/resource_inventory/models.py') diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py index a8b75d9..20e080b 100644 --- a/src/resource_inventory/models.py +++ b/src/resource_inventory/models.py @@ -8,21 +8,30 @@ ############################################################################## from django.contrib.auth.models import User +from django.core.exceptions import ValidationError from django.db import models -from django.core.validators import RegexValidator +from django.db.models import Q import re from account.models import Lab +from dashboard.utils import AbstractModelQuery -# profile of resources hosted by labs -class HostProfile(models.Model): +""" +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) - host_type = models.PositiveSmallIntegerField(default=0) name = models.CharField(max_length=200, unique=True) description = models.TextField() - labs = models.ManyToManyField(Lab, related_name="hostprofiles") + labs = models.ManyToManyField(Lab, related_name="resourceprofiles") def validate(self): validname = re.compile(r"^[A-Za-z0-9\-\_\.\/\, ]+$") @@ -34,12 +43,33 @@ class HostProfile(models.Model): 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(HostProfile, on_delete=models.CASCADE, related_name='interfaceprofile') + host = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE, related_name='interfaceprofile') nic_type = models.CharField( max_length=50, choices=[ @@ -48,6 +78,7 @@ class InterfaceProfile(models.Model): ], default="onboard" ) + order = models.IntegerField(default=-1) def __str__(self): return self.name + " for " + str(self.host) @@ -61,7 +92,7 @@ class DiskProfile(models.Model): ("HDD", "HDD") ]) name = models.CharField(max_length=50) - host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='storageprofile') + host = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE, related_name='storageprofile') rotation = models.IntegerField(default=0) interface = models.CharField( max_length=50, @@ -88,7 +119,7 @@ class CpuProfile(models.Model): ("aarch64", "aarch64") ]) cpus = models.IntegerField() - host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='cpuprofile') + host = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE, related_name='cpuprofile') cflags = models.TextField(null=True) def __str__(self): @@ -99,16 +130,115 @@ class RamProfile(models.Model): id = models.AutoField(primary_key=True) amount = models.IntegerField() channels = models.IntegerField() - host = models.ForeignKey(HostProfile, on_delete=models.CASCADE, related_name='ramprofile') + 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 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, unique=True) + 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) + hidden = models.BooleanField(default=False) + + def getConfigs(self): + return list(self.resourceConfigurations.all()) + + 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 + + +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) + + def __str__(self): + return "config with " + str(self.template) + " and image " + str(self.image) + + +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, null=True) + profile = models.ForeignKey(ResourceProfile, on_delete=models.CASCADE) + config = models.ForeignKey(ResourceConfiguration, on_delete=models.SET_NULL, 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. @@ -129,38 +259,122 @@ class Resource(models.Model): def get_interfaces(self): """ - Returns a list of interfaces on this resource. + 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) -# Generic resource templates -class GenericResourceBundle(models.Model): + +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" + + return { + "id": self.labid, + "image": self.config.image.lab_id, + "hostname": self.template.resource.name, + "power": power, + "ipmi_create": str(ipmi) + } + + def get_interfaces(self): + return list(self.interfaces.all().order_by('bus_address')) + + def release(self): + 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 + + +class Opsys(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.SET_NULL) - lab = models.ForeignKey(Lab, null=True, on_delete=models.SET_NULL) - description = models.CharField(max_length=1000, default="") - public = models.BooleanField(default=False) - hidden = models.BooleanField(default=False) + name = models.CharField(max_length=100) + sup_installers = models.ManyToManyField("Installer", blank=True) + + def __str__(self): + return self.name - def getResources(self): - my_resources = [] - for genericResource in self.generic_resources.all(): - my_resources.append(genericResource.getResource()) - return my_resources +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(ResourceProfile, on_delete=models.CASCADE) + description = models.TextField() + os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE) def __str__(self): return self.name + 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=100) - bundle = models.ForeignKey(GenericResourceBundle, on_delete=models.CASCADE, related_name="networks") + bundle = models.ForeignKey(ResourceTemplate, on_delete=models.CASCADE, related_name="networks") is_public = models.BooleanField() def __str__(self): @@ -205,65 +419,21 @@ class Vlan(models.Model): return str(self.vlan_id) + ("_T" if self.tagged else "") -class ConfigState: - NEW = 0 - RESET = 100 - CLEAN = 200 - - -class GenericResource(models.Model): - bundle = models.ForeignKey(GenericResourceBundle, related_name='generic_resources', on_delete=models.CASCADE) - 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 (_)") - name = models.CharField(max_length=200, validators=[hostname_validchars]) - - def getResource(self): - # TODO: This will have to be dealt with - return self.generic_host - - def __str__(self): - return self.name - - def validate(self): - validname = re.compile(r'(?=^.{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.CASCADE) - resource = models.OneToOneField(GenericResource, related_name='generic_host', on_delete=models.CASCADE) - - def __str__(self): - return self.resource.name - - -# Physical, actual resources -class ResourceBundle(Resource): - template = models.ForeignKey(GenericResourceBundle, 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_host(self, role="Jumphost"): - return Host.objects.filter(bundle=self, config__is_head_node=True).first() # should only ever be one, but it is not an invariant in the models - - -class GenericInterface(models.Model): +class InterfaceConfiguration(models.Model): id = models.AutoField(primary_key=True) profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE) - host = models.ForeignKey(GenericHost, on_delete=models.CASCADE, related_name='generic_interfaces') + resource_config = models.ForeignKey(ResourceConfiguration, on_delete=models.CASCADE, related_name='interface_configs') connections = models.ManyToManyField(NetworkConnection) def __str__(self): return "type " + str(self.profile) + " on host " + str(self.host) +""" +OPNFV / Software configuration models +""" + + class Scenario(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=300) @@ -281,38 +451,16 @@ class Installer(models.Model): 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 NetworkRole(models.Model): name = models.CharField(max_length=100) network = models.ForeignKey(Network, on_delete=models.CASCADE) -class ConfigBundle(models.Model): - id = models.AutoField(primary_key=True) - owner = models.ForeignKey(User, on_delete=models.CASCADE) - 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) - public = models.BooleanField(default=False) - hidden = models.BooleanField(default=False) - - 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) + 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="") @@ -330,105 +478,14 @@ class OPNFVRole(models.Model): 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) - description = models.TextField() - os = models.ForeignKey(Opsys, null=True, on_delete=models.CASCADE) - - def __str__(self): - return self.name - - def in_use(self): - return Host.objects.filter(booked=True, config__image=self).exists() - - def get_sentinal_opnfv_role(): return OPNFVRole.objects.get_or_create(name="deleted", description="Role was deleted.") -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) - is_head_node = models.BooleanField(default=False) - - def __str__(self): - return "config with " + str(self.host) + " and image " + str(self.image) - - -class HostOPNFVConfig(models.Model): - role = models.ForeignKey(OPNFVRole, related_name="host_opnfv_configs", on_delete=models.CASCADE) - host_config = models.ForeignKey(HostConfiguration, related_name="host_opnfv_config", on_delete=models.CASCADE) - opnfv_config = models.ForeignKey(OPNFVConfig, related_name="host_opnfv_config", on_delete=models.CASCADE) - - -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 - - -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 - - -# Concrete host, actual machine in a lab -class Host(Resource): - 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") - remote_management = models.ForeignKey(RemoteInfo, default=get_default_remote_info, on_delete=models.SET(get_default_remote_info)) - - def __str__(self): - return self.name - - def get_configuration(self, state): - ipmi = state == ConfigState.NEW - power = "off" if state == ConfigState.CLEAN else "on" - - return { - "id": self.labid, - "image": self.config.image.lab_id, - "hostname": self.template.resource.name, - "power": power, - "ipmi_create": str(ipmi) - } - - def release(self): - self.booked = False - self.save() - - def get_interfaces(self): - return list(self.interfaces.all().order_by('bus_address')) +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): @@ -436,15 +493,33 @@ class Interface(models.Model): mac_address = models.CharField(max_length=17) bus_address = models.CharField(max_length=50) config = models.ManyToManyField(Vlan) - host = models.ForeignKey(Host, on_delete=models.CASCADE, related_name='interfaces') + acts_as = models.OneToOneField(InterfaceConfiguration, null=True, on_delete=models.SET_NULL) profile = models.ForeignKey(InterfaceProfile, on_delete=models.CASCADE) def __str__(self): return self.mac_address + " on host " + str(self.host) +""" +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] -- cgit 1.2.3-korg From 8c012f8a9bc64add11920688abcd6981278cb0ea Mon Sep 17 00:00:00 2001 From: Parker Berberian Date: Thu, 13 Feb 2020 14:25:24 -0500 Subject: Fix Imports Fixes stale import statements. The dashboard can now come up and we can run our unit tests Change-Id: I7189afb2cd37aaa2492de065c236b6aa9a35de5b Signed-off-by: Parker Berberian --- src/account/views.py | 10 +++---- src/api/serializers/booking_serializer.py | 4 +-- src/booking/quick_deployer.py | 20 ++++++------- src/booking/views.py | 6 ++-- src/dashboard/utils.py | 18 ++++++++++++ src/dashboard/views.py | 4 +-- src/resource_inventory/admin.py | 47 ++++++++++++++++--------------- src/resource_inventory/models.py | 1 + src/resource_inventory/views.py | 6 ++-- src/workflow/booking_workflow.py | 7 ++--- src/workflow/models.py | 4 +-- src/workflow/opnfv_workflow.py | 4 +-- src/workflow/resource_bundle_workflow.py | 30 +++++++++----------- src/workflow/snapshot_workflow.py | 4 +-- src/workflow/sw_bundle_workflow.py | 12 ++++---- src/workflow/workflow_manager.py | 13 ++++----- 16 files changed, 104 insertions(+), 86 deletions(-) (limited to 'src/resource_inventory/models.py') diff --git a/src/account/views.py b/src/account/views.py index ccc4c8d..a8bb02b 100644 --- a/src/account/views.py +++ b/src/account/views.py @@ -33,7 +33,7 @@ from account.forms import AccountSettingsForm from account.jira_util import SignatureMethod_RSA_SHA1 from account.models import UserProfile from booking.models import Booking -from resource_inventory.models import GenericResourceBundle, ConfigBundle, Image +from resource_inventory.models import ResourceTemplate, Image @method_decorator(login_required, name='dispatch') @@ -177,7 +177,7 @@ def account_resource_view(request): if not request.user.is_authenticated: return render(request, "dashboard/login.html", {'title': 'Authentication Required'}) template = "account/resource_list.html" - resources = GenericResourceBundle.objects.filter( + resources = ResourceTemplate.objects.filter( owner=request.user).prefetch_related("configbundle_set") mapping = {} resource_list = [] @@ -218,7 +218,7 @@ def account_configuration_view(request): if not request.user.is_authenticated: return render(request, "dashboard/login.html", {'title': 'Authentication Required'}) template = "account/configuration_list.html" - configs = list(ConfigBundle.objects.filter(owner=request.user)) + configs = list(ResourceTemplate.objects.filter(owner=request.user)) context = {"title": "Configuration List", "configurations": configs} return render(request, template, context=context) @@ -245,7 +245,7 @@ def account_images_view(request): def resource_delete_view(request, resource_id=None): if not request.user.is_authenticated: return HttpResponse('no') # 403? - grb = get_object_or_404(GenericResourceBundle, pk=resource_id) + grb = get_object_or_404(ResourceTemplate, pk=resource_id) if not request.user.id == grb.owner.id: return HttpResponse('no') # 403? if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists(): @@ -257,7 +257,7 @@ def resource_delete_view(request, resource_id=None): def configuration_delete_view(request, config_id=None): if not request.user.is_authenticated: return HttpResponse('no') # 403? - config = get_object_or_404(ConfigBundle, pk=config_id) + config = get_object_or_404(ResourceTemplate, pk=config_id) if not request.user.id == config.owner.id: return HttpResponse('no') # 403? if Booking.objects.filter(config_bundle=config, end__gt=timezone.now()).exists(): diff --git a/src/api/serializers/booking_serializer.py b/src/api/serializers/booking_serializer.py index 46a2348..993eb22 100644 --- a/src/api/serializers/booking_serializer.py +++ b/src/api/serializers/booking_serializer.py @@ -11,7 +11,7 @@ from rest_framework import serializers from resource_inventory.models import ( - HostConfiguration, + ResourceConfiguration, CpuProfile, DiskProfile, InterfaceProfile, @@ -35,7 +35,7 @@ class BookingField(serializers.Field): host_configs = {} # mapping hostname -> config networks = {} # mapping vlan id -> network_hosts for host in booking.resource.hosts.all(): - host_configs[host.name] = HostConfiguration.objects.get(host=host.template) + host_configs[host.name] = ResourceConfiguration.objects.get(host=host.template) if "jumphost" not in ser and host_configs[host.name].opnfvRole.name.lower() == "jumphost": ser['jumphost'] = host.name # host is a Host model diff --git a/src/booking/quick_deployer.py b/src/booking/quick_deployer.py index 94ad14d..917f578 100644 --- a/src/booking/quick_deployer.py +++ b/src/booking/quick_deployer.py @@ -12,7 +12,7 @@ import json from django.db.models import Q from datetime import timedelta from django.utils import timezone -from django.form import ValidationException +from django.core.exceptions import ValidationError from account.models import Lab from resource_inventory.models import ( @@ -21,7 +21,7 @@ from resource_inventory.models import ( Image, OPNFVRole, OPNFVConfig, - HostOPNFVConfig, + ResourceOPNFVConfig, ) from resource_inventory.resource_manager import ResourceManager from resource_inventory.pdf_templater import PDFTemplater @@ -49,9 +49,9 @@ def parse_resource_field(resource_json): template = ResourceTemplate.objects.get(pk=resource_info['id']) if lab is None: - raise ValidationException("No lab was selected") + raise ValidationError("No lab was selected") if template is None: - raise ValidationException("No Host was selected") + raise ValidationError("No Host was selected") return lab, template @@ -82,7 +82,7 @@ def generate_hostopnfv(hostconfig, opnfvconfig): name="Jumphost", description="Single server jumphost role" ) - return HostOPNFVConfig.objects.create( + return ResourceOPNFVConfig.objects.create( role=role, host_config=hostconfig, opnfv_config=opnfvconfig @@ -107,15 +107,15 @@ def check_invariants(request, **kwargs): if installer in image.os.sup_installers.all(): # if installer not here, we can omit that and not check for scenario if not scenario: - raise ValidationException("An OPNFV Installer needs a scenario to be chosen to work properly") + raise ValidationError("An OPNFV Installer needs a scenario to be chosen to work properly") if scenario not in installer.sup_scenarios.all(): - raise ValidationException("The chosen installer does not support the chosen scenario") + raise ValidationError("The chosen installer does not support the chosen scenario") if image.from_lab != lab: - raise ValidationException("The chosen image is not available at the chosen hosting lab") + raise ValidationError("The chosen image is not available at the chosen hosting lab") if image.host_type != host_profile: - raise ValidationException("The chosen image is not available for the chosen host type") + raise ValidationError("The chosen image is not available for the chosen host type") if not image.public and image.owner != request.user: - raise ValidationException("You are not the owner of the chosen private image") + raise ValidationError("You are not the owner of the chosen private image") if length < 1 or length > 21: raise BookingLengthException("Booking must be between 1 and 21 days long") diff --git a/src/booking/views.py b/src/booking/views.py index 8e25952..39b24a7 100644 --- a/src/booking/views.py +++ b/src/booking/views.py @@ -18,7 +18,7 @@ from django.shortcuts import redirect, render from django.db.models import Q from django.urls import reverse -from resource_inventory.models import ResourceBundle, HostProfile, Image, Host +from resource_inventory.models import ResourceBundle, ResourceProfile, Image, ResourceQuery from resource_inventory.resource_manager import ResourceManager from account.models import Lab, Downtime from booking.models import Booking @@ -130,7 +130,7 @@ class ResourceBookingsJSON(View): def build_image_mapping(lab, user): mapping = {} - for profile in HostProfile.objects.filter(labs=lab): + for profile in ResourceProfile.objects.filter(labs=lab): images = Image.objects.filter( from_lab=lab, host_type=profile @@ -178,7 +178,7 @@ def booking_modify_image(request, booking_id): if timezone.now() > booking.end: return HttpResponse("unauthorized") new_image = Image.objects.get(id=form.cleaned_data['image_id']) - host = Host.objects.get(id=form.cleaned_data['host_id']) + host = ResourceQuery.get(labid=form.cleaned_data['host_id']) host.config.image = new_image host.config.save() JobFactory.reimageHost(new_image, booking, host) diff --git a/src/dashboard/utils.py b/src/dashboard/utils.py index af2461e..3d63366 100644 --- a/src/dashboard/utils.py +++ b/src/dashboard/utils.py @@ -1,3 +1,14 @@ +############################################################################## +# Copyright (c) 2020 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.core.exceptions import ObjectDoesNotExist + class AbstractModelQuery(): """ @@ -22,3 +33,10 @@ class AbstractModelQuery(): result = [] for model in cls.model_list: result += list(model.objects.filter(*args, **kwargs)) + + @classmethod + def get(cls, *args, **kwargs): + try: + return cls.filter(*args, **kwargs)[0] + except IndexError: + raise ObjectDoesNotExist() diff --git a/src/dashboard/views.py b/src/dashboard/views.py index 2f37774..498bd9d 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -15,7 +15,7 @@ from django.shortcuts import render from account.models import Lab -from resource_inventory.models import Image, HostProfile +from resource_inventory.models import Image, ResourceProfile from workflow.workflow_manager import ManagerTracker @@ -80,7 +80,7 @@ class LandingView(TemplateView): hosts = [] - for host_profile in HostProfile.objects.all(): + for host_profile in ResourceProfile.objects.all(): name = host_profile.name description = host_profile.description in_labs = host_profile.labs diff --git a/src/resource_inventory/admin.py b/src/resource_inventory/admin.py index ab21dd1..13afd99 100644 --- a/src/resource_inventory/admin.py +++ b/src/resource_inventory/admin.py @@ -16,11 +16,10 @@ from resource_inventory.models import ( DiskProfile, CpuProfile, RamProfile, - GenericResourceBundle, - GenericResource, - GenericHost, - GenericInterface, - Host, + ResourceTemplate, + ResourceConfiguration, + InterfaceConfiguration, + Server, Interface, Network, Vlan, @@ -28,26 +27,30 @@ from resource_inventory.models import ( Scenario, Installer, Opsys, - ConfigBundle, OPNFVConfig, OPNFVRole, Image, - ResourceConfiguration, RemoteInfo ) -profiles = [HostProfile, InterfaceProfile, DiskProfile, CpuProfile, RamProfile] - -admin.site.register(profiles) - -generics = [GenericResourceBundle, GenericResource, GenericHost, GenericInterface] - -admin.site.register(generics) - -physical = [Host, Interface, Network, Vlan, ResourceBundle] - -admin.site.register(physical) - -config = [Scenario, Installer, Opsys, ConfigBundle, OPNFVConfig, OPNFVRole, Image, HostConfiguration, RemoteInfo] - -admin.site.register(config) +admin.site.register([ + ResourceProfile, + InterfaceProfile, + DiskProfile, + CpuProfile, + RamProfile, + ResourceTemplate, + ResourceConfiguration, + InterfaceConfiguration, + Server, + Interface, + Network, + Vlan, + ResourceBundle, + Scenario, + Installer, + Opsys, + OPNFVConfig, + OPNFVRole, + Image, + RemoteInfo]) diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py index 20e080b..eec009a 100644 --- a/src/resource_inventory/models.py +++ b/src/resource_inventory/models.py @@ -200,6 +200,7 @@ class ResourceConfiguration(models.Model): 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? def __str__(self): return "config with " + str(self.template) + " and image " + str(self.image) diff --git a/src/resource_inventory/views.py b/src/resource_inventory/views.py index 8c3d899..52f8c75 100644 --- a/src/resource_inventory/views.py +++ b/src/resource_inventory/views.py @@ -12,7 +12,7 @@ from django.views.generic import TemplateView from django.shortcuts import get_object_or_404 from django.shortcuts import render -from resource_inventory.models import HostProfile, Host +from resource_inventory.models import ResourceProfile, ResourceQuery class HostView(TemplateView): @@ -20,13 +20,13 @@ class HostView(TemplateView): def get_context_data(self, **kwargs): context = super(HostView, self).get_context_data(**kwargs) - hosts = Host.objects.filter(working=True) + 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(HostProfile, id=hostprofile_id) + hostprofile = get_object_or_404(ResourceProfile, id=hostprofile_id) return render( request, diff --git a/src/workflow/booking_workflow.py b/src/workflow/booking_workflow.py index c96e1b9..00fa0f9 100644 --- a/src/workflow/booking_workflow.py +++ b/src/workflow/booking_workflow.py @@ -14,7 +14,7 @@ from datetime import timedelta from booking.models import Booking from workflow.models import WorkflowStep, AbstractSelectOrCreate from workflow.forms import ResourceSelectorForm, SWConfigSelectorForm, BookingMetaForm, OPNFVSelectForm -from resource_inventory.models import GenericResourceBundle, ConfigBundle, OPNFVConfig +from resource_inventory.models import OPNFVConfig, ResourceTemplate from django.db.models import Q @@ -44,8 +44,7 @@ class Abstract_Resource_Select(AbstractSelectOrCreate): def get_form_queryset(self): user = self.repo_get(self.repo.SESSION_USER) - qs = GenericResourceBundle.objects.filter(Q(hidden=False) & (Q(owner=user) | Q(public=True))) - return qs + return ResourceTemplate.objects.filter(Q(hidden=False) & (Q(owner=user) | Q(public=True))) def get_page_context(self): return { @@ -83,7 +82,7 @@ class SWConfig_Select(AbstractSelectOrCreate): def get_form_queryset(self): user = self.repo_get(self.repo.SESSION_USER) grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE) - qs = ConfigBundle.objects.filter(Q(hidden=False) & (Q(owner=user) | Q(public=True))).filter(bundle=grb) + qs = ResourceTemplate.objects.filter(Q(hidden=False) & (Q(owner=user) | Q(public=True))).filter(bundle=grb) return qs def put_confirm_info(self, bundle): diff --git a/src/workflow/models.py b/src/workflow/models.py index 4d32869..df00d21 100644 --- a/src/workflow/models.py +++ b/src/workflow/models.py @@ -18,7 +18,7 @@ import requests from workflow.forms import ConfirmationForm from api.models import JobFactory from dashboard.exceptions import ResourceAvailabilityException, ModelValidationException -from resource_inventory.models import Image, InterfaceProfile, OPNFVConfig, ResourceOPNFVConfig, NetworkRole +from resource_inventory.models import Image, InterfaceConfiguration, OPNFVConfig, ResourceOPNFVConfig, NetworkRole from resource_inventory.resource_manager import ResourceManager from resource_inventory.pdf_templater import PDFTemplater from notifier.manager import NotificationHandler @@ -725,7 +725,7 @@ class Repository(): config = config_bundle.hostConfigurations.get( host__resource__name=host_role['host_name'] ) - HostOPNFVConfig.objects.create( + ResourceOPNFVConfig.objects.create( role=host_role['role'], host_config=config, opnfv_config=opnfv_config diff --git a/src/workflow/opnfv_workflow.py b/src/workflow/opnfv_workflow.py index 0cac48e..6ffc91d 100644 --- a/src/workflow/opnfv_workflow.py +++ b/src/workflow/opnfv_workflow.py @@ -11,7 +11,7 @@ from django.forms import formset_factory from workflow.models import WorkflowStep, AbstractSelectOrCreate -from resource_inventory.models import ConfigBundle, OPNFV_SETTINGS +from resource_inventory.models import ResourceTemplate, OPNFV_SETTINGS from workflow.forms import OPNFVSelectionForm, OPNFVNetworkRoleForm, OPNFVHostRoleForm, SWConfigSelectorForm, BasicMetaForm @@ -27,7 +27,7 @@ class OPNFV_Resource_Select(AbstractSelectOrCreate): def get_form_queryset(self): user = self.repo_get(self.repo.SESSION_USER) - qs = ConfigBundle.objects.filter(owner=user) + qs = ResourceTemplate.objects.filter(owner=user) return qs def put_confirm_info(self, bundle): diff --git a/src/workflow/resource_bundle_workflow.py b/src/workflow/resource_bundle_workflow.py index f57476b..89baae7 100644 --- a/src/workflow/resource_bundle_workflow.py +++ b/src/workflow/resource_bundle_workflow.py @@ -22,11 +22,10 @@ from workflow.forms import ( ResourceMetaForm, ) from resource_inventory.models import ( - GenericResourceBundle, - GenericInterface, - GenericHost, - GenericResource, - HostProfile, + ResourceProfile, + ResourceTemplate, + ResourceConfiguration, + InterfaceConfiguration, Network, NetworkConnection ) @@ -64,12 +63,12 @@ class Define_Hardware(WorkflowStep): models['hosts'] = [] # This will always clear existing data when this step changes models['interfaces'] = {} if "bundle" not in models: - models['bundle'] = GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER)) + models['bundle'] = ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER)) host_data = data['host'] names = {} for host_profile_dict in host_data.values(): id = host_profile_dict['id'] - profile = HostProfile.objects.get(id=id) + profile = ResourceProfile.objects.get(id=id) # instantiate genericHost and store in repo for name in host_profile_dict['values'].values(): if not re.match(r"(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})", name): @@ -77,14 +76,13 @@ class Define_Hardware(WorkflowStep): if name in names: raise NonUniqueHostnameException("All hosts must have unique names") names[name] = True - genericResource = GenericResource(bundle=models['bundle'], name=name) - genericHost = GenericHost(profile=profile, resource=genericResource) - models['hosts'].append(genericHost) + resourceConfig = ResourceConfiguration(profile=profile, template=models['bundle']) + models['hosts'].append(resourceConfig) for interface_profile in profile.interfaceprofile.all(): - genericInterface = GenericInterface(profile=interface_profile, host=genericHost) - if genericHost.resource.name not in models['interfaces']: - models['interfaces'][genericHost.resource.name] = [] - models['interfaces'][genericHost.resource.name].append(genericInterface) + genericInterface = InterfaceConfiguration(profile=interface_profile, resource_config=resourceConfig) + if resourceConfig.name not in models['interfaces']: + models['interfaces'][resourceConfig.name] = [] + models['interfaces'][resourceConfig.name].append(genericInterface) # add selected lab to models for lab_dict in data['lab'].values(): @@ -226,7 +224,7 @@ class Define_Nets(WorkflowStep): for host in existing_host_list: existing_hosts[host.resource.name] = host - bundle = models.get("bundle", GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER))) + bundle = models.get("bundle", ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER))) for net_id, net in networks.items(): network = Network() @@ -381,7 +379,7 @@ class Resource_Meta_Info(WorkflowStep): models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}) name = form.cleaned_data['bundle_name'] desc = form.cleaned_data['bundle_description'] - bundle = models.get("bundle", GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER))) + bundle = models.get("bundle", ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER))) bundle.name = name bundle.description = desc models['bundle'] = bundle diff --git a/src/workflow/snapshot_workflow.py b/src/workflow/snapshot_workflow.py index c2f4cd6..c0e2052 100644 --- a/src/workflow/snapshot_workflow.py +++ b/src/workflow/snapshot_workflow.py @@ -12,7 +12,7 @@ from django.utils import timezone import json from booking.models import Booking -from resource_inventory.models import Host, Image +from resource_inventory.models import ResourceQuery, Image from workflow.models import WorkflowStep from workflow.forms import BasicMetaForm, SnapshotHostSelectForm @@ -61,7 +61,7 @@ class Select_Host_Step(WorkflowStep): name = host['name'] booking_id = host['booking'] booking = Booking.objects.get(pk=booking_id) - host = Host.objects.get(bundle=booking.resource, template__resource__name=name) + host = ResourceQuery.get(bundle=booking.resource, template__resource__name=name) models = self.repo_get(self.repo.SNAPSHOT_MODELS, {}) if "host" not in models: models['host'] = host diff --git a/src/workflow/sw_bundle_workflow.py b/src/workflow/sw_bundle_workflow.py index ebd8c86..686f46f 100644 --- a/src/workflow/sw_bundle_workflow.py +++ b/src/workflow/sw_bundle_workflow.py @@ -13,7 +13,7 @@ from django.forms import formset_factory from workflow.models import WorkflowStep from workflow.forms import BasicMetaForm, HostSoftwareDefinitionForm from workflow.booking_workflow import Abstract_Resource_Select -from resource_inventory.models import Image, GenericHost, ConfigBundle, HostConfiguration +from resource_inventory.models import Image, ResourceConfiguration, ResourceTemplate class SWConf_Resource_Select(Abstract_Resource_Select): @@ -38,7 +38,7 @@ class Define_Software(WorkflowStep): user = self.repo_get(self.repo.SESSION_USER) lab = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE).lab for i, host_data in enumerate(hosts_data): - host = GenericHost.objects.get(pk=host_data['host_id']) + host = ResourceConfiguration.objects.get(pk=host_data['host_id']) wrong_owner = Image.objects.exclude(owner=user).exclude(public=True) wrong_host = Image.objects.exclude(host_type=host.profile) wrong_lab = Image.objects.exclude(from_lab=lab) @@ -86,7 +86,7 @@ class Define_Software(WorkflowStep): if not grb: return [] if grb.id: - return GenericHost.objects.filter(resource__bundle=grb) + return ResourceConfiguration.objects.filter(resource__bundle=grb) generic_hosts = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("hosts", []) return generic_hosts @@ -109,7 +109,7 @@ class Define_Software(WorkflowStep): def post(self, post_data, user): models = self.repo_get(self.repo.CONFIG_MODELS, {}) if "bundle" not in models: - models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER)) + models['bundle'] = ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER)) confirm = self.repo_get(self.repo.CONFIRMATION, {}) @@ -127,7 +127,7 @@ class Define_Software(WorkflowStep): if headnode: has_headnode = True bundle = models['bundle'] - hostConfig = HostConfiguration( + hostConfig = ResourceConfiguration( host=host, image=image, bundle=bundle, @@ -175,7 +175,7 @@ class Config_Software(WorkflowStep): def post(self, post_data, user): models = self.repo_get(self.repo.CONFIG_MODELS, {}) if "bundle" not in models: - models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER)) + models['bundle'] = ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER)) confirm = self.repo_get(self.repo.CONFIRMATION, {}) if "configuration" not in confirm: diff --git a/src/workflow/workflow_manager.py b/src/workflow/workflow_manager.py index fda105e..e31e14c 100644 --- a/src/workflow/workflow_manager.py +++ b/src/workflow/workflow_manager.py @@ -16,9 +16,8 @@ from booking.models import Booking from workflow.workflow_factory import WorkflowFactory from workflow.models import Repository from resource_inventory.models import ( - GenericResourceBundle, - ConfigBundle, - HostConfiguration, + ResourceTemplate, + ResourceConfiguration, OPNFVConfig ) from workflow.forms import ManagerForm @@ -154,10 +153,10 @@ class SessionManager(): edit_object = Booking.objects.get(pk=target_id) self.prefill_booking(edit_object) elif workflow_type == 1: - edit_object = GenericResourceBundle.objects.get(pk=target_id) + edit_object = ResourceTemplate.objects.get(pk=target_id) self.prefill_resource(edit_object) elif workflow_type == 2: - edit_object = ConfigBundle.objects.get(pk=target_id) + edit_object = ResourceTemplate.objects.get(pk=target_id) self.prefill_config(edit_object) def prefill_booking(self, booking): @@ -213,7 +212,7 @@ class SessionManager(): models = self.active_workflow().repository.el.get(self.active_workflow().repository.CONFIG_MODELS, {}) models['bundle'] = config models['host_configs'] = [] - for host_conf in HostConfiguration.objects.filter(bundle=config): + for host_conf in ResourceConfiguration.objects.filter(bundle=config): models['host_configs'].append(host_conf) models['opnfv'] = OPNFVConfig.objects.filter(bundle=config).last() return models @@ -227,7 +226,7 @@ class SessionManager(): opnfv = OPNFVConfig.objects.filter(bundle=config).last() confirm['configuration']['installer'] = opnfv.installer.name confirm['configuration']['scenario'] = opnfv.scenario.name - for host_conf in HostConfiguration.objects.filter(bundle=config): + for host_conf in ResourceConfiguration.objects.filter(bundle=config): h = {"name": host_conf.host.resource.name, "image": host_conf.image.name, "role": host_conf.opnfvRole.name} confirm['configuration']['hosts'].append(h) return confirm -- cgit 1.2.3-korg From 4940cda1806aa13591485b996264ddc887202d35 Mon Sep 17 00:00:00 2001 From: Parker Berberian Date: Thu, 27 Feb 2020 16:26:47 -0500 Subject: Improve Data Migration Improved migration files so that they preserve data Change-Id: I53283fd3fd207ed3f9773beea7b1ce2062b5bd9f Signed-off-by: Parker Berberian --- src/api/migrations/0011_auto_20200218_1536.py | 25 ---- src/api/migrations/0012_manual_20200218_1536.py | 22 +++ src/api/migrations/0012_manual_20200220.py | 18 --- src/api/migrations/0013_manual_20200218_1536.py | 29 ++++ src/api/migrations/0014_manual_20200220.py | 18 +++ .../migrations/0012_auto_20200103_1850.py | 37 ++--- .../migrations/0012_manual_20200218_1536.py | 25 ++++ .../migrations/0013_auto_20200218_1536.py | 161 ++++++++------------- src/resource_inventory/models.py | 2 +- 9 files changed, 163 insertions(+), 174 deletions(-) create mode 100644 src/api/migrations/0012_manual_20200218_1536.py delete mode 100644 src/api/migrations/0012_manual_20200220.py create mode 100644 src/api/migrations/0013_manual_20200218_1536.py create mode 100644 src/api/migrations/0014_manual_20200220.py create mode 100644 src/resource_inventory/migrations/0012_manual_20200218_1536.py (limited to 'src/resource_inventory/models.py') diff --git a/src/api/migrations/0011_auto_20200218_1536.py b/src/api/migrations/0011_auto_20200218_1536.py index a399495..0fd7029 100644 --- a/src/api/migrations/0011_auto_20200218_1536.py +++ b/src/api/migrations/0011_auto_20200218_1536.py @@ -3,14 +3,6 @@ from django.db import migrations, models -def set_resource_id(apps, schema_editor): - for cls in ["HardwareConfig", "NetworkConfig", "SnapshotConfig"]: - model = apps.get_model('api', cls) - for m in model.objects.all(): - m.resource_id = m.host.labid - m.save() - - class Migration(migrations.Migration): dependencies = [ @@ -34,21 +26,4 @@ class Migration(migrations.Migration): name='resource_id', field=models.CharField(default='default_id', max_length=200), ), - migrations.RunPython(set_resource_id), - migrations.RemoveField( - model_name='hosthardwarerelation', - name='host', - ), - migrations.RemoveField( - model_name='hostnetworkrelation', - name='host', - ), - migrations.RemoveField( - model_name='snapshotconfig', - name='host', - ), - migrations.RemoveField( - model_name='opnfvapiconfig', - name='roles', - ), ] diff --git a/src/api/migrations/0012_manual_20200218_1536.py b/src/api/migrations/0012_manual_20200218_1536.py new file mode 100644 index 0000000..55befbd --- /dev/null +++ b/src/api/migrations/0012_manual_20200218_1536.py @@ -0,0 +1,22 @@ +# Generated by Django 2.2 on 2020-02-18 15:36 + +from django.db import migrations + + +def set_resource_id(apps, schema_editor): + for cls in ["HostHardwareRelation", "HostNetworkRelation", "SnapshotConfig"]: + model = apps.get_model('api', cls) + for m in model.objects.all(): + m.resource_id = m.host.labid + m.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0011_auto_20200218_1536'), + ] + + operations = [ + migrations.RunPython(set_resource_id), + ] diff --git a/src/api/migrations/0012_manual_20200220.py b/src/api/migrations/0012_manual_20200220.py deleted file mode 100644 index 40f7e79..0000000 --- a/src/api/migrations/0012_manual_20200220.py +++ /dev/null @@ -1,18 +0,0 @@ - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0011_auto_20200218_1536'), - ('resource_inventory', '0013_auto_20200218_1536') - ] - - operations = [ - migrations.AddField( - model_name='opnfvapiconfig', - name='roles', - field=models.ManyToManyField(to='resource_inventory.ResourceOPNFVConfig'), - ), - ] diff --git a/src/api/migrations/0013_manual_20200218_1536.py b/src/api/migrations/0013_manual_20200218_1536.py new file mode 100644 index 0000000..0b76e84 --- /dev/null +++ b/src/api/migrations/0013_manual_20200218_1536.py @@ -0,0 +1,29 @@ +# Generated by Django 2.2 on 2020-02-18 15:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0012_manual_20200218_1536'), + ] + + operations = [ + migrations.RemoveField( + model_name='hosthardwarerelation', + name='host', + ), + migrations.RemoveField( + model_name='hostnetworkrelation', + name='host', + ), + migrations.RemoveField( + model_name='snapshotconfig', + name='host', + ), + migrations.RemoveField( + model_name='opnfvapiconfig', + name='roles', + ), + ] diff --git a/src/api/migrations/0014_manual_20200220.py b/src/api/migrations/0014_manual_20200220.py new file mode 100644 index 0000000..2e2cd58 --- /dev/null +++ b/src/api/migrations/0014_manual_20200220.py @@ -0,0 +1,18 @@ + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0013_manual_20200218_1536'), + ('resource_inventory', '0013_auto_20200218_1536') + ] + + operations = [ + migrations.AddField( + model_name='opnfvapiconfig', + name='roles', + field=models.ManyToManyField(to='resource_inventory.ResourceOPNFVConfig'), + ), + ] diff --git a/src/resource_inventory/migrations/0012_auto_20200103_1850.py b/src/resource_inventory/migrations/0012_auto_20200103_1850.py index 2bb203e..569e433 100644 --- a/src/resource_inventory/migrations/0012_auto_20200103_1850.py +++ b/src/resource_inventory/migrations/0012_auto_20200103_1850.py @@ -4,30 +4,23 @@ from django.db import migrations, models import django.db.models.deletion -def genTempVlanNetwork(apps, editor): +def pairVlanPhysicalNetworks(apps, editor): + PhysicalNetwork = apps.get_model("resource_inventory", "PhysicalNetwork") Vlan = apps.get_model("resource_inventory", "Vlan") - Network = apps.get_model("resource_inventory", "Network") - tempVlanNetwork = apps.get_model("resource_inventory", "tempVlanNetwork") for vlan in Vlan.objects.filter(network__isnull=False): - tempVlanNetwork.objects.create(network=vlan.network, vlan=vlan) + if PhysicalNetwork.objects.filter(id=vlan.network.id).exists(): + continue + PhysicalNetwork.objects.create(id=vlan.network.id, vlan_id=vlan.vlan_id, generic_network=vlan.network) -def deleteTempVlanNetworks(apps, editor): - tempVlanNetwork = apps.get_model("resource_inventory", "tempVlanNetwork") - tempVlanNetwork.objects.all().delete() - - -def pairVlanPhysicalNetworks(apps, editor): - PhysicalNetwork = apps.get_model("resource_inventory", "PhysicalNetwork") - tempVlanPair = apps.get_model("resource_inventory", "tempVlanNetwork") - for pair in tempVlanPair.objects.all(): - physicalNetwork = PhysicalNetwork.objects.create(vlan_id=vlan.vlan_id, - generic_network=pair.network) - pair.vlan.network = physicalNetwork def deletePhysicalNetworks(apps, editor): + Vlan = apps.get_model("resource_inventory", "Vlan") + for vlan in Vlan.objects.all(): + vlan.network = None PhysicalNetwork = apps.get_model("resource_inventory", "PhysicalNetwork") PhysicalNetwork.objects.all().delete() + class Migration(migrations.Migration): dependencies = [ @@ -56,21 +49,11 @@ class Migration(migrations.Migration): name='id', field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), ), - migrations.CreateModel( - name='tempVlanNetwork', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('vlan', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.vlan')), - ('network', models.ForeignKey(null=True, to='resource_inventory.network', on_delete=django.db.models.deletion.CASCADE)), - ] - ), - migrations.RunPython(genTempVlanNetwork, deleteTempVlanNetworks), + migrations.RunPython(pairVlanPhysicalNetworks, deletePhysicalNetworks), migrations.AlterField( model_name='vlan', name='network', field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='resource_inventory.PhysicalNetwork', null=True), ), - migrations.RunPython(pairVlanPhysicalNetworks, deletePhysicalNetworks), - migrations.DeleteModel("tempVlanNetwork") ] diff --git a/src/resource_inventory/migrations/0012_manual_20200218_1536.py b/src/resource_inventory/migrations/0012_manual_20200218_1536.py new file mode 100644 index 0000000..378bdc3 --- /dev/null +++ b/src/resource_inventory/migrations/0012_manual_20200218_1536.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2 on 2020-02-18 15:36 + +from django.conf import settings +from django.db import migrations + + +def clear_networks(apps, schema_editor): + Network = apps.get_model('resource_inventory', 'Network') + Vlan = apps.get_model('resource_inventory', 'Vlan') + for vlan in Vlan.objects.all(): + vlan.delete() + for net in Network.objects.all(): + net.delete() + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('resource_inventory', '0012_auto_20200103_1850'), + ] + + operations = [ + migrations.RunPython(clear_networks) + ] diff --git a/src/resource_inventory/migrations/0013_auto_20200218_1536.py b/src/resource_inventory/migrations/0013_auto_20200218_1536.py index 3049dc7..014cb2f 100644 --- a/src/resource_inventory/migrations/0013_auto_20200218_1536.py +++ b/src/resource_inventory/migrations/0013_auto_20200218_1536.py @@ -6,17 +6,33 @@ import django.db.models.deletion import resource_inventory.models +def clear_resource_bundles(apps, schema_editor): + ResourceBundle = apps.get_model('resource_inventory', 'ResourceBundle') + for rb in ResourceBundle.objects.all(): + rb.template = None + rb.save() + + +def create_default_template(apps, schema_editor): + ResourceTemplate = apps.get_model('resource_inventory', 'ResourceTemplate') + ResourceTemplate.objects.create(id=1, name="Default Template") + + def populate_servers(apps, schema_editor): """Convert old Host models to Server Resources.""" Host = apps.get_model('resource_inventory', 'Host') Server = apps.get_model('resource_inventory', 'Server') + ResourceProfile = apps.get_model('resource_inventory', 'ResourceProfile') for h in Host.objects.all(): + rp = ResourceProfile.objects.get(id=h.profile.id) Server.objects.create( working=h.working, vendor=h.vendor, labid=h.labid, booked=h.booked, - name=h.labid + name=h.labid, + lab=h.lab, + profile=rp ) @@ -42,14 +58,16 @@ def populate_resource_profiles(apps, schema_editor): HostProfile = apps.get_model('resource_inventory', 'HostProfile') ResourceProfile = apps.get_model('resource_inventory', 'ResourceProfile') for hp in HostProfile.objects.all(): - rp = ResourceProfile.objects.create(name=hp.name, description=hp.description) + rp = ResourceProfile.objects.create(id=hp.id, name=hp.name, description=hp.description) rp.labs.add(*list(hp.labs.all())) + """ + TODO: link these models together rp.interfaceprofile = hp.interfaceprofile rp.storageprofile = hp.storageprofile rp.cpuprofile = hp.cpuprofile rp.ramprofile = hp.ramprofile rp.save() - rp.interfaceprofile.host = rp + hp.interfaceprofile.host = rp rp.storageprofile.host = rp rp.cpuprofile.host = rp rp.ramprofile.host = rp @@ -57,30 +75,17 @@ def populate_resource_profiles(apps, schema_editor): rp.storageprofile.save() rp.cpuprofile.save() rp.ramprofile.save() + """ class Migration(migrations.Migration): - """TODO: Creation of the following models. - - - Server - - ResourceTemplate - - ResourceProfile - - ResourceConfiguration (?) - - InterfaceConfiguration (?) - - And set the correct defaults for the following fields: - - Interface.profile - - OpnfvConfig.template (?) - - physicalnetwork.lab - - physicalnetwork.profile - """ dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('booking', '0007_remove_booking_config_bundle'), ('account', '0004_downtime'), - ('api', '0011_auto_20200218_1536'), - ('resource_inventory', '0012_auto_20200103_1850'), + ('api', '0013_manual_20200218_1536'), + ('resource_inventory', '0012_manual_20200218_1536'), ] operations = [ @@ -143,6 +148,36 @@ class Migration(migrations.Migration): 'abstract': False, }, ), + migrations.AddField( + model_name='server', + name='bundle', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceBundle'), + ), + migrations.AddField( + model_name='server', + name='config', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceConfiguration'), + ), + migrations.AddField( + model_name='server', + name='interfaces', + field=models.ManyToManyField(to='resource_inventory.Interface'), + ), + migrations.AddField( + model_name='server', + name='lab', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab'), + ), + migrations.AddField( + model_name='server', + name='profile', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceProfile'), + ), + migrations.AddField( + model_name='server', + name='remote_management', + field=models.ForeignKey(default=resource_inventory.models.get_default_remote_info, on_delete=models.SET(resource_inventory.models.get_default_remote_info), to='resource_inventory.RemoteInfo'), + ), migrations.RunPython(populate_servers), migrations.RemoveField( model_name='generichost', @@ -251,47 +286,6 @@ class Migration(migrations.Migration): name='order', field=models.IntegerField(default=-1), ), - migrations.AddField( - model_name='physicalnetwork', - name='bundle', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceBundle'), - ), - migrations.AddField( - model_name='physicalnetwork', - name='interfaces', - field=models.ManyToManyField(to='resource_inventory.Interface'), - ), - migrations.AddField( - model_name='physicalnetwork', - name='lab', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='account.Lab'), - preserve_default=False, - ), - migrations.AddField( - model_name='physicalnetwork', - name='labid', - field=models.CharField(default='default_id', max_length=200, unique=True), - ), - migrations.AddField( - model_name='physicalnetwork', - name='model', - field=models.CharField(default='unknown', max_length=150), - ), - migrations.AddField( - model_name='physicalnetwork', - name='remote_management', - field=models.ForeignKey(default=resource_inventory.models.get_default_remote_info, on_delete=models.SET(resource_inventory.models.get_default_remote_info), to='resource_inventory.RemoteInfo'), - ), - migrations.AddField( - model_name='physicalnetwork', - name='vendor', - field=models.CharField(default='unknown', max_length=100), - ), - migrations.AddField( - model_name='physicalnetwork', - name='working', - field=models.BooleanField(default=True), - ), migrations.AlterField( model_name='cpuprofile', name='host', @@ -322,6 +316,7 @@ class Migration(migrations.Migration): name='host', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ramprofile', to='resource_inventory.ResourceProfile'), ), + migrations.RunPython(clear_resource_bundles), migrations.AlterField( model_name='resourcebundle', name='template', @@ -342,9 +337,6 @@ class Migration(migrations.Migration): migrations.DeleteModel( name='GenericResourceBundle', ), - migrations.DeleteModel( - name='Host', - ), migrations.DeleteModel( name='HostConfiguration', ), @@ -354,35 +346,8 @@ class Migration(migrations.Migration): migrations.DeleteModel( name='HostProfile', ), - migrations.AddField( - model_name='server', - name='bundle', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceBundle'), - ), - migrations.AddField( - model_name='server', - name='config', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceConfiguration'), - ), - migrations.AddField( - model_name='server', - name='interfaces', - field=models.ManyToManyField(to='resource_inventory.Interface'), - ), - migrations.AddField( - model_name='server', - name='lab', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab'), - ), - migrations.AddField( - model_name='server', - name='profile', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceProfile'), - ), - migrations.AddField( - model_name='server', - name='remote_management', - field=models.ForeignKey(default=resource_inventory.models.get_default_remote_info, on_delete=models.SET(resource_inventory.models.get_default_remote_info), to='resource_inventory.RemoteInfo'), + migrations.DeleteModel( + name='Host', ), migrations.AddField( model_name='resourceopnfvconfig', @@ -429,21 +394,11 @@ class Migration(migrations.Migration): name='acts_as', field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.InterfaceConfiguration'), ), + migrations.RunPython(create_default_template), migrations.AddField( model_name='opnfvconfig', name='template', field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='opnfv_config', to='resource_inventory.ResourceTemplate'), preserve_default=False, ), - migrations.AddField( - model_name='physicalnetwork', - name='config', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='resource_inventory.ResourceConfiguration'), - ), - migrations.AddField( - model_name='physicalnetwork', - name='profile', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.ResourceProfile'), - preserve_default=False, - ), ] diff --git a/src/resource_inventory/models.py b/src/resource_inventory/models.py index eec009a..d11f71b 100644 --- a/src/resource_inventory/models.py +++ b/src/resource_inventory/models.py @@ -382,7 +382,7 @@ class Network(models.Model): return self.name -class PhysicalNetwork(Resource): +class PhysicalNetwork(models.Model): vlan_id = models.IntegerField() generic_network = models.ForeignKey(Network, on_delete=models.CASCADE) -- cgit 1.2.3-korg