diff --git a/sahara_dashboard/api/sahara.py b/sahara_dashboard/api/sahara.py
index af5b3ec2..28a74369 100644
--- a/sahara_dashboard/api/sahara.py
+++ b/sahara_dashboard/api/sahara.py
@@ -558,3 +558,7 @@ def job_types_list(request):
def verification_update(request, cluster_id, status):
return client(request).clusters.verification_update(cluster_id, status)
+
+
+def plugin_update(request, plugin_name, values):
+ return client(request).plugins.update(plugin_name, values)
diff --git a/sahara_dashboard/content/data_processing/data_plugins/tables.py b/sahara_dashboard/content/data_processing/data_plugins/tables.py
index d1d17707..258bc0c2 100644
--- a/sahara_dashboard/content/data_processing/data_plugins/tables.py
+++ b/sahara_dashboard/content/data_processing/data_plugins/tables.py
@@ -16,6 +16,16 @@ from django.utils.translation import ugettext_lazy as _
from horizon import tables
+from sahara_dashboard.content.data_processing.utils \
+ import workflow_helpers as w_helpers
+
+
+class UpdatePluginAction(tables.LinkAction):
+ name = "update_plugin"
+ verbose_name = _("Update Plugin")
+ url = "horizon:project:data_processing.data_plugins:update"
+ classes = ("ajax-modal", "btn-edit")
+
class PluginsTable(tables.DataTable):
title = tables.Column("title",
@@ -23,8 +33,8 @@ class PluginsTable(tables.DataTable):
link=("horizon:project:data_processing."
"data_plugins:plugin-details"))
- versions = tables.Column("versions",
- verbose_name=_("Supported Versions"),
+ versions = tables.Column(w_helpers.get_pretty_enabled_versions,
+ verbose_name=_("Enabled Versions"),
wrap_list=True,
filters=(filters.unordered_list,))
@@ -34,3 +44,5 @@ class PluginsTable(tables.DataTable):
class Meta(object):
name = "plugins"
verbose_name = _("Plugins")
+
+ row_actions = (UpdatePluginAction,)
diff --git a/sahara_dashboard/content/data_processing/data_plugins/tabs.py b/sahara_dashboard/content/data_processing/data_plugins/tabs.py
index 2c29570b..058d6772 100644
--- a/sahara_dashboard/content/data_processing/data_plugins/tabs.py
+++ b/sahara_dashboard/content/data_processing/data_plugins/tabs.py
@@ -14,6 +14,7 @@
import logging
from django.utils.translation import ugettext_lazy as _
+import six
from horizon import exceptions
from horizon import tabs
@@ -48,6 +49,10 @@ class DetailsTab(tabs.Tab):
slug = "plugin_details_tab"
template_name = "data_plugins/_details.html"
+ def _generate_context(self, plugin):
+ if not plugin:
+ return {'plugin': plugin}
+
def get_context_data(self, request):
plugin_id = self.tab_group.kwargs['plugin_id']
plugin = None
@@ -61,7 +66,52 @@ class DetailsTab(tabs.Tab):
return {"plugin": plugin}
+class LabelsTab(tabs.Tab):
+ name = _("Label details")
+ slug = "label_details_tab"
+ template_name = "data_plugins/_label_details.html"
+
+ def _label_color(self, label):
+ color = 'info'
+ if label == 'deprecated':
+ color = 'danger'
+ elif label == 'stable':
+ color = 'success'
+ return color
+
+ def get_context_data(self, request, **kwargs):
+ plugin_id = self.tab_group.kwargs['plugin_id']
+ plugin = None
+ try:
+ plugin = saharaclient.plugin_get(request, plugin_id)
+ except Exception as e:
+ LOG.error("Unable to get plugin with plugin_id %s (%s)" %
+ (plugin_id, str(e)))
+ exceptions.handle(self.tab_group.request,
+ _('Unable to retrieve plugin.'))
+
+ labels = []
+ for label, data in six.iteritems(plugin.plugin_labels):
+ labels.append(
+ {'name': label,
+ 'color': self._label_color(label),
+ 'description': data.get('description', _("No description")),
+ 'scope': _("Plugin"), 'status': data.get('status', False)})
+
+ for version, version_data in six.iteritems(plugin.version_labels):
+ for label, data in six.iteritems(version_data):
+ labels.append(
+ {'name': label,
+ 'color': self._label_color(label),
+ 'description': data.get('description',
+ _("No description")),
+ 'scope': _("Plugin version %s") % version,
+ 'status': data.get('status', False)})
+
+ return {"labels": labels}
+
+
class PluginDetailsTabs(tabs.TabGroup):
slug = "cluster_details"
- tabs = (DetailsTab,)
+ tabs = (DetailsTab, LabelsTab)
sticky = True
diff --git a/sahara_dashboard/content/data_processing/data_plugins/templates/data_plugins/_label_details.html b/sahara_dashboard/content/data_processing/data_plugins/templates/data_plugins/_label_details.html
new file mode 100644
index 00000000..5d4d9378
--- /dev/null
+++ b/sahara_dashboard/content/data_processing/data_plugins/templates/data_plugins/_label_details.html
@@ -0,0 +1,27 @@
+{% load i18n %}
+
+
{% trans "Cluster health checks" %}
+
+
+
+ {% trans "Label" %} |
+ {% trans "Scope" %} |
+ {% trans "Checked?" %} |
+ {% trans "Description" %} |
+
+
+
+ {% for label in labels %}
+
+
+
+ {{ label.name }}
+
+ |
+ {{ label.scope }} |
+ {{ label.status|yesno }} |
+ {{ label.description }} |
+
+ {% endfor %}
+
+
diff --git a/sahara_dashboard/content/data_processing/data_plugins/templates/data_plugins/update.html b/sahara_dashboard/content/data_processing/data_plugins/templates/data_plugins/update.html
new file mode 100644
index 00000000..da4f846d
--- /dev/null
+++ b/sahara_dashboard/content/data_processing/data_plugins/templates/data_plugins/update.html
@@ -0,0 +1,7 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Update Plugin" %}{% endblock %}
+
+{% block main %}
+ {% include 'horizon/common/_workflow.html' %}
+{% endblock %}
\ No newline at end of file
diff --git a/sahara_dashboard/content/data_processing/data_plugins/tests.py b/sahara_dashboard/content/data_processing/data_plugins/tests.py
index fc18477b..0c5c86a9 100644
--- a/sahara_dashboard/content/data_processing/data_plugins/tests.py
+++ b/sahara_dashboard/content/data_processing/data_plugins/tests.py
@@ -38,7 +38,7 @@ class DataProcessingPluginsTests(test.TestCase):
@test.create_stubs({api.sahara: ('plugin_get',)})
def test_details(self):
- api.sahara.plugin_get(IsA(http.HttpRequest), IsA(six.text_type)) \
+ api.sahara.plugin_get(IsA(http.HttpRequest), IsA(six.text_type)).MultipleTimes() \
.AndReturn(self.plugins.list()[0])
self.mox.ReplayAll()
res = self.client.get(DETAILS_URL)
diff --git a/sahara_dashboard/content/data_processing/data_plugins/urls.py b/sahara_dashboard/content/data_processing/data_plugins/urls.py
index 82073913..6aeb2949 100644
--- a/sahara_dashboard/content/data_processing/data_plugins/urls.py
+++ b/sahara_dashboard/content/data_processing/data_plugins/urls.py
@@ -20,4 +20,7 @@ urlpatterns = [
url(r'^$', views.PluginsView.as_view(), name='index'),
url(r'^(?P[^/]+)$',
views.PluginDetailsView.as_view(), name='plugin-details'),
+ url(r'^(?P[^/]+)/update',
+ views.UpdatePluginView.as_view(),
+ name='update'),
]
diff --git a/sahara_dashboard/content/data_processing/data_plugins/views.py b/sahara_dashboard/content/data_processing/data_plugins/views.py
index 9e2a7796..e9a14a48 100644
--- a/sahara_dashboard/content/data_processing/data_plugins/views.py
+++ b/sahara_dashboard/content/data_processing/data_plugins/views.py
@@ -16,12 +16,15 @@ from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from horizon import tabs
+from horizon import workflows
from sahara_dashboard.api import sahara as saharaclient
import sahara_dashboard.content.data_processing.data_plugins. \
tables as p_tables
import sahara_dashboard.content.data_processing.data_plugins. \
tabs as p_tabs
+from sahara_dashboard.content.data_processing.data_plugins.workflows \
+ import update
class PluginsView(tables.DataTableView):
@@ -43,3 +46,33 @@ class PluginDetailsView(tabs.TabView):
tab_group_class = p_tabs.PluginDetailsTabs
template_name = 'horizon/common/_detail.html'
page_title = _("Data Processing Plugin Details")
+
+
+class UpdatePluginView(workflows.WorkflowView):
+ workflow_class = update.UpdatePlugin
+ success_url = "horizon:project:data_processing.data_plugins"
+ classes = ("ajax-modal",)
+ template_name = "data_plugins/update.html"
+ page_title = _("Update Plugin")
+
+ def get_context_data(self, **kwargs):
+ context = super(UpdatePluginView, self) \
+ .get_context_data(**kwargs)
+ context["plugin_name"] = kwargs["plugin_name"]
+ return context
+
+ def get_object(self, *args, **kwargs):
+ if not hasattr(self, "_object"):
+ plugin_name = self.kwargs['plugin_name']
+ try:
+ plugin = saharaclient.plugin_get(self.request, plugin_name)
+ except Exception:
+ exceptions.handle(self.request,
+ _("Unable to fetch plugin object."))
+ self._object = plugin
+ return self._object
+
+ def get_initial(self):
+ initial = super(UpdatePluginView, self).get_initial()
+ initial['plugin_name'] = self.kwargs['plugin_name']
+ return initial
diff --git a/sahara_dashboard/content/data_processing/data_plugins/workflows/__init__.py b/sahara_dashboard/content/data_processing/data_plugins/workflows/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/sahara_dashboard/content/data_processing/data_plugins/workflows/update.py b/sahara_dashboard/content/data_processing/data_plugins/workflows/update.py
new file mode 100644
index 00000000..e50d2209
--- /dev/null
+++ b/sahara_dashboard/content/data_processing/data_plugins/workflows/update.py
@@ -0,0 +1,111 @@
+# 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 saharaclient.api import base as api_base
+import six
+
+from horizon import exceptions
+from horizon import forms
+from horizon import workflows
+
+from sahara_dashboard.api import sahara as saharaclient
+
+
+class UpdateLabelsAction(workflows.Action):
+ def __init__(self, request, *args, **kwargs):
+ super(UpdateLabelsAction, self).__init__(request, *args, **kwargs)
+ plugin_name = [
+ x['plugin_name'] for x in args if 'plugin_name' in x][0]
+ plugin = saharaclient.plugin_get(request, plugin_name)
+ self._serialize_labels(
+ 'plugin_', _("Plugin label"), plugin.plugin_labels)
+ vers_labels = plugin.version_labels
+ for version in vers_labels.keys():
+ field_label = _("Plugin version %(version)s label") % {
+ 'version': version}
+ self._serialize_labels(
+ 'version_%s_' % version, field_label, vers_labels[version])
+ self.fields["plugin_name"] = forms.CharField(
+ widget=forms.HiddenInput(),
+ initial=plugin_name)
+
+ def _serialize_labels(self, prefix, prefix_trans, labels):
+ for name, label in six.iteritems(labels):
+ if not label['mutable']:
+ continue
+ res_name_translated = "%s: %s" % (prefix_trans, name)
+ res_name = "label_%s%s" % (prefix, name)
+ self.fields[res_name] = forms.BooleanField(
+ label=res_name_translated,
+ help_text=label['description'],
+ widget=forms.CheckboxInput(),
+ initial=label['status'],
+ required=False,
+ )
+
+ class Meta(object):
+ name = _("Plugin")
+ help_text = _("Update the plugin labels")
+
+
+class UpdatePluginStep(workflows.Step):
+ action_class = UpdateLabelsAction
+ depends_on = ('plugin_name', )
+
+ def contribute(self, data, context):
+ for name, item in six.iteritems(data):
+ context[name] = item
+ return context
+
+
+class UpdatePlugin(workflows.Workflow):
+ slug = "update_plugin"
+ name = _("Update Plugin")
+ success_message = _("Updated")
+ failure_message = _("Could not update plugin")
+ success_url = "horizon:project:data_processing.data_plugins:index"
+ default_steps = (UpdatePluginStep,)
+
+ def __init__(self, request, context_seed, entry_point, *args, **kwargs):
+ super(UpdatePlugin, self).__init__(
+ request, context_seed, entry_point, *args, **kwargs)
+
+ def _get_update_values(self, context):
+ values = {'plugin_labels': {}, 'version_labels': {}}
+ for item, item_value in six.iteritems(context):
+ if not item.startswith('label_'):
+ continue
+ name = item.split('_')[1:]
+ if name[0] == 'plugin':
+ values['plugin_labels'][name[1]] = {'status': item_value}
+ else:
+ if name[1] not in values['version_labels']:
+ values['version_labels'][name[1]] = {}
+ values['version_labels'][
+ name[1]][name[2]] = {'status': item_value}
+ return values
+
+ def handle(self, request, context):
+ try:
+ update_values = self._get_update_values(context)
+ saharaclient.plugin_update(
+ request, context['plugin_name'], update_values)
+ return True
+ except api_base.APIException as e:
+ self.error_description = str(e)
+ return False
+ except Exception:
+ exceptions.handle(request,
+ _("Plugin update failed."))
+ return False
diff --git a/sahara_dashboard/content/data_processing/utils/workflow_helpers.py b/sahara_dashboard/content/data_processing/utils/workflow_helpers.py
index d566caef..1b549c1f 100644
--- a/sahara_dashboard/content/data_processing/utils/workflow_helpers.py
+++ b/sahara_dashboard/content/data_processing/utils/workflow_helpers.py
@@ -25,6 +25,7 @@ from openstack_dashboard.api import network
from sahara_dashboard.api import sahara as saharaclient
+
LOG = logging.getLogger(__name__)
@@ -249,6 +250,7 @@ def populate_image_choices(self, request, context, empty_choice=False):
class PluginAndVersionMixin(object):
def _generate_plugin_version_fields(self, sahara):
plugins = sahara.plugins.list()
+ plugins = filter(is_plugin_not_hidden_for_user, plugins)
plugin_choices = [(plugin.name, plugin.title) for plugin in plugins]
self.fields["plugin_name"] = forms.ChoiceField(
@@ -262,7 +264,8 @@ class PluginAndVersionMixin(object):
field_name = plugin.name + "_version"
choice_field = forms.ChoiceField(
label=_("Version"),
- choices=[(version, version) for version in plugin.versions],
+ choices=[(version, version)
+ for version in get_enabled_versions(plugin)],
widget=forms.Select(
attrs={"class": "plugin_version_choice switched "
+ field_name + "_choice",
@@ -417,3 +420,33 @@ class MultipleShareChoiceField(forms.MultipleChoiceField):
raise ValidationError(
_("The value of shares must be a list of values")
)
+
+
+def is_plugin_not_hidden_for_user(plugin):
+ hidden_lbl = plugin.plugin_labels.get('hidden')
+ if hidden_lbl and hidden_lbl['status']:
+ return False
+ if get_enabled_versions(plugin):
+ return True
+ return False
+
+
+def get_enabled_versions(plugin):
+ lbs = plugin.version_labels
+
+ versions = []
+ for version, data in six.iteritems(lbs):
+ if data.get('enabled', {'status': True}).get('status', True):
+ versions.append(version)
+
+ if not plugin.plugin_labels.get(
+ 'enabled', {'status': True}).get('status', True):
+ versions = []
+ return versions
+
+
+def get_pretty_enabled_versions(plugin):
+ versions = get_enabled_versions(plugin)
+ if len(versions) == 0:
+ versions = [_("No enabled versions")]
+ return versions
diff --git a/sahara_dashboard/test/test_data/sahara_data.py b/sahara_dashboard/test/test_data/sahara_data.py
index 3ff22180..8b6c0da6 100644
--- a/sahara_dashboard/test/test_data/sahara_data.py
+++ b/sahara_dashboard/test/test_data/sahara_data.py
@@ -42,7 +42,24 @@ def data(TEST):
"description": "vanilla plugin",
"name": "vanilla",
"title": "Vanilla Apache Hadoop",
- "versions": ["2.3.0", "1.2.1"]
+ "versions": ["2.3.0", "1.2.1"],
+ 'version_labels': {
+ '2.3.0': {
+ 'enabled': {
+ 'status': True
+ }
+ },
+ '1.2.1': {
+ 'enabled': {
+ 'status': True
+ }
+ }
+ },
+ 'plugin_labels': {
+ 'enabled': {
+ 'status': True
+ }
+ }
}
plugin1 = plugins.Plugin(plugins.PluginManager(None), plugin1_dict)
@@ -95,7 +112,19 @@ def data(TEST):
},
],
"title": "Vanilla Apache Hadoop",
- "name": "vanilla"
+ "name": "vanilla",
+ 'version_labels': {
+ '1.2.1': {
+ 'enabled': {
+ 'status': True
+ }
+ }
+ },
+ 'plugin_labels': {
+ 'enabled': {
+ 'status': True
+ }
+ }
}
TEST.plugins_configs.add(plugins.Plugin(plugins.PluginManager(None),
diff --git a/tools/gate/integration/commons b/tools/gate/integration/commons
index dda9d524..28bd051f 100644
--- a/tools/gate/integration/commons
+++ b/tools/gate/integration/commons
@@ -2,4 +2,6 @@
set -ex
+export DEST=${DEST:-$BASE/new}
+export DEVSTACK_DIR=${DEVSTACK_DIR:-$DEST/devstack}
export SAHARA_DASHBOARD_SCREENSHOTS_DIR=/opt/stack/new/sahara-dashboard/.tox/py27integration/src/horizon/openstack_dashboard/test/integration_tests/integration_tests_screenshots
diff --git a/tools/gate/integration/post_test_hook.sh b/tools/gate/integration/post_test_hook.sh
index 47fe5fc7..86d00d6c 100755
--- a/tools/gate/integration/post_test_hook.sh
+++ b/tools/gate/integration/post_test_hook.sh
@@ -13,6 +13,22 @@ sudo apt-get -y purge firefox
sudo dpkg -i firefox.deb
sudo rm firefox.deb
+cat >> /tmp/fake_config.json <