Adding cluster template support for shares
The Sahara data processing service now supports manila shares when creating or editing cluster templates. Change-Id: I32c44b6a7fa860df41e06d36c77e600b84bd5733 Partial-Implements: bp sahara-add-shares-to-clusters
This commit is contained in:
parent
f79498fe85
commit
f8660bd893
@ -217,7 +217,8 @@ def nodegroup_template_update(request, ngt_id, name, plugin_name,
|
||||
def cluster_template_create(request, name, plugin_name, hadoop_version,
|
||||
description=None, cluster_configs=None,
|
||||
node_groups=None, anti_affinity=None,
|
||||
net_id=None, use_autoconfig=None):
|
||||
net_id=None, use_autoconfig=None,
|
||||
shares=None):
|
||||
return client(request).cluster_templates.create(
|
||||
name=name,
|
||||
plugin_name=plugin_name,
|
||||
@ -227,7 +228,8 @@ def cluster_template_create(request, name, plugin_name, hadoop_version,
|
||||
node_groups=node_groups,
|
||||
anti_affinity=anti_affinity,
|
||||
net_id=net_id,
|
||||
use_autoconfig=use_autoconfig)
|
||||
use_autoconfig=use_autoconfig,
|
||||
shares=shares)
|
||||
|
||||
|
||||
def cluster_template_list(request, search_opts=None):
|
||||
@ -246,7 +248,7 @@ def cluster_template_update(request, ct_id, name, plugin_name,
|
||||
hadoop_version, description=None,
|
||||
cluster_configs=None, node_groups=None,
|
||||
anti_affinity=None, net_id=None,
|
||||
use_autoconfig=None):
|
||||
use_autoconfig=None, shares=None):
|
||||
try:
|
||||
template = client(request).cluster_templates.update(
|
||||
cluster_template_id=ct_id,
|
||||
@ -258,7 +260,8 @@ def cluster_template_update(request, ct_id, name, plugin_name,
|
||||
node_groups=node_groups,
|
||||
anti_affinity=anti_affinity,
|
||||
net_id=net_id,
|
||||
use_autoconfig=use_autoconfig)
|
||||
use_autoconfig=use_autoconfig,
|
||||
shares=shares)
|
||||
|
||||
except APIException as e:
|
||||
raise exceptions.Conflict(e)
|
||||
|
@ -21,9 +21,10 @@ from oslo_serialization import jsonutils
|
||||
import six
|
||||
|
||||
from openstack_dashboard import api as dash_api
|
||||
from sahara_dashboard.test import helpers as test
|
||||
|
||||
from sahara_dashboard import api
|
||||
from sahara_dashboard.test import helpers as test
|
||||
|
||||
|
||||
INDEX_URL = reverse('horizon:project:data_processing.cluster_templates:index')
|
||||
DETAILS_URL = reverse(
|
||||
@ -132,7 +133,8 @@ class DataProcessingClusterTemplateTests(test.TestCase):
|
||||
cluster_configs=ct.cluster_configs,
|
||||
node_groups=ct.node_groups,
|
||||
anti_affinity=ct.anti_affinity,
|
||||
use_autoconfig=False)\
|
||||
use_autoconfig=False,
|
||||
shares=ct.shares)\
|
||||
.AndReturn(new_ct)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
|
@ -94,6 +94,31 @@ class CopyClusterTemplate(create_flow.ConfigureClusterTemplate):
|
||||
fields['use_autoconfig'].initial = (
|
||||
self.template.use_autoconfig)
|
||||
fields["description"].initial = self.template.description
|
||||
elif isinstance(step, create_flow.SelectClusterShares):
|
||||
fields = step.action.fields
|
||||
fields["shares"].initial = (
|
||||
self._get_share_defaults(fields["shares"].choices)
|
||||
)
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Unable to fetch template to copy."))
|
||||
|
||||
def _get_share_defaults(self, choices):
|
||||
values = dict()
|
||||
for i, choice in enumerate(choices):
|
||||
share_id = choice[0]
|
||||
s = filter(lambda s: s['id'] == share_id, self.template.shares)
|
||||
if len(s) > 0:
|
||||
path = s[0]["path"] if "path" in s[0] else ""
|
||||
values["share_id_{0}".format(i)] = {
|
||||
"id": s[0]["id"],
|
||||
"path": path,
|
||||
"access_level": s[0]["access_level"]
|
||||
}
|
||||
else:
|
||||
values["share_id_{0}".format(i)] = {
|
||||
"id": None,
|
||||
"path": None,
|
||||
"access_level": None
|
||||
}
|
||||
return values
|
||||
|
@ -21,15 +21,16 @@ from saharaclient.api import base as api_base
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
from sahara_dashboard.api import manila as manilaclient
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
from sahara_dashboard.content.data_processing. \
|
||||
utils import helpers as helpers
|
||||
|
||||
from sahara_dashboard.content.data_processing.utils import helpers as helpers
|
||||
from sahara_dashboard.content.data_processing. \
|
||||
utils import anti_affinity as aa
|
||||
import sahara_dashboard.content.data_processing. \
|
||||
utils.workflow_helpers as whelpers
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -238,6 +239,60 @@ class ConfigureNodegroups(workflows.Step):
|
||||
return context
|
||||
|
||||
|
||||
class SelectClusterSharesAction(workflows.Action):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SelectClusterSharesAction, self).__init__(
|
||||
request, *args, **kwargs)
|
||||
|
||||
possible_shares = self.get_possible_shares(request)
|
||||
|
||||
self.fields["shares"] = whelpers.MultipleShareChoiceField(
|
||||
label=_("Select Shares"),
|
||||
widget=whelpers.ShareWidget(choices=possible_shares),
|
||||
required=False,
|
||||
choices=possible_shares
|
||||
)
|
||||
|
||||
def get_possible_shares(self, request):
|
||||
try:
|
||||
shares = manilaclient.share_list(request)
|
||||
choices = [(s.id, s.name) for s in shares]
|
||||
except Exception:
|
||||
exceptions.handle(request, _("Failed to get list of shares"))
|
||||
choices = []
|
||||
return choices
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(SelectClusterSharesAction, self).clean()
|
||||
self._errors = dict()
|
||||
return cleaned_data
|
||||
|
||||
class Meta(object):
|
||||
name = _("Shares")
|
||||
help_text = _("Select the manila shares for this cluster")
|
||||
|
||||
|
||||
class SelectClusterShares(workflows.Step):
|
||||
action_class = SelectClusterSharesAction
|
||||
|
||||
def contribute(self, data, context):
|
||||
post = self.workflow.request.POST
|
||||
shares_details = []
|
||||
for index in range(0, len(self.action.fields['shares'].choices) * 3):
|
||||
if index % 3 == 0:
|
||||
share = post.get("shares_{0}".format(index))
|
||||
if share:
|
||||
path = post.get("shares_{0}".format(index + 1))
|
||||
permissions = post.get("shares_{0}".format(index + 2))
|
||||
shares_details.append({
|
||||
"id": share,
|
||||
"path": path,
|
||||
"access_level": permissions
|
||||
})
|
||||
context['ct_shares'] = shares_details
|
||||
return context
|
||||
|
||||
|
||||
class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
|
||||
whelpers.StatusFormatMixin):
|
||||
slug = "configure_cluster_template"
|
||||
@ -262,6 +317,9 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
|
||||
plugin,
|
||||
hadoop_version)
|
||||
|
||||
if saharaclient.base.is_service_enabled(request, 'share'):
|
||||
ConfigureClusterTemplate._register_step(self, SelectClusterShares)
|
||||
|
||||
self._populate_tabs(general_parameters, service_parameters)
|
||||
|
||||
super(ConfigureClusterTemplate, self).__init__(request,
|
||||
@ -308,6 +366,10 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
|
||||
ct_shares = []
|
||||
if "ct_shares" in context:
|
||||
ct_shares = context["ct_shares"]
|
||||
|
||||
# TODO(nkonovalov): Fix client to support default_image_id
|
||||
saharaclient.cluster_template_create(
|
||||
request,
|
||||
@ -318,7 +380,8 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
|
||||
configs_dict,
|
||||
node_groups,
|
||||
context["anti_affinity_info"],
|
||||
use_autoconfig=context['general_use_autoconfig']
|
||||
use_autoconfig=context['general_use_autoconfig'],
|
||||
shares=ct_shares
|
||||
)
|
||||
|
||||
hlps = helpers.Helpers(request)
|
||||
|
@ -81,6 +81,10 @@ class EditClusterTemplate(copy_flow.CopyClusterTemplate):
|
||||
plugin, hadoop_version = whelpers. \
|
||||
get_plugin_and_hadoop_version(request)
|
||||
|
||||
ct_shares = []
|
||||
if "ct_shares" in context:
|
||||
ct_shares = context["ct_shares"]
|
||||
|
||||
saharaclient.cluster_template_update(
|
||||
request=request,
|
||||
ct_id=self.cluster_template_id,
|
||||
@ -91,7 +95,8 @@ class EditClusterTemplate(copy_flow.CopyClusterTemplate):
|
||||
cluster_configs=configs_dict,
|
||||
node_groups=node_groups,
|
||||
anti_affinity=context["anti_affinity_info"],
|
||||
use_autoconfig=context['general_use_autoconfig']
|
||||
use_autoconfig=context['general_use_autoconfig'],
|
||||
shares=ct_shares
|
||||
)
|
||||
return True
|
||||
except exceptions.Conflict as e:
|
||||
|
@ -15,7 +15,6 @@ import itertools
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils import encoding
|
||||
from django.utils import html
|
||||
from django.utils import safestring
|
||||
@ -334,66 +333,6 @@ class SelectNodeProcessesAction(workflows.Action):
|
||||
help_text = _("Select node processes for the node group")
|
||||
|
||||
|
||||
class ShareWidget(forms.MultiWidget):
|
||||
def __init__(self, choices=()):
|
||||
widgets = []
|
||||
for choice in choices:
|
||||
widgets.append(forms.CheckboxInput(
|
||||
attrs={
|
||||
"label": choice[1],
|
||||
"value": choice[0],
|
||||
}))
|
||||
widgets.append(forms.TextInput())
|
||||
widgets.append(forms.Select(
|
||||
choices=(("rw", _("Read/Write")), ("ro", _("Read only")))))
|
||||
super(ShareWidget, self).__init__(widgets)
|
||||
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
values = []
|
||||
for share in value:
|
||||
values.append(value[share]["id"])
|
||||
values.append(value[share]["path"])
|
||||
values.append(value[share]["access_level"])
|
||||
return values
|
||||
return [None] * len(self.widgets)
|
||||
|
||||
def format_output(self, rendered_widgets):
|
||||
output = []
|
||||
output.append("<table>")
|
||||
output.append("<tr><th>Share</th><th>Enabled</th>"
|
||||
"<th>Path</th><th>Permissions</th></tr>")
|
||||
for i, widget in enumerate(rendered_widgets):
|
||||
item_widget_index = i % 3
|
||||
if item_widget_index == 0:
|
||||
output.append("<tr>")
|
||||
output.append(
|
||||
"<td class='col-sm-2 small-padding'>{0}</td>".format(
|
||||
self.widgets[i].attrs["label"]))
|
||||
# The last 2 form field td need get a larger size
|
||||
if item_widget_index in [1, 2]:
|
||||
size = 4
|
||||
else:
|
||||
size = 2
|
||||
output.append("<td class='col-sm-{0} small-padding'>".format(size)
|
||||
+ widget + "</td>")
|
||||
if item_widget_index == 2:
|
||||
output.append("</tr>")
|
||||
output.append("</table>")
|
||||
return safestring.mark_safe('\n'.join(output))
|
||||
|
||||
|
||||
class MultipleShareChoiceField(forms.MultipleChoiceField):
|
||||
def validate(self, value):
|
||||
if self.required and not value:
|
||||
raise ValidationError(
|
||||
self.error_messages['required'], code='required')
|
||||
if not isinstance(value, list):
|
||||
raise ValidationError(
|
||||
_("The value of shares must be a list of values")
|
||||
)
|
||||
|
||||
|
||||
class SelectNodeGroupSharesAction(workflows.Action):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SelectNodeGroupSharesAction, self).__init__(
|
||||
@ -401,9 +340,9 @@ class SelectNodeGroupSharesAction(workflows.Action):
|
||||
|
||||
possible_shares = self.get_possible_shares(request)
|
||||
|
||||
self.fields["shares"] = MultipleShareChoiceField(
|
||||
self.fields["shares"] = workflow_helpers.MultipleShareChoiceField(
|
||||
label=_("Select Shares"),
|
||||
widget=ShareWidget(choices=possible_shares),
|
||||
widget=workflow_helpers.ShareWidget(choices=possible_shares),
|
||||
required=False,
|
||||
choices=possible_shares
|
||||
)
|
||||
|
@ -12,6 +12,8 @@
|
||||
# limitations under the License.
|
||||
import logging
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils import safestring
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import six
|
||||
@ -324,3 +326,63 @@ class StatusFormatMixin(workflows.Workflow):
|
||||
return error_description
|
||||
else:
|
||||
return message % self.context[self.name_property]
|
||||
|
||||
|
||||
class ShareWidget(forms.MultiWidget):
|
||||
def __init__(self, choices=()):
|
||||
widgets = []
|
||||
for choice in choices:
|
||||
widgets.append(forms.CheckboxInput(
|
||||
attrs={
|
||||
"label": choice[1],
|
||||
"value": choice[0],
|
||||
}))
|
||||
widgets.append(forms.TextInput())
|
||||
widgets.append(forms.Select(
|
||||
choices=(("rw", _("Read/Write")), ("ro", _("Read only")))))
|
||||
super(ShareWidget, self).__init__(widgets)
|
||||
|
||||
def decompress(self, value):
|
||||
if value:
|
||||
values = []
|
||||
for share in value:
|
||||
values.append(value[share]["id"])
|
||||
values.append(value[share]["path"])
|
||||
values.append(value[share]["access_level"])
|
||||
return values
|
||||
return [None] * len(self.widgets)
|
||||
|
||||
def format_output(self, rendered_widgets):
|
||||
output = []
|
||||
output.append("<table>")
|
||||
output.append("<tr><th>Share</th><th>Enabled</th>"
|
||||
"<th>Path</th><th>Permissions</th></tr>")
|
||||
for i, widget in enumerate(rendered_widgets):
|
||||
item_widget_index = i % 3
|
||||
if item_widget_index == 0:
|
||||
output.append("<tr>")
|
||||
output.append(
|
||||
"<td class='col-sm-2 small-padding'>{0}</td>".format(
|
||||
self.widgets[i].attrs["label"]))
|
||||
# The last 2 form field td need get a larger size
|
||||
if item_widget_index in [1, 2]:
|
||||
size = 4
|
||||
else:
|
||||
size = 2
|
||||
output.append("<td class='col-sm-{0} small-padding'>".format(size)
|
||||
+ widget + "</td>")
|
||||
if item_widget_index == 2:
|
||||
output.append("</tr>")
|
||||
output.append("</table>")
|
||||
return safestring.mark_safe('\n'.join(output))
|
||||
|
||||
|
||||
class MultipleShareChoiceField(forms.MultipleChoiceField):
|
||||
def validate(self, value):
|
||||
if self.required and not value:
|
||||
raise ValidationError(
|
||||
self.error_messages['required'], code='required')
|
||||
if not isinstance(value, list):
|
||||
raise ValidationError(
|
||||
_("The value of shares must be a list of values")
|
||||
)
|
||||
|
@ -204,6 +204,7 @@ def data(TEST):
|
||||
"is_proxy_gateway": False
|
||||
}
|
||||
],
|
||||
"shares": [],
|
||||
"plugin_name": "vanilla",
|
||||
"tenant_id": "429ad8447c2d47bc8e0382d244e1d1df",
|
||||
"updated_at": None
|
||||
|
Loading…
Reference in New Issue
Block a user