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, def cluster_template_create(request, name, plugin_name, hadoop_version,
description=None, cluster_configs=None, description=None, cluster_configs=None,
node_groups=None, anti_affinity=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( return client(request).cluster_templates.create(
name=name, name=name,
plugin_name=plugin_name, plugin_name=plugin_name,
@ -227,7 +228,8 @@ def cluster_template_create(request, name, plugin_name, hadoop_version,
node_groups=node_groups, node_groups=node_groups,
anti_affinity=anti_affinity, anti_affinity=anti_affinity,
net_id=net_id, net_id=net_id,
use_autoconfig=use_autoconfig) use_autoconfig=use_autoconfig,
shares=shares)
def cluster_template_list(request, search_opts=None): 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, hadoop_version, description=None,
cluster_configs=None, node_groups=None, cluster_configs=None, node_groups=None,
anti_affinity=None, net_id=None, anti_affinity=None, net_id=None,
use_autoconfig=None): use_autoconfig=None, shares=None):
try: try:
template = client(request).cluster_templates.update( template = client(request).cluster_templates.update(
cluster_template_id=ct_id, cluster_template_id=ct_id,
@ -258,7 +260,8 @@ def cluster_template_update(request, ct_id, name, plugin_name,
node_groups=node_groups, node_groups=node_groups,
anti_affinity=anti_affinity, anti_affinity=anti_affinity,
net_id=net_id, net_id=net_id,
use_autoconfig=use_autoconfig) use_autoconfig=use_autoconfig,
shares=shares)
except APIException as e: except APIException as e:
raise exceptions.Conflict(e) raise exceptions.Conflict(e)

View File

@ -21,9 +21,10 @@ from oslo_serialization import jsonutils
import six import six
from openstack_dashboard import api as dash_api from openstack_dashboard import api as dash_api
from sahara_dashboard.test import helpers as test
from sahara_dashboard import api from sahara_dashboard import api
from sahara_dashboard.test import helpers as test
INDEX_URL = reverse('horizon:project:data_processing.cluster_templates:index') INDEX_URL = reverse('horizon:project:data_processing.cluster_templates:index')
DETAILS_URL = reverse( DETAILS_URL = reverse(
@ -132,7 +133,8 @@ class DataProcessingClusterTemplateTests(test.TestCase):
cluster_configs=ct.cluster_configs, cluster_configs=ct.cluster_configs,
node_groups=ct.node_groups, node_groups=ct.node_groups,
anti_affinity=ct.anti_affinity, anti_affinity=ct.anti_affinity,
use_autoconfig=False)\ use_autoconfig=False,
shares=ct.shares)\
.AndReturn(new_ct) .AndReturn(new_ct)
self.mox.ReplayAll() self.mox.ReplayAll()

View File

@ -94,6 +94,31 @@ class CopyClusterTemplate(create_flow.ConfigureClusterTemplate):
fields['use_autoconfig'].initial = ( fields['use_autoconfig'].initial = (
self.template.use_autoconfig) self.template.use_autoconfig)
fields["description"].initial = self.template.description 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: except Exception:
exceptions.handle(request, exceptions.handle(request,
_("Unable to fetch template to copy.")) _("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 exceptions
from horizon import forms from horizon import forms
from horizon import workflows from horizon import workflows
from sahara_dashboard.api import manila as manilaclient
from sahara_dashboard.api import sahara as saharaclient 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. \ from sahara_dashboard.content.data_processing. \
utils import anti_affinity as aa utils import anti_affinity as aa
import sahara_dashboard.content.data_processing. \ import sahara_dashboard.content.data_processing. \
utils.workflow_helpers as whelpers utils.workflow_helpers as whelpers
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -238,6 +239,60 @@ class ConfigureNodegroups(workflows.Step):
return context 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, class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
whelpers.StatusFormatMixin): whelpers.StatusFormatMixin):
slug = "configure_cluster_template" slug = "configure_cluster_template"
@ -262,6 +317,9 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
plugin, plugin,
hadoop_version) hadoop_version)
if saharaclient.base.is_service_enabled(request, 'share'):
ConfigureClusterTemplate._register_step(self, SelectClusterShares)
self._populate_tabs(general_parameters, service_parameters) self._populate_tabs(general_parameters, service_parameters)
super(ConfigureClusterTemplate, self).__init__(request, super(ConfigureClusterTemplate, self).__init__(request,
@ -308,6 +366,10 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
plugin, hadoop_version = whelpers.\ plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request) 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 # TODO(nkonovalov): Fix client to support default_image_id
saharaclient.cluster_template_create( saharaclient.cluster_template_create(
request, request,
@ -318,7 +380,8 @@ class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
configs_dict, configs_dict,
node_groups, node_groups,
context["anti_affinity_info"], context["anti_affinity_info"],
use_autoconfig=context['general_use_autoconfig'] use_autoconfig=context['general_use_autoconfig'],
shares=ct_shares
) )
hlps = helpers.Helpers(request) hlps = helpers.Helpers(request)

View File

@ -81,6 +81,10 @@ class EditClusterTemplate(copy_flow.CopyClusterTemplate):
plugin, hadoop_version = whelpers. \ plugin, hadoop_version = whelpers. \
get_plugin_and_hadoop_version(request) get_plugin_and_hadoop_version(request)
ct_shares = []
if "ct_shares" in context:
ct_shares = context["ct_shares"]
saharaclient.cluster_template_update( saharaclient.cluster_template_update(
request=request, request=request,
ct_id=self.cluster_template_id, ct_id=self.cluster_template_id,
@ -91,7 +95,8 @@ class EditClusterTemplate(copy_flow.CopyClusterTemplate):
cluster_configs=configs_dict, cluster_configs=configs_dict,
node_groups=node_groups, node_groups=node_groups,
anti_affinity=context["anti_affinity_info"], anti_affinity=context["anti_affinity_info"],
use_autoconfig=context['general_use_autoconfig'] use_autoconfig=context['general_use_autoconfig'],
shares=ct_shares
) )
return True return True
except exceptions.Conflict as e: except exceptions.Conflict as e:

View File

@ -15,7 +15,6 @@ import itertools
import logging import logging
import uuid import uuid
from django.core.exceptions import ValidationError
from django.utils import encoding from django.utils import encoding
from django.utils import html from django.utils import html
from django.utils import safestring from django.utils import safestring
@ -334,66 +333,6 @@ class SelectNodeProcessesAction(workflows.Action):
help_text = _("Select node processes for the node group") 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): class SelectNodeGroupSharesAction(workflows.Action):
def __init__(self, request, *args, **kwargs): def __init__(self, request, *args, **kwargs):
super(SelectNodeGroupSharesAction, self).__init__( super(SelectNodeGroupSharesAction, self).__init__(
@ -401,9 +340,9 @@ class SelectNodeGroupSharesAction(workflows.Action):
possible_shares = self.get_possible_shares(request) possible_shares = self.get_possible_shares(request)
self.fields["shares"] = MultipleShareChoiceField( self.fields["shares"] = workflow_helpers.MultipleShareChoiceField(
label=_("Select Shares"), label=_("Select Shares"),
widget=ShareWidget(choices=possible_shares), widget=workflow_helpers.ShareWidget(choices=possible_shares),
required=False, required=False,
choices=possible_shares choices=possible_shares
) )

View File

@ -12,6 +12,8 @@
# limitations under the License. # limitations under the License.
import logging import logging
from django.core.exceptions import ValidationError
from django.utils import safestring
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import six import six
@ -324,3 +326,63 @@ class StatusFormatMixin(workflows.Workflow):
return error_description return error_description
else: else:
return message % self.context[self.name_property] 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 "is_proxy_gateway": False
} }
], ],
"shares": [],
"plugin_name": "vanilla", "plugin_name": "vanilla",
"tenant_id": "429ad8447c2d47bc8e0382d244e1d1df", "tenant_id": "429ad8447c2d47bc8e0382d244e1d1df",
"updated_at": None "updated_at": None