aboutsummaryrefslogtreecommitdiffstats
path: root/src/workflow
diff options
context:
space:
mode:
Diffstat (limited to 'src/workflow')
-rw-r--r--src/workflow/forms.py169
-rw-r--r--src/workflow/resource_bundle_workflow.py80
-rw-r--r--src/workflow/workflow_manager.py6
3 files changed, 106 insertions, 149 deletions
diff --git a/src/workflow/forms.py b/src/workflow/forms.py
index 0fb45d6..ee44ecd 100644
--- a/src/workflow/forms.py
+++ b/src/workflow/forms.py
@@ -242,124 +242,101 @@ class BookingMetaForm(forms.Form):
class MultipleSelectFilterWidget(forms.Widget):
- def __init__(self, attrs=None):
- super(MultipleSelectFilterWidget, self).__init__(attrs)
- self.attrs = attrs
+ def __init__(self, *args, display_objects=None, filter_items=None, neighbors=None, **kwargs):
+ super(MultipleSelectFilterWidget, self).__init__(*args, **kwargs)
+ self.display_objects = display_objects
+ self.filter_items = filter_items
+ self.neighbors = neighbors
self.template_name = "dashboard/multiple_select_filter_widget.html"
def render(self, name, value, attrs=None, renderer=None):
- attrs = self.attrs
- self.context = self.get_context(name, value, attrs)
- html = render_to_string(self.template_name, context=self.context)
+ context = self.get_context(name, value, attrs)
+ html = render_to_string(self.template_name, context=context)
return mark_safe(html)
def get_context(self, name, value, attrs):
- return attrs
+ return {
+ 'display_objects': self.display_objects,
+ 'neighbors': self.neighbors,
+ 'filter_items': self.filter_items,
+ 'initial_value': value
+ }
class MultipleSelectFilterField(forms.Field):
- def __init__(self, required=True, widget=None, label=None, initial=None,
- help_text='', error_messages=None, show_hidden_initial=False,
- validators=(), localize=False, disabled=False, label_suffix=None):
- """from the documentation:
- # required -- Boolean that specifies whether the field is required.
- # True by default.
- # widget -- A Widget class, or instance of a Widget class, that should
- # be used for this Field when displaying it. Each Field has a
- # default Widget that it'll use if you don't specify this. In
- # most cases, the default widget is TextInput.
- # label -- A verbose name for this field, for use in displaying this
- # field in a form. By default, Django will use a "pretty"
- # version of the form field name, if the Field is part of a
- # Form.
- # initial -- A value to use in this Field's initial display. This value
- # is *not* used as a fallback if data isn't given.
- # help_text -- An optional string to use as "help; text" for this Field.
- # error_messages -- An optional dictionary to override the default
- # messages that the field will raise.
- # show_hidden_initial -- Boolean that specifies if it is needed to render a
- # hidden widget with initial value after widget.
- # validators -- List of additional validators to use
- # localize -- Boolean that specifies if the field should be localized.
- # disabled -- Boolean that specifies whether the field is disabled, that
- # is its widget is shown in the form but not editable.
- # label_suffix -- Suffix to be added to the label. Overrides
- # form's label_suffix.
- """
- # this is bad, but django forms are annoying
- self.widget = widget
- if self.widget is None:
- self.widget = MultipleSelectFilterWidget()
- super(MultipleSelectFilterField, self).__init__(
- required=required,
- widget=self.widget,
- label=label,
- initial=None,
- help_text=help_text,
- error_messages=error_messages,
- show_hidden_initial=show_hidden_initial,
- validators=validators,
- localize=localize,
- disabled=disabled,
- label_suffix=label_suffix
- )
+ def __init__(self, **kwargs):
+ self.initial = kwargs.get("initial")
+ super().__init__(**kwargs)
- def clean(data):
- """
- This method will raise a django.forms.ValidationError or return clean data
- """
- return data
+ def to_python(self, value):
+ return json.loads(value)
class FormUtils:
@staticmethod
- def getLabData(multiple_selectable_hosts):
+ def getLabData(multiple_hosts=False):
"""
Gets all labs and thier host profiles and returns a serialized version the form can understand.
Should be rewritten with a related query to make it faster
- Should be moved outside of global scope
"""
+ # javascript truthy variables
+ true = 1
+ false = 0
+ if multiple_hosts:
+ multiple_hosts = true
+ else:
+ multiple_hosts = false
labs = {}
hosts = {}
items = {}
- mapping = {}
+ neighbors = {}
for lab in Lab.objects.all():
- slab = {}
- slab['id'] = "lab_" + str(lab.lab_user.id)
- slab['name'] = lab.name
- slab['description'] = lab.description
- slab['selected'] = 0
- slab['selectable'] = 1
- slab['follow'] = 1
- if not multiple_selectable_hosts:
- slab['follow'] = 0
- slab['multiple'] = 0
- items[slab['id']] = slab
- mapping[slab['id']] = []
- labs[slab['id']] = slab
+ lab_node = {
+ 'id': "lab_" + str(lab.lab_user.id),
+ 'model_id': lab.lab_user.id,
+ 'name': lab.name,
+ 'description': lab.description,
+ 'selected': false,
+ 'selectable': true,
+ 'follow': false,
+ 'multiple': false,
+ 'class': 'lab'
+ }
+ if multiple_hosts:
+ # "follow" this lab node to discover more hosts if allowed
+ lab_node['follow'] = true
+ items[lab_node['id']] = lab_node
+ neighbors[lab_node['id']] = []
+ labs[lab_node['id']] = lab_node
+
for host in lab.hostprofiles.all():
- shost = {}
- shost['forms'] = [{"name": "host_name", "type": "text", "placeholder": "hostname"}]
- shost['id'] = "host_" + str(host.id)
- shost['name'] = host.name
- shost['description'] = host.description
- shost['selected'] = 0
- shost['selectable'] = 1
- shost['follow'] = 0
- shost['multiple'] = multiple_selectable_hosts
- items[shost['id']] = shost
- mapping[slab['id']].append(shost['id'])
- if shost['id'] not in mapping:
- mapping[shost['id']] = []
- mapping[shost['id']].append(slab['id'])
- hosts[shost['id']] = shost
-
- filter_objects = [("labs", labs.values()), ("hosts", hosts.values())]
+ host_node = {
+ 'form': {"name": "host_name", "type": "text", "placeholder": "hostname"},
+ 'id': "host_" + str(host.id),
+ 'model_id': host.id,
+ 'name': host.name,
+ 'description': host.description,
+ 'selected': false,
+ 'selectable': true,
+ 'follow': false,
+ 'multiple': multiple_hosts,
+ 'class': 'host'
+ }
+ if multiple_hosts:
+ host_node['values'] = [] # place to store multiple values
+ items[host_node['id']] = host_node
+ neighbors[lab_node['id']].append(host_node['id'])
+ if host_node['id'] not in neighbors:
+ neighbors[host_node['id']] = []
+ neighbors[host_node['id']].append(lab_node['id'])
+ hosts[host_node['id']] = host_node
+
+ display_objects = [("lab", labs.values()), ("host", hosts.values())]
context = {
- 'filter_objects': filter_objects,
- 'mapping': mapping,
+ 'display_objects': display_objects,
+ 'neighbors': neighbors,
'filter_items': items
}
return context
@@ -368,14 +345,10 @@ class FormUtils:
class HardwareDefinitionForm(forms.Form):
def __init__(self, *args, **kwargs):
- selection_data = kwargs.pop("selection_data", False)
super(HardwareDefinitionForm, self).__init__(*args, **kwargs)
- attrs = FormUtils.getLabData(1)
- attrs['selection_data'] = selection_data
+ attrs = FormUtils.getLabData(multiple_hosts=True)
self.fields['filter_field'] = MultipleSelectFilterField(
- widget=MultipleSelectFilterWidget(
- attrs=attrs
- )
+ widget=MultipleSelectFilterWidget(**attrs)
)
diff --git a/src/workflow/resource_bundle_workflow.py b/src/workflow/resource_bundle_workflow.py
index ced355f..a4657ab 100644
--- a/src/workflow/resource_bundle_workflow.py
+++ b/src/workflow/resource_bundle_workflow.py
@@ -52,65 +52,47 @@ class Define_Hardware(WorkflowStep):
description = "Choose the type and amount of machines you want"
short_title = "hosts"
+ def __init__(self, *args, **kwargs):
+ self.form = None
+ super().__init__(*args, **kwargs)
+
def get_context(self):
context = super(Define_Hardware, self).get_context()
- selection_data = {"hosts": {}, "labs": {}}
- models = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {})
- hosts = models.get("hosts", [])
- for host in hosts:
- profile_id = "host_" + str(host.profile.id)
- if profile_id not in selection_data['hosts']:
- selection_data['hosts'][profile_id] = []
- selection_data['hosts'][profile_id].append({"host_name": host.resource.name, "class": profile_id})
-
- if models.get("bundle", GenericResourceBundle()).lab:
- selection_data['labs'] = {"lab_" + str(models.get("bundle").lab.lab_user.id): "true"}
-
- form = HardwareDefinitionForm(
- selection_data=selection_data
- )
- context['form'] = form
+ context['form'] = self.form or HardwareDefinitionForm()
return context
- def render(self, request):
- self.context = self.get_context()
- return render(request, self.template, self.context)
-
def update_models(self, data):
- data = json.loads(data['filter_field'])
+ 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['interfaces'] = {}
if "bundle" not in models:
models['bundle'] = GenericResourceBundle(owner=self.repo_get(self.repo.SESSION_USER))
- host_data = data['hosts']
+ host_data = data['host']
names = {}
- for host_dict in host_data:
- id = host_dict['class']
- # bit of formatting
- id = int(id.split("_")[-1])
+ for host_profile_dict in host_data.values():
+ id = host_profile_dict['id']
profile = HostProfile.objects.get(id=id)
# instantiate genericHost and store in repo
- name = host_dict['host_name']
- if not re.match(r"(?=^.{1,253}$)(^([A-Za-z0-9-_]{1,62}\.)*[A-Za-z0-9-_]{1,63})", name):
- raise InvalidHostnameException("Hostname must comply to RFC 952 and all extensions to it until this point")
- if name in names:
- raise NonUniqueHostnameException("All hosts must have unique names")
- names[name] = True
- genericResource = GenericResource(bundle=models['bundle'], name=name)
- genericHost = GenericHost(profile=profile, resource=genericResource)
- models['hosts'].append(genericHost)
- for interface_profile in profile.interfaceprofile.all():
- genericInterface = GenericInterface(profile=interface_profile, host=genericHost)
- if genericHost.resource.name not in models['interfaces']:
- models['interfaces'][genericHost.resource.name] = []
- models['interfaces'][genericHost.resource.name].append(genericInterface)
+ 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
+ genericResource = GenericResource(bundle=models['bundle'], name=name)
+ genericHost = GenericHost(profile=profile, resource=genericResource)
+ models['hosts'].append(genericHost)
+ for interface_profile in profile.interfaceprofile.all():
+ genericInterface = GenericInterface(profile=interface_profile, host=genericHost)
+ if genericHost.resource.name not in models['interfaces']:
+ models['interfaces'][genericHost.resource.name] = []
+ models['interfaces'][genericHost.resource.name].append(genericInterface)
# add selected lab to models
- for lab_dict in data['labs']:
- if list(lab_dict.values())[0]: # True for lab the user selected
- lab_user_id = int(list(lab_dict.keys())[0].split("_")[-1])
- models['bundle'].lab = Lab.objects.get(lab_user__id=lab_user_id)
+ for lab_dict in data['lab'].values():
+ if lab_dict['selected']:
+ models['bundle'].lab = Lab.objects.get(lab_user__id=lab_dict['id'])
break # if somehow we get two 'true' labs, we only use one
# return to repo
@@ -133,15 +115,11 @@ class Define_Hardware(WorkflowStep):
try:
self.form = HardwareDefinitionForm(request.POST)
if self.form.is_valid():
- if len(json.loads(self.form.cleaned_data['filter_field'])['labs']) != 1:
- self.set_invalid("Please select one lab")
- else:
- self.update_models(self.form.cleaned_data)
- self.update_confirmation()
- self.set_valid("Step Completed")
+ self.update_models(self.form.cleaned_data)
+ self.update_confirmation()
+ self.set_valid("Step Completed")
else:
self.set_invalid("Please complete the fields highlighted in red to continue")
- pass
except Exception as e:
self.set_invalid(str(e))
self.context = self.get_context()
diff --git a/src/workflow/workflow_manager.py b/src/workflow/workflow_manager.py
index 26f926e..80b8a67 100644
--- a/src/workflow/workflow_manager.py
+++ b/src/workflow/workflow_manager.py
@@ -97,7 +97,13 @@ class SessionManager():
def post_render(self, request):
return self.active_workflow().steps[self.active_workflow().active_index].post_render(request)
+ def get_active_step(self):
+ return self.active_workflow().steps[self.active_workflow().active_index]
+
def go_next(self, **kwargs):
+ # need to verify current step is valid to allow this
+ if self.get_active_step().valid < 200:
+ return
next_step = self.active_workflow().active_index + 1
if next_step >= len(self.active_workflow().steps):
raise Exception("Out of bounds request for step")