From 8864dae63b9512835862aabbe7f288fbe3c661e0 Mon Sep 17 00:00:00 2001 From: Sawyer Bergeron Date: Tue, 9 Apr 2019 16:30:57 -0400 Subject: Implement OPNFV workflow This is a counterpart to an update to network models, and allows for configuring baremetal OPNFV and Openstack deploys Change-Id: I0185dbfa6c9105d7e63a7e7d7dd1f5cf228a8877 Signed-off-by: Sawyer Bergeron Signed-off-by: Parker Berberian --- dashboard/src/workflow/forms.py | 53 +++-- dashboard/src/workflow/models.py | 61 ++++- dashboard/src/workflow/opnfv_workflow.py | 327 +++++++++++++++++++++++++++ dashboard/src/workflow/snapshot_workflow.py | 8 +- dashboard/src/workflow/sw_bundle_workflow.py | 193 +++++++--------- dashboard/src/workflow/workflow_factory.py | 15 ++ 6 files changed, 522 insertions(+), 135 deletions(-) create mode 100644 dashboard/src/workflow/opnfv_workflow.py (limited to 'dashboard/src/workflow') diff --git a/dashboard/src/workflow/forms.py b/dashboard/src/workflow/forms.py index b40713f..6d26b5c 100644 --- a/dashboard/src/workflow/forms.py +++ b/dashboard/src/workflow/forms.py @@ -20,9 +20,8 @@ from resource_inventory.models import ( GenericResourceBundle, ConfigBundle, OPNFVRole, - Image, Installer, - Scenario + Scenario, ) @@ -125,6 +124,7 @@ class SWConfigSelectorForm(forms.Form): bundle = None edit = False resource = None + user = None if "chosen_software" in kwargs: chosen_software = kwargs.pop("chosen_software") @@ -134,18 +134,25 @@ class SWConfigSelectorForm(forms.Form): edit = kwargs.pop("edit") if "resource" in kwargs: resource = kwargs.pop("resource") + if "user" in kwargs: + user = kwargs.pop("user") super(SWConfigSelectorForm, self).__init__(*args, **kwargs) - attrs = self.build_search_widget_attrs(chosen_software, bundle, edit, resource) + attrs = self.build_search_widget_attrs(chosen_software, bundle, edit, resource, user) self.fields['software_bundle'] = forms.CharField( widget=SearchableSelectMultipleWidget(attrs=attrs) ) - def build_search_widget_attrs(self, chosen, bundle, edit, resource): + def build_search_widget_attrs(self, chosen, bundle, edit, resource, user): configs = {} queryset = ConfigBundle.objects.select_related('owner').all() if resource: + if user is None: + user = resource.owner queryset = queryset.filter(bundle=resource) + if user: + queryset = queryset.filter(owner=user) + for config in queryset: displayable = {} displayable['small_name'] = config.name @@ -424,20 +431,14 @@ class NetworkConfigurationForm(forms.Form): class HostSoftwareDefinitionForm(forms.Form): - fields = ["host_name", "role", "image"] host_name = forms.CharField(max_length=200, disabled=True, required=False) - role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all()) - image = forms.ModelChoiceField(queryset=Image.objects.all()) - + headnode = forms.BooleanField(required=False, widget=forms.HiddenInput) -class SoftwareConfigurationForm(forms.Form): - - name = forms.CharField(max_length=200) - description = forms.CharField(widget=forms.Textarea) - opnfv = forms.BooleanField(disabled=True, required=False) - installer = forms.ModelChoiceField(queryset=Installer.objects.all(), disabled=True, required=False) - scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), disabled=True, required=False) + def __init__(self, *args, **kwargs): + imageQS = kwargs.pop("imageQS") + super(HostSoftwareDefinitionForm, self).__init__(*args, **kwargs) + self.fields['image'] = forms.ModelChoiceField(queryset=imageQS) class WorkflowSelectionForm(forms.Form): @@ -461,7 +462,7 @@ class SnapshotHostSelectForm(forms.Form): host = forms.CharField() -class SnapshotMetaForm(forms.Form): +class BasicMetaForm(forms.Form): name = forms.CharField() description = forms.CharField(widget=forms.Textarea) @@ -475,3 +476,23 @@ class ConfirmationForm(forms.Form): (False, "Cancel") ) ) + + +class OPNFVSelectionForm(forms.Form): + installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=True) + scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=True) + + +class OPNFVNetworkRoleForm(forms.Form): + role = forms.CharField(max_length=200, disabled=True, required=False) + + def __init__(self, *args, config_bundle, **kwargs): + super(OPNFVNetworkRoleForm, self).__init__(*args, **kwargs) + self.fields['network'] = forms.ModelChoiceField( + queryset=config_bundle.bundle.networks.all() + ) + + +class OPNFVHostRoleForm(forms.Form): + host_name = forms.CharField(max_length=200, disabled=True, required=False) + role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all().order_by("name").distinct("name")) diff --git a/dashboard/src/workflow/models.py b/dashboard/src/workflow/models.py index 4ebb042..bf5751d 100644 --- a/dashboard/src/workflow/models.py +++ b/dashboard/src/workflow/models.py @@ -19,7 +19,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, GenericInterface +from resource_inventory.models import Image, GenericInterface, OPNFVConfig, HostOPNFVConfig, NetworkRole from resource_inventory.resource_manager import ResourceManager from resource_inventory.pdf_templater import PDFTemplater from notifier.manager import NotificationHandler @@ -259,6 +259,7 @@ class Repository(): CONFIRMATION = "confirmation" SELECTED_GRESOURCE_BUNDLE = "selected generic bundle 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" BOOKING = "booking" @@ -268,6 +269,7 @@ class Repository(): SWCONF_HOSTS = "swconf_hosts" BOOKING_MODELS = "booking models" CONFIG_MODELS = "configuration bundle models" + OPNFV_MODELS = "opnfv configuration models" SESSION_USER = "session owner user account" VALIDATED_MODEL_GRB = "valid grb config model instance in db" VALIDATED_MODEL_CONFIG = "valid config model instance in db" @@ -339,6 +341,14 @@ class Repository(): self.el[self.RESULT_KEY] = self.SELECTED_CONFIG_BUNDLE return + if self.OPNFV_MODELS in self.el: + errors = self.make_opnfv_config() + if errors: + return errors + else: + self.el[self.HAS_RESULT] = True + self.el[self.RESULT_KEY] = self.SELECTED_OPNFV_CONFIG + if self.BOOKING_MODELS in self.el: errors = self.make_booking() if errors: @@ -536,7 +546,7 @@ class Repository(): booking.collaborators.add(collaborator) try: - booking.pdf = PDFTemplater.makePDF(booking.resource) + booking.pdf = PDFTemplater.makePDF(booking) booking.save() except Exception as e: return "BOOK, failed to create Pod Desriptor File: " + str(e) @@ -551,6 +561,53 @@ class Repository(): except Exception as e: return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016" + def make_opnfv_config(self): + opnfv_models = self.el[self.OPNFV_MODELS] + config_bundle = opnfv_models['configbundle'] + if not config_bundle: + return "No Configuration bundle selected" + info = opnfv_models.get("meta", {}) + name = info.get("name", False) + desc = info.get("description", False) + if not (name and desc): + return "No name or description given" + installer = opnfv_models['installer_chosen'] + if not installer: + return "No OPNFV Installer chosen" + scenario = opnfv_models['scenario_chosen'] + if not scenario: + return "No OPNFV Scenario chosen" + + opnfv_config = OPNFVConfig.objects.create( + bundle=config_bundle, + name=name, + description=desc, + installer=installer, + scenario=scenario + ) + + network_roles = opnfv_models['network_roles'] + for net_role in network_roles: + opnfv_config.networks.add( + NetworkRole.objects.create( + name=net_role['role'], + network=net_role['network'] + ) + ) + + host_roles = opnfv_models['host_roles'] + for host_role in host_roles: + config = config_bundle.hostConfigurations.get( + host__resource__name=host_role['host_name'] + ) + HostOPNFVConfig.objects.create( + role=host_role['role'], + host_config=config, + opnfv_config=opnfv_config + ) + + self.el[self.RESULT] = opnfv_config + def __init__(self): self.el = {} self.el[self.CONFIRMATION] = {} diff --git a/dashboard/src/workflow/opnfv_workflow.py b/dashboard/src/workflow/opnfv_workflow.py new file mode 100644 index 0000000..26e1d7c --- /dev/null +++ b/dashboard/src/workflow/opnfv_workflow.py @@ -0,0 +1,327 @@ +############################################################################## +# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +from django.forms import formset_factory +from django.contrib import messages + +import json + +from workflow.models import WorkflowStep +from resource_inventory.models import ConfigBundle, OPNFV_SETTINGS +from workflow.forms import OPNFVSelectionForm, OPNFVNetworkRoleForm, OPNFVHostRoleForm, SWConfigSelectorForm, BasicMetaForm + + +class OPNFV_Resource_Select(WorkflowStep): + template = 'booking/steps/swconfig_select.html' + title = "Select Software Configuration" + description = "Choose the software and related configurations you want to use to configure OPNFV" + short_title = "software configuration" + modified_key = "configbundle_step" + + def update_confirmation(self): + confirm = self.repo_get(self.repo.CONFIRMATION, {}) + config_bundle = self.repo_get(self.repo.OPNFV_MODELS, {}).get("configbundle") + if not config_bundle: + return + confirm['software bundle'] = config_bundle.name + confirm['hardware POD'] = config_bundle.bundle.name + self.repo_put(self.repo.CONFIRMATION, confirm) + + def post_render(self, request): + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + form = SWConfigSelectorForm(request.POST) + if form.is_valid(): + bundle_json = form.cleaned_data['software_bundle'] + bundle_json = bundle_json[2:-2] # Stupid django string bug + if not bundle_json: + self.metastep.set_invalid("Please select a valid config") + return self.render(request) + bundle_json = json.loads(bundle_json) + if len(bundle_json) < 1: + self.metastep.set_invalid("Please select a valid config") + return self.render(request) + bundle = None + id = int(bundle_json[0]['id']) + bundle = ConfigBundle.objects.get(id=id) + + models['configbundle'] = bundle + self.repo_put(self.repo.OPNFV_MODELS, models) + self.metastep.set_valid("Step Completed") + messages.add_message(request, messages.SUCCESS, 'Form Validated Successfully', fail_silently=True) + self.update_confirmation() + else: + self.metastep.set_invalid("Please select or create a valid config") + messages.add_message(request, messages.ERROR, "Form Didn't Validate", fail_silently=True) + + return self.render(request) + + def get_context(self): + context = super(OPNFV_Resource_Select, self).get_context() + default = [] + user = self.repo_get(self.repo.SESSION_USER) + + context['form'] = SWConfigSelectorForm(chosen_software=default, bundle=None, edit=True, resource=None, user=user) + return context + + +class Pick_Installer(WorkflowStep): + template = 'config_bundle/steps/pick_installer.html' + title = 'Pick OPNFV Installer' + description = 'Choose which OPNFV installer to use' + short_title = "opnfv installer" + modified_key = "installer_step" + + def update_confirmation(self): + confirm = self.repo_get(self.repo.CONFIRMATION, {}) + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + installer = models.get("installer_chosen") + scenario = models.get("scenario_chosen") + if not (installer and scenario): + return + confirm['installer'] = installer.name + confirm['scenario'] = scenario.name + self.repo_put(self.repo.CONFIRMATION, confirm) + + def get_context(self): + context = super(Pick_Installer, self).get_context() + + models = self.repo_get(self.repo.OPNFV_MODELS, None) + initial = { + "installer": models.get("installer_chosen"), + "scenario": models.get("scenario_chosen") + } + + context["form"] = OPNFVSelectionForm(initial=initial) + return context + + def post_render(self, request): + form = OPNFVSelectionForm(request.POST) + if form.is_valid(): + installer = form.cleaned_data['installer'] + scenario = form.cleaned_data['scenario'] + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + models['installer_chosen'] = installer + models['scenario_chosen'] = scenario + self.repo_put(self.repo.OPNFV_MODELS, models) + self.update_confirmation() + self.metastep.set_valid("Step Completed") + else: + self.metastep.set_invalid("Please select an Installer and Scenario") + + return self.render(request) + + +class Assign_Network_Roles(WorkflowStep): + template = 'config_bundle/steps/assign_network_roles.html' + title = 'Pick Network Roles' + description = 'Choose what role each network should get' + short_title = "network roles" + modified_key = "net_roles_step" + + """ + to do initial filling, repo should have a "network_roles" array with the following structure for each element: + { + "role": , + "network": + } + """ + def create_netformset(self, roles, config_bundle, data=None): + roles_initial = [] + set_roles = self.repo_get(self.repo.OPNFV_MODELS, {}).get("network_roles") + if set_roles: + roles_initial = set_roles + else: + for role in OPNFV_SETTINGS.NETWORK_ROLES: + roles_initial.append({"role": role}) + + Formset = formset_factory(OPNFVNetworkRoleForm, extra=0) + kwargs = { + "initial": roles_initial, + "form_kwargs": {"config_bundle": config_bundle} + } + formset = None + if data: + formset = Formset(data, **kwargs) + else: + formset = Formset(**kwargs) + return formset + + def get_context(self): + context = super(Assign_Network_Roles, self).get_context() + config_bundle = self.repo_get(self.repo.OPNFV_MODELS, {}).get("configbundle") + if config_bundle is None: + context["unavailable"] = True + return context + + roles = OPNFV_SETTINGS.NETWORK_ROLES + formset = self.create_netformset(roles, config_bundle) + context['formset'] = formset + + return context + + def update_confirmation(self): + confirm = self.repo_get(self.repo.CONFIRMATION, {}) + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + roles = models.get("network_roles") + if not roles: + return + confirm['network roles'] = {} + for role in roles: + confirm['network roles'][role['role']] = role['network'].name + self.repo_put(self.repo.CONFIRMATION, confirm) + + def post_render(self, request): + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + config_bundle = models.get("configbundle") + roles = OPNFV_SETTINGS.NETWORK_ROLES + net_role_formset = self.create_netformset(roles, config_bundle, data=request.POST) + if net_role_formset.is_valid(): + results = [] + for form in net_role_formset: + results.append({ + "role": form.cleaned_data['role'], + "network": form.cleaned_data['network'] + }) + models['network_roles'] = results + self.metastep.set_valid("Completed") + self.repo_put(self.repo.OPNFV_MODELS, models) + self.update_confirmation() + else: + self.metastep.set_invalid("Please complete all fields") + return self.render(request) + + +class Assign_Host_Roles(WorkflowStep): # taken verbatim from Define_Software in sw workflow, merge the two? + template = 'config_bundle/steps/assign_host_roles.html' + title = 'Pick Host Roles' + description = "Choose the role each machine will have in your OPNFV pod" + short_title = "host roles" + modified_key = "host_roles_step" + + def create_host_role_formset(self, hostlist=[], data=None): + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + host_roles = models.get("host_roles", []) + if not host_roles: + for host in hostlist: + initial = {"host_name": host.resource.name} + host_roles.append(initial) + models['host_roles'] = host_roles + self.repo_put(self.repo.OPNFV_MODELS, models) + + HostFormset = formset_factory(OPNFVHostRoleForm, extra=0) + + kwargs = {"initial": host_roles} + formset = None + if data: + formset = HostFormset(data, **kwargs) + else: + formset = HostFormset(**kwargs) + + return formset + + def get_context(self): + context = super(Assign_Host_Roles, self).get_context() + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + config = models.get("configbundle") + if config is None: + context['error'] = "Please select a Configuration on the first step" + + formset = self.create_host_role_formset(hostlist=config.bundle.getHosts()) + context['formset'] = formset + + return context + + def get_host_role_mapping(self, host_roles, hostname): + for obj in host_roles: + if hostname == obj['host_name']: + return obj + return None + + def update_confirmation(self): + confirm = self.repo_get(self.repo.CONFIRMATION, {}) + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + roles = models.get("host_roles") + if not roles: + return + confirm['host roles'] = {} + for role in roles: + confirm['host roles'][role['host_name']] = role['role'].name + self.repo_put(self.repo.CONFIRMATION, confirm) + + def post_render(self, request): + formset = self.create_host_role_formset(data=request.POST) + + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + host_roles = models.get("host_roles", []) + + has_jumphost = False + if formset.is_valid(): + for form in formset: + hostname = form.cleaned_data['host_name'] + role = form.cleaned_data['role'] + mapping = self.get_host_role_mapping(host_roles, hostname) + mapping['role'] = role + if "jumphost" in role.name.lower(): + has_jumphost = True + + models['host_roles'] = host_roles + self.repo_put(self.repo.OPNFV_MODELS, models) + self.update_confirmation() + + if not has_jumphost: + self.metastep.set_invalid('Must have at least one "Jumphost" per POD') + else: + self.metastep.set_valid("Completed") + else: + self.metastep.set_invalid("Please complete all fields") + + return self.render(request) + + +class MetaInfo(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(MetaInfo, self).get_context() + + initial = self.repo_get(self.repo.OPNFV_MODELS, {}).get("meta", {}) + context["form"] = BasicMetaForm(initial=initial) + return context + + def update_confirmation(self): + confirm = self.repo_get(self.repo.CONFIRMATION, {}) + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + meta = models.get("meta") + if not meta: + return + confirm['name'] = meta['name'] + confirm['description'] = meta['description'] + self.repo_put(self.repo.CONFIRMATION, confirm) + + def post_render(self, request): + models = self.repo_get(self.repo.OPNFV_MODELS, {}) + info = models.get("meta", {}) + + form = BasicMetaForm(request.POST) + if form.is_valid(): + info['name'] = form.cleaned_data['name'] + info['description'] = form.cleaned_data['description'] + models['meta'] = info + self.repo_put(self.repo.OPNFV_MODELS, models) + self.update_confirmation() + self.metastep.set_valid("Complete") + else: + self.metastep.set_invalid("Please correct the errors shown below") + + self.repo_put(self.repo.OPNFV_MODELS, models) + return self.render(request) diff --git a/dashboard/src/workflow/snapshot_workflow.py b/dashboard/src/workflow/snapshot_workflow.py index 002aee5..34ac3a5 100644 --- a/dashboard/src/workflow/snapshot_workflow.py +++ b/dashboard/src/workflow/snapshot_workflow.py @@ -14,7 +14,7 @@ import json from booking.models import Booking from resource_inventory.models import Host, Image from workflow.models import WorkflowStep -from workflow.forms import SnapshotMetaForm, SnapshotHostSelectForm +from workflow.forms import BasicMetaForm, SnapshotHostSelectForm class Select_Host_Step(WorkflowStep): @@ -91,14 +91,14 @@ class Image_Meta_Step(WorkflowStep): desc = self.repo_get(self.repo.SNAPSHOT_DESC, False) form = None if name and desc: - form = SnapshotMetaForm(initial={"name": name, "description": desc}) + form = BasicMetaForm(initial={"name": name, "description": desc}) else: - form = SnapshotMetaForm() + form = BasicMetaForm() context['form'] = form return context def post_render(self, request): - form = SnapshotMetaForm(request.POST) + form = BasicMetaForm(request.POST) if form.is_valid(): name = form.cleaned_data['name'] self.repo_put(self.repo.SNAPSHOT_NAME, name) diff --git a/dashboard/src/workflow/sw_bundle_workflow.py b/dashboard/src/workflow/sw_bundle_workflow.py index fd41018..a6a7464 100644 --- a/dashboard/src/workflow/sw_bundle_workflow.py +++ b/dashboard/src/workflow/sw_bundle_workflow.py @@ -11,9 +11,9 @@ from django.forms import formset_factory from workflow.models import WorkflowStep -from workflow.forms import SoftwareConfigurationForm, HostSoftwareDefinitionForm +from workflow.forms import BasicMetaForm, HostSoftwareDefinitionForm from workflow.booking_workflow import Resource_Select -from resource_inventory.models import Image, GenericHost, ConfigBundle, HostConfiguration, Installer, OPNFVConfig +from resource_inventory.models import Image, GenericHost, ConfigBundle, HostConfiguration # resource selection step is reused from Booking workflow @@ -39,48 +39,57 @@ class Define_Software(WorkflowStep): description = "Choose the opnfv and image of your machines" short_title = "host config" - def create_hostformset(self, hostlist): + def build_filter_data(self, hosts_data): + """ + 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 = GenericHost.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: - host_initial = {'host_id': config.host.id, 'host_name': config.host.resource.name} - host_initial['role'] = config.opnfvRole - host_initial['image'] = config.image - hosts_initial.append(host_initial) - + 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: - host_initial = {'host_id': host.id, 'host_name': host.resource.name} - - hosts_initial.append(host_initial) + hosts_initial.append({ + 'host_id': host.id, + 'host_name': host.resource.name + }) HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0) - host_formset = HostFormset(initial=hosts_initial) + filter_data = self.build_filter_data(hosts_initial) - filter_data = {} - user = self.repo_get(self.repo.SESSION_USER) - i = 0 - for host_data in hosts_initial: - host_profile = None - try: - host = GenericHost.objects.get(pk=host_data['host_id']) - host_profile = host.profile - except Exception: - for host in hostlist: - if host.resource.name == host_data['host_name']: - host_profile = host.profile - break - excluded_images = Image.objects.exclude(owner=user).exclude(public=True) - excluded_images = excluded_images | Image.objects.exclude(host_type=host_profile) - lab = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE).lab - excluded_images = excluded_images | Image.objects.exclude(from_lab=lab) - filter_data["id_form-" + str(i) + "-image"] = [] - for image in excluded_images: - filter_data["id_form-" + str(i) + "-image"].append(image.name) - i += 1 + 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 - return host_formset, filter_data + if data: + return SpecialHostFormset(data, initial=hosts_initial) + return SpecialHostFormset(initial=hosts_initial) def get_host_list(self, grb=None): if grb is None: @@ -99,9 +108,9 @@ class Define_Software(WorkflowStep): if grb: context["grb"] = grb - formset, filter_data = self.create_hostformset(self.get_host_list(grb)) + formset = self.create_hostformset(self.get_host_list(grb)) context["formset"] = formset - context["filter_data"] = filter_data + context['headnode'] = self.repo_get(self.repo.CONFIG_MODELS, {}).get("headnode_index", 1) else: context["error"] = "Please select a resource first" self.metastep.set_invalid("Step requires information that is not yet provided by previous step") @@ -115,47 +124,35 @@ class Define_Software(WorkflowStep): confirm = self.repo_get(self.repo.CONFIRMATION, {}) - HostFormset = formset_factory(HostSoftwareDefinitionForm, extra=0) - formset = HostFormset(request.POST) hosts = self.get_host_list() - has_jumphost = False + models['headnode_index'] = request.POST.get("headnode", 1) + formset = self.create_hostformset(hosts, data=request.POST) + has_headnode = False if formset.is_valid(): models['host_configs'] = [] - i = 0 confirm_hosts = [] - for form in formset: + for i, form in enumerate(formset): host = hosts[i] - i += 1 image = form.cleaned_data['image'] - # checks image compatability - grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE) - lab = None - if grb: - lab = grb.lab - try: - owner = self.repo_get(self.repo.SESSION_USER) - q = Image.objects.filter(owner=owner) | Image.objects.filter(public=True) - q.filter(host_type=host.profile) - q.filter(from_lab=lab) - q.get(id=image.id) # will throw exception if image is not in q - except Exception: - self.metastep.set_invalid("Image " + image.name + " is not compatible with host " + host.resource.name) - role = form.cleaned_data['role'] - if "jumphost" in role.name.lower(): - has_jumphost = True + headnode = form.cleaned_data['headnode'] + if headnode: + has_headnode = True bundle = models['bundle'] hostConfig = HostConfiguration( host=host, image=image, bundle=bundle, - opnfvRole=role + is_head_node=headnode ) models['host_configs'].append(hostConfig) - confirm_host = {"name": host.resource.name, "image": image.name, "role": role.name} - confirm_hosts.append(confirm_host) - - if not has_jumphost: - self.metastep.set_invalid('Must have at least one "Jumphost" per POD') + confirm_hosts.append({ + "name": host.resource.name, + "image": image.name, + "headnode": headnode + }) + + if not has_headnode: + self.metastep.set_invalid('Must have one "Headnode" per POD') return self.render(request) self.repo_put(self.repo.CONFIG_MODELS, models) @@ -172,8 +169,6 @@ class Define_Software(WorkflowStep): class Config_Software(WorkflowStep): template = 'config_bundle/steps/config_software.html' - form = SoftwareConfigurationForm - context = {'workspace_form': form} title = "Other Info" description = "Give your software config a name, description, and other stuff" short_title = "config info" @@ -187,58 +182,30 @@ class Config_Software(WorkflowStep): if bundle: initial['name'] = bundle.name initial['description'] = bundle.description - opnfv = models.get("opnfv", False) - if opnfv: - initial['installer'] = opnfv.installer - initial['scenario'] = opnfv.scenario - else: - initial['opnfv'] = False - supported = {} - for installer in Installer.objects.all(): - supported[str(installer)] = [] - for scenario in installer.sup_scenarios.all(): - supported[str(installer)].append(str(scenario)) - - context["form"] = SoftwareConfigurationForm(initial=initial) - context['supported'] = supported - + context["form"] = BasicMetaForm(initial=initial) return context def post_render(self, request): - try: - 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 = self.repo_get(self.repo.CONFIG_MODELS, {}) + if "bundle" not in models: + models['bundle'] = ConfigBundle(owner=self.repo_get(self.repo.SESSION_USER)) - confirm = self.repo_get(self.repo.CONFIRMATION, {}) - if "configuration" not in confirm: - confirm['configuration'] = {} + confirm = self.repo_get(self.repo.CONFIRMATION, {}) + if "configuration" not in confirm: + confirm['configuration'] = {} - form = self.form(request.POST) - if form.is_valid(): - models['bundle'].name = form.cleaned_data['name'] - models['bundle'].description = form.cleaned_data['description'] - if form.cleaned_data['opnfv']: - installer = form.cleaned_data['installer'] - scenario = form.cleaned_data['scenario'] - opnfv = OPNFVConfig( - bundle=models['bundle'], - installer=installer, - scenario=scenario - ) - models['opnfv'] = opnfv - confirm['configuration']['installer'] = form.cleaned_data['installer'].name - confirm['configuration']['scenario'] = form.cleaned_data['scenario'].name - - confirm['configuration']['name'] = form.cleaned_data['name'] - confirm['configuration']['description'] = form.cleaned_data['description'] - self.metastep.set_valid("Complete") - else: - self.metastep.set_invalid("Please correct the errors shown below") + form = BasicMetaForm(request.POST) + if form.is_valid(): + models['bundle'].name = form.cleaned_data['name'] + models['bundle'].description = form.cleaned_data['description'] - self.repo_put(self.repo.CONFIG_MODELS, models) - self.repo_put(self.repo.CONFIRMATION, confirm) + confirm['configuration']['name'] = form.cleaned_data['name'] + confirm['configuration']['description'] = form.cleaned_data['description'] + self.metastep.set_valid("Complete") + else: + self.metastep.set_invalid("Please correct the errors shown below") + + self.repo_put(self.repo.CONFIG_MODELS, models) + self.repo_put(self.repo.CONFIRMATION, confirm) - except Exception: - pass return self.render(request) diff --git a/dashboard/src/workflow/workflow_factory.py b/dashboard/src/workflow/workflow_factory.py index f5e2ad1..db2bba1 100644 --- a/dashboard/src/workflow/workflow_factory.py +++ b/dashboard/src/workflow/workflow_factory.py @@ -12,6 +12,7 @@ from workflow.booking_workflow import Booking_Resource_Select, SWConfig_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.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 import uuid @@ -36,6 +37,11 @@ class ConfigMetaWorkflow(object): color = "#00ffcc" +class OPNFVMetaWorkflow(object): + workflow_type = 3 + color = "000000" + + class MetaStep(object): UNTOUCHED = 0 @@ -110,12 +116,21 @@ class WorkflowFactory(): Image_Meta_Step, ] + opnfv_steps = [ + OPNFV_Resource_Select, + Pick_Installer, + Assign_Network_Roles, + Assign_Host_Roles, + MetaInfo + ] + def conjure(self, workflow_type=None, repo=None): workflow_types = [ self.booking_steps, self.resource_steps, self.config_steps, self.snapshot_steps, + self.opnfv_steps, ] steps = self.make_steps(workflow_types[workflow_type], repository=repo) -- cgit 1.2.3-korg