Begin Resource Refactor 56/68856/12
authorParker Berberian <pberberian@iol.unh.edu>
Tue, 12 Nov 2019 17:54:20 +0000 (12:54 -0500)
committerBrandon Lo <lobrandon1217@gmail.com>
Tue, 28 Jan 2020 18:28:34 +0000 (13:28 -0500)
Begins the Resource Refactor by creating new interfaces
to the resources through a Resource super class
and using that new interface in the api

Change-Id: I15a8179bfe915d2cde6d658d056e11cbd2c70e43
Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
13 files changed:
src/account/views.py
src/api/migrations/0010_auto_20191219_2004.py [new file with mode: 0644]
src/api/models.py
src/api/tests/test_models_unittest.py
src/booking/quick_deployer.py
src/dashboard/tasks.py
src/resource_inventory/migrations/0012_auto_20200103_1850.py [new file with mode: 0644]
src/resource_inventory/models.py
src/resource_inventory/resource_manager.py
src/workflow/models.py
src/workflow/opnfv_workflow.py
src/workflow/snapshot_workflow.py
src/workflow/workflow_manager.py

index 2b4eccb..5b91550 100644 (file)
@@ -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, Host
+from resource_inventory.models import GenericResourceBundle, ConfigBundle, Image
 
 
 @method_decorator(login_required, name='dispatch')
@@ -229,7 +229,7 @@ def account_images_view(request):
     public_images = Image.objects.filter(public=True)
     used_images = {}
     for image in my_images:
-        if Host.objects.filter(booked=True, config__image=image).exists():
+        if image.in_use():
             used_images[image.id] = "true"
     context = {
         "title": "Images",
@@ -286,7 +286,7 @@ def image_delete_view(request, image_id=None):
     if image.public or image.owner.id != request.user.id:
         return HttpResponse('no')  # 403?
     # check if used in booking
-    if Host.objects.filter(booked=True, config__image=image).exists():
+    if image.in_use():
         return HttpResponse('no')  # 403?
     image.delete()
     return HttpResponse('')
diff --git a/src/api/migrations/0010_auto_20191219_2004.py b/src/api/migrations/0010_auto_20191219_2004.py
new file mode 100644 (file)
index 0000000..ec48584
--- /dev/null
@@ -0,0 +1,23 @@
+# Generated by Django 2.2 on 2019-12-19 20:04
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('api', '0009_merge_20190508_1317'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='taskconfig',
+            name='delta_keys_list',
+            field=models.CharField(default='[]', max_length=200),
+        ),
+        migrations.AddField(
+            model_name='taskconfig',
+            name='state',
+            field=models.IntegerField(default=200),
+        ),
+    ]
index 682785b..520e747 100644 (file)
@@ -27,7 +27,8 @@ from resource_inventory.models import (
     Interface,
     HostOPNFVConfig,
     RemoteInfo,
-    OPNFVConfig
+    OPNFVConfig,
+    ConfigState
 )
 from resource_inventory.idf_templater import IDFTemplater
 from resource_inventory.pdf_templater import PDFTemplater
@@ -294,32 +295,12 @@ class Job(models.Model):
 
     def to_dict(self):
         d = {}
-        j = {}
-        j['id'] = self.id
-        for relation in AccessRelation.objects.filter(job=self):
-            if 'access' not in d:
-                d['access'] = {}
-            d['access'][relation.task_id] = relation.config.to_dict()
-        for relation in SoftwareRelation.objects.filter(job=self):
-            if 'software' not in d:
-                d['software'] = {}
-            d['software'][relation.task_id] = relation.config.to_dict()
-        for relation in HostHardwareRelation.objects.filter(job=self):
-            if 'hardware' not in d:
-                d['hardware'] = {}
-            d['hardware'][relation.task_id] = relation.config.to_dict()
-        for relation in HostNetworkRelation.objects.filter(job=self):
-            if 'network' not in d:
-                d['network'] = {}
-            d['network'][relation.task_id] = relation.config.to_dict()
-        for relation in SnapshotRelation.objects.filter(job=self):
-            if 'snapshot' not in d:
-                d['snapshot'] = {}
-            d['snapshot'][relation.task_id] = relation.config.to_dict()
-
-        j['payload'] = d
-
-        return j
+        for relation in self.get_tasklist():
+            if relation.job_key not in d:
+                d[relation.job_key] = {}
+            d[relation.job_key][relation.task_id] = relation.config.to_dict()
+
+        return {"id": self.id, "payload": d}
 
     def get_tasklist(self, status="all"):
         tasklist = []
@@ -351,48 +332,54 @@ class Job(models.Model):
 
     def get_delta(self, status):
         d = {}
-        j = {}
-        j['id'] = self.id
-        for relation in AccessRelation.objects.filter(job=self).filter(status=status):
-            if 'access' not in d:
-                d['access'] = {}
-            d['access'][relation.task_id] = relation.config.get_delta()
-        for relation in SoftwareRelation.objects.filter(job=self).filter(status=status):
-            if 'software' not in d:
-                d['software'] = {}
-            d['software'][relation.task_id] = relation.config.get_delta()
-        for relation in HostHardwareRelation.objects.filter(job=self).filter(status=status):
-            if 'hardware' not in d:
-                d['hardware'] = {}
-            d['hardware'][relation.task_id] = relation.config.get_delta()
-        for relation in HostNetworkRelation.objects.filter(job=self).filter(status=status):
-            if 'network' not in d:
-                d['network'] = {}
-            d['network'][relation.task_id] = relation.config.get_delta()
-        for relation in SnapshotRelation.objects.filter(job=self).filter(status=status):
-            if 'snapshot' not in d:
-                d['snapshot'] = {}
-            d['snapshot'][relation.task_id] = relation.config.get_delta()
-
-        j['payload'] = d
-        return j
+        for relation in self.get_tasklist(status=status):
+            if relation.job_key not in d:
+                d[relation.job_key] = {}
+            d[relation.job_key][relation.task_id] = relation.config.get_delta()
+
+        return {"id": self.id, "payload": d}
 
     def to_json(self):
         return json.dumps(self.to_dict())
 
 
 class TaskConfig(models.Model):
+    state = models.IntegerField(default=ConfigState.CLEAN)
+
+    keys = set()  # TODO: This needs to be an instance variable, not a class variable
+    delta_keys_list = models.CharField(max_length=200, default="[]")
+
+    @property
+    def delta_keys(self):
+        return list(set(json.loads(self.delta_keys_list)))
+
+    @delta_keys.setter
+    def delta_keys(self, keylist):
+        self.delta_keys_list = json.dumps(keylist)
+
     def to_dict(self):
-        pass
+        raise NotImplementedError
 
     def get_delta(self):
-        pass
+        raise NotImplementedError
+
+    def format_delta(self, config, token):
+        delta = {k: config[k] for k in self.delta_keys}
+        delta['lab_token'] = token
+        return delta
 
     def to_json(self):
         return json.dumps(self.to_dict())
 
     def clear_delta(self):
-        self.delta = '{}'
+        self.delta_keys = []
+
+    def set(self, *args):
+        dkeys = self.delta_keys
+        for arg in args:
+            if arg in self.keys:
+                dkeys.append(arg)
+        self.delta_keys = dkeys
 
 
 class BridgeConfig(models.Model):
@@ -606,55 +593,12 @@ class HardwareConfig(TaskConfig):
     ipmi_create = models.BooleanField(default=False)
     delta = models.TextField()
 
-    def to_dict(self):
-        d = {}
-        d['image'] = self.image
-        d['power'] = self.power
-        d['hostname'] = self.hostname
-        d['ipmi_create'] = str(self.ipmi_create)
-        d['id'] = self.hosthardwarerelation.host.labid
-        return d
-
-    def to_json(self):
-        return json.dumps(self.to_dict())
+    keys = set(["id", "image", "power", "hostname", "ipmi_create"])
 
     def get_delta(self):
-        if not self.delta:
-            self.delta = self.to_json()
-            self.save()
-        d = json.loads(self.delta)
-        d['lab_token'] = self.hosthardwarerelation.lab_token
-        return d
-
-    def clear_delta(self):
-        d = {}
-        d["id"] = self.hosthardwarerelation.host.labid
-        d["lab_token"] = self.hosthardwarerelation.lab_token
-        self.delta = json.dumps(d)
-
-    def set_image(self, image):
-        self.image = image
-        d = json.loads(self.delta)
-        d['image'] = self.image
-        self.delta = json.dumps(d)
-
-    def set_power(self, power):
-        self.power = power
-        d = json.loads(self.delta)
-        d['power'] = power
-        self.delta = json.dumps(d)
-
-    def set_hostname(self, hostname):
-        self.hostname = hostname
-        d = json.loads(self.delta)
-        d['hostname'] = hostname
-        self.delta = json.dumps(d)
-
-    def set_ipmi_create(self, ipmi_create):
-        self.ipmi_create = ipmi_create
-        d = json.loads(self.delta)
-        d['ipmi_create'] = ipmi_create
-        self.delta = json.dumps(d)
+        return self.format_delta(
+            self.hosthardwarerelation.host.get_configuration(self.state),
+            self.hosthardwarerelation.lab_token)
 
 
 class NetworkConfig(TaskConfig):
@@ -781,6 +725,8 @@ class TaskRelation(models.Model):
     lab_token = models.CharField(default="null", max_length=50)
     message = models.TextField(default="")
 
+    job_key = None
+
     def delete(self, *args, **kwargs):
         self.config.delete()
         return super(self.__class__, self).delete(*args, **kwargs)
@@ -794,6 +740,7 @@ class TaskRelation(models.Model):
 
 class AccessRelation(TaskRelation):
     config = models.OneToOneField(AccessConfig, on_delete=models.CASCADE)
+    job_key = "access"
 
     def type_str(self):
         return "Access Task"
@@ -805,6 +752,7 @@ class AccessRelation(TaskRelation):
 
 class SoftwareRelation(TaskRelation):
     config = models.OneToOneField(SoftwareConfig, on_delete=models.CASCADE)
+    job_key = "software"
 
     def type_str(self):
         return "Software Configuration Task"
@@ -817,6 +765,7 @@ class SoftwareRelation(TaskRelation):
 class HostHardwareRelation(TaskRelation):
     host = models.ForeignKey(Host, on_delete=models.CASCADE)
     config = models.OneToOneField(HardwareConfig, on_delete=models.CASCADE)
+    job_key = "hardware"
 
     def type_str(self):
         return "Hardware Configuration Task"
@@ -832,6 +781,7 @@ class HostHardwareRelation(TaskRelation):
 class HostNetworkRelation(TaskRelation):
     host = models.ForeignKey(Host, on_delete=models.CASCADE)
     config = models.OneToOneField(NetworkConfig, on_delete=models.CASCADE)
+    job_key = "network"
 
     def type_str(self):
         return "Network Configuration Task"
@@ -844,6 +794,7 @@ class HostNetworkRelation(TaskRelation):
 class SnapshotRelation(TaskRelation):
     snapshot = models.ForeignKey(Image, on_delete=models.CASCADE)
     config = models.OneToOneField(SnapshotConfig, on_delete=models.CASCADE)
+    job_key = "snapshot"
 
     def type_str(self):
         return "Snapshot Task"
@@ -960,11 +911,7 @@ class JobFactory(object):
             relation.config = relation.config
             relation.save()
 
-            hardware_config.clear_delta()
-            hardware_config.set_image(host.config.image.lab_id)
-            hardware_config.set_hostname(host.template.resource.name)
-            hardware_config.set_power("on")
-            hardware_config.set_ipmi_create(True)
+            hardware_config.set("image", "hostname", "power", "ipmi_create")
             hardware_config.save()
 
     @classmethod
index e6f97a6..2ecbe42 100644 (file)
@@ -1,4 +1,3 @@
-##############################################################################
 # Copyright (c) 2019 Sawyer Bergeron, Parker Berberian, and others.
 #
 # All rights reserved. This program and the accompanying materials
@@ -21,6 +20,7 @@ from api.models import (
 from resource_inventory.models import (
     OPNFVRole,
     HostProfile,
+    ConfigState,
 )
 
 from django.test import TestCase, Client
@@ -201,7 +201,7 @@ class ValidBookingCreatesValidJob(TestCase):
             self.assertEqual(relation.status, JobStatus.NEW)
             config = relation.config
             host = relation.host
-            self.assertEqual(config.hostname, host.template.resource.name)
+            self.assertEqual(config.get_delta()["hostname"], host.template.resource.name)
 
     def test_complete_job_makes_software_configs(self):
         JobFactory.makeCompleteJob(self.booking)
@@ -261,9 +261,10 @@ class ValidBookingCreatesValidJob(TestCase):
                 host_set.remove(relation.host.id)
             except KeyError:
                 self.fail("Hardware Relation/Config not created for host " + str(relation.host))
-
-            self.assertEqual(relation.config.power, "on")
-            self.assertTrue(relation.config.ipmi_create)
+            # TODO: ConfigState needs to be fixed in factory methods
+            relation.config.state = ConfigState.NEW
+            self.assertEqual(relation.config.get_delta()["power"], "on")
+            self.assertTrue(relation.config.get_delta()["ipmi_create"])
             # TODO: the rest of hwconf attrs
 
         self.assertEqual(len(host_set), 0)
index 0e0cc5a..4ec488e 100644 (file)
@@ -245,7 +245,7 @@ def configure_networking(grb, config):
     # create network
     net = Network.objects.create(name="public", bundle=grb, is_public=True)
     # connect network to generic host
-    grb.getHosts()[0].generic_interfaces.first().connections.add(
+    grb.getResources()[0].generic_interfaces.first().connections.add(
         NetworkConnection.objects.create(network=net, vlan_is_tagged=False)
     )
     # asign network role
index 597629f..71afed2 100644 (file)
@@ -23,7 +23,7 @@ def booking_poll():
         for hostrelation in qs:
             config = hostrelation.config
             config.clear_delta()
-            config.set_power("off")
+            config.power = "off"
             config.save()
             hostrelation.status = JobStatus.NEW
             hostrelation.save()
diff --git a/src/resource_inventory/migrations/0012_auto_20200103_1850.py b/src/resource_inventory/migrations/0012_auto_20200103_1850.py
new file mode 100644 (file)
index 0000000..2bb203e
--- /dev/null
@@ -0,0 +1,76 @@
+# Generated by Django 2.2 on 2020-01-03 18:50
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+def genTempVlanNetwork(apps, editor):
+    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)
+
+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):
+    PhysicalNetwork = apps.get_model("resource_inventory", "PhysicalNetwork")
+    PhysicalNetwork.objects.all().delete()
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('resource_inventory', '0011_auto_20191106_2024'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PhysicalNetwork',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('vlan_id', models.IntegerField()),
+                ('generic_network', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='resource_inventory.Network')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.AlterField(
+            model_name='host',
+            name='id',
+            field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+        ),
+        migrations.AlterField(
+            model_name='resourcebundle',
+            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.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")
+    ]
index 031ccce..d152698 100644 (file)
@@ -105,6 +105,33 @@ class RamProfile(models.Model):
         return str(self.amount) + "G for " + str(self.host)
 
 
+class Resource(models.Model):
+    class Meta:
+        abstract = True
+
+    def get_configuration(self, state):
+        """
+        Returns the desired configuration for this host as a
+        JSON object as defined in the rest api spec.
+        state is a ConfigState
+        TODO: single method, or different methods for hw, network, snapshot, etc?
+        """
+        raise NotImplementedError("Must implement in concrete Resource classes")
+
+    def reserve(self):
+        """
+        Reserves this resource for its currently
+        assigned booking.
+        """
+        raise NotImplementedError("Must implement in concrete Resource classes")
+
+    def release(self):
+        """
+        Makes this resource available again for new boookings
+        """
+        raise NotImplementedError("Must implement in concrete Resource classes")
+
+
 # Generic resource templates
 class GenericResourceBundle(models.Model):
     id = models.AutoField(primary_key=True)
@@ -116,12 +143,12 @@ class GenericResourceBundle(models.Model):
     public = models.BooleanField(default=False)
     hidden = models.BooleanField(default=False)
 
-    def getHosts(self):
-        return_hosts = []
+    def getResources(self):
+        my_resources = []
         for genericResource in self.generic_resources.all():
-            return_hosts.append(genericResource.getHost())
+            my_resources.append(genericResource.getResource())
 
-        return return_hosts
+        return my_resources
 
     def __str__(self):
         return self.name
@@ -137,6 +164,29 @@ class Network(models.Model):
         return self.name
 
 
+class PhysicalNetwork(Resource):
+    vlan_id = models.IntegerField()
+    generic_network = models.ForeignKey(Network, on_delete=models.CASCADE)
+
+    def get_configuration(self, state):
+        """
+        Returns the network configuration
+        Collects info about each attached network interface and vlan, etc
+        """
+        return {}
+
+    def reserve(self):
+        """
+        Reserves vlan(s) associated with this network
+        """
+        # vlan_manager = self.bundle.lab.vlan_manager
+        return False
+
+    def release(self):
+        # vlan_manager = self.bundle.lab.vlan_manager
+        return False
+
+
 class NetworkConnection(models.Model):
     network = models.ForeignKey(Network, on_delete=models.CASCADE)
     vlan_is_tagged = models.BooleanField()
@@ -147,18 +197,25 @@ class Vlan(models.Model):
     vlan_id = models.IntegerField()
     tagged = models.BooleanField()
     public = models.BooleanField(default=False)
-    network = models.ForeignKey(Network, on_delete=models.DO_NOTHING, null=True)
+    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 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 getHost(self):
+    def getResource(self):
+        # TODO: This will have to be dealt with
         return self.generic_host
 
     def __str__(self):
@@ -183,8 +240,7 @@ class GenericHost(models.Model):
 
 
 # Physical, actual resources
-class ResourceBundle(models.Model):
-    id = models.AutoField(primary_key=True)
+class ResourceBundle(Resource):
     template = models.ForeignKey(GenericResourceBundle, on_delete=models.SET_NULL, null=True)
 
     def __str__(self):
@@ -289,6 +345,9 @@ class Image(models.Model):
     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.")
@@ -336,8 +395,7 @@ def get_default_remote_info():
 
 
 # Concrete host, actual machine in a lab
-class Host(models.Model):
-    id = models.AutoField(primary_key=True)
+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)
@@ -354,6 +412,18 @@ class Host(models.Model):
     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)
+        }
+
 
 class Interface(models.Model):
     id = models.AutoField(primary_key=True)
index e94b4ec..7df4263 100644 (file)
@@ -50,7 +50,7 @@ class ResourceManager:
 
         # count up hosts
         profile_count = {}
-        for host in grb.getHosts():
+        for host in grb.getResources():
             if host.profile not in profile_count:
                 profile_count[host.profile] = 0
             profile_count[host.profile] += 1
@@ -71,7 +71,7 @@ class ResourceManager:
     # public interface
     def deleteResourceBundle(self, resourceBundle):
         for host in Host.objects.filter(bundle=resourceBundle):
-            self.releaseHost(host)
+            host.release()
         resourceBundle.delete()
 
     def get_vlans(self, genericResourceBundle):
@@ -93,7 +93,7 @@ class ResourceManager:
         Takes in a GenericResourceBundle and 'converts' it into a ResourceBundle
         """
         resource_bundle = ResourceBundle.objects.create(template=genericResourceBundle)
-        generic_hosts = genericResourceBundle.getHosts()
+        generic_hosts = genericResourceBundle.getResources()
         physical_hosts = []
 
         vlan_map = self.get_vlans(genericResourceBundle)
@@ -154,12 +154,6 @@ class ResourceManager:
         host.save()
         return host
 
-    def releaseHost(self, host):
-        host.template = None
-        host.bundle = None
-        host.booked = False
-        host.save()
-
     def releaseNetworks(self, grb, vlan_manager, vlans):
         for net_name, vlan_id in vlans.items():
             net = Network.objects.get(name=net_name, bundle=grb)
@@ -172,7 +166,7 @@ class ResourceManager:
         vlan_manager = grb.lab.vlan_manager
         self.releaseNetworks(grb, vlan_manager, vlans)
         for host in hosts:
-            self.releaseHost(host)
+            host.release()
 
 
 class HostNameValidator(object):
index 9d1fac2..99608f6 100644 (file)
@@ -146,7 +146,7 @@ class BookingAuthManager():
             return True  # admin override for this user
         if Booking.objects.filter(owner=booking.owner, end__gt=timezone.now()).count() >= 3:
             return False
-        if len(booking.resource.template.getHosts()) < 2:
+        if len(booking.resource.template.getResources()) < 2:
             return True  # if they only have one server, we dont care
         if repo.BOOKING_INFO_FILE not in repo.el:
             return False  # INFO file not provided
index a192d6e..0cac48e 100644 (file)
@@ -202,7 +202,7 @@ class Assign_Host_Roles(WorkflowStep):  # taken verbatim from Define_Software in
         if config is None:
             context['error'] = "Please select a Configuration on the first step"
 
-        formset = self.create_host_role_formset(hostlist=config.bundle.getHosts())
+        formset = self.create_host_role_formset(hostlist=config.bundle.getResources())
         context['formset'] = formset
 
         return context
index 4266587..c2f4cd6 100644 (file)
@@ -36,7 +36,7 @@ class Select_Host_Step(WorkflowStep):
             booking_hosts[booking.id]['start'] = booking.start.strftime("%Y-%m-%d")
             booking_hosts[booking.id]['end'] = booking.end.strftime("%Y-%m-%d")
             booking_hosts[booking.id]['hosts'] = []
-            for genericHost in booking.resource.template.getHosts():
+            for genericHost in booking.resource.template.getResources():
                 booking_hosts[booking.id]['hosts'].append({"name": genericHost.resource.name})
 
         context['booking_hosts'] = booking_hosts
index 4677829..fda105e 100644 (file)
@@ -189,7 +189,7 @@ class SessionManager():
         models['bundle'] = resource
         models['interfaces'] = {}
         models['vlans'] = {}
-        for host in resource.getHosts():
+        for host in resource.getResources():
             models['hosts'].append(host)
             models['interfaces'][host.resource.name] = []
             models['vlans'][host.resource.name] = {}
@@ -205,7 +205,7 @@ class SessionManager():
         confirm['resource'] = {}
         confirm['resource']['hosts'] = []
         confirm['resource']['lab'] = resource.lab.lab_user.username
-        for host in resource.getHosts():
+        for host in resource.getResources():
             confirm['resource']['hosts'].append({"name": host.resource.name, "profile": host.profile.name})
         return confirm