diff options
Diffstat (limited to 'dashboard/src/workflow/models.py')
-rw-r--r-- | dashboard/src/workflow/models.py | 302 |
1 files changed, 205 insertions, 97 deletions
diff --git a/dashboard/src/workflow/models.py b/dashboard/src/workflow/models.py index beaaee4..6c6bd9a 100644 --- a/dashboard/src/workflow/models.py +++ b/dashboard/src/workflow/models.py @@ -19,8 +19,9 @@ 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 from booking.models import Booking @@ -142,10 +143,12 @@ class BookingAuthManager(): currently checks if the booking uses multiple servers. if it does, then the owner must be a PTL, which is checked using the provided info file """ - if len(booking.resource.template.getHosts()) < 2: - return True # if they only have one server, we dont care if booking.owner.userprofile.booking_privledge: 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: + 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 ptl_info = self.parse_url(repo.el.get(repo.BOOKING_INFO_FILE)) @@ -155,12 +158,52 @@ class BookingAuthManager(): return False +class WorkflowStepStatus(object): + UNTOUCHED = 0 + INVALID = 100 + VALID = 200 + + class WorkflowStep(object): template = 'bad_request.html' title = "Generic Step" description = "You were led here by mistake" short_title = "error" metastep = None + # phasing out metastep: + + valid = WorkflowStepStatus.UNTOUCHED + message = "" + + enabled = True + + def cleanup(self): + raise Exception("WorkflowStep subclass of type " + str(type(self)) + " has no concrete implemented cleanup() method") + + def enable(self): + if not self.enabled: + self.enabled = True + + def disable(self): + if self.enabled: + self.cleanup() + self.enabled = False + + def set_invalid(self, message, code=WorkflowStepStatus.INVALID): + self.valid = code + self.message = message + + def set_valid(self, message, code=WorkflowStepStatus.VALID): + self.valid = code + self.message = message + + def to_json(self): + return { + 'title': self.short_title, + 'enabled': self.enabled, + 'valid': self.valid, + 'message': self.message, + } def __init__(self, id, repo=None): self.repo = repo @@ -197,31 +240,73 @@ class WorkflowStep(object): return self.repo.put(key, value, self.id) +""" +subclassing notes: + subclasses have to define the following class attributes: + self.select_repo_key: where the selected "object" or "bundle" is to be placed in the repo + self.form: the form to be used + alert_bundle_missing(): what message to display if a user does not select/selects an invalid object + get_form_queryset(): generate a queryset to be used to filter available items for the field + get_page_context(): return simple context such as page header and other info +""" + + +class AbstractSelectOrCreate(WorkflowStep): + template = 'dashboard/genericselect.html' + title = "Select a Bundle" + short_title = "select" + description = "Generic bundle selector step" + + select_repo_key = None + form = None # subclasses are expected to use a form that is a subclass of SearchableSelectGenericForm + + def alert_bundle_missing(self): # override in subclasses to change message if field isn't filled out + self.set_invalid("Please select a valid bundle") + + def post_render(self, request): + context = self.get_context() + form = self.form(request.POST, queryset=self.get_form_queryset()) + if form.is_valid(): + bundle = form.get_validated_bundle() + if not bundle: + self.alert_bundle_missing() + return render(request, self.template, context) + self.repo_put(self.select_repo_key, bundle) + self.put_confirm_info(bundle) + self.set_valid("Step Completed") + else: + self.alert_bundle_missing() + messages.add_message(request, messages.ERROR, "Form Didn't Validate", fail_silently=True) + + return self.render(request) + + def get_context(self): + default = [] + + bundle = self.repo_get(self.select_repo_key, False) + if bundle: + default.append(bundle) + + form = self.form(queryset=self.get_form_queryset(), initial=default) + + context = {'form': form, **self.get_page_context()} + context.update(super().get_context()) + + return context + + def get_page_context(): + return { + 'select_type': 'generic', + 'select_type_title': 'Generic Bundle' + } + + class Confirmation_Step(WorkflowStep): template = 'workflow/confirm.html' title = "Confirm Changes" description = "Does this all look right?" - def get_vlan_warning(self): - grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False) - if not grb: - return 0 - if self.repo.BOOKING_MODELS not in self.repo.el: - return 0 - vlan_manager = grb.lab.vlan_manager - if vlan_manager is None: - return 0 - hosts = grb.getHosts() - for host in hosts: - for interface in host.generic_interfaces.all(): - for vlan in interface.vlans.all(): - if vlan.public: - if not vlan_manager.public_vlan_is_available(vlan.vlan_id): - return 1 - else: - if not vlan_manager.is_available(vlan.vlan_id): - return 1 # There is a problem with these vlans - return 0 + short_title = "confirm" def get_context(self): context = super(Confirmation_Step, self).get_context() @@ -230,7 +315,6 @@ class Confirmation_Step(WorkflowStep): self.repo_get(self.repo.CONFIRMATION), default_flow_style=False ).strip() - context['vlan_warning'] = self.get_vlan_warning() return context @@ -261,39 +345,8 @@ class Confirmation_Step(WorkflowStep): pass else: - if "vlan_input" in request.POST: - if request.POST.get("vlan_input") == "True": - self.translate_vlans() - return self.render(request) pass - def translate_vlans(self): - grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False) - if not grb: - return 0 - vlan_manager = grb.lab.vlan_manager - if vlan_manager is None: - return 0 - hosts = grb.getHosts() - for host in hosts: - for interface in host.generic_interfaces.all(): - for vlan in interface.vlans.all(): - if not vlan.public: - if not vlan_manager.is_available(vlan.vlan_id): - vlan.vlan_id = vlan_manager.get_vlan() - vlan.save() - else: - if not vlan_manager.public_vlan_is_available(vlan.vlan_id): - pub_vlan = vlan_manager.get_public_vlan() - vlan.vlan_id = pub_vlan.vlan - vlan.save() - - -class Workflow(): - - steps = [] - active_index = 0 - class Repository(): @@ -303,6 +356,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" @@ -312,7 +366,9 @@ 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" + SESSION_MANAGER = "session manager for current session" VALIDATED_MODEL_GRB = "valid grb config model instance in db" VALIDATED_MODEL_CONFIG = "valid config model instance in db" VALIDATED_MODEL_BOOKING = "valid booking model instance in db" @@ -323,8 +379,8 @@ class Repository(): SNAPSHOT_DESC = "description of the snapshot" BOOKING_INFO_FILE = "the INFO.yaml file for this user's booking" - #migratory elements of segmented workflow - #each of these is the end result of a different workflow. + # 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" RESULT_KEY = "key for target index that result will be put into in parent" RESULT = "result object from workflow" @@ -340,6 +396,7 @@ class Repository(): self.el[key] = value def get(self, key, default, id): + self.add_get_history(key, id) return self.el.get(key, default) @@ -364,6 +421,7 @@ class Repository(): errors = self.make_snapshot() if errors: return errors + # if GRB WF, create it if self.GRESOURCE_BUNDLE_MODELS in self.el: errors = self.make_generic_resource_bundle() @@ -383,6 +441,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: @@ -424,6 +490,9 @@ class Repository(): pass JobFactory.makeSnapshotTask(image, booking, host) + self.el[self.RESULT] = image + self.el[self.HAS_RESULT] = True + def make_generic_resource_bundle(self): owner = self.el[self.SESSION_USER] if self.GRESOURCE_BUNDLE_MODELS in self.el: @@ -452,31 +521,37 @@ class Repository(): 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 as e: + except Exception: return "GRB, saving interface " + str(interface) + " failed. CODE:0x0019" else: return "GRB, no interface set provided. CODE:0x001a" - if 'vlans' in models: - for resource_name, mapping in models['vlans'].items(): - for profile_name, vlan_set in mapping.items(): + if 'connections' in models: + for resource_name, mapping in models['connections'].items(): + for profile_name, connection_set in mapping.items(): interface = GenericInterface.objects.get( profile__name=profile_name, host__resource__name=resource_name, host__resource__bundle=models['bundle'] ) - for vlan in vlan_set: + for connection in connection_set: try: - vlan.save() - interface.vlans.add(vlan) + connection.network = connection.network + connection.save() + interface.connections.add(connection) except Exception as e: - return "GRB, saving vlan " + str(vlan) + " failed. Exception: " + str(e) + ". CODE:0x0017" + return "GRB, saving vlan " + str(connection) + " failed. Exception: " + str(e) + ". CODE:0x0017" else: return "GRB, no vlan set provided. CODE:0x0018" @@ -484,13 +559,14 @@ class Repository(): 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 = bundle.bundle + bundle.bundle = self.el[self.SELECTED_GRESOURCE_BUNDLE] try: bundle.save() except Exception as e: @@ -528,18 +604,22 @@ class Repository(): models = self.el[self.BOOKING_MODELS] owner = self.el[self.SESSION_USER] + if 'booking' in models: + booking = models['booking'] + else: + return "BOOK, no booking model exists. CODE:0x000f" + + selected_grb = None + if self.SELECTED_GRESOURCE_BUNDLE in self.el: selected_grb = self.el[self.SELECTED_GRESOURCE_BUNDLE] else: return "BOOK, no selected resource. CODE:0x000e" - if not self.reserve_vlans(selected_grb): - return "BOOK, vlans not available" + if self.SELECTED_CONFIG_BUNDLE not in self.el: + return "BOOK, no selected config bundle. CODE:0x001f" - if 'booking' in models: - booking = models['booking'] - else: - return "BOOK, no booking model exists. CODE:0x000f" + booking.config_bundle = self.el[self.SELECTED_CONFIG_BUNDLE] if not booking.start: return "BOOK, booking has no start. CODE:0x0010" @@ -561,7 +641,6 @@ class Repository(): booking.resource = resource_bundle booking.owner = owner - booking.config_bundle = booking.config_bundle booking.lab = selected_grb.lab is_allowed = BookingAuthManager().booking_allowed(booking, self) @@ -577,7 +656,7 @@ class Repository(): booking.collaborators.add(collaborator) try: - booking.pdf = ResourceManager().makePDF(booking.resource) + booking.pdf = PDFTemplater.makePDF(booking) booking.save() except Exception as e: return "BOOK, failed to create Pod Desriptor File: " + str(e) @@ -592,33 +671,62 @@ class Repository(): except Exception as e: return "BOOK, saving booking generated exception: " + str(e) + " CODE:0x0016" - def reserve_vlans(self, grb): - """ - True is success - """ - vlans = [] - public_vlan = None - vlan_manager = grb.lab.vlan_manager - if vlan_manager is None: - return True - for host in grb.getHosts(): - for interface in host.generic_interfaces.all(): - for vlan in interface.vlans.all(): - if vlan.public: - public_vlan = vlan - else: - vlans.append(vlan.vlan_id) - - try: - vlan_manager.reserve_vlans(vlans) - vlan_manager.reserve_public_vlan(public_vlan.vlan_id) - return True - except Exception: - return False + self.el[self.RESULT] = booking + self.el[self.HAS_RESULT] = True + + def make_opnfv_config(self): + opnfv_models = self.el[self.OPNFV_MODELS] + config_bundle = self.el[self.SELECTED_CONFIG_BUNDLE] + 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 + self.el[self.HAS_RESULT] = True def __init__(self): self.el = {} self.el[self.CONFIRMATION] = {} self.el["active_step"] = 0 + self.el[self.HAS_RESULT] = False + self.el[self.RESULT] = None self.get_history = {} self.put_history = {} |