aboutsummaryrefslogtreecommitdiffstats
path: root/src/workflow
diff options
context:
space:
mode:
Diffstat (limited to 'src/workflow')
-rw-r--r--src/workflow/booking_workflow.py10
-rw-r--r--src/workflow/forms.py11
-rw-r--r--src/workflow/models.py109
-rw-r--r--src/workflow/resource_bundle_workflow.py405
-rw-r--r--src/workflow/sw_bundle_workflow.py196
-rw-r--r--src/workflow/tests/test_steps.py4
-rw-r--r--src/workflow/views.py2
-rw-r--r--src/workflow/workflow_factory.py13
-rw-r--r--src/workflow/workflow_manager.py14
9 files changed, 359 insertions, 405 deletions
diff --git a/src/workflow/booking_workflow.py b/src/workflow/booking_workflow.py
index 00fa0f9..128f179 100644
--- a/src/workflow/booking_workflow.py
+++ b/src/workflow/booking_workflow.py
@@ -36,20 +36,20 @@ class Abstract_Resource_Select(AbstractSelectOrCreate):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.select_repo_key = self.repo.SELECTED_GRESOURCE_BUNDLE
+ self.select_repo_key = self.repo.SELECTED_RESOURCE_TEMPLATE
self.confirm_key = self.workflow_type
def alert_bundle_missing(self):
- self.set_invalid("Please select a valid resource bundle")
+ self.set_invalid("Please select a valid resource template")
def get_form_queryset(self):
user = self.repo_get(self.repo.SESSION_USER)
- return ResourceTemplate.objects.filter(Q(hidden=False) & (Q(owner=user) | Q(public=True)))
+ return ResourceTemplate.objects.filter((Q(owner=user) | Q(public=True)))
def get_page_context(self):
return {
'select_type': 'resource',
- 'select_type_title': 'Resource Bundle',
+ 'select_type_title': 'Resource template',
'addable_type_num': 1
}
@@ -81,7 +81,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)
+ grb = self.repo_get(self.repo.SELECTED_RESOURCE_TEMPLATE)
qs = ResourceTemplate.objects.filter(Q(hidden=False) & (Q(owner=user) | Q(public=True))).filter(bundle=grb)
return qs
diff --git a/src/workflow/forms.py b/src/workflow/forms.py
index a8d3413..4220dea 100644
--- a/src/workflow/forms.py
+++ b/src/workflow/forms.py
@@ -24,6 +24,7 @@ from resource_inventory.models import (
Installer,
Scenario,
)
+from resource_inventory.resource_manager import ResourceManager
from booking.lib import get_user_items, get_user_field_opts
@@ -286,7 +287,7 @@ class MultipleSelectFilterField(forms.Field):
class FormUtils:
@staticmethod
- def getLabData(multiple_hosts=False):
+ def getLabData(multiple_hosts=False, user=None):
"""
Get all labs and thier host profiles, returns a serialized version the form can understand.
@@ -319,7 +320,7 @@ class FormUtils:
neighbors[lab_node['id']] = []
labs[lab_node['id']] = lab_node
- for template in lab.resourcetemplates.all():
+ for template in ResourceManager.getInstance().getAvailableResourceTemplates(lab, user):
resource_node = {
'form': {"name": "host_name", "type": "text", "placeholder": "hostname"},
'id': "resource_" + str(template.id),
@@ -353,9 +354,9 @@ class FormUtils:
class HardwareDefinitionForm(forms.Form):
- def __init__(self, *args, **kwargs):
+ def __init__(self, user, *args, **kwargs):
super(HardwareDefinitionForm, self).__init__(*args, **kwargs)
- attrs = FormUtils.getLabData(multiple_hosts=True)
+ attrs = FormUtils.getLabData(multiple_hosts=True, user=user)
self.fields['filter_field'] = MultipleSelectFilterField(
widget=MultipleSelectFilterWidget(**attrs)
)
@@ -391,7 +392,7 @@ class NetworkConfigurationForm(forms.Form):
class HostSoftwareDefinitionForm(forms.Form):
- host_name = forms.CharField(max_length=200, disabled=True, required=False)
+ host_name = forms.CharField(max_length=200, disabled=False, required=True)
headnode = forms.BooleanField(required=False, widget=forms.HiddenInput)
def __init__(self, *args, **kwargs):
diff --git a/src/workflow/models.py b/src/workflow/models.py
index df00d21..173fdba 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, InterfaceConfiguration, OPNFVConfig, ResourceOPNFVConfig, NetworkRole
+from resource_inventory.models import Image, OPNFVConfig, ResourceOPNFVConfig, NetworkRole
from resource_inventory.resource_manager import ResourceManager
from resource_inventory.pdf_templater import PDFTemplater
from notifier.manager import NotificationHandler
@@ -352,6 +352,7 @@ class Confirmation_Step(WorkflowStep):
self.set_valid("Confirmed")
elif data == "False":
+ self.repo.cancel()
self.set_valid("Canceled")
else:
self.set_invalid("Bad Form Contents")
@@ -366,14 +367,14 @@ class Repository():
MODELS = "models"
RESOURCE_SELECT = "resource_select"
CONFIRMATION = "confirmation"
- SELECTED_GRESOURCE_BUNDLE = "selected generic bundle pk"
+ SELECTED_RESOURCE_TEMPLATE = "selected resource template pk"
SELECTED_CONFIG_BUNDLE = "selected config bundle pk"
SELECTED_OPNFV_CONFIG = "selected opnfv deployment config"
- GRESOURCE_BUNDLE_MODELS = "generic_resource_bundle_models"
- GRESOURCE_BUNDLE_INFO = "generic_resource_bundle_info"
+ RESOURCE_TEMPLATE_MODELS = "generic_resource_template_models"
+ RESOURCE_TEMPLATE_INFO = "generic_resource_template_info"
BOOKING = "booking"
LAB = "lab"
- GRB_LAST_HOSTLIST = "grb_network_previous_hostlist"
+ RCONFIG_LAST_HOSTLIST = "resource_configuration_network_previous_hostlist"
BOOKING_FORMS = "booking_forms"
SWCONF_HOSTS = "swconf_hosts"
BOOKING_MODELS = "booking models"
@@ -391,6 +392,9 @@ class Repository():
SNAPSHOT_DESC = "description of the snapshot"
BOOKING_INFO_FILE = "the INFO.yaml file for this user's booking"
+ # new keys for migration to using ResourceTemplates:
+ RESOURCE_TEMPLATE_MODELS = "current working model of resource template"
+
# migratory elements of segmented workflow
# each of these is the end result of a different workflow.
HAS_RESULT = "whether or not workflow has a result"
@@ -399,7 +403,7 @@ class Repository():
def get_child_defaults(self):
return_tuples = []
- for key in [self.SELECTED_GRESOURCE_BUNDLE, self.SESSION_USER]:
+ for key in [self.SELECTED_RESOURCE_TEMPLATE, self.SESSION_USER]:
return_tuples.append((key, self.el.get(key)))
return return_tuples
@@ -428,6 +432,14 @@ class Repository():
else:
history[key].append(id)
+ def cancel(self):
+ if self.RESOURCE_TEMPLATE_MODELS in self.el:
+ models = self.el[self.RESOURCE_TEMPLATE_MODELS]
+ if models['template'].temporary:
+ models['template'].delete()
+ # deleting current template should cascade delete all
+ # necessary related models
+
def make_models(self):
if self.SNAPSHOT_MODELS in self.el:
errors = self.make_snapshot()
@@ -435,13 +447,13 @@ class Repository():
return errors
# if GRB WF, create it
- if self.GRESOURCE_BUNDLE_MODELS in self.el:
+ if self.RESOURCE_TEMPLATE_MODELS in self.el:
errors = self.make_generic_resource_bundle()
if errors:
return errors
else:
self.el[self.HAS_RESULT] = True
- self.el[self.RESULT_KEY] = self.SELECTED_GRESOURCE_BUNDLE
+ self.el[self.RESULT_KEY] = self.SELECTED_RESOURCE_TEMPLATE
return
if self.CONFIG_MODELS in self.el:
@@ -507,78 +519,23 @@ class Repository():
def make_generic_resource_bundle(self):
owner = self.el[self.SESSION_USER]
- if self.GRESOURCE_BUNDLE_MODELS in self.el:
- models = self.el[self.GRESOURCE_BUNDLE_MODELS]
- if 'hosts' in models:
- hosts = models['hosts']
- else:
- return "GRB has no hosts. CODE:0x0002"
- if 'bundle' in models:
- bundle = models['bundle']
- else:
- return "GRB, no bundle in models. CODE:0x0003"
-
- try:
- bundle.owner = owner
- bundle.save()
- except Exception as e:
- return "GRB, saving bundle generated exception: " + str(e) + " CODE:0x0004"
- try:
- for host in hosts:
- genericresource = host.resource
- genericresource.bundle = bundle
- genericresource.save()
- host.resource = genericresource
- host.save()
- except Exception as e:
- return "GRB, saving hosts generated exception: " + str(e) + " CODE:0x0005"
-
- if 'networks' in models:
- for net in models['networks'].values():
- net.bundle = bundle
- net.save()
-
- if 'interfaces' in models:
- for interface_set in models['interfaces'].values():
- for interface in interface_set:
- try:
- interface.host = interface.host
- interface.save()
- except Exception:
- return "GRB, saving interface " + str(interface) + " failed. CODE:0x0019"
- else:
- return "GRB, no interface set provided. CODE:0x001a"
-
- if 'connections' in models:
- for resource_name, mapping in models['connections'].items():
- for profile_name, connection_set in mapping.items():
- interface = InterfaceConfiguration.objects.get(
- profile__name=profile_name,
- host__resource__name=resource_name,
- host__resource__bundle=models['bundle']
- )
- for connection in connection_set:
- try:
- connection.network = connection.network
- connection.save()
- interface.connections.add(connection)
- except Exception as e:
- return "GRB, saving vlan " + str(connection) + " failed. Exception: " + str(e) + ". CODE:0x0017"
- else:
- return "GRB, no vlan set provided. CODE:0x0018"
+ if self.RESOURCE_TEMPLATE_MODELS in self.el:
+ models = self.el[self.RESOURCE_TEMPLATE_MODELS]
+ models['template'].owner = owner
+ models['template'].temporary = False
+ models['template'].save()
+ self.el[self.RESULT] = models['template']
+ self.el[self.HAS_RESULT] = True
+ return False
else:
return "GRB no models given. CODE:0x0001"
- self.el[self.RESULT] = bundle
- self.el[self.HAS_RESULT] = True
- return False
-
def make_software_config_bundle(self):
models = self.el[self.CONFIG_MODELS]
if 'bundle' in models:
bundle = models['bundle']
- bundle.bundle = self.el[self.SELECTED_GRESOURCE_BUNDLE]
+ bundle.bundle = self.el[self.SELECTED_RESOURCE_TEMPLATE]
try:
bundle.save()
except Exception as e:
@@ -589,8 +546,8 @@ class Repository():
if 'host_configs' in models:
host_configs = models['host_configs']
for host_config in host_configs:
- host_config.bundle = host_config.bundle
- host_config.host = host_config.host
+ host_config.template = host_config.template
+ host_config.profile = host_config.profile
try:
host_config.save()
except Exception as e:
@@ -623,8 +580,8 @@ class Repository():
selected_grb = None
- if self.SELECTED_GRESOURCE_BUNDLE in self.el:
- selected_grb = self.el[self.SELECTED_GRESOURCE_BUNDLE]
+ if self.SELECTED_RESOURCE_TEMPLATE in self.el:
+ selected_grb = self.el[self.SELECTED_RESOURCE_TEMPLATE]
else:
return "BOOK, no selected resource. CODE:0x000e"
diff --git a/src/workflow/resource_bundle_workflow.py b/src/workflow/resource_bundle_workflow.py
index 89baae7..391d33e 100644
--- a/src/workflow/resource_bundle_workflow.py
+++ b/src/workflow/resource_bundle_workflow.py
@@ -9,10 +9,13 @@
from django.conf import settings
+from django.forms import formset_factory
+
+from typing import List
import json
-import re
from xml.dom import minidom
+import traceback
from workflow.models import WorkflowStep
from account.models import Lab
@@ -20,20 +23,19 @@ from workflow.forms import (
HardwareDefinitionForm,
NetworkDefinitionForm,
ResourceMetaForm,
+ HostSoftwareDefinitionForm,
)
from resource_inventory.models import (
- ResourceProfile,
ResourceTemplate,
ResourceConfiguration,
InterfaceConfiguration,
Network,
- NetworkConnection
+ NetworkConnection,
+ Image,
)
from dashboard.exceptions import (
InvalidVlanConfigurationException,
NetworkExistsException,
- InvalidHostnameException,
- NonUniqueHostnameException,
ResourceAvailabilityException
)
@@ -54,61 +56,112 @@ class Define_Hardware(WorkflowStep):
def get_context(self):
context = super(Define_Hardware, self).get_context()
- context['form'] = self.form or HardwareDefinitionForm()
+ user = self.repo_get(self.repo.SESSION_USER)
+ context['form'] = self.form or HardwareDefinitionForm(user)
return context
def update_models(self, data):
data = data['filter_field']
- models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
- models['hosts'] = [] # This will always clear existing data when this step changes
+ models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
+ models['resources'] = [] # This will always clear existing data when this step changes
+ models['connections'] = []
models['interfaces'] = {}
- if "bundle" not in models:
- 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 = ResourceProfile.objects.get(id=id)
+ if "template" not in models:
+ template = ResourceTemplate.objects.create(temporary=True)
+ models['template'] = template
+
+ resource_data = data['resource']
+
+ new_template = models['template']
+
+ public_network = Network.objects.create(name="public", bundle=new_template, is_public=True)
+
+ all_networks = {public_network.id: public_network}
+
+ for resource_template_dict in resource_data.values():
+ id = resource_template_dict['id']
+ old_template = ResourceTemplate.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):
- raise InvalidHostnameException("Invalid hostname: '" + name + "'")
- if name in names:
- raise NonUniqueHostnameException("All hosts must have unique names")
- names[name] = True
- resourceConfig = ResourceConfiguration(profile=profile, template=models['bundle'])
- models['hosts'].append(resourceConfig)
- for interface_profile in profile.interfaceprofile.all():
- 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)
+ for _ in range(0, resource_template_dict['count']):
+ resource_configs = old_template.resourceConfigurations.all()
+ for config in resource_configs:
+ # need to save now for connections to refer to it later
+ new_config = ResourceConfiguration.objects.create(
+ profile=config.profile,
+ image=config.image,
+ name=config.name,
+ template=new_template)
+
+ for interface_config in config.interface_configs.all():
+ new_interface_config = InterfaceConfiguration.objects.create(
+ profile=interface_config.profile,
+ resource_config=new_config)
+
+ for connection in interface_config.connections.all():
+ network = None
+ if connection.network.is_public:
+ network = public_network
+ else:
+ # check if network is known
+ if connection.network.id not in all_networks:
+ # create matching one
+ new_network = Network(
+ name=connection.network.name + "_" + str(new_config.id),
+ bundle=new_template,
+ is_public=False)
+ new_network.save()
+
+ all_networks[connection.network.id] = new_network
+
+ network = all_networks[connection.network.id]
+
+ new_connection = NetworkConnection(
+ network=network,
+ vlan_is_tagged=connection.vlan_is_tagged)
+
+ new_interface_config.save() # can't do later because M2M on next line
+ new_connection.save()
+
+ new_interface_config.connections.add(new_connection)
+
+ unique_resource_ref = new_config.name + "_" + str(new_config.id)
+ if unique_resource_ref not in models['interfaces']:
+ models['interfaces'][unique_resource_ref] = []
+ models['interfaces'][unique_resource_ref].append(interface_config)
+
+ models['resources'].append(new_config)
+
+ models['networks'] = all_networks
# add selected lab to models
for lab_dict in data['lab'].values():
if lab_dict['selected']:
- models['bundle'].lab = Lab.objects.get(lab_user__id=lab_dict['id'])
+ models['template'].lab = Lab.objects.get(lab_user__id=lab_dict['id'])
+ models['template'].save()
break # if somehow we get two 'true' labs, we only use one
# return to repo
- self.repo_put(self.repo.GRESOURCE_BUNDLE_MODELS, models)
+ self.repo_put(self.repo.RESOURCE_TEMPLATE_MODELS, models)
def update_confirmation(self):
confirm = self.repo_get(self.repo.CONFIRMATION, {})
- if "resource" not in confirm:
- confirm['resource'] = {}
- confirm['resource']['hosts'] = []
- models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {"hosts": []})
- for host in models['hosts']:
- host_dict = {"name": host.resource.name, "profile": host.profile.name}
- confirm['resource']['hosts'].append(host_dict)
- if "lab" in models:
- confirm['resource']['lab'] = models['lab'].lab_user.username
+ if "template" not in confirm:
+ confirm['template'] = {}
+ confirm['template']['resources'] = []
+ models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
+ if 'template' in models:
+ for resource in models['template'].getConfigs():
+ host_dict = {"name": resource.name, "profile": resource.profile.name}
+ confirm['template']['resources'].append(host_dict)
+ if "template" in models:
+ confirm['template']['lab'] = models['template'].lab.lab_user.username
self.repo_put(self.repo.CONFIRMATION, confirm)
def post(self, post_data, user):
try:
- self.form = HardwareDefinitionForm(post_data)
+ user = self.repo_get(self.repo.SESSION_USER)
+ self.form = HardwareDefinitionForm(user, post_data)
if self.form.is_valid():
self.update_models(self.form.cleaned_data)
self.update_confirmation()
@@ -116,9 +169,107 @@ class Define_Hardware(WorkflowStep):
else:
self.set_invalid("Please complete the fields highlighted in red to continue")
except Exception as e:
+ print("Caught exception: " + str(e))
+ traceback.print_exc()
self.set_invalid(str(e))
+class Define_Software(WorkflowStep):
+ template = 'config_bundle/steps/define_software.html'
+ title = "Pick Software"
+ description = "Choose the opnfv and image of your machines"
+ short_title = "host config"
+
+ def build_filter_data(self, hosts_data):
+ """
+ Build list of Images to filter out.
+
+ returns a 2D array of images to exclude
+ based on the ordering of the passed
+ hosts_data
+ """
+
+ filter_data = []
+ user = self.repo_get(self.repo.SESSION_USER)
+ lab = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS)['template'].lab
+ for i, host_data in enumerate(hosts_data):
+ 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)
+ excluded_images = wrong_owner | wrong_host | wrong_lab
+ filter_data.append([])
+ for image in excluded_images:
+ filter_data[i].append(image.pk)
+ return filter_data
+
+ def create_hostformset(self, hostlist, data=None):
+ hosts_initial = []
+ configs = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {}).get("resources")
+ if configs:
+ for config in configs:
+ hosts_initial.append({
+ 'host_id': config.id,
+ 'host_name': config.name,
+ 'headnode': config.is_head_node,
+ 'image': config.image
+ })
+ else:
+ for host in hostlist:
+ hosts_initial.append({
+ 'host_id': host.id,
+ 'host_name': host.name
+ })
+
+ HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0)
+ filter_data = self.build_filter_data(hosts_initial)
+
+ class SpecialHostFormset(HostFormset):
+ def get_form_kwargs(self, index):
+ kwargs = super(SpecialHostFormset, self).get_form_kwargs(index)
+ if index is not None:
+ kwargs['imageQS'] = Image.objects.exclude(pk__in=filter_data[index])
+ return kwargs
+
+ if data:
+ return SpecialHostFormset(data, initial=hosts_initial)
+ return SpecialHostFormset(initial=hosts_initial)
+
+ def get_host_list(self, grb=None):
+ return self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS).get("resources")
+
+ def get_context(self):
+ context = super(Define_Software, self).get_context()
+
+ context["formset"] = self.create_hostformset(self.get_host_list())
+
+ return context
+
+ def post(self, post_data, user):
+ hosts = self.get_host_list()
+
+ # TODO: fix headnode in form, currently doesn't return a selected one
+ # models['headnode_index'] = post_data.get("headnode", 1)
+ formset = self.create_hostformset(hosts, data=post_data)
+ has_headnode = False
+ if formset.is_valid():
+ for i, form in enumerate(formset):
+ host = hosts[i]
+ image = form.cleaned_data['image']
+ hostname = form.cleaned_data['host_name']
+ headnode = form.cleaned_data['headnode']
+ if headnode:
+ has_headnode = True
+ host.is_head_node = headnode
+ host.name = hostname
+ host.image = image
+ host.save()
+
+ self.set_valid("Completed")
+ else:
+ self.set_invalid("Please complete all fields")
+
+
class Define_Nets(WorkflowStep):
template = 'resource/steps/pod_definition.html'
title = "Define Networks"
@@ -131,7 +282,7 @@ class Define_Nets(WorkflowStep):
if vlans:
return vlans
# try to grab some vlans from lab
- models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
if "bundle" not in models:
return None
lab = models['bundle'].lab
@@ -144,12 +295,46 @@ class Define_Nets(WorkflowStep):
except Exception:
return None
+ def make_mx_network_dict(self, network):
+ return {
+ 'id': network.id,
+ 'name': network.name,
+ 'public': network.is_public
+ }
+
+ def make_mx_resource_dict(self, resource_config):
+ resource_dict = {
+ 'id': resource_config.id,
+ 'interfaces': [],
+ 'value': {
+ 'name': resource_config.name,
+ 'id': resource_config.id,
+ 'description': resource_config.profile.description
+ }
+ }
+
+ for interface_config in resource_config.interface_configs.all():
+ connections = []
+ for connection in interface_config.connections.all():
+ connections.append({'tagged': connection.vlan_is_tagged, 'network': connection.network.id})
+
+ interface_dict = {
+ "id": interface_config.id,
+ "name": interface_config.profile.name,
+ "description": "speed: " + str(interface_config.profile.speed) + "M\ntype: " + interface_config.profile.nic_type,
+ "connections": connections
+ }
+
+ resource_dict['interfaces'].append(interface_dict)
+
+ return resource_dict
+
def make_mx_host_dict(self, generic_host):
host = {
- 'id': generic_host.resource.name,
+ 'id': generic_host.profile.name,
'interfaces': [],
'value': {
- "name": generic_host.resource.name,
+ "name": generic_host.profile.name,
"description": generic_host.profile.description
}
}
@@ -160,50 +345,34 @@ class Define_Nets(WorkflowStep):
})
return host
+ # first step guards this one, so can't get here without at least empty
+ # models being populated by step one
def get_context(self):
context = super(Define_Nets, self).get_context()
context.update({
'form': NetworkDefinitionForm(),
'debug': settings.DEBUG,
+ 'resources': {},
+ 'networks': {},
+ 'vlans': [],
+ # remove others
'hosts': [],
'added_hosts': [],
'removed_hosts': []
})
- vlans = self.get_vlans()
- if vlans:
- context['vlans'] = vlans
- try:
- models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
- hosts = models.get("hosts", [])
- # calculate if the selected hosts have changed
- added_hosts = set()
- host_set = set(self.repo_get(self.repo.GRB_LAST_HOSTLIST, []))
- if len(host_set):
- new_host_set = set([h.resource.name + "*" + h.profile.name for h in models['hosts']])
- context['removed_hosts'] = [h.split("*")[0] for h in (host_set - new_host_set)]
- added_hosts.update([h.split("*")[0] for h in (new_host_set - host_set)])
-
- # add all host info to context
- for generic_host in hosts:
- host = self.make_mx_host_dict(generic_host)
- host_serialized = json.dumps(host)
- context['hosts'].append(host_serialized)
- if host['id'] in added_hosts:
- context['added_hosts'].append(host_serialized)
- bundle = models.get("bundle", False)
- if bundle:
- context['xml'] = bundle.xml or False
- except Exception:
- pass
+ models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS) # infallible, guarded by prior step
+ for resource in models['resources']:
+ d = self.make_mx_resource_dict(resource)
+ context['resources'][d['id']] = d
+
+ for network in models['networks'].values():
+ d = self.make_mx_network_dict(network)
+ context['networks'][d['id']] = d
return context
def post(self, post_data, user):
- models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
- if 'hosts' in models:
- host_set = set([h.resource.name + "*" + h.profile.name for h in models['hosts']])
- self.repo_put(self.repo.GRB_LAST_HOSTLIST, host_set)
try:
xmlData = post_data.get("xml")
self.updateModels(xmlData)
@@ -212,42 +381,59 @@ class Define_Nets(WorkflowStep):
except ResourceAvailabilityException:
self.set_invalid("Public network not availble")
except Exception as e:
+ traceback.print_exc()
self.set_invalid("An error occurred when applying networks: " + str(e))
+ def resetNetworks(self, networks: List[Network]): # potentially just pass template here?
+ for network in networks:
+ network.delete()
+
def updateModels(self, xmlData):
- models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
- models["connections"] = {}
- models['networks'] = {}
- given_hosts, interfaces, networks = self.parseXml(xmlData)
- existing_host_list = models.get("hosts", [])
- existing_hosts = {} # maps id to host
- for host in existing_host_list:
- existing_hosts[host.resource.name] = host
+ models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
+ given_hosts = None
+ interfaces = None
+ networks = None
+ try:
+ given_hosts, interfaces, networks = self.parseXml(xmlData)
+ except Exception as e:
+ print("tried to parse Xml, got exception instead:")
+ print(e)
+
+ existing_rconfig_list = models.get("resources", [])
+ existing_rconfigs = {} # maps id to host
+ for rconfig in existing_rconfig_list:
+ existing_rconfigs["host_" + str(rconfig.id)] = rconfig
- bundle = models.get("bundle", ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER)))
+ bundle = models.get("template") # hard fail if not in repo
+
+ self.resetNetworks(models['networks'].values())
+ models['networks'] = {}
for net_id, net in networks.items():
- network = Network()
- network.name = net['name']
- network.bundle = bundle
- network.is_public = net['public']
+ network = Network.objects.create(
+ name=net['name'],
+ bundle=bundle,
+ is_public=net['public'])
+
models['networks'][net_id] = network
+ network.save()
for hostid, given_host in given_hosts.items():
- existing_host = existing_hosts[hostid[5:]]
-
for ifaceId in given_host['interfaces']:
iface = interfaces[ifaceId]
- if existing_host.resource.name not in models['connections']:
- models['connections'][existing_host.resource.name] = {}
- models['connections'][existing_host.resource.name][iface['profile_name']] = []
+
+ iface_config = InterfaceConfiguration.objects.get(id=iface['config_id'])
+ if iface_config.resource_config.template.id != bundle.id:
+ raise ValidationError("User does not own the template they are editing")
+
for connection in iface['connections']:
network_id = connection['network']
net = models['networks'][network_id]
connection = NetworkConnection(vlan_is_tagged=connection['tagged'], network=net)
- models['connections'][existing_host.resource.name][iface['profile_name']].append(connection)
- bundle.xml = xmlData
- self.repo_put(self.repo.GRESOURCE_BUNDLE_MODELS, models)
+ connection.save()
+ iface_config.connections.add(connection)
+ iface_config.save()
+ self.repo_put(self.repo.RESOURCE_TEMPLATE_MODELS, models)
def decomposeXml(self, xmlString):
"""
@@ -303,7 +489,7 @@ class Define_Nets(WorkflowStep):
for cellId, cell in xml_hosts.items():
cell_json_str = cell.getAttribute("value")
cell_json = json.loads(cell_json_str)
- host = {"interfaces": [], "name": cellId, "profile_name": cell_json['name']}
+ host = {"interfaces": [], "name": cellId, "hostname": cell_json['name']}
hosts[cellId] = host
# parse networks
@@ -324,7 +510,7 @@ class Define_Nets(WorkflowStep):
parentId = cell.getAttribute('parent')
cell_json_str = cell.getAttribute("value")
cell_json = json.loads(cell_json_str)
- iface = {"name": cellId, "connections": [], "profile_name": cell_json['name']}
+ iface = {"graph_id": cellId, "connections": [], "config_id": cell_json['id'], "profile_name": cell_json['name']}
hosts[parentId]['interfaces'].append(cellId)
interfaces[cellId] = iface
@@ -346,9 +532,9 @@ class Define_Nets(WorkflowStep):
network = networks[xml_ports[src]]
if not tagged:
- if interface['name'] in untagged_ifaces:
+ if interface['config_id'] in untagged_ifaces:
raise InvalidVlanConfigurationException("More than one untagged vlan on an interface")
- untagged_ifaces.add(interface['name'])
+ untagged_ifaces.add(interface['config_id'])
# add connection to interface
interface['connections'].append({"tagged": tagged, "network": network['id']})
@@ -362,12 +548,23 @@ class Resource_Meta_Info(WorkflowStep):
description = "Please fill out the rest of the information about your resource"
short_title = "pod info"
+ def update_confirmation(self):
+ confirm = self.repo_get(self.repo.CONFIRMATION, {})
+ if "template" not in confirm:
+ confirm['template'] = {}
+ models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
+ if "template" in models:
+ confirm['template']['description'] = models['template'].description
+ confirm['template']['name'] = models['template'].name
+ self.repo_put(self.repo.CONFIRMATION, confirm)
+
def get_context(self):
context = super(Resource_Meta_Info, self).get_context()
name = ""
desc = ""
- bundle = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("bundle", False)
- if bundle and bundle.name:
+ models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, None)
+ bundle = models['template']
+ if bundle:
name = bundle.name
desc = bundle.description
context['form'] = ResourceMetaForm(initial={"bundle_name": name, "bundle_description": desc})
@@ -376,14 +573,14 @@ class Resource_Meta_Info(WorkflowStep):
def post(self, post_data, user):
form = ResourceMetaForm(post_data)
if form.is_valid():
- models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
+ models = self.repo_get(self.repo.RESOURCE_TEMPLATE_MODELS, {})
name = form.cleaned_data['bundle_name']
desc = form.cleaned_data['bundle_description']
- bundle = models.get("bundle", ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER)))
+ bundle = models['template'] # infallible
bundle.name = name
bundle.description = desc
- models['bundle'] = bundle
- self.repo_put(self.repo.GRESOURCE_BUNDLE_MODELS, models)
+ bundle.save()
+ self.repo_put(self.repo.RESOURCE_TEMPLATE_MODELS, models)
confirm = self.repo_get(self.repo.CONFIRMATION)
if "resource" not in confirm:
confirm['resource'] = {}
diff --git a/src/workflow/sw_bundle_workflow.py b/src/workflow/sw_bundle_workflow.py
deleted file mode 100644
index 686f46f..0000000
--- a/src/workflow/sw_bundle_workflow.py
+++ /dev/null
@@ -1,196 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from django.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, ResourceConfiguration, ResourceTemplate
-
-
-class SWConf_Resource_Select(Abstract_Resource_Select):
- workflow_type = "configuration"
-
-
-class Define_Software(WorkflowStep):
- template = 'config_bundle/steps/define_software.html'
- title = "Pick Software"
- description = "Choose the opnfv and image of your machines"
- short_title = "host config"
-
- def build_filter_data(self, hosts_data):
- """
- Build list of Images to filter out.
-
- returns a 2D array of images to exclude
- based on the ordering of the passed
- hosts_data
- """
- filter_data = []
- 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 = 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)
- excluded_images = wrong_owner | wrong_host | wrong_lab
- filter_data.append([])
- for image in excluded_images:
- filter_data[i].append(image.pk)
- return filter_data
-
- def create_hostformset(self, hostlist, data=None):
- hosts_initial = []
- host_configs = self.repo_get(self.repo.CONFIG_MODELS, {}).get("host_configs", False)
- if host_configs:
- for config in host_configs:
- hosts_initial.append({
- 'host_id': config.host.id,
- 'host_name': config.host.resource.name,
- 'headnode': config.is_head_node,
- 'image': config.image
- })
- else:
- for host in hostlist:
- hosts_initial.append({
- 'host_id': host.id,
- 'host_name': host.resource.name
- })
-
- HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0)
- filter_data = self.build_filter_data(hosts_initial)
-
- class SpecialHostFormset(HostFormset):
- def get_form_kwargs(self, index):
- kwargs = super(SpecialHostFormset, self).get_form_kwargs(index)
- if index is not None:
- kwargs['imageQS'] = Image.objects.exclude(pk__in=filter_data[index])
- return kwargs
-
- if data:
- return SpecialHostFormset(data, initial=hosts_initial)
- return SpecialHostFormset(initial=hosts_initial)
-
- def get_host_list(self, grb=None):
- if grb is None:
- grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
- if not grb:
- return []
- if grb.id:
- return ResourceConfiguration.objects.filter(resource__bundle=grb)
- generic_hosts = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("hosts", [])
- return generic_hosts
-
- def get_context(self):
- context = super(Define_Software, self).get_context()
-
- grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
-
- if grb:
- context["grb"] = grb
- formset = self.create_hostformset(self.get_host_list(grb))
- context["formset"] = formset
- context['headnode'] = self.repo_get(self.repo.CONFIG_MODELS, {}).get("headnode_index", 1)
- else:
- context["error"] = "Please select a resource first"
- self.set_invalid("Step requires information that is not yet provided by previous step")
-
- return context
-
- def post(self, post_data, user):
- models = self.repo_get(self.repo.CONFIG_MODELS, {})
- if "bundle" not in models:
- models['bundle'] = ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER))
-
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
-
- hosts = self.get_host_list()
- models['headnode_index'] = post_data.get("headnode", 1)
- formset = self.create_hostformset(hosts, data=post_data)
- has_headnode = False
- if formset.is_valid():
- models['host_configs'] = []
- confirm_hosts = []
- for i, form in enumerate(formset):
- host = hosts[i]
- image = form.cleaned_data['image']
- headnode = form.cleaned_data['headnode']
- if headnode:
- has_headnode = True
- bundle = models['bundle']
- hostConfig = ResourceConfiguration(
- host=host,
- image=image,
- bundle=bundle,
- is_head_node=headnode
- )
- models['host_configs'].append(hostConfig)
- confirm_hosts.append({
- "name": host.resource.name,
- "image": image.name,
- "headnode": headnode
- })
-
- if not has_headnode:
- self.set_invalid('Must have one "Headnode" per POD')
- return
-
- self.repo_put(self.repo.CONFIG_MODELS, models)
- if "configuration" not in confirm:
- confirm['configuration'] = {}
- confirm['configuration']['hosts'] = confirm_hosts
- self.repo_put(self.repo.CONFIRMATION, confirm)
- self.set_valid("Completed")
- else:
- self.set_invalid("Please complete all fields")
-
-
-class Config_Software(WorkflowStep):
- template = 'config_bundle/steps/config_software.html'
- title = "Other Info"
- description = "Give your software config a name, description, and other stuff"
- short_title = "config info"
-
- def get_context(self):
- context = super(Config_Software, self).get_context()
-
- initial = {}
- models = self.repo_get(self.repo.CONFIG_MODELS, {})
- bundle = models.get("bundle", False)
- if bundle:
- initial['name'] = bundle.name
- initial['description'] = bundle.description
- context["form"] = BasicMetaForm(initial=initial)
- return context
-
- def post(self, post_data, user):
- models = self.repo_get(self.repo.CONFIG_MODELS, {})
- if "bundle" not in models:
- models['bundle'] = ResourceTemplate(owner=self.repo_get(self.repo.SESSION_USER))
-
- confirm = self.repo_get(self.repo.CONFIRMATION, {})
- if "configuration" not in confirm:
- confirm['configuration'] = {}
-
- form = BasicMetaForm(post_data)
- if form.is_valid():
- models['bundle'].name = form.cleaned_data['name']
- models['bundle'].description = form.cleaned_data['description']
-
- confirm['configuration']['name'] = form.cleaned_data['name']
- confirm['configuration']['description'] = form.cleaned_data['description']
- self.set_valid("Complete")
- else:
- self.set_invalid("Please correct the errors shown below")
-
- self.repo_put(self.repo.CONFIG_MODELS, models)
- self.repo_put(self.repo.CONFIRMATION, confirm)
diff --git a/src/workflow/tests/test_steps.py b/src/workflow/tests/test_steps.py
index 6101d4f..57bf6a3 100644
--- a/src/workflow/tests/test_steps.py
+++ b/src/workflow/tests/test_steps.py
@@ -180,7 +180,7 @@ class SoftwareSelectTestCase(SelectStepTestCase):
def add_to_repo(self, repo):
repo.el[repo.SESSION_USER] = self.user
- repo.el[repo.SELECTED_GRESOURCE_BUNDLE] = self.conf.grb
+ repo.el[repo.SELECTED_RESOURCE_TEMPLATE] = self.conf.grb
@classmethod
def setUpTestData(cls):
@@ -253,7 +253,7 @@ class DefineSoftwareTestCase(StepTestCase):
}
def add_to_repo(self, repo):
- repo.el[repo.SELECTED_GRESOURCE_BUNDLE] = self.conf.grb
+ repo.el[repo.SELECTED_RESOURCE_TEMPLATE] = self.conf.grb
@classmethod
def setUpTestData(cls):
diff --git a/src/workflow/views.py b/src/workflow/views.py
index 9ff444d..9666d72 100644
--- a/src/workflow/views.py
+++ b/src/workflow/views.py
@@ -35,7 +35,7 @@ def remove_workflow(request):
if not manager:
return no_workflow(request)
- has_more_workflows, result = manager.pop_workflow()
+ has_more_workflows, result = manager.pop_workflow(discard=True)
if not has_more_workflows: # this was the last workflow, so delete the reference to it in the tracker
del ManagerTracker.managers[request.session['manager_session']]
diff --git a/src/workflow/workflow_factory.py b/src/workflow/workflow_factory.py
index 03c8126..04ed280 100644
--- a/src/workflow/workflow_factory.py
+++ b/src/workflow/workflow_factory.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
+# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, Sean Smith, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -9,8 +9,7 @@
from workflow.booking_workflow import Booking_Resource_Select, SWConfig_Select, Booking_Meta, OPNFV_Select
-from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info
-from workflow.sw_bundle_workflow import Config_Software, Define_Software, SWConf_Resource_Select
+from workflow.resource_bundle_workflow import Define_Hardware, Define_Nets, Resource_Meta_Info, Define_Software
from workflow.snapshot_workflow import Select_Host_Step, Image_Meta_Step
from workflow.opnfv_workflow import Pick_Installer, Assign_Network_Roles, Assign_Host_Roles, OPNFV_Resource_Select, MetaInfo
from workflow.models import Confirmation_Step
@@ -81,16 +80,11 @@ class WorkflowFactory():
resource_steps = [
Define_Hardware,
+ Define_Software,
Define_Nets,
Resource_Meta_Info,
]
- config_steps = [
- SWConf_Resource_Select,
- Define_Software,
- Config_Software,
- ]
-
snapshot_steps = [
Select_Host_Step,
Image_Meta_Step,
@@ -108,7 +102,6 @@ class WorkflowFactory():
workflow_types = [
self.booking_steps,
self.resource_steps,
- self.config_steps,
self.snapshot_steps,
self.opnfv_steps,
]
diff --git a/src/workflow/workflow_manager.py b/src/workflow/workflow_manager.py
index e31e14c..a48efe5 100644
--- a/src/workflow/workflow_manager.py
+++ b/src/workflow/workflow_manager.py
@@ -66,7 +66,7 @@ class SessionManager():
return reverse('booking:booking_detail', kwargs={'booking_id': self.result.id})
return "/"
- def pop_workflow(self):
+ def pop_workflow(self, discard=False):
multiple_wfs = len(self.workflows) > 1
if multiple_wfs:
if self.workflows[-1].repository.el[Repository.RESULT]: # move result
@@ -79,6 +79,8 @@ class SessionManager():
else:
current_repo = prev_workflow.repository
self.result = current_repo.el[current_repo.RESULT]
+ if discard:
+ current_repo.cancel()
return multiple_wfs, self.result
def status(self, request):
@@ -164,14 +166,14 @@ class SessionManager():
confirmation = self.make_booking_confirm(booking)
self.active_workflow().repository.el[self.active_workflow().repository.BOOKING_MODELS] = models
self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirmation
- self.active_workflow().repository.el[self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS] = self.make_grb_models(booking.resource.template)
- self.active_workflow().repository.el[self.active_workflow().repository.SELECTED_GRESOURCE_BUNDLE] = self.make_grb_models(booking.resource.template)['bundle']
+ self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = self.make_grb_models(booking.resource.template)
+ self.active_workflow().repository.el[self.active_workflow().repository.SELECTED_RESOURCE_TEMPLATE] = self.make_grb_models(booking.resource.template)['bundle']
self.active_workflow().repository.el[self.active_workflow().repository.CONFIG_MODELS] = self.make_config_models(booking.config_bundle)
def prefill_resource(self, resource):
models = self.make_grb_models(resource)
confirm = self.make_grb_confirm(resource)
- self.active_workflow().repository.el[self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS] = models
+ self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = models
self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirm
def prefill_config(self, config):
@@ -180,10 +182,10 @@ class SessionManager():
self.active_workflow().repository.el[self.active_workflow().repository.CONFIG_MODELS] = models
self.active_workflow().repository.el[self.active_workflow().repository.CONFIRMATION] = confirm
grb_models = self.make_grb_models(config.bundle)
- self.active_workflow().repository.el[self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS] = grb_models
+ self.active_workflow().repository.el[self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS] = grb_models
def make_grb_models(self, resource):
- models = self.active_workflow().repository.el.get(self.active_workflow().repository.GRESOURCE_BUNDLE_MODELS, {})
+ models = self.active_workflow().repository.el.get(self.active_workflow().repository.RESOURCE_TEMPLATE_MODELS, {})
models['hosts'] = []
models['bundle'] = resource
models['interfaces'] = {}