diff --git a/sahara_dashboard/api/sahara.py b/sahara_dashboard/api/sahara.py index c07d4594..6dd0f9dc 100644 --- a/sahara_dashboard/api/sahara.py +++ b/sahara_dashboard/api/sahara.py @@ -166,7 +166,8 @@ def nodegroup_template_create(request, name, plugin_name, hadoop_version, use_autoconfig=None, shares=None, is_public=None, - is_protected=None): + is_protected=None, + volume_mount_prefix=None): return client(request).node_group_templates.create( name=name, plugin_name=plugin_name, @@ -189,7 +190,8 @@ def nodegroup_template_create(request, name, plugin_name, hadoop_version, use_autoconfig=use_autoconfig, shares=shares, is_public=is_public, - is_protected=is_protected) + is_protected=is_protected, + volume_mount_prefix=volume_mount_prefix) def nodegroup_template_list(request, search_opts=None, diff --git a/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/forms/__init__.py b/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/forms/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/forms/import_forms.py b/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/forms/import_forms.py new file mode 100644 index 00000000..eeedbe10 --- /dev/null +++ b/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/forms/import_forms.py @@ -0,0 +1,163 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from django.utils.translation import ugettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from openstack_dashboard.api import neutron +from openstack_dashboard.dashboards.project.instances \ + import utils as nova_utils +from oslo_serialization import jsonutils as json +from saharaclient.api import base as api_base + +from sahara_dashboard.api import sahara as saharaclient + +BASE_IMAGE_URL = "horizon:project:data_processing.clusters:register" + + +class ImportNodegroupTemplateFileForm(forms.SelfHandlingForm): + + class Meta(object): + name = _("Import Node Group Template") + + def __init__(self, *args, **kwargs): + self.next_view = kwargs.pop('next_view') + super(ImportNodegroupTemplateFileForm, self).__init__( + *args, **kwargs) + + template_upload = forms.FileField( + label=_('Template File'), + required=True) + + def handle(self, request, data): + kwargs = {'template_upload': data['template_upload']} + request.method = 'GET' + return self.next_view.as_view()(request, **kwargs) + + +class ImportNodegroupTemplateDetailsForm(forms.SelfHandlingForm): + + class Meta(object): + name = _("Import Node Group Template") + + template = forms.CharField( + widget=forms.widgets.HiddenInput) + + name = forms.CharField(label=_("Name"), + required=False, + help_text=_("Name must be provided " + "either here or in the template. If " + "provided in both places, this one " + "will be used.")) + + security_groups = forms.MultipleChoiceField( + label=_("Security Groups"), + widget=forms.CheckboxSelectMultiple(), + help_text=_("Launch instances in these security groups. " + "Auto security group will be determined by the " + "value present in the imported template."), + required=False) + + floating_ip_pool = forms.ChoiceField( + label=_("Floating IP Pool"), + required=False) + + flavor = forms.ChoiceField(label=_("Openstack Flavor")) + + image_id = forms.DynamicChoiceField(label=_("Base Image"), + add_item_link=BASE_IMAGE_URL) + + def _populate_image_choices(self, request, plugin, hadoop_version): + all_images = saharaclient.image_list(request) + details = saharaclient.plugin_get_version_details(request, + plugin, + hadoop_version) + return [(image.id, image.name) for image in all_images + if (set(details.required_image_tags). + issubset(set(image.tags)))] + + def __init__(self, *args, **kwargs): + try: + request = args[0] + template_string = "" + + if "template_upload" in kwargs: + template_upload = kwargs.pop('template_upload') + super(ImportNodegroupTemplateDetailsForm, self).__init__( + *args, **kwargs) + + template_string = template_upload.read() + self.fields["template"].initial = template_string + + else: + super(ImportNodegroupTemplateDetailsForm, self).__init__( + *args, **kwargs) + template_string = self.data["template"] + + template_json = json.loads(template_string) + template_json = template_json["node_group_template"] + + security_group_list = neutron.security_group_list(request) + security_group_choices = \ + [(sg.id, sg.name) for sg in security_group_list] + self.fields["security_groups"].choices = security_group_choices + + pools = neutron.floating_ip_pools_list(request) + pool_choices = [(pool.id, pool.name) for pool in pools] + pool_choices.insert(0, (None, "Do not assign floating IPs")) + self.fields["floating_ip_pool"].choices = pool_choices + + flavors = nova_utils.flavor_list(request) + if flavors: + self.fields["flavor"].choices = nova_utils.sort_flavor_list( + request, flavors) + else: + self.fields["flavor"].choices = [] + + self.fields["image_id"].choices = \ + self._populate_image_choices(request, + template_json["plugin_name"], + template_json["hadoop_version"]) + except (ValueError, KeyError): + raise exceptions.BadRequest(_("Could not parse template")) + except Exception: + exceptions.handle(request) + + def handle(self, request, data): + try: + template = data["template"] + template = json.loads(template) + template = template["node_group_template"] + + if not data["name"] and "name" not in template.keys(): + return False + if data["name"]: + template["name"] = data["name"] + + template["security_groups"] = data["security_groups"] + template["floating_ip_pool"] = data["floating_ip_pool"] + template["flavor_id"] = data["flavor"] + template["image_id"] = data["image_id"] + + saharaclient.nodegroup_template_create(request, **template) + return True + except api_base.APIException as e: + self.error_description = str(e) + return False + except Exception as e: + if isinstance(e, TypeError): + raise exceptions.BadRequest( + _("Template JSON contained invalid key")) + else: + raise exceptions.BadRequest(_("Could not parse template")) diff --git a/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/tables.py b/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/tables.py index 61e5d321..15082c89 100644 --- a/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/tables.py +++ b/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/tables.py @@ -43,6 +43,15 @@ class CreateNodegroupTemplate(tables.LinkAction): icon = "plus" +class ImportNodegroupTemplate(tables.LinkAction): + name = "import" + verbose_name = _("Import Template") + url = ("horizon:project:data_processing.clusters:" + "import-nodegroup-template-file") + classes = ("ajax-modal",) + icon = "plus" + + class ConfigureNodegroupTemplate(tables.LinkAction): name = "configure" verbose_name = _("Configure Template") @@ -149,6 +158,7 @@ class NodegroupTemplatesTable(sahara_table.SaharaPaginateTabbedTable): name = "nodegroup_templates" verbose_name = _("Node Group Templates") table_actions = (CreateNodegroupTemplate, + ImportNodegroupTemplate, ConfigureNodegroupTemplate, DeleteTemplate, NodeGroupTemplatesFilterAction,) diff --git a/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/views.py b/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/views.py index e3f8796e..848e4d28 100644 --- a/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/views.py +++ b/sahara_dashboard/content/data_processing/clusters/nodegroup_templates/views.py @@ -12,9 +12,11 @@ # limitations under the License. from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ from horizon import exceptions +from horizon import forms from horizon import tabs from horizon.utils import memoized from horizon import workflows @@ -30,6 +32,8 @@ import sahara_dashboard.content.data_processing.clusters. \ nodegroup_templates.workflows.create as create_flow import sahara_dashboard.content.data_processing.clusters. \ nodegroup_templates.workflows.edit as edit_flow +import sahara_dashboard.content.data_processing.clusters. \ + nodegroup_templates.forms.import_forms as import_forms class NodegroupTemplateDetailsView(tabs.TabView): @@ -49,8 +53,8 @@ class NodegroupTemplateDetailsView(tabs.TabView): exceptions.handle(self.request, msg, redirect=redirect) def get_context_data(self, **kwargs): - context = super(NodegroupTemplateDetailsView, self)\ - .get_context_data(**kwargs) + context = super( + NodegroupTemplateDetailsView, self).get_context_data(**kwargs) node_group_template = self.get_object() context['template'] = node_group_template context['url'] = self.get_redirect_url() @@ -97,8 +101,8 @@ class CopyNodegroupTemplateView(workflows.WorkflowView): template_name = "nodegroup_templates/configure.html" def get_context_data(self, **kwargs): - context = super(CopyNodegroupTemplateView, self)\ - .get_context_data(**kwargs) + context = super( + CopyNodegroupTemplateView, self).get_context_data(**kwargs) context["template_id"] = kwargs["template_id"] return context @@ -127,3 +131,38 @@ class EditNodegroupTemplateView(CopyNodegroupTemplateView): success_url = ("horizon:project:" "data_processing.clusters:index") template_name = "nodegroup_templates/configure.html" + + +class ImportNodegroupTemplateFileView(forms.ModalFormView): + template_name = "nodegroup_templates/import.html" + form_class = import_forms.ImportNodegroupTemplateFileForm + submit_label = _("Next") + submit_url = reverse_lazy("horizon:project:data_processing." + "clusters:import-nodegroup-template-file") + success_url = reverse_lazy("horizon:project:data_processing." + "clusters:import-nodegroup-template-details") + page_title = _("Import Node Group Template") + + def get_form_kwargs(self): + kwargs = super( + ImportNodegroupTemplateFileView, self).get_form_kwargs() + kwargs['next_view'] = ImportNodegroupTemplateDetailsView + return kwargs + + +class ImportNodegroupTemplateDetailsView(forms.ModalFormView): + template_name = "nodegroup_templates/import.html" + form_class = import_forms.ImportNodegroupTemplateDetailsForm + submit_label = _("Import") + submit_url = reverse_lazy("horizon:project:data_processing." + "clusters:import-nodegroup-template-details") + success_url = reverse_lazy("horizon:project:data_processing." + "clusters:index") + page_title = _("Import Node Group Template") + + def get_form_kwargs(self): + kwargs = super( + ImportNodegroupTemplateDetailsView, self).get_form_kwargs() + if 'template_upload' in self.kwargs: + kwargs['template_upload'] = self.kwargs['template_upload'] + return kwargs diff --git a/sahara_dashboard/content/data_processing/clusters/templates/nodegroup_templates/_import.html b/sahara_dashboard/content/data_processing/clusters/templates/nodegroup_templates/_import.html new file mode 100644 index 00000000..02f21c2b --- /dev/null +++ b/sahara_dashboard/content/data_processing/clusters/templates/nodegroup_templates/_import.html @@ -0,0 +1,24 @@ +{% extends "horizon/common/_modal_form.html" %} + + +{% load i18n %} + +{% block form_id %}import-nodegroup-template{% endblock %} +{% block form_action %}{{ submit_url }}{% endblock %} +{% block form_attrs %}enctype="multipart/form-data"{% endblock %} + +{% block modal-header %}{{ page_title }}{% endblock %} + +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+{% endblock %} + +{% block modal-footer %} + + {% trans "Cancel" %} +{% endblock %} diff --git a/sahara_dashboard/content/data_processing/clusters/templates/nodegroup_templates/import.html b/sahara_dashboard/content/data_processing/clusters/templates/nodegroup_templates/import.html new file mode 100644 index 00000000..34b2bf16 --- /dev/null +++ b/sahara_dashboard/content/data_processing/clusters/templates/nodegroup_templates/import.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Import Node Group Template" %}{% endblock %} + +{% block main %} + {% include 'nodegroup_templates/_import.html' %} +{% endblock %} diff --git a/sahara_dashboard/content/data_processing/clusters/urls.py b/sahara_dashboard/content/data_processing/clusters/urls.py index 1e10860f..8e2b935a 100644 --- a/sahara_dashboard/content/data_processing/clusters/urls.py +++ b/sahara_dashboard/content/data_processing/clusters/urls.py @@ -48,6 +48,12 @@ urlpatterns = [url(r'^$', views.IndexView.as_view(), name='index'), url(r'^configure-nodegroup-template$', ngt_views.ConfigureNodegroupTemplateView.as_view(), name='configure-nodegroup-template'), + url(r'^import-nodegroup-template-file$', + ngt_views.ImportNodegroupTemplateFileView.as_view(), + name='import-nodegroup-template-file'), + url(r'^import-nodegroup-template-details$', + ngt_views.ImportNodegroupTemplateDetailsView.as_view(), + name='import-nodegroup-template-details'), url(r'^cluster-template/(?P[^/]+)$', ct_views.ClusterTemplateDetailsView.as_view(), name='ct-details'),