Merge "Adding cluster template support for shares"

This commit is contained in:
Jenkins 2016-01-06 21:00:30 +00:00 committed by Gerrit Code Review
commit d773bac4e8
8 changed files with 174 additions and 74 deletions

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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
)

View File

@ -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")
)

View File

@ -204,6 +204,7 @@ def data(TEST):
"is_proxy_gateway": False
}
],
"shares": [],
"plugin_name": "vanilla",
"tenant_id": "429ad8447c2d47bc8e0382d244e1d1df",
"updated_at": None