Merge "Styled the Booking Statistics page"
[pharos-tools.git] / dashboard / src / workflow / models.py
index e5a23b2..495ce07 100644 (file)
@@ -8,32 +8,30 @@
 ##############################################################################
 
 
-from django.contrib.auth.models import User
-from django.db import models
 from django.shortcuts import render
 from django.contrib import messages
+from django.http import HttpResponse
 
 import yaml
 import requests
 
 from workflow.forms import ConfirmationForm
-from api.models import *
-from dashboard.exceptions import *
-from resource_inventory.models import *
+from api.models import JobFactory
+from dashboard.exceptions import ResourceAvailabilityException, ModelValidationException
+from resource_inventory.models import Image, GenericInterface
 from resource_inventory.resource_manager import ResourceManager
 from notifier.manager import NotificationHandler
+from booking.models import Booking
 
 
 class BookingAuthManager():
     LFN_PROJECTS = ["opnfv"]  # TODO
 
-    def parse_url(self, info_url):
-        """
-        will return the PTL in the INFO file on success, or None
-        """
+    def parse_github_url(self, url):
+        project_leads = []
         try:
-            parts = info_url.split("/")
-            if parts[0].find("http") > -1:  # the url include http(s)://
+            parts = url.split("/")
+            if "http" in parts[0]:  # the url include http(s)://
                 parts = parts[2:]
             if parts[-1] != "INFO.yaml":
                 return None
@@ -48,13 +46,83 @@ class BookingAuthManager():
             info_file = requests.get(url, timeout=15).text
             info_parsed = yaml.load(info_file)
             ptl = info_parsed.get('project_lead')
-            if not ptl:
+            if ptl:
+                project_leads.append(ptl)
+            sub_ptl = info_parsed.get("subproject_lead")
+            if sub_ptl:
+                project_leads.append(sub_ptl)
+
+        except Exception:
+            pass
+
+        return project_leads
+
+    def parse_gerrit_url(self, url):
+        project_leads = []
+        try:
+            parts = url.split("/")
+            if "http" in parts[0]:  # the url include http(s)://
+                parts = parts[2:]
+            if "f=INFO.yaml" not in parts[-1].split(";"):
                 return None
-            return ptl
+            if "gerrit.opnfv.org" not in parts[0]:
+                return None
+            # now to download and parse file
+            url = "https://" + "/".join(parts)
+            info_file = requests.get(url, timeout=15).text
+            info_parsed = yaml.load(info_file)
+            ptl = info_parsed.get('project_lead')
+            if ptl:
+                project_leads.append(ptl)
+            sub_ptl = info_parsed.get("subproject_lead")
+            if sub_ptl:
+                project_leads.append(sub_ptl)
 
-        except Exception as e:
+        except Exception:
             return None
 
+        return project_leads
+
+    def parse_opnfv_git_url(self, url):
+        project_leads = []
+        try:
+            parts = url.split("/")
+            if "http" in parts[0]:  # the url include http(s)://
+                parts = parts[2:]
+            if "INFO.yaml" not in parts[-1]:
+                return None
+            if "git.opnfv.org" not in parts[0]:
+                return None
+            if parts[-2] == "tree":
+                parts[-2] = "plain"
+            # now to download and parse file
+            url = "https://" + "/".join(parts)
+            info_file = requests.get(url, timeout=15).text
+            info_parsed = yaml.load(info_file)
+            ptl = info_parsed.get('project_lead')
+            if ptl:
+                project_leads.append(ptl)
+            sub_ptl = info_parsed.get("subproject_lead")
+            if sub_ptl:
+                project_leads.append(sub_ptl)
+
+        except Exception:
+            return None
+
+        return project_leads
+
+    def parse_url(self, info_url):
+        """
+        will return the PTL in the INFO file on success, or None
+        """
+        if "github" in info_url:
+            return self.parse_github_url(info_url)
+
+        if "gerrit.opnfv.org" in info_url:
+            return self.parse_gerrit_url(info_url)
+
+        if "git.opnfv.org" in info_url:
+            return self.parse_opnfv_git_url(info_url)
 
     def booking_allowed(self, booking, repo):
         """
@@ -64,18 +132,16 @@ class BookingAuthManager():
         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
+            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 repo.BOOKING_INFO_FILE not in repo.el:
             return False  # INFO file not provided
         ptl_info = self.parse_url(repo.BOOKING_INFO_FILE)
-        return ptl_info and  ptl_info == booking.owner.userprofile.email_addr
-
+        return ptl_info and ptl_info == booking.owner.userprofile.email_addr
 
 
 class WorkflowStep(object):
-
     template = 'bad_request.html'
     title = "Generic Step"
     description = "You were led here by mistake"
@@ -116,15 +182,18 @@ class WorkflowStep(object):
     def repo_put(self, key, value):
         return self.repo.put(key, value, self.id)
 
+
 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.BOOKING_SELECTED_GRB, False)
+        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
@@ -140,14 +209,13 @@ class Confirmation_Step(WorkflowStep):
                             return 1  # There is a problem with these vlans
         return 0
 
-
     def get_context(self):
         context = super(Confirmation_Step, self).get_context()
         context['form'] = ConfirmationForm()
         context['confirmation_info'] = yaml.dump(
-                self.repo_get(self.repo.CONFIRMATION),
-                default_flow_style=False
-                ).strip()
+            self.repo_get(self.repo.CONFIRMATION),
+            default_flow_style=False
+        ).strip()
         context['vlan_warning'] = self.get_vlan_warning()
 
         return context
@@ -167,9 +235,10 @@ class Confirmation_Step(WorkflowStep):
                 errors = self.flush_to_db()
                 if errors:
                     messages.add_message(request, messages.ERROR, "ERROR OCCURRED: " + errors)
-                    return render(request, self.template, context)
-                messages.add_message(request, messages.SUCCESS, "Confirmed")
-                return render(request, self.template, context)
+                else:
+                    messages.add_message(request, messages.SUCCESS, "Confirmed")
+
+                return HttpResponse('')
             elif data == "False":
                 context["bypassed"] = "true"
                 messages.add_message(request, messages.SUCCESS, "Canceled")
@@ -185,7 +254,7 @@ class Confirmation_Step(WorkflowStep):
             pass
 
     def translate_vlans(self):
-        grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB, False)
+        grb = self.repo_get(self.repo.SELECTED_GRESOURCE_BUNDLE, False)
         if not grb:
             return 0
         vlan_manager = grb.lab.vlan_manager
@@ -211,6 +280,7 @@ class Workflow():
     steps = []
     active_index = 0
 
+
 class Repository():
 
     EDIT = "editing"
@@ -218,6 +288,7 @@ class Repository():
     RESOURCE_SELECT = "resource_select"
     CONFIRMATION = "confirmation"
     SELECTED_GRESOURCE_BUNDLE = "selected generic bundle pk"
+    SELECTED_CONFIG_BUNDLE = "selected config bundle pk"
     GRESOURCE_BUNDLE_MODELS = "generic_resource_bundle_models"
     GRESOURCE_BUNDLE_INFO = "generic_resource_bundle_info"
     BOOKING = "booking"
@@ -225,8 +296,6 @@ class Repository():
     GRB_LAST_HOSTLIST = "grb_network_previous_hostlist"
     BOOKING_FORMS = "booking_forms"
     SWCONF_HOSTS = "swconf_hosts"
-    SWCONF_SELECTED_GRB = "swconf_selected_grb_pk"
-    BOOKING_SELECTED_GRB = "booking_selected_grb_pk"
     BOOKING_MODELS = "booking models"
     CONFIG_MODELS = "configuration bundle models"
     SESSION_USER = "session owner user account"
@@ -240,12 +309,27 @@ 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.
+    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"
+
+    def get_child_defaults(self):
+        return_tuples = []
+        for key in [self.SELECTED_GRESOURCE_BUNDLE, self.SESSION_USER]:
+            return_tuples.append((key, self.el.get(key)))
+        return return_tuples
+
+    def set_defaults(self, defaults):
+        for key, value in defaults:
+            self.el[key] = value
 
     def get(self, key, default, id):
         self.add_get_history(key, id)
         return self.el.get(key, default)
 
-    def put(self,key,val, id):
+    def put(self, key, val, id):
         self.add_put_history(key, id)
         self.el[key] = val
 
@@ -256,7 +340,7 @@ class Repository():
         self.add_history(key, id, self.put_history)
 
     def add_history(self, key, id, history):
-        if not key in history:
+        if key not in history:
             history[key] = [id]
         else:
             history[key].append(id)
@@ -266,16 +350,24 @@ class Repository():
             errors = self.make_snapshot()
             if errors:
                 return errors
-        #if GRB WF, create it
+        # if GRB WF, create it
         if self.GRESOURCE_BUNDLE_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
+                return
 
         if self.CONFIG_MODELS in self.el:
             errors = self.make_software_config_bundle()
             if errors:
                 return errors
+            else:
+                self.el[self.HAS_RESULT] = True
+                self.el[self.RESULT_KEY] = self.SELECTED_CONFIG_BUNDLE
+                return
 
         if self.BOOKING_MODELS in self.el:
             errors = self.make_booking()
@@ -285,7 +377,6 @@ class Repository():
             booking = self.el[self.BOOKING_MODELS]['booking']
             NotificationHandler.notify_new_booking(booking)
 
-
     def make_snapshot(self):
         owner = self.el[self.SESSION_USER]
         models = self.el[self.SNAPSHOT_MODELS]
@@ -310,7 +401,6 @@ class Repository():
         image.host_type = host.profile
         image.save()
 
-
     def make_generic_resource_bundle(self):
         owner = self.el[self.SESSION_USER]
         if self.GRESOURCE_BUNDLE_MODELS in self.el:
@@ -354,10 +444,10 @@ class Repository():
                 for resource_name, mapping in models['vlans'].items():
                     for profile_name, vlan_set in mapping.items():
                         interface = GenericInterface.objects.get(
-                                profile__name=profile_name,
-                                host__resource__name=resource_name,
-                                host__resource__bundle=models['bundle']
-                                )
+                            profile__name=profile_name,
+                            host__resource__name=resource_name,
+                            host__resource__bundle=models['bundle']
+                        )
                         for vlan in vlan_set:
                             try:
                                 vlan.save()
@@ -367,16 +457,13 @@ class Repository():
             else:
                 return "GRB, no vlan set provided. CODE:0x0018"
 
-
         else:
             return "GRB no models given. CODE:0x0001"
 
-        self.el[self.VALIDATED_MODEL_GRB] = bundle
+        self.el[self.RESULT] = bundle
         return False
 
-
     def make_software_config_bundle(self):
-        owner = self.el[self.SESSION_USER]
         models = self.el[self.CONFIG_MODELS]
         if 'bundle' in models:
             bundle = models['bundle']
@@ -402,7 +489,7 @@ class Repository():
         if 'opnfv' in models:
             opnfvconfig = models['opnfv']
             opnfvconfig.bundle = opnfvconfig.bundle
-            if not opnfvconfig.scenario in opnfvconfig.installer.sup_scenarios.all():
+            if opnfvconfig.scenario not in opnfvconfig.installer.sup_scenarios.all():
                 return "SWC, scenario not supported by installer. CODE:0x000d"
             try:
                 opnfvconfig.save()
@@ -411,16 +498,15 @@ class Repository():
         else:
             pass
 
-        self.el[self.VALIDATED_MODEL_CONFIG] = bundle
+        self.el[self.RESULT] = bundle
         return False
 
-
     def make_booking(self):
         models = self.el[self.BOOKING_MODELS]
         owner = self.el[self.SESSION_USER]
 
-        if self.BOOKING_SELECTED_GRB in self.el:
-            selected_grb = self.el[self.BOOKING_SELECTED_GRB]
+        if self.SELECTED_GRESOURCE_BUNDLE in self.el:
+            selected_grb = self.el[self.SELECTED_GRESOURCE_BUNDLE]
         else:
             return "BOOK, no selected resource. CODE:0x000e"
 
@@ -467,6 +553,12 @@ class Repository():
         for collaborator in collaborators:
             booking.collaborators.add(collaborator)
 
+        try:
+            booking.pdf = ResourceManager().makePDF(booking.resource)
+            booking.save()
+        except Exception as e:
+            return "BOOK, failed to create Pod Desriptor File: " + str(e)
+
         try:
             JobFactory.makeCompleteJob(booking)
         except Exception as e:
@@ -498,12 +590,12 @@ class Repository():
             vlan_manager.reserve_vlans(vlans)
             vlan_manager.reserve_public_vlan(public_vlan.vlan_id)
             return True
-        except Exception as e:
+        except Exception:
             return False
 
-
     def __init__(self):
         self.el = {}
         self.el[self.CONFIRMATION] = {}
+        self.el["active_step"] = 0
         self.get_history = {}
         self.put_history = {}