Repository cleanup

The dashboard is now available in Horizon under the data_processing
module.

Partially-implements bp: merge-sahara-dashboard-to-horizon
Change-Id: Ic728f25dc337ccca56c4d147458d61dd20acaaea
This commit is contained in:
Nikita Konovalov 2014-08-15 14:32:55 +04:00
parent c76ec6ee1f
commit 8706077667
157 changed files with 16 additions and 7917 deletions

View File

@ -1,5 +1,20 @@
OpenStack Dashboard plugin for Sahara project
==============================================
=============================================
NOTE:
=====
Sahara Dashboard is now integrated into the Horizon project. http://github.com/openstack/horizon
The panels can now be found in the data_processing module of Projects Dashboard.
https://github.com/openstack/horizon/tree/master/openstack_dashboard/dashboards/project/data_processing
This repository will now contain only selenium based tests for the Data Processing Panels.
This change is not meant to be backported to stable/icehouse or any other tags or branches.
So you can still use Sahara Dasboard as a separate dashboard with stable/icehouse version of Horizon.
Links:
------
Sahara project: https://github.com/openstack/sahara

View File

@ -1,4 +1 @@
pbr>=0.6,!=0.7,<1.0
six>=1.7.0
python-saharaclient>=0.6.0

View File

@ -1,78 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from horizon import exceptions
from saharaclient.api import base as api_base
from saharaclient import client as api_client
from saharadashboard.utils import importutils
# horizon.api is for backward compatibility with folsom
base = importutils.import_any('openstack_dashboard.api.base',
'horizon.api.base')
keystone = importutils.import_any('openstack_dashboard.api.keystone',
'horizon.api.keystone')
LOG = logging.getLogger(__name__)
def get_horizon_parameter(name, default_value):
import openstack_dashboard.settings
if hasattr(openstack_dashboard.settings, name):
return getattr(openstack_dashboard.settings, name)
else:
logging.info('Parameter %s is not found in local_settings.py, '
'using default "%s"' % (name, default_value))
return default_value
# These parameters should be defined in Horizon's local_settings.py
# Example SAHARA_URL - http://localhost:9000/v1.0
SAHARA_URL = get_horizon_parameter('SAHARA_URL', None)
# "type" of Sahara service registered in keystone
SAHARA_SERVICE = get_horizon_parameter('SAHARA_SERVICE', 'data_processing')
# hint to generate additional Neutron network field
SAHARA_USE_NEUTRON = get_horizon_parameter('SAHARA_USE_NEUTRON', False)
AUTO_ASSIGNMENT_ENABLED = get_horizon_parameter('AUTO_ASSIGNMENT_ENABLED',
True)
exceptions.RECOVERABLE += (api_base.APIException,)
def get_sahara_url(request):
if SAHARA_URL is not None:
url = SAHARA_URL.rstrip('/')
if url.split('/')[-1] in ['v1.0', 'v1.1']:
url = SAHARA_URL + '/' + request.user.tenant_id
return url
return base.url_for(request, SAHARA_SERVICE)
def client(request):
endpoint_type = get_horizon_parameter('OPENSTACK_ENDPOINT_TYPE',
'internalURL')
auth_url = keystone._get_endpoint_url(request, endpoint_type)
return api_client.Client('1.1', sahara_url=get_sahara_url(request),
service_type=SAHARA_SERVICE,
project_id=request.user.tenant_id,
input_auth_token=request.user.token.id,
auth_url=auth_url)

View File

@ -1,76 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 saharadashboard.utils import workflow_helpers as work_helpers
class Helpers(object):
def __init__(self, sahara_client):
self.sahara = sahara_client
self.plugins = self.sahara.plugins
def _get_node_processes(self, plugin):
processes = []
for proc_lst in plugin.node_processes.values():
processes += proc_lst
return [(proc_name, proc_name) for proc_name in processes]
def get_node_processes(self, plugin_name, hadoop_version):
plugin = self.plugins.get_version_details(plugin_name, hadoop_version)
return self._get_node_processes(plugin)
def _extract_parameters(self, configs, scope, applicable_target):
parameters = []
for config in configs:
if (config['scope'] == scope and
config['applicable_target'] == applicable_target):
parameters.append(work_helpers.Parameter(config))
return parameters
def get_cluster_general_configs(self, plugin_name, hadoop_version):
plugin = self.plugins.get_version_details(plugin_name, hadoop_version)
return self._extract_parameters(plugin.configs, 'cluster', "general")
def get_general_node_group_configs(self, plugin_name, hadoop_version):
plugin = self.plugins.get_version_details(plugin_name, hadoop_version)
return self._extract_parameters(plugin.configs, 'node', 'general')
def get_targeted_node_group_configs(self, plugin_name, hadoop_version):
plugin = self.plugins.get_version_details(plugin_name, hadoop_version)
parameters = {}
for service in plugin.node_processes.keys():
parameters[service] = self._extract_parameters(plugin.configs,
'node', service)
return parameters
def get_targeted_cluster_configs(self, plugin_name, hadoop_version):
plugin = self.plugins.get_version_details(plugin_name, hadoop_version)
parameters = {}
for service in plugin.node_processes.keys():
parameters[service] = self._extract_parameters(plugin.configs,
'cluster', service)
return parameters

View File

@ -1,58 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 forms
from horizon import messages
from saharaclient.api import base as api_base
from saharadashboard.api import client as saharaclient
from saharadashboard.utils import workflow_helpers
class UploadFileForm(forms.SelfHandlingForm,
workflow_helpers.PluginAndVersionMixin):
template_name = forms.CharField(max_length=80,
label=_("Cluster Template Name"))
def __init__(self, request, *args, **kwargs):
super(UploadFileForm, self).__init__(request, *args, **kwargs)
sahara = saharaclient.client(request)
self._generate_plugin_version_fields(sahara)
self.fields['template_file'] = forms.FileField(label=_("Template"),
required=True)
def handle(self, request, data):
try:
# we can set a limit on file size, but should we?
filecontent = self.files['template_file'].read()
plugin_name = data['plugin_name']
hadoop_version = data.get(plugin_name + "_version")
sahara = saharaclient.client(request)
sahara.plugins.convert_to_cluster_template(plugin_name,
hadoop_version,
data['template_name'],
filecontent)
return True
except api_base.APIException as e:
messages.error(request, str(e))
return False
except Exception as e:
messages.error(request, str(e))
return True

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class ClusterTemplatesPanel(horizon.Panel):
name = _("Cluster Templates")
slug = 'cluster_templates'
dashboard.SaharaDashboard.register(ClusterTemplatesPanel)

View File

@ -1,120 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.core import urlresolvers
from django import template
from django.utils import http
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
def render_node_groups(cluster_template):
template_name = 'cluster_templates/_nodegroups_list.html'
context = {"node_groups": cluster_template.node_groups}
return template.loader.render_to_string(template_name, context)
class UploadFile(tables.LinkAction):
name = 'upload_file'
verbose_name = _("Upload Template")
url = 'horizon:sahara:cluster_templates:upload_file'
classes = ("btn-launch", "ajax-modal")
class CreateCluster(tables.LinkAction):
name = "create cluster"
verbose_name = _("Launch Cluster")
url = "horizon:sahara:clusters:configure-cluster"
classes = ("btn-launch", "ajax-modal")
def get_link_url(self, datum):
base_url = urlresolvers.reverse(self.url)
params = http.urlencode({"hadoop_version": datum.hadoop_version,
"plugin_name": datum.plugin_name,
"cluster_template_id": datum.id})
return "?".join([base_url, params])
class CopyTemplate(tables.LinkAction):
name = "copy"
verbose_name = _("Copy Template")
url = "horizon:sahara:cluster_templates:copy"
classes = ("ajax-modal", )
class DeleteTemplate(tables.BatchAction):
name = "delete_cluster_template"
verbose_name = _("Delete Template")
classes = ("btn-terminate", "btn-danger")
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Template")
data_type_plural = _("Templates")
def allowed(self, request, template):
return True
def action(self, request, template_id):
sahara = saharaclient(request)
sahara.cluster_templates.delete(template_id)
class CreateClusterTemplate(tables.LinkAction):
name = "create"
verbose_name = _("Create Template")
url = "horizon:sahara:cluster_templates:create-cluster-template"
classes = ("ajax-modal", "btn-create", "create-clustertemplate-btn")
class ConfigureClusterTemplate(tables.LinkAction):
name = "configure"
verbose_name = _("Configure Cluster Template")
url = "horizon:sahara:cluster_templates:configure-cluster-template"
classes = ("ajax-modal", "btn-create", "configure-clustertemplate-btn")
attrs = {"style": "display: none"}
class ClusterTemplatesTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:sahara:cluster_templates:details"))
plugin_name = tables.Column("plugin_name",
verbose_name=_("Plugin"))
hadoop_version = tables.Column("hadoop_version",
verbose_name=_("Hadoop Version"))
node_groups = tables.Column(render_node_groups,
verbose_name=_("Node Groups"))
description = tables.Column("description",
verbose_name=_("Description"))
class Meta:
name = "cluster_templates"
verbose_name = _("Cluster Templates")
table_actions = (UploadFile,
CreateClusterTemplate,
ConfigureClusterTemplate,
DeleteTemplate,)
row_actions = (CreateCluster,
CopyTemplate,
DeleteTemplate,)

View File

@ -1,64 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
from saharadashboard.utils import importutils
from saharadashboard.utils import workflow_helpers as helpers
nova = importutils.import_any('openstack_dashboard.api.nova',
'horizon.api.nova')
LOG = logging.getLogger(__name__)
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "cluster_template_details_tab"
template_name = ("cluster_templates/_details.html")
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']
sahara = saharaclient(request)
template = sahara.cluster_templates.get(template_id)
return {"template": template}
class NodeGroupsTab(tabs.Tab):
name = _("Node Groups")
slug = "cluster_template_nodegroups_tab"
template_name = ("cluster_templates/_nodegroups_details.html")
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']
sahara = saharaclient(request)
template = sahara.cluster_templates.get(template_id)
for ng in template.node_groups:
if not ng["flavor_id"]:
continue
ng["flavor_name"] = nova.flavor_get(request, ng["flavor_id"]).name
ng["node_group_template"] = helpers.safe_call(
sahara.node_group_templates.get,
ng.get("node_group_template_id", None))
return {"template": template}
class ClusterTemplateDetailsTabs(tabs.TabGroup):
slug = "cluster_template_details"
tabs = (GeneralTab, NodeGroupsTab, )
sticky = True

View File

@ -1,45 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import saharadashboard.cluster_templates.views as views
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('',
url(r'^$', views.ClusterTemplatesView.as_view(),
name='index'),
url(r'^$', views.ClusterTemplatesView.as_view(),
name='cluster-templates'),
url(r'^upload_file$',
views.UploadFileView.as_view(),
name='upload_file'),
url(r'^create-cluster-template$',
views.CreateClusterTemplateView.as_view(),
name='create-cluster-template'),
url(r'^configure-cluster-template$',
views.ConfigureClusterTemplateView.as_view(),
name='configure-cluster-template'),
url(r'^(?P<template_id>[^/]+)$',
views.ClusterTemplateDetailsView.as_view(),
name='details'),
url(r'^(?P<template_id>[^/]+)/copy$',
views.CopyClusterTemplateView.as_view(),
name='copy'))

View File

@ -1,100 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.core.urlresolvers import reverse_lazy
from horizon import forms
from horizon import tables
from horizon import tabs
from horizon import workflows
from saharadashboard.api.client import client as saharaclient
from saharadashboard.cluster_templates import forms as cluster_forms
from saharadashboard.cluster_templates.tables import ClusterTemplatesTable
import saharadashboard.cluster_templates.tabs as _tabs
import saharadashboard.cluster_templates.workflows.copy as copy_flow
import saharadashboard.cluster_templates.workflows.create as create_flow
LOG = logging.getLogger(__name__)
class ClusterTemplatesView(tables.DataTableView):
table_class = ClusterTemplatesTable
template_name = 'cluster_templates/cluster_templates.html'
def get_data(self):
sahara = saharaclient(self.request)
cluster_templates = sahara.cluster_templates.list()
return cluster_templates
class ClusterTemplateDetailsView(tabs.TabView):
tab_group_class = _tabs.ClusterTemplateDetailsTabs
template_name = 'cluster_templates/details.html'
def get_context_data(self, **kwargs):
context = super(ClusterTemplateDetailsView, self)\
.get_context_data(**kwargs)
return context
def get_data(self):
pass
class UploadFileView(forms.ModalFormView):
form_class = cluster_forms.UploadFileForm
template_name = 'cluster_templates/upload_file.html'
success_url = reverse_lazy('horizon:sahara:cluster_templates:index')
class CreateClusterTemplateView(workflows.WorkflowView):
workflow_class = create_flow.CreateClusterTemplate
success_url = \
"horizon:sahara:cluster_templates:create-cluster-template"
classes = ("ajax-modal")
template_name = "cluster_templates/create.html"
class ConfigureClusterTemplateView(workflows.WorkflowView):
workflow_class = create_flow.ConfigureClusterTemplate
success_url = "horizon:sahara:cluster_templates"
template_name = "cluster_templates/configure.html"
class CopyClusterTemplateView(workflows.WorkflowView):
workflow_class = copy_flow.CopyClusterTemplate
success_url = "horizon:sahara:cluster_templates"
template_name = "cluster_templates/configure.html"
def get_context_data(self, **kwargs):
context = super(CopyClusterTemplateView, self)\
.get_context_data(**kwargs)
context["template_id"] = kwargs["template_id"]
return context
def get_object(self, *args, **kwargs):
if not hasattr(self, "_object"):
template_id = self.kwargs['template_id']
sahara = saharaclient(self.request)
template = sahara.cluster_templates.get(template_id)
self._object = template
return self._object
def get_initial(self):
initial = super(CopyClusterTemplateView, self).get_initial()
initial.update({'template_id': self.kwargs['template_id']})
return initial

View File

@ -1,78 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext as _
from saharadashboard.api.client import client as saharaclient
import saharadashboard.cluster_templates.workflows.create as create_flow
from saharadashboard.utils.workflow_helpers import build_node_group_fields
LOG = logging.getLogger(__name__)
class CopyClusterTemplate(create_flow.ConfigureClusterTemplate):
success_message = _("Cluster Template copy %s created")
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
sahara = saharaclient(request)
template_id = context_seed["template_id"]
template = sahara.cluster_templates.get(template_id)
self._set_configs_to_copy(template.cluster_configs)
request.GET = request.GET.copy()
request.GET.update({"plugin_name": template.plugin_name})
request.GET.update({"hadoop_version": template.hadoop_version})
request.GET.update({"aa_groups": template.anti_affinity})
super(CopyClusterTemplate, self).__init__(request, context_seed,
entry_point, *args,
**kwargs)
# init Node Groups
for step in self.steps:
if isinstance(step, create_flow.ConfigureNodegroups):
ng_action = step.action
template_ngs = template.node_groups
if 'forms_ids' not in request.POST:
ng_action.groups = []
for id in range(0, len(template_ngs), 1):
group_name = "group_name_" + str(id)
template_id = "template_id_" + str(id)
count = "count_" + str(id)
templ_ng = template_ngs[id]
ng_action.groups.append(
{"name": templ_ng["name"],
"template_id": templ_ng["node_group_template_id"],
"count": templ_ng["count"],
"id": id,
"deletable": "true"})
build_node_group_fields(ng_action,
group_name,
template_id,
count)
elif isinstance(step, create_flow.GeneralConfig):
fields = step.action.fields
fields["cluster_template_name"].initial = \
template.name + "-copy"
fields["description"].initial = template.description

View File

@ -1,299 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import json
import logging
from django.utils.translation import ugettext as _
from horizon import exceptions
from horizon import forms
from horizon import workflows
from saharaclient.api import base as api_base
from saharadashboard.api import client as saharaclient
from saharadashboard.api import helpers as helpers
from saharadashboard.utils import anti_affinity as aa
import saharadashboard.utils.workflow_helpers as whelpers
LOG = logging.getLogger(__name__)
class SelectPluginAction(workflows.Action):
hidden_create_field = forms.CharField(
required=False,
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
def __init__(self, request, *args, **kwargs):
super(SelectPluginAction, self).__init__(request, *args, **kwargs)
sahara = saharaclient.client(request)
plugins = sahara.plugins.list()
plugin_choices = [(plugin.name, plugin.title) for plugin in plugins]
self.fields["plugin_name"] = forms.ChoiceField(
label=_("Plugin name"),
required=True,
choices=plugin_choices,
widget=forms.Select(attrs={"class": "plugin_name_choice"}))
for plugin in plugins:
field_name = plugin.name + "_version"
choice_field = forms.ChoiceField(
label=_("Hadoop version"),
required=True,
choices=[(version, version) for version in plugin.versions],
widget=forms.Select(
attrs={"class": "plugin_version_choice "
+ field_name + "_choice"})
)
self.fields[field_name] = choice_field
class Meta:
name = _("Select plugin and hadoop version for cluster template")
help_text_template = ("cluster_templates/_create_general_help.html")
class SelectPlugin(workflows.Step):
action_class = SelectPluginAction
class CreateClusterTemplate(workflows.Workflow):
slug = "create_cluster_template"
name = _("Create Cluster Template")
finalize_button_name = _("Create")
success_message = _("Created")
failure_message = _("Could not create")
success_url = "horizon:sahara:cluster_templates:index"
default_steps = (SelectPlugin,)
class GeneralConfigAction(workflows.Action):
hidden_configure_field = forms.CharField(
required=False,
widget=forms.HiddenInput(attrs={"class": "hidden_configure_field"}))
hidden_to_delete_field = forms.CharField(
required=False,
widget=forms.HiddenInput(attrs={"class": "hidden_to_delete_field"}))
cluster_template_name = forms.CharField(label=_("Template Name"),
required=True)
description = forms.CharField(label=_("Description"),
required=False,
widget=forms.Textarea)
anti_affinity = aa.anti_affinity_field()
def __init__(self, request, *args, **kwargs):
super(GeneralConfigAction, self).__init__(request, *args, **kwargs)
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
self.fields["plugin_name"] = forms.CharField(
widget=forms.HiddenInput(),
initial=plugin
)
self.fields["hadoop_version"] = forms.CharField(
widget=forms.HiddenInput(),
initial=hadoop_version
)
populate_anti_affinity_choices = aa.populate_anti_affinity_choices
def get_help_text(self):
extra = dict()
plugin, hadoop_version = whelpers\
.get_plugin_and_hadoop_version(self.request)
extra["plugin_name"] = plugin
extra["hadoop_version"] = hadoop_version
return super(GeneralConfigAction, self).get_help_text(extra)
def clean(self):
cleaned_data = super(GeneralConfigAction, self).clean()
if cleaned_data.get("hidden_configure_field", None) \
== "create_nodegroup":
self._errors = dict()
return cleaned_data
class Meta:
name = _("Details")
help_text_template = \
("cluster_templates/_configure_general_help.html")
class GeneralConfig(workflows.Step):
action_class = GeneralConfigAction
contributes = ("hidden_configure_field", )
def contribute(self, data, context):
for k, v in data.items():
context["general_" + k] = v
post = self.workflow.request.POST
context['anti_affinity_info'] = post.getlist("anti_affinity")
return context
class ConfigureNodegroupsAction(workflows.Action):
hidden_nodegroups_field = forms.CharField(
required=False,
widget=forms.HiddenInput(attrs={"class": "hidden_nodegroups_field"}))
forms_ids = forms.CharField(
required=False,
widget=forms.HiddenInput())
def __init__(self, request, *args, **kwargs):
super(ConfigureNodegroupsAction, self). \
__init__(request, *args, **kwargs)
sahara = saharaclient.client(request)
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
self.templates = sahara.node_group_templates.find(
plugin_name=plugin,
hadoop_version=hadoop_version)
deletable = request.REQUEST.get("deletable", dict())
if 'forms_ids' in request.POST:
self.groups = []
for id in json.loads(request.POST['forms_ids']):
group_name = "group_name_" + str(id)
template_id = "template_id_" + str(id)
count = "count_" + str(id)
self.groups.append({"name": request.POST[group_name],
"template_id": request.POST[template_id],
"count": request.POST[count],
"id": id,
"deletable": deletable.get(
request.POST[group_name], "true")})
whelpers.build_node_group_fields(self,
group_name,
template_id,
count)
def clean(self):
cleaned_data = super(ConfigureNodegroupsAction, self).clean()
if cleaned_data.get("hidden_nodegroups_field", None) \
== "create_nodegroup":
self._errors = dict()
return cleaned_data
class Meta:
name = _("Node Groups")
class ConfigureNodegroups(workflows.Step):
action_class = ConfigureNodegroupsAction
contributes = ("hidden_nodegroups_field", )
template_name = "cluster_templates/cluster_node_groups_template.html"
def contribute(self, data, context):
for k, v in data.items():
context["ng_" + k] = v
return context
class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
whelpers.StatusFormatMixin):
slug = "configure_cluster_template"
name = _("Create Cluster Template")
finalize_button_name = _("Create")
success_message = _("Created Cluster Template %s")
name_property = "general_cluster_template_name"
success_url = "horizon:sahara:cluster_templates:index"
default_steps = (GeneralConfig,
ConfigureNodegroups)
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
ConfigureClusterTemplate._cls_registry = set([])
sahara = saharaclient.client(request)
hlps = helpers.Helpers(sahara)
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
general_parameters = hlps.get_cluster_general_configs(
plugin,
hadoop_version)
service_parameters = hlps.get_targeted_cluster_configs(
plugin,
hadoop_version)
self._populate_tabs(general_parameters, service_parameters)
super(ConfigureClusterTemplate, self).__init__(request,
context_seed,
entry_point,
*args, **kwargs)
def is_valid(self):
steps_valid = True
for step in self.steps:
if not step.action.is_valid():
steps_valid = False
step.has_errors = True
errors_fields = []
for k, v in step.action.errors.items():
errors_fields.append(k)
step.action.errors_fields = errors_fields
if not steps_valid:
return steps_valid
return self.validate(self.context)
def handle(self, request, context):
try:
sahara = saharaclient.client(request)
node_groups = []
configs_dict = whelpers.parse_configs_from_context(context,
self.defaults)
ids = json.loads(context['ng_forms_ids'])
for id in ids:
name = context['ng_group_name_' + str(id)]
template_id = context['ng_template_id_' + str(id)]
count = context['ng_count_' + str(id)]
ng = {"name": name,
"node_group_template_id": template_id,
"count": count}
node_groups.append(ng)
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
# TODO(nkonovalov): Fix client to support default_image_id
sahara.cluster_templates.create(
context["general_cluster_template_name"],
plugin,
hadoop_version,
context["general_description"],
configs_dict,
node_groups,
context["anti_affinity_info"])
return True
except api_base.APIException as e:
self.error_description = str(e)
return False
except Exception:
exceptions.handle(request)

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class ClustersPanel(horizon.Panel):
name = _("Clusters")
slug = 'clusters'
dashboard.SaharaDashboard.register(ClustersPanel)

View File

@ -1,114 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.http import Http404 # noqa
from django.utils.translation import ugettext_lazy as _
from horizon import messages
from horizon import tables
from saharaclient.api import base as api_base
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class CreateCluster(tables.LinkAction):
name = "create"
verbose_name = _("Launch Cluster")
url = "horizon:sahara:clusters:create-cluster"
classes = ("btn-launch", "ajax-modal")
class ScaleCluster(tables.LinkAction):
name = "scale"
verbose_name = _("Scale Cluster")
url = "horizon:sahara:clusters:scale"
classes = ("ajax-modal", "btn-edit")
def allowed(self, request, cluster=None):
return cluster.status == "Active"
class DeleteCluster(tables.BatchAction):
name = "delete"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Cluster")
data_type_plural = _("Clusters")
classes = ('btn-danger', 'btn-terminate')
def action(self, request, obj_id):
sahara = saharaclient(request)
sahara.clusters.delete(obj_id)
class UpdateRow(tables.Row):
ajax = True
def get_data(self, request, instance_id):
sahara = saharaclient(request)
try:
return sahara.clusters.get(instance_id)
except api_base.APIException as e:
if e.error_code == 404:
# returning 404 to the ajax call removes the
# row from the table on the ui
raise Http404
else:
messages.error(request, e)
def get_instances_count(cluster):
return sum([len(ng["instances"])
for ng in cluster.node_groups])
class ConfigureCluster(tables.LinkAction):
name = "configure"
verbose_name = _("Configure Cluster")
url = "horizon:sahara:clusters:configure-cluster"
classes = ("ajax-modal", "btn-create", "configure-cluster-btn")
attrs = {"style": "display: none"}
class ClustersTable(tables.DataTable):
STATUS_CHOICES = (
("active", True),
("error", False)
)
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:sahara:clusters:details"))
status = tables.Column("status",
verbose_name=_("Status"),
status=True,
status_choices=STATUS_CHOICES)
instances_count = tables.Column(get_instances_count,
verbose_name=_("Instances Count"))
class Meta:
name = "clusters"
verbose_name = _("Clusters")
row_class = UpdateRow
status_columns = ["status"]
table_actions = (CreateCluster,
ConfigureCluster,
DeleteCluster)
row_actions = (ScaleCluster,
DeleteCluster,)

View File

@ -1,154 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
from saharadashboard.utils import compatibility
from saharadashboard.utils import importutils
from saharadashboard.utils import workflow_helpers as helpers
neutron = importutils.import_any('openstack_dashboard.api.quantum',
'horizon.api.quantum',
'openstack_dashboard.api.neutron')
nova = importutils.import_any('openstack_dashboard.api.nova',
'horizon.api.nova')
glance = importutils.import_any('openstack_dashboard.api.glance',
'horizon.api.glance')
LOG = logging.getLogger(__name__)
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "cluster_details_tab"
template_name = "clusters/_details.html"
def get_context_data(self, request):
cluster_id = self.tab_group.kwargs['cluster_id']
sahara = saharaclient(request)
cluster = sahara.clusters.get(cluster_id)
for info_key, info_val in cluster.info.items():
for key, val in info_val.items():
if str(val).startswith(('http://', 'https://')):
cluster.info[info_key][key] = build_link(val)
base_image = glance.image_get(request,
cluster.default_image_id)
if getattr(cluster, 'cluster_template_id', None):
cluster_template = helpers.safe_call(sahara.cluster_templates.get,
cluster.cluster_template_id)
else:
cluster_template = None
if getattr(cluster, 'neutron_management_network', None):
net_id = cluster.neutron_management_network
network = neutron.network_get(request, net_id)
network.set_id_as_name_if_empty()
net_name = network.name
else:
net_name = None
return {"cluster": cluster,
"base_image": base_image,
"cluster_template": cluster_template,
"network": net_name}
def build_link(url):
return "<a href='" + url + "' target=\"_blank\">" + url + "</a>"
class NodeGroupsTab(tabs.Tab):
name = _("Node Groups")
slug = "cluster_nodegroups_tab"
template_name = "clusters/_nodegroups_details.html"
def get_context_data(self, request):
cluster_id = self.tab_group.kwargs['cluster_id']
sahara = saharaclient(request)
cluster = sahara.clusters.get(cluster_id)
for ng in cluster.node_groups:
if not ng["flavor_id"]:
continue
ng["flavor_name"] = nova.flavor_get(request, ng["flavor_id"]).name
ng["node_group_template"] = helpers.safe_call(
sahara.node_group_templates.get,
ng.get("node_group_template_id", None))
return {"cluster": cluster}
class Instance(object):
def __init__(self, name=None, id=None, internal_ip=None,
management_ip=None):
self.name = name
self.id = id
self.internal_ip = internal_ip
self.management_ip = management_ip
class InstancesTable(tables.DataTable):
name = tables.Column("name",
link=(compatibility.convert_url(
"horizon:project:instances:detail")),
verbose_name=_("Name"))
internal_ip = tables.Column("internal_ip",
verbose_name=_("Internal IP"))
management_ip = tables.Column("management_ip",
verbose_name=_("Management IP"))
class Meta:
name = "cluster_instances"
# just ignoring the name
verbose_name = _(" ")
class InstancesTab(tabs.TableTab):
name = _("Instances")
slug = "cluster_instances_tab"
template_name = "clusters/_instances_details.html"
table_classes = (InstancesTable, )
def get_cluster_instances_data(self):
cluster_id = self.tab_group.kwargs['cluster_id']
sahara = saharaclient(self.request)
cluster = sahara.clusters.get(cluster_id)
instances = []
for ng in cluster.node_groups:
for instance in ng["instances"]:
instances.append(Instance(
name=instance["instance_name"],
id=instance["instance_id"],
internal_ip=instance.get("internal_ip",
"Not assigned"),
management_ip=instance.get("management_ip",
"Not assigned")))
return instances
class ClusterDetailsTabs(tabs.TabGroup):
slug = "cluster_details"
tabs = (GeneralTab, NodeGroupsTab, InstancesTab, )
sticky = True

View File

@ -1,42 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import saharadashboard.clusters.views as views
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('',
url(r'^$', views.ClustersView.as_view(),
name='index'),
url(r'^$', views.ClustersView.as_view(),
name='clusters'),
url(r'^create-cluster$',
views.CreateClusterView.as_view(),
name='create-cluster'),
url(r'^configure-cluster$',
views.ConfigureClusterView.as_view(),
name='configure-cluster'),
url(r'^(?P<cluster_id>[^/]+)$',
views.ClusterDetailsView.as_view(),
name='details'),
url(r'^(?P<cluster_id>[^/]+)/scale$',
views.ScaleClusterView.as_view(),
name='scale'))

View File

@ -1,92 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from horizon import tables
from horizon import tabs
from horizon import workflows
from saharadashboard.api.client import client as saharaclient
from saharadashboard.clusters.tables import ClustersTable
import saharadashboard.clusters.tabs as _tabs
import saharadashboard.clusters.workflows.create as create_flow
import saharadashboard.clusters.workflows.scale as scale_flow
LOG = logging.getLogger(__name__)
class ClustersView(tables.DataTableView):
table_class = ClustersTable
template_name = 'clusters/clusters.html'
def get_data(self):
sahara = saharaclient(self.request)
clusters = sahara.clusters.list()
return clusters
class ClusterDetailsView(tabs.TabView):
tab_group_class = _tabs.ClusterDetailsTabs
template_name = 'clusters/details.html'
def get_context_data(self, **kwargs):
context = super(ClusterDetailsView, self)\
.get_context_data(**kwargs)
return context
def get_data(self):
pass
class CreateClusterView(workflows.WorkflowView):
workflow_class = create_flow.CreateCluster
success_url = \
"horizon:sahara:clusters:create-cluster"
classes = ("ajax-modal")
template_name = "clusters/create.html"
class ConfigureClusterView(workflows.WorkflowView):
workflow_class = create_flow.ConfigureCluster
success_url = "horizon:sahara:clusters"
template_name = "clusters/configure.html"
class ScaleClusterView(workflows.WorkflowView):
workflow_class = scale_flow.ScaleCluster
success_url = "horizon:sahara:clusters"
classes = ("ajax-modal")
template_name = "clusters/scale.html"
def get_context_data(self, **kwargs):
context = super(ScaleClusterView, self)\
.get_context_data(**kwargs)
context["cluster_id"] = kwargs["cluster_id"]
return context
def get_object(self, *args, **kwargs):
if not hasattr(self, "_object"):
template_id = self.kwargs['cluster_id']
sahara = saharaclient(self.request)
template = sahara.cluster_templates.get(template_id)
self._object = template
return self._object
def get_initial(self):
initial = super(ScaleClusterView, self).get_initial()
initial.update({'cluster_id': self.kwargs['cluster_id']})
return initial

View File

@ -1,223 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext as _
from horizon import exceptions
from horizon import forms
from horizon import workflows
from saharaclient.api import base as api_base
from saharadashboard.api import client as saharaclient
from saharadashboard.api.client import SAHARA_USE_NEUTRON
import saharadashboard.cluster_templates.workflows.create as t_flows
from saharadashboard.utils import importutils
from saharadashboard.utils import neutron_support
import saharadashboard.utils.workflow_helpers as whelpers
neutron = importutils.import_any('openstack_dashboard.api.quantum',
'openstack_dashboard.api.neutron',
'horizon.api.quantum',
'horizon.api.neutron')
nova = importutils.import_any('openstack_dashboard.api.nova',
'horizon.api.nova')
LOG = logging.getLogger(__name__)
KEYPAIR_IMPORT_URL = "horizon:project:access_and_security:keypairs:import"
class SelectPluginAction(t_flows.SelectPluginAction):
class Meta:
name = _("Select plugin and hadoop version for cluster")
help_text_template = ("clusters/_create_general_help.html")
class SelectPlugin(t_flows.SelectPlugin):
action_class = SelectPluginAction
class CreateCluster(t_flows.CreateClusterTemplate):
slug = "create_cluster"
name = _("Launch Cluster")
success_url = "horizon:sahara:cluster_templates:index"
default_steps = (SelectPlugin,)
class GeneralConfigAction(workflows.Action):
hidden_configure_field = forms.CharField(
required=False,
widget=forms.HiddenInput(attrs={"class": "hidden_configure_field"}))
hidden_to_delete_field = forms.CharField(
required=False,
widget=forms.HiddenInput(attrs={"class": "hidden_to_delete_field"}))
cluster_name = forms.CharField(label=_("Cluster Name"),
required=True)
description = forms.CharField(label=_("Description"),
required=False,
widget=forms.Textarea)
cluster_template = forms.ChoiceField(label=_("Cluster Template"),
initial=(None, "None"),
required=True)
image = forms.ChoiceField(label=_("Base Image"),
required=True)
keypair = forms.DynamicChoiceField(
label=_("Keypair"),
required=False,
help_text=_("Which keypair to use for authentication."),
add_item_link=KEYPAIR_IMPORT_URL)
def __init__(self, request, *args, **kwargs):
super(GeneralConfigAction, self).__init__(request, *args, **kwargs)
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
if SAHARA_USE_NEUTRON:
self.fields["neutron_management_network"] = forms.ChoiceField(
label=_("Neutron Management Network"),
required=True,
choices=self.populate_neutron_management_network_choices(
request, {})
)
self.fields["plugin_name"] = forms.CharField(
widget=forms.HiddenInput(),
initial=plugin
)
self.fields["hadoop_version"] = forms.CharField(
widget=forms.HiddenInput(),
initial=hadoop_version
)
def populate_image_choices(self, request, context):
sahara = saharaclient.client(request)
all_images = sahara.images.list()
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
details = sahara.plugins.get_version_details(plugin,
hadoop_version)
return [(image.id, image.name) for image in all_images
if set(details.required_image_tags).issubset(set(image.tags))]
def populate_keypair_choices(self, request, context):
keypairs = nova.keypair_list(request)
keypair_list = [(kp.name, kp.name) for kp in keypairs]
keypair_list.insert(0, ("", "No keypair"))
return keypair_list
def populate_cluster_template_choices(self, request, context):
sahara = saharaclient.client(request)
templates = sahara.cluster_templates.list()
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
choices = [(template.id, template.name)
for template in templates
if (template.hadoop_version == hadoop_version and
template.plugin_name == plugin)]
# cluster_template_id comes from cluster templates table, when
# Create Cluster from template is clicked there
selected_template_id = request.REQUEST.get("cluster_template_id", None)
for template in templates:
if template.id == selected_template_id:
self.fields['cluster_template'].initial = template.id
return choices
populate_neutron_management_network_choices = \
neutron_support.populate_neutron_management_network_choices
def get_help_text(self):
extra = dict()
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(self.request)
extra["plugin_name"] = plugin
extra["hadoop_version"] = hadoop_version
return super(GeneralConfigAction, self).get_help_text(extra)
def clean(self):
cleaned_data = super(GeneralConfigAction, self).clean()
if cleaned_data.get("hidden_configure_field", None) \
== "create_nodegroup":
self._errors = dict()
return cleaned_data
class Meta:
name = _("Configure Cluster")
help_text_template = \
("clusters/_configure_general_help.html")
class GeneralConfig(workflows.Step):
action_class = GeneralConfigAction
contributes = ("hidden_configure_field", )
def contribute(self, data, context):
for k, v in data.items():
context["general_" + k] = v
return context
class ConfigureCluster(whelpers.StatusFormatMixin, workflows.Workflow):
slug = "configure_cluster"
name = _("Launch Cluster")
finalize_button_name = _("Create")
success_message = _("Created Cluster %s")
name_property = "general_cluster_name"
success_url = "horizon:sahara:clusters:index"
default_steps = (GeneralConfig, )
def handle(self, request, context):
try:
sahara = saharaclient.client(request)
# TODO(nkonovalov) Implement AJAX Node Groups
node_groups = None
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
cluster_template_id = context["general_cluster_template"] or None
user_keypair = context["general_keypair"] or None
sahara.clusters.create(
context["general_cluster_name"],
plugin, hadoop_version,
cluster_template_id=cluster_template_id,
default_image_id=context["general_image"],
description=context["general_description"],
node_groups=node_groups,
user_keypair_id=user_keypair,
net_id=context.get("general_neutron_management_network", None))
return True
except api_base.APIException as e:
self.error_description = str(e)
return False
except Exception:
exceptions.handle(request)

View File

@ -1,157 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import json
import logging
from django.utils.translation import ugettext as _
from horizon import exceptions
from saharaclient.api import base as api_base
from saharadashboard.api import client as saharaclient
import saharadashboard.cluster_templates.workflows.create as clt_create_flow
import saharadashboard.clusters.workflows.create as cl_create_flow
import saharadashboard.utils.workflow_helpers as whelpers
LOG = logging.getLogger(__name__)
class NodeGroupsStep(clt_create_flow.ConfigureNodegroups):
pass
class ScaleCluster(cl_create_flow.ConfigureCluster,
whelpers.StatusFormatMixin):
slug = "scale_cluster"
name = _("Scale Cluster")
finalize_button_name = _("Scale")
success_url = "horizon:sahara:clusters:index"
default_steps = (NodeGroupsStep, )
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
ScaleCluster._cls_registry = set([])
sahara = saharaclient.client(request)
cluster_id = context_seed["cluster_id"]
cluster = sahara.clusters.get(cluster_id)
self.success_message = "Scaling Cluster %s successfully started" \
% cluster.name
plugin = cluster.plugin_name
hadoop_version = cluster.hadoop_version
# init deletable nodegroups
deletable = dict()
for group in cluster.node_groups:
deletable[group["name"]] = "false"
request.GET = request.GET.copy()
request.GET.update({"cluster_id": cluster_id})
request.GET.update({"plugin_name": plugin})
request.GET.update({"hadoop_version": hadoop_version})
request.GET.update({"deletable": deletable})
super(ScaleCluster, self).__init__(request, context_seed,
entry_point, *args,
**kwargs)
# init Node Groups
for step in self.steps:
if isinstance(step, clt_create_flow.ConfigureNodegroups):
ng_action = step.action
template_ngs = cluster.node_groups
if 'forms_ids' not in request.POST:
ng_action.groups = []
for id in range(0, len(template_ngs), 1):
group_name = "group_name_" + str(id)
template_id = "template_id_" + str(id)
count = "count_" + str(id)
templ_ng = template_ngs[id]
ng_action.groups.append(
{"name": templ_ng["name"],
"template_id": templ_ng["node_group_template_id"],
"count": templ_ng["count"],
"id": id,
"deletable": "false"})
whelpers.build_node_group_fields(ng_action,
group_name,
template_id,
count)
def format_status_message(self, message):
# Scaling form requires special handling because it has no Cluster name
# in it's context
error_description = getattr(self, 'error_description', None)
if error_description:
return error_description
else:
return self.success_message
def handle(self, request, context):
sahara = saharaclient.client(request)
cluster_id = request.GET["cluster_id"]
cluster = sahara.clusters.get(cluster_id)
existing_node_groups = set([])
for ng in cluster.node_groups:
existing_node_groups.add(ng["name"])
scale_object = dict()
ids = json.loads(context["ng_forms_ids"])
for _id in ids:
name = context["ng_group_name_%s" % _id]
template_id = context["ng_template_id_%s" % _id]
count = context["ng_count_%s" % _id]
if name not in existing_node_groups:
if "add_node_groups" not in scale_object:
scale_object["add_node_groups"] = []
scale_object["add_node_groups"].append(
{"name": name,
"node_group_template_id": template_id,
"count": int(count)})
else:
old_count = None
for ng in cluster.node_groups:
if name == ng["name"]:
old_count = ng["count"]
break
if old_count != count:
if "resize_node_groups" not in scale_object:
scale_object["resize_node_groups"] = []
scale_object["resize_node_groups"].append(
{"name": name,
"count": int(count)}
)
try:
sahara.clusters.scale(cluster_id, scale_object)
return True
except api_base.APIException as e:
self.error_description = str(e)
return False
except Exception:
exceptions.handle(request)

View File

@ -1,46 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext as _
import horizon
from saharadashboard.utils import compatibility
LOG = logging.getLogger(__name__)
class SaharaDashboard(horizon.Dashboard):
name = _("Sahara")
slug = "sahara"
panels = ('clusters',
'cluster_templates',
'nodegroup_templates',
'job_executions',
'jobs',
'job_binaries',
'data_sources',
'image_registry',
'plugins')
default_panel = 'clusters'
nav = True
supports_tenants = True
horizon.register(SaharaDashboard)
LOG.info('Sahara recognizes Dashboard release as "%s"' %
compatibility.get_dashboard_release())

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class DataSourcesPanel(horizon.Panel):
name = _("Data Sources")
slug = 'data_sources'
dashboard.SaharaDashboard.register(DataSourcesPanel)

View File

@ -1,60 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class CreateDataSource(tables.LinkAction):
name = "create data source"
verbose_name = _("Create Data Source")
url = "horizon:sahara:data_sources:create-data-source"
classes = ("btn-launch", "ajax-modal")
class DeleteDataSource(tables.BatchAction):
name = "delete"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Data source")
data_type_plural = _("Data sources")
classes = ('btn-danger', 'btn-terminate')
def action(self, request, obj_id):
sahara = saharaclient(request)
sahara.data_sources.delete(obj_id)
class DataSourcesTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:sahara:data_sources:details"))
type = tables.Column("type",
verbose_name=_("Type"))
description = tables.Column("description",
verbose_name=_("Description"))
class Meta:
name = "data_sources"
verbose_name = _("Data Sources")
table_actions = (CreateDataSource,
DeleteDataSource)
row_actions = (DeleteDataSource,)

View File

@ -1,41 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "data_source_details_tab"
template_name = ("data_sources/_details.html")
def get_context_data(self, request):
data_source_id = self.tab_group.kwargs['data_source_id']
sahara = saharaclient(request)
data_source = sahara.data_sources.get(data_source_id)
return {"data_source": data_source}
class DataSourceDetailsTabs(tabs.TabGroup):
slug = "data_source_details"
tabs = (GeneralTab,)
sticky = True

View File

@ -1,36 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import saharadashboard.data_sources.views as views
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('',
url(r'^$', views.DataSourcesView.as_view(),
name='index'),
url(r'^$', views.DataSourcesView.as_view(),
name='data-sources'),
url(r'^create-data-source$',
views.CreateDataSourceView.as_view(),
name='create-data-source'),
url(r'^(?P<data_source_id>[^/]+)$',
views.DataSourceDetailsView.as_view(),
name='details'))

View File

@ -1,58 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from horizon import tables
from horizon import tabs
from horizon import workflows
from saharadashboard.api.client import client as saharaclient
from saharadashboard.data_sources.tables import DataSourcesTable
import saharadashboard.data_sources.tabs as _tabs
import saharadashboard.data_sources.workflows.create as create_flow
LOG = logging.getLogger(__name__)
class DataSourcesView(tables.DataTableView):
table_class = DataSourcesTable
template_name = 'data_sources/data_sources.html'
def get_data(self):
sahara = saharaclient(self.request)
data_sources = sahara.data_sources.list()
return data_sources
class CreateDataSourceView(workflows.WorkflowView):
workflow_class = create_flow.CreateDataSource
success_url = \
"horizon:sahara:data-sources:create-data-source"
classes = ("ajax-modal")
template_name = "data_sources/create.html"
class DataSourceDetailsView(tabs.TabView):
tab_group_class = _tabs.DataSourceDetailsTabs
template_name = 'data_sources/details.html'
def get_context_data(self, **kwargs):
context = super(DataSourceDetailsView, self)\
.get_context_data(**kwargs)
return context
def get_data(self):
pass

View File

@ -1,109 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.utils.translation import ugettext as _
from horizon import forms
from horizon import workflows
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class GeneralConfigAction(workflows.Action):
data_source_name = forms.CharField(label=_("Name"),
required=True)
data_source_type = forms.ChoiceField(
label=_("Data Source Type"),
required=True,
choices=[("swift", "Swift"), ("hdfs", "HDFS")],
widget=forms.Select(attrs={"class": "data_source_type_choice"}))
data_source_url = forms.CharField(label=_("URL"),
required=True)
data_source_credential_user = forms.CharField(label=_("Source username"),
required=True)
data_source_credential_pass = forms.CharField(
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
label=_("Source password"),
required=True)
data_source_description = forms.CharField(
label=_("Description"),
required=False,
widget=forms.Textarea)
def clean(self):
cleaned_data = super(workflows.Action, self).clean()
ds_type = cleaned_data.get("data_source_type", "")
if ds_type == "hdfs":
if "data_source_credential_user" in self._errors:
del self._errors["data_source_credential_user"]
if "data_source_credential_pass" in self._errors:
del self._errors["data_source_credential_pass"]
return cleaned_data
def __init__(self, request, *args, **kwargs):
super(GeneralConfigAction, self).__init__(request, *args, **kwargs)
class Meta:
name = _("Create Data Source")
help_text_template = \
("data_sources/_create_data_source_help.html")
class GeneralConfig(workflows.Step):
action_class = GeneralConfigAction
def contribute(self, data, context):
for k, v in data.items():
context["general_" + k] = v
context["source_url"] = context["general_data_source_url"]
if context["general_data_source_type"] == "swift":
if not context["general_data_source_url"].startswith("swift://"):
context["source_url"] = "%s://%s" % \
("swift",
context["general_data_source_url"])
return context
class CreateDataSource(workflows.Workflow):
slug = "create_data_source"
name = _("Create Data Source")
finalize_button_name = _("Create")
success_message = _("Data source created")
failure_message = _("Could not create data source")
success_url = "horizon:sahara:data_sources:index"
default_steps = (GeneralConfig, )
def handle(self, request, context):
sahara = saharaclient(request)
sahara.data_sources.create(
context["general_data_source_name"],
context["general_data_source_description"],
context["general_data_source_type"],
context["source_url"],
context.get("general_data_source_credential_user", None),
context.get("general_data_source_credential_pass", None))
return True

View File

@ -1,127 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import json
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import messages
from saharaclient.api import base as api_base
from saharadashboard.api import client as saharaclient
from saharadashboard.utils import importutils
# horizon.api is for backward compatibility with folsom
glance = importutils.import_any('openstack_dashboard.api.glance',
'horizon.api.glance')
class ImageForm(forms.SelfHandlingForm):
image_id = forms.CharField(widget=forms.HiddenInput())
tags_list = forms.CharField(widget=forms.HiddenInput())
user_name = forms.CharField(max_length=80, label=_("User Name"))
description = forms.CharField(max_length=80,
label=_("Description"),
required=False,
widget=forms.Textarea(attrs={'cols': 80,
'rows': 20}))
def handle(self, request, data):
try:
sahara = saharaclient.client(request)
image_id = data['image_id']
user_name = data['user_name']
desc = data['description']
sahara.images.update_image(image_id, user_name, desc)
image_tags = json.loads(data["tags_list"])
sahara.images.update_tags(image_id, image_tags)
messages.success(request, self.message)
return True
except api_base.APIException as e:
messages.error(request, str(e))
return False
except Exception:
exceptions.handle(request)
class EditTagsForm(ImageForm):
message = _("Image updated.")
image_id = forms.CharField(widget=forms.HiddenInput())
class RegisterImageForm(ImageForm):
message = _("Image registered.")
image_id = forms.ChoiceField(label=_("Image"),
required=True)
def __init__(self, request, *args, **kwargs):
super(RegisterImageForm, self).__init__(request, *args, **kwargs)
self._populate_image_id_choices()
def _populate_image_id_choices(self):
images = self._get_available_images(self.request)
choices = [(image.id, image.name)
for image in images
if image.properties.get("image_type", '') != "snapshot"]
if choices:
choices.insert(0, ("", _("Select Image")))
else:
choices.insert(0, ("", _("No images available.")))
self.fields['image_id'].choices = choices
def _get_images(self, request, filter):
try:
images, _more, _prev = glance.image_list_detailed(
request, filters=filter)
except Exception:
images = []
exceptions.handle(request,
_("Unable to retrieve images with filter %s.") %
filter)
return images
def _get_public_images(self, request):
filter = {"is_public": True,
"status": "active"}
return self._get_images(request, filter)
def _get_tenant_images(self, request):
filter = {"owner": request.user.tenant_id,
"status": "active"}
return self._get_images(request, filter)
def _get_available_images(self, request):
images = self._get_tenant_images(request)
if request.user.is_superuser:
images += self._get_public_images(request)
final_images = []
sahara = saharaclient.client(request)
image_ids = [img.id for img in sahara.images.list()]
for image in images:
if image.id not in image_ids:
image_ids.append(image.id)
final_images.append(image)
return [image for image in final_images
if image.container_format
not in ('aki', 'ari')]

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class ImageRegistryPanel(horizon.Panel):
name = _("Image Registry")
slug = 'image_registry'
dashboard.SaharaDashboard.register(ImageRegistryPanel)

View File

@ -1,75 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django import template
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from saharadashboard.api.client import client as saharaclient
from saharadashboard.utils import compatibility
LOG = logging.getLogger(__name__)
class EditTagsAction(tables.LinkAction):
name = "edit_tags"
verbose_name = _("Edit Tags")
url = "horizon:sahara:image_registry:edit_tags"
classes = ("ajax-modal", "btn-create")
def tags_to_string(image):
template_name = 'image_registry/_list_tags.html'
context = {"image": image}
return template.loader.render_to_string(template_name, context)
class RegisterImage(tables.LinkAction):
name = "register"
verbose_name = _("Register Image")
url = "horizon:sahara:image_registry:register"
classes = ("btn-launch", "ajax-modal")
class UnregisterImages(tables.BatchAction):
name = "Unregister"
action_present = _("Unregister")
action_past = _("Unregistered")
data_type_singular = _("Image")
data_type_plural = _("Images")
classes = ('btn-danger', 'btn-terminate')
def action(self, request, obj_id):
sahara = saharaclient(request)
sahara.images.unregister_image(obj_id)
class ImageRegistryTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Image"),
link=compatibility.convert_url(
"horizon:project:"
"images:images:detail"))
tags = tables.Column(tags_to_string,
verbose_name=_("Tags"))
class Meta:
name = "image_registry"
verbose_name = _("Image Registry")
table_actions = ()
table_actions = (RegisterImage, UnregisterImages,)
row_actions = (EditTagsAction, UnregisterImages,)

View File

@ -1,39 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 saharadashboard.image_registry.views import EditTagsView
from saharadashboard.image_registry.views import ImageRegistryView
from saharadashboard.image_registry.views import RegisterImageView
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('',
url(r'^$', ImageRegistryView.as_view(),
name='index'),
url(r'^$', ImageRegistryView.as_view(),
name='image_registry'),
url(r'^edit_tags/(?P<image_id>[^/]+)/$',
EditTagsView.as_view(),
name='edit_tags'),
url(r'^register/$',
RegisterImageView.as_view(),
name='register'),
)

View File

@ -1,98 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import json
import logging
from django.core.urlresolvers import reverse_lazy
from horizon import forms
from horizon import tables
from saharadashboard.api.client import client as saharaclient
from saharadashboard.image_registry.forms import EditTagsForm
from saharadashboard.image_registry.forms import RegisterImageForm
from saharadashboard.image_registry.tables import ImageRegistryTable
LOG = logging.getLogger(__name__)
class ImageRegistryView(tables.DataTableView):
table_class = ImageRegistryTable
template_name = 'image_registry/image_registry.html'
def get_data(self):
sahara = saharaclient(self.request)
return sahara.images.list()
def update_context_with_plugin_tags(request, context):
sahara = saharaclient(request)
plugins = sahara.plugins.list()
plugins_object = dict()
for plugin in plugins:
plugins_object[plugin.name] = dict()
for version in plugin.versions:
plugins_object[plugin.name][version] = []
details = sahara.plugins.get_version_details(plugin.name,
version)
for tag in details.required_image_tags:
plugins_object[plugin.name][version].append(tag)
context["plugins"] = plugins_object
return context
class EditTagsView(forms.ModalFormView):
form_class = EditTagsForm
template_name = 'image_registry/edit_tags.html'
success_url = reverse_lazy('horizon:sahara:image_registry:index')
def get_context_data(self, **kwargs):
context = super(EditTagsView, self).get_context_data(**kwargs)
context['image'] = self.get_object()
context = update_context_with_plugin_tags(self.request, context)
return context
def get_object(self):
sahara = saharaclient(self.request)
return sahara.images.get(self.kwargs["image_id"])
def get_initial(self):
image = self.get_object()
return {"image_id": image.id,
"tags_list": json.dumps(image.tags),
"user_name": image.username,
"description": image.description}
class RegisterImageView(forms.ModalFormView):
form_class = RegisterImageForm
template_name = 'image_registry/register_image.html'
success_url = reverse_lazy('horizon:sahara:image_registry:index')
def get_context_data(self, **kwargs):
context = super(RegisterImageView, self).get_context_data(**kwargs)
context = update_context_with_plugin_tags(self.request, context)
return context
def get_initial(self):
# need this initialization to allow registration
# of images without tags
return {"tags_list": json.dumps([])}

View File

@ -1,183 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
import uuid
from django.forms.util import flatatt
from django.forms import widgets
from django import template
from django.template.defaultfilters import linebreaks
from django.template.defaultfilters import safe
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from horizon import forms
from horizon import messages
from saharaclient.api import base as api_base
from saharadashboard.api import client as saharaclient
LOG = logging.getLogger(__name__)
class LabeledInput(widgets.Input):
def render(self, name, values, attrs=None):
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
output = "<span id='%s'>%s</span>%s" %\
("id_%s_label" % name,
"internal-db://",
('<input%s />' % flatatt(final_attrs)))
return mark_safe(output)
class JobBinaryCreateForm(forms.SelfHandlingForm):
NEW_SCRIPT = "%%%NEWSCRIPT%%%"
UPLOAD_BIN = "%%%UPLOADFILE%%%"
job_binary_name = forms.CharField(label=_("Name"),
required=True)
job_binary_type = forms.ChoiceField(label=_("Storage type"),
required=True)
job_binary_url = forms.CharField(label=_("URL"),
required=False,
widget=LabeledInput())
job_binary_internal = forms.ChoiceField(label=_("Internal binary"),
required=False)
job_binary_file = forms.FileField(label=_("Upload File"),
required=False)
job_binary_script_name = forms.CharField(label=_("Script name"),
required=False)
job_binary_script = forms.CharField(label=_("Script text"),
required=False,
widget=forms.Textarea())
job_binary_username = forms.CharField(label=_("Username"),
required=False)
job_binary_password = forms.CharField(label=_("Password"),
required=False,
widget=forms.PasswordInput(
attrs={'autocomplete': 'off'}))
job_binary_description = forms.CharField(label=_("Description"),
required=False,
widget=forms.Textarea())
def __init__(self, request, *args, **kwargs):
super(JobBinaryCreateForm, self).__init__(request, *args, **kwargs)
self.help_text_template = "job_binaries/_create_job_binary_help.html"
self.fields["job_binary_type"].choices =\
[("internal-db", "Internal database"),
("swift", "Swift")]
self.fields["job_binary_internal"].choices =\
self.populate_job_binary_internal_choices(request)
def populate_job_binary_internal_choices(self, request):
sahara = saharaclient.client(request)
job_binaries = sahara.job_binary_internals.list()
choices = [(job_binary.id, job_binary.name)
for job_binary in job_binaries]
choices.insert(0, (self.NEW_SCRIPT, '*Create a script'))
choices.insert(0, (self.UPLOAD_BIN, '*Upload a new file'))
return choices
def handle(self, request, context):
try:
sahara = saharaclient.client(request)
extra = {}
bin_url = "%s://%s" % (context["job_binary_type"],
context["job_binary_url"])
if(context["job_binary_type"] == "internal-db"):
bin_url = self.handle_internal(request, context)
elif(context["job_binary_type"] == "swift"):
extra = self.handle_swift(request, context)
sahara.job_binaries.create(
context["job_binary_name"],
bin_url,
context["job_binary_description"],
extra)
messages.success(request, "Successfully created job binary")
return True
except api_base.APIException as e:
messages.error(request, str(e))
return False
except Exception as e:
messages.error(request, str(e))
return False
def get_help_text(self, extra_context=None):
text = ""
extra_context = extra_context or {}
if self.help_text_template:
tmpl = template.loader.get_template(self.help_text_template)
context = template.RequestContext(self.request, extra_context)
text += tmpl.render(context)
else:
text += linebreaks(force_unicode(self.help_text))
return safe(text)
class Meta:
name = _("Create Job Binary")
help_text_template = \
("job_binaries/_create_job_binary_help.html")
def handle_internal(self, request, context):
result = ""
sahara = saharaclient.client(request)
bin_id = context["job_binary_internal"]
if(bin_id == self.UPLOAD_BIN):
result = sahara.job_binary_internals.create(
self.get_unique_binary_name(
request, request.FILES["job_binary_file"].name),
request.FILES["job_binary_file"].read())
elif(bin_id == self.NEW_SCRIPT):
result = sahara.job_binary_internals.create(
self.get_unique_binary_name(
request, context["job_binary_script_name"]),
context["job_binary_script"])
bin_id = result.id
return "internal-db://%s" % bin_id
def handle_swift(self, request, context):
username = context["job_binary_username"]
password = context["job_binary_password"]
extra = {
"user": username,
"password": password
}
return extra
def get_unique_binary_name(self, request, base_name):
sahara = saharaclient.client(request)
internals = sahara.job_binary_internals.list()
names = [internal.name for internal in internals]
if base_name in names:
return "%s_%s" % (base_name, uuid.uuid1())
return base_name

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class JobBinariesPanel(horizon.Panel):
name = _("Job Binaries")
slug = 'job_binaries'
dashboard.SaharaDashboard.register(JobBinariesPanel)

View File

@ -1,79 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from saharaclient.api import base as api_base
from saharadashboard.api import client as saharaclient
LOG = logging.getLogger(__name__)
class CreateJobBinary(tables.LinkAction):
name = "create job binary"
verbose_name = _("Create Job Binary")
url = "horizon:sahara:job_binaries:create-job-binary"
classes = ("btn-launch", "ajax-modal")
class DeleteJobBinary(tables.BatchAction):
name = "delete"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Job binary")
data_type_plural = _("Job binaries")
classes = ('btn-danger', 'btn-terminate')
def action(self, request, obj_id):
sahara = saharaclient.client(request)
jb = sahara.job_binaries.get(obj_id)
(jb_type, jb_internal_id) = jb.url.split("://")
if jb_type == "internal-db":
try:
sahara.job_binary_internals.delete(jb_internal_id)
except api_base.APIException:
# nothing to do for job-binary-internal if
# it does not exist.
pass
sahara.job_binaries.delete(obj_id)
class DownloadJobBinary(tables.LinkAction):
name = "download job binary"
verbose_name = _("Download Job Binary")
url = "horizon:sahara:job_binaries:download"
classes = ("btn-edit")
class JobBinariesTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:sahara:job_binaries:details"))
type = tables.Column("url",
verbose_name=_("Url"))
description = tables.Column("description",
verbose_name=_("Description"))
class Meta:
name = "job_binaries"
verbose_name = _("Job Binaries")
table_actions = (CreateJobBinary,
DeleteJobBinary)
row_actions = (DeleteJobBinary, DownloadJobBinary)

View File

@ -1,41 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "job_binaries_details_tab"
template_name = ("job_binaries/_details.html")
def get_context_data(self, request):
job_binary_id = self.tab_group.kwargs['job_binary_id']
sahara = saharaclient(request)
job_binary = sahara.job_binaries.get(job_binary_id)
return {"job_binary": job_binary}
class JobBinaryDetailsTabs(tabs.TabGroup):
slug = "job_binary_details"
tabs = (GeneralTab,)
sticky = True

View File

@ -1,39 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import saharadashboard.job_binaries.views as views
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('',
url(r'^$', views.JobBinariesView.as_view(),
name='index'),
url(r'^$', views.JobBinariesView.as_view(),
name='job-binaries'),
url(r'^create-job-binary$',
views.CreateJobBinaryView.as_view(),
name='create-job-binary'),
url(r'^(?P<job_binary_id>[^/]+)$',
views.JobBinaryDetailsView.as_view(),
name='details'),
url(r'^(?P<job_binary_id>[^/]+)/download/$',
views.DownloadJobBinaryView.as_view(),
name='download'))

View File

@ -1,85 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django import http
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _
from django.views.generic import View
from horizon import exceptions
from horizon import forms
from horizon import tables
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
import saharadashboard.job_binaries.forms as job_binary_forms
from saharadashboard.job_binaries.tables import JobBinariesTable
import saharadashboard.job_binaries.tabs as _tabs
LOG = logging.getLogger(__name__)
class JobBinariesView(tables.DataTableView):
table_class = JobBinariesTable
template_name = 'job_binaries/job_binaries.html'
def get_data(self):
sahara = saharaclient(self.request)
job_binaries = sahara.job_binaries.list()
return job_binaries
class CreateJobBinaryView(forms.ModalFormView):
form_class = job_binary_forms.JobBinaryCreateForm
success_url = reverse_lazy('horizon:sahara:job_binaries:index')
classes = ("ajax-modal")
template_name = "job_binaries/create.html"
class JobBinaryDetailsView(tabs.TabView):
tab_group_class = _tabs.JobBinaryDetailsTabs
template_name = 'job_binaries/details.html'
def get_context_data(self, **kwargs):
context = super(JobBinaryDetailsView, self)\
.get_context_data(**kwargs)
return context
def get_data(self):
pass
class DownloadJobBinaryView(View):
def get(self, request, job_binary_id=None):
try:
sahara = saharaclient(request)
jb = sahara.job_binaries.get(job_binary_id)
data = sahara.job_binaries.get_file(job_binary_id)
except Exception:
redirect = reverse('horizon:sahara:job_binaries:index')
exceptions.handle(self.request,
_('Unable to fetch job binary: %(exc)s'),
redirect=redirect)
response = http.HttpResponse(mimetype='application/binary')
response['Content-Disposition'] = \
'attachment; filename=%s' % slugify(jb.name)
response.write(data)
response['Content-Length'] = str(len(data))
return response

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class JobExecutionsPanel(horizon.Panel):
name = _("Job Executions")
slug = 'job_executions'
dashboard.SaharaDashboard.register(JobExecutionsPanel)

View File

@ -1,113 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.core import urlresolvers
from django.utils import http
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from saharadashboard.api.client import client as saharaclient
from saharadashboard.jobs import tables as j_t
LOG = logging.getLogger(__name__)
class DeleteJobExecution(tables.BatchAction):
name = "delete"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Job execution")
data_type_plural = _("Job executions")
classes = ('btn-danger', 'btn-terminate')
def action(self, request, obj_id):
sahara = saharaclient(request)
sahara.job_executions.delete(obj_id)
class ReLaunchJobExistingCluster(j_t.ChoosePlugin):
name = "relaunch-job-existing"
verbose_name = _("Relaunch On Existing Cluster")
action_present = _("Launch")
action_past = _("Launched")
data_type_singular = _("Job")
data_type_plural = _("Jobs")
url = "horizon:sahara:jobs:launch-job"
classes = ('ajax-modal', 'btn-launch')
def get_link_url(self, datum):
base_url = urlresolvers.reverse(self.url)
params = http.urlencode({'job_id': datum.job_id,
'job_execution_id': datum.id})
return "?".join([base_url, params])
class ReLaunchJobNewCluster(ReLaunchJobExistingCluster):
name = "relaunch-job-new"
verbose_name = _("Relaunch On New Cluster")
action_present = _("Launch")
action_past = _("Launched")
data_type_singular = _("Job")
data_type_plural = _("Jobs")
url = "horizon:sahara:jobs:choose-plugin"
classes = ('ajax-modal', 'btn-launch')
class UpdateRow(tables.Row):
ajax = True
def get_data(self, request, job_execution_id):
sahara = saharaclient(request)
job_execution = sahara.job_executions.get(job_execution_id)
return job_execution
class JobExecutionsTable(tables.DataTable):
class StatusColumn(tables.Column):
def get_raw_data(self, datum):
return datum.info['status']
STATUS_CHOICES = (
("DONEWITHERROR", False),
("FAILED", False),
("KILLED", False),
("SUCCEEDED", True),
)
name = tables.Column("id",
verbose_name=_("ID"),
display_choices=(("id", "ID"), ("name", "Name")),
link=("horizon:sahara:job_executions:details"))
status = StatusColumn("info",
status=True,
status_choices=STATUS_CHOICES,
verbose_name=_("Status"))
def get_object_display(self, datum):
return datum.id
class Meta:
name = "job_executions"
row_class = UpdateRow
status_columns = ["status"]
verbose_name = _("Job Executions")
table_actions = [DeleteJobExecution]
row_actions = [DeleteJobExecution,
ReLaunchJobExistingCluster,
ReLaunchJobNewCluster]

View File

@ -1,73 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "job_execution_tab"
template_name = ("job_executions/_details.html")
def get_context_data(self, request):
job_execution_id = self.tab_group.kwargs['job_execution_id']
sahara = saharaclient(request)
job_execution = sahara.job_executions.get(job_execution_id)
object_names = self.get_object_names(job_execution, sahara)
return {"job_execution": job_execution,
"object_names": object_names}
def get_object_names(self, job_ex, s_client):
object_names = {}
obj_names_map = {'input_name': {'obj': 'data_sources',
'obj_id': job_ex.input_id},
'output_name': {'obj': 'data_sources',
'obj_id': job_ex.output_id},
'cluster_name': {'obj': 'clusters',
'obj_id': job_ex.cluster_id},
'job_name': {'obj': 'jobs',
'obj_id': job_ex.job_id}}
for item in obj_names_map:
object_names[item] = (
self.get_object_name(obj_names_map[item]['obj_id'],
obj_names_map[item]['obj'],
s_client))
return object_names
def get_object_name(self, obj_id, sahara_obj, s_client):
object_name = None
try:
s_obj = getattr(s_client, sahara_obj)
obj = s_obj.get(obj_id)
object_name = obj.name
except Exception as e:
LOG.warn("Unable to get name for %s with object_id %s (%s)" %
(sahara_obj, obj_id, str(e)))
return object_name
class JobExecutionDetailsTabs(tabs.TabGroup):
slug = "job_execution_details"
tabs = (GeneralTab,)
sticky = True

View File

@ -1,38 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import saharadashboard.job_executions.views as views
from saharadashboard.jobs import views as job_views
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('',
url(r'^$', views.JobExecutionsView.as_view(),
name='index'),
url(r'^$', views.JobExecutionsView.as_view(),
name='job-executions'),
url(r'^launch-job$',
job_views.LaunchJobView.as_view()),
url(r'^launch-job-new-cluster$',
job_views.LaunchJobNewClusterView.as_view()),
url(r'^(?P<job_execution_id>[^/]+)$',
views.JobExecutionDetailsView.as_view(),
name='details'))

View File

@ -1,48 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from horizon import tables
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
from saharadashboard.job_executions.tables import JobExecutionsTable
import saharadashboard.job_executions.tabs as _tabs
LOG = logging.getLogger(__name__)
class JobExecutionsView(tables.DataTableView):
table_class = JobExecutionsTable
template_name = 'job_executions/job_executions.html'
def get_data(self):
sahara = saharaclient(self.request)
jobs = sahara.job_executions.list()
return jobs
class JobExecutionDetailsView(tabs.TabView):
tab_group_class = _tabs.JobExecutionDetailsTabs
template_name = 'job_executions/details.html'
def get_context_data(self, **kwargs):
context = super(JobExecutionDetailsView, self)\
.get_context_data(**kwargs)
return context
def get_data(self):
pass

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class JobsPanel(horizon.Panel):
name = _("Jobs")
slug = 'jobs'
dashboard.SaharaDashboard.register(JobsPanel)

View File

@ -1,111 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.core import urlresolvers
from django.utils import http
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class CreateJob(tables.LinkAction):
name = "create job"
verbose_name = _("Create Job")
url = "horizon:sahara:jobs:create-job"
classes = ("btn-launch", "ajax-modal", "create_job_class")
class DeleteJob(tables.BatchAction):
name = "delete"
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Job")
data_type_plural = _("Jobs")
classes = ('btn-danger', 'btn-terminate')
def action(self, request, obj_id):
sahara = saharaclient(request)
sahara.jobs.delete(obj_id)
class LaunchJobExistingCluster(tables.LinkAction):
name = "launch-job-existing"
verbose_name = _("Launch On Existing Cluster")
action_present = _("Launch")
action_past = _("Launched")
data_type_singular = _("Job")
data_type_plural = _("Jobs")
url = "horizon:sahara:jobs:launch-job"
classes = ('ajax-modal', 'btn-launch')
def get_link_url(self, datum):
base_url = urlresolvers.reverse(self.url)
params = http.urlencode({"job_id": datum.id})
return "?".join([base_url, params])
class LaunchJobNewCluster(tables.LinkAction):
name = "launch-job-new"
verbose_name = _("Launch On New Cluster")
action_present = _("Launch")
action_past = _("Launched")
data_type_singular = _("Job")
data_type_plural = _("Jobs")
url = "horizon:sahara:jobs:launch-job-new-cluster"
classes = ('ajax-modal', 'btn-launch')
def get_link_url(self, datum):
base_url = urlresolvers.reverse(self.url)
params = http.urlencode({"job_id": datum.id})
return "?".join([base_url, params])
class ChoosePlugin(tables.LinkAction):
name = "launch-job-new"
verbose_name = _("Launch On New Cluster")
action_present = _("Launch")
action_past = _("Launched")
data_type_singular = _("Job")
data_type_plural = _("Jobs")
url = "horizon:sahara:jobs:choose-plugin"
classes = ('ajax-modal', 'btn-launch')
def get_link_url(self, datum):
base_url = urlresolvers.reverse(self.url)
params = http.urlencode({"job_id": datum.id})
return "?".join([base_url, params])
class JobsTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:sahara:jobs:details"))
description = tables.Column("description",
verbose_name=_("Description"))
class Meta:
name = "jobs"
verbose_name = _("Jobs")
table_actions = (CreateJob,
DeleteJob)
row_actions = (ChoosePlugin, LaunchJobExistingCluster, DeleteJob,)

View File

@ -1,41 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "job_details_tab"
template_name = ("jobs/_details.html")
def get_context_data(self, request):
job_id = self.tab_group.kwargs['job_id']
sahara = saharaclient(request)
job = sahara.jobs.get(job_id)
return {"job": job}
class JobDetailsTabs(tabs.TabGroup):
slug = "job_details"
tabs = (GeneralTab,)
sticky = True

View File

@ -1,45 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import saharadashboard.jobs.views as views
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('',
url(r'^$', views.JobsView.as_view(),
name='index'),
url(r'^$', views.JobsView.as_view(),
name='jobs'),
url(r'^create-job$',
views.CreateJobView.as_view(),
name='create-job'),
url(r'^launch-job$',
views.LaunchJobView.as_view(),
name='launch-job'),
url(r'^launch-job-new-cluster$',
views.LaunchJobNewClusterView.as_view(),
name='launch-job-new-cluster'),
url(r'^choose-plugin$',
views.ChoosePluginView.as_view(),
name='choose-plugin'),
url(r'^(?P<job_id>[^/]+)$',
views.JobDetailsView.as_view(),
name='details'))

View File

@ -1,103 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import json
import logging
from django.http import HttpResponse
from horizon import tables
from horizon import tabs
from horizon import workflows
from saharadashboard.api.client import client as saharaclient
import saharadashboard.jobs.tables as _tables
import saharadashboard.jobs.tabs as _tabs
import saharadashboard.jobs.workflows.create as create_flow
import saharadashboard.jobs.workflows.launch as launch_flow
LOG = logging.getLogger(__name__)
class JobsView(tables.DataTableView):
table_class = _tables.JobsTable
template_name = 'jobs/jobs.html'
def get_data(self):
sahara = saharaclient(self.request)
jobs = sahara.jobs.list()
return jobs
class CreateJobView(workflows.WorkflowView):
workflow_class = create_flow.CreateJob
success_url = "horizon:sahara:jobs:create-job"
classes = ("ajax-modal")
template_name = "jobs/create.html"
class JobDetailsView(tabs.TabView):
tab_group_class = _tabs.JobDetailsTabs
template_name = 'jobs/details.html'
def get_context_data(self, **kwargs):
context = super(JobDetailsView, self).get_context_data(**kwargs)
return context
def get_data(self):
pass
class LaunchJobView(workflows.WorkflowView):
workflow_class = launch_flow.LaunchJob
success_url = "horizon:sahara:jobs"
classes = ("ajax-modal")
template_name = "jobs/launch.html"
def get(self, request, *args, **kwargs):
if request.is_ajax():
if request.REQUEST.get("json", None):
job_id = request.REQUEST.get("job_id")
sahara = saharaclient(request)
job_type = sahara.jobs.get(job_id).type
return HttpResponse(json.dumps({"job_type": job_type}),
mimetype='application/json')
return super(LaunchJobView, self).get(request, args, kwargs)
def get_context_data(self, **kwargs):
context = super(LaunchJobView, self).get_context_data(**kwargs)
return context
class LaunchJobNewClusterView(workflows.WorkflowView):
workflow_class = launch_flow.LaunchJobNewCluster
success_url = "horizon:sahara:jobs"
classes = ("ajax-modal")
template_name = "jobs/launch.html"
def get_context_data(self, **kwargs):
context = super(LaunchJobNewClusterView, self).\
get_context_data(**kwargs)
return context
class ChoosePluginView(workflows.WorkflowView):
workflow_class = launch_flow.ChosePluginVersion
success_url = "horizon:sahara:jobs"
classes = ("ajax-modal")
template_name = "jobs/launch.html"
def get_context_data(self, **kwargs):
context = super(ChoosePluginView, self).get_context_data(**kwargs)
return context

View File

@ -1,144 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import json
import logging
from django.utils.translation import ugettext as _
from horizon import forms
from horizon import workflows
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class AdditionalLibsAction(workflows.Action):
lib_binaries = forms.ChoiceField(label=_("Choose libraries"),
required=False)
lib_ids = forms.CharField(
required=False,
widget=forms.HiddenInput())
def populate_lib_binaries_choices(self, request, context):
sahara = saharaclient(request)
job_binaries = sahara.job_binaries.list()
choices = [(job_binary.id, job_binary.name)
for job_binary in job_binaries]
choices.insert(0, ('', '-- not selected --'))
return choices
class Meta:
name = _("Libs")
help_text_template = "jobs/_create_job_libs_help.html"
class GeneralConfigAction(workflows.Action):
job_name = forms.CharField(label=_("Name"),
required=True)
job_type = forms.ChoiceField(label=_("Job Type"),
required=True)
main_binary = forms.ChoiceField(label=_("Choose a main binary"),
required=False,
help_text=_("Choose the binary which "
"should be used in this "
"Job."))
job_description = forms.CharField(label=_("Description"),
required=False,
widget=forms.Textarea)
def populate_job_type_choices(self, request, context):
choices = [("Pig", "Pig"), ("Hive", "Hive"),
("MapReduce", "MapReduce"),
("MapReduce.Streaming", "Streaming MapReduce"),
("Java", "Java Action")]
return choices
def populate_main_binary_choices(self, request, context):
sahara = saharaclient(request)
job_binaries = sahara.job_binaries.list()
choices = [(job_binary.id, job_binary.name)
for job_binary in job_binaries]
choices.insert(0, ('', '-- not selected --'))
return choices
def clean(self):
cleaned_data = super(workflows.Action, self).clean()
job_type = cleaned_data.get("job_type", "")
if job_type in ["Java", "MapReduce"]:
cleaned_data['main_binary'] = None
return cleaned_data
class Meta:
name = _("Create Job")
help_text_template = "jobs/_create_job_help.html"
class GeneralConfig(workflows.Step):
action_class = GeneralConfigAction
contributes = ("job_name", "job_type", "job_description", "main_binary")
class ConfigureLibs(workflows.Step):
action_class = AdditionalLibsAction
template_name = "jobs/library_template.html"
def contribute(self, data, context):
chosen_libs = json.loads(data.get("lib_ids", '[]'))
for k in xrange(len(chosen_libs)):
context["lib_" + str(k)] = chosen_libs[k]
return context
class CreateJob(workflows.Workflow):
slug = "create_job"
name = _("Create Job")
finalize_button_name = _("Create")
success_message = _("Job created")
failure_message = _("Could not create job")
success_url = "horizon:sahara:jobs:index"
default_steps = (GeneralConfig, ConfigureLibs)
def handle(self, request, context):
sahara = saharaclient(request)
main_locations = []
lib_locations = []
for k in context.keys():
if k.startswith('lib_'):
lib_locations.append(context.get(k))
if context.get("main_binary", None):
main_locations.append(context["main_binary"])
sahara.jobs.create(
context["job_name"],
context["job_type"],
main_locations,
lib_locations,
context["job_description"])
return True

View File

@ -1,443 +0,0 @@
# Copyright (c) 2013 Red Hat Inc.
#
# 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.
import json
import logging
from django.utils.translation import ugettext as _
from horizon import forms
from horizon import workflows
from saharadashboard.api.client import client as saharaclient
import saharadashboard.cluster_templates.workflows.create as t_flows
import saharadashboard.clusters.workflows.create as c_flow
import saharadashboard.utils.workflow_helpers as whelpers
LOG = logging.getLogger(__name__)
class JobExecutionGeneralConfigAction(workflows.Action):
job_input = forms.ChoiceField(
label=_("Input"),
required=True,
initial=(None, "None"),
widget=forms.Select(attrs={"class": "job_input_choice"}))
job_output = forms.ChoiceField(
label=_("Output"),
required=True,
initial=(None, "None"),
widget=forms.Select(attrs={"class": "job_output_choice"}))
def __init__(self, request, *args, **kwargs):
super(JobExecutionGeneralConfigAction, self).__init__(request,
*args,
**kwargs)
if request.REQUEST.get("job_id", None) is None:
self.fields["job"] = forms.ChoiceField(
label=_("Job"),
required=True)
self.fields["job"].choices = self.populate_job_choices(request)
else:
self.fields["job"] = forms.CharField(
widget=forms.HiddenInput(),
initial=request.REQUEST.get("job_id", None))
def populate_job_input_choices(self, request, context):
return self.get_data_source_choices(request, context)
def populate_job_output_choices(self, request, context):
return self.get_data_source_choices(request, context)
def get_data_source_choices(self, request, context):
sahara = saharaclient(request)
data_sources = sahara.data_sources.list()
choices = [(data_source.id, data_source.name)
for data_source in data_sources]
choices.insert(0, (None, 'None'))
return choices
def populate_job_choices(self, request):
sahara = saharaclient(request)
jobs = sahara.jobs.list()
choices = [(job.id, job.name)
for job in jobs]
return choices
class Meta:
name = _("Job")
help_text_template = "jobs/_launch_job_help.html"
class JobExecutionExistingGeneralConfigAction(JobExecutionGeneralConfigAction):
cluster = forms.ChoiceField(
label=_("Cluster"),
required=True,
initial=(None, "None"),
widget=forms.Select(attrs={"class": "cluster_choice"}))
def populate_cluster_choices(self, request, context):
sahara = saharaclient(request)
clusters = sahara.clusters.list()
choices = [(cluster.id, cluster.name)
for cluster in clusters]
return choices
class Meta:
name = _("Job")
help_text_template = "jobs/_launch_job_help.html"
class JobConfigAction(workflows.Action):
MAIN_CLASS = "edp.java.main_class"
JAVA_OPTS = "edp.java.java_opts"
EDP_MAPPER = "edp.streaming.mapper"
EDP_REDUCER = "edp.streaming.reducer"
EDP_PREFIX = "edp."
property_name = forms.ChoiceField(
required=False,
)
job_configs = forms.CharField(
required=False,
widget=forms.HiddenInput())
job_params = forms.CharField(
required=False,
widget=forms.HiddenInput())
job_args_array = forms.CharField(
required=False,
widget=forms.HiddenInput())
job_type = forms.CharField(
required=False,
widget=forms.HiddenInput())
main_class = forms.CharField(label=_("Main Class"),
required=False)
java_opts = forms.CharField(label=_("Java Opts"),
required=False)
streaming_mapper = forms.CharField(label=_("Mapper"),
required=True)
streaming_reducer = forms.CharField(label=_("Reducer"),
required=True)
def __init__(self, request, *args, **kwargs):
super(JobConfigAction, self).__init__(request, *args, **kwargs)
job_ex_id = request.REQUEST.get("job_execution_id")
if job_ex_id is not None:
client = saharaclient(request)
job_ex_id = request.REQUEST.get("job_execution_id")
job_ex = client.job_executions.get(job_ex_id)
job_configs = job_ex.job_configs
edp_configs = {}
if 'configs' in job_configs:
configs, edp_configs = (
self.clean_edp_configs(job_configs['configs']))
self.fields['job_configs'].initial = (
json.dumps(configs))
if 'params' in job_configs:
self.fields['job_params'].initial = (
json.dumps(job_configs['params']))
job_args = json.dumps(job_configs['args'])
self.fields['job_args_array'].initial = job_args
if self.MAIN_CLASS in edp_configs:
self.fields['main_class'].initial = (
edp_configs[self.MAIN_CLASS])
if self.JAVA_OPTS in edp_configs:
self.fields['java_opts'].initial = (
edp_configs[self.JAVA_OPTS])
if self.EDP_MAPPER in edp_configs:
self.fields['streaming_mapper'].initial = (
edp_configs[self.EDP_MAPPER])
if self.EDP_REDUCER in edp_configs:
self.fields['streaming_reducer'].initial = (
edp_configs[self.EDP_REDUCER])
def clean(self):
cleaned_data = super(workflows.Action, self).clean()
job_type = cleaned_data.get("job_type", None)
if job_type != "MapReduce.Streaming":
if "streaming_mapper" in self._errors:
del self._errors["streaming_mapper"]
if "streaming_reducer" in self._errors:
del self._errors["streaming_reducer"]
return cleaned_data
def populate_property_name_choices(self, request, context):
client = saharaclient(request)
job_id = request.REQUEST.get("job_id") or request.REQUEST.get("job")
job_type = client.jobs.get(job_id).type
job_configs = client.jobs.get_configs(job_type).job_config
choices = [(param['value'], param['name'])
for param in job_configs['configs']]
return choices
def clean_edp_configs(self, configs):
edp_configs = {}
for key, value in configs.iteritems():
if key.startswith(self.EDP_PREFIX):
edp_configs[key] = value
for rmkey in edp_configs.keys():
del configs[rmkey]
return (configs, edp_configs)
class Meta:
name = _("Configure")
help_text_template = "jobs/_launch_job_configure_help.html"
class JobExecutionGeneralConfig(workflows.Step):
action_class = JobExecutionGeneralConfigAction
def contribute(self, data, context):
for k, v in data.items():
if k in ["job_input", "job_output"]:
context["job_general_" + k] = None if v == "None" else v
else:
context["job_general_" + k] = v
return context
class JobExecutionExistingGeneralConfig(workflows.Step):
action_class = JobExecutionExistingGeneralConfigAction
def contribute(self, data, context):
for k, v in data.items():
if k in ["job_input", "job_output"]:
context["job_general_" + k] = None if v == "None" else v
else:
context["job_general_" + k] = v
return context
class JobConfig(workflows.Step):
action_class = JobConfigAction
template_name = 'jobs/config_template.html'
def contribute(self, data, context):
job_config = self.clean_configs(
json.loads(data.get("job_configs", '{}')))
job_params = self.clean_configs(
json.loads(data.get("job_params", '{}')))
job_args_array = self.clean_configs(
json.loads(data.get("job_args_array", '[]')))
job_type = data.get("job_type", '')
context["job_type"] = job_type
context["job_config"] = {"configs": job_config}
context["job_config"]["args"] = job_args_array
if job_type == "Java":
context["job_config"]["configs"][JobConfigAction.MAIN_CLASS] = (
data.get("main_class", ""))
context["job_config"]["configs"][JobConfigAction.JAVA_OPTS] = (
data.get("java_opts", ""))
elif job_type == "MapReduce.Streaming":
context["job_config"]["configs"][JobConfigAction.EDP_MAPPER] = (
data.get("streaming_mapper", ""))
context["job_config"]["configs"][JobConfigAction.EDP_REDUCER] = (
data.get("streaming_reducer", ""))
else:
context["job_config"]["params"] = job_params
return context
@staticmethod
def clean_configs(configs):
cleaned_conf = None
if isinstance(configs, dict):
cleaned_conf = dict([(k.strip(), v.strip())
for k, v in configs.items()
if len(v.strip()) > 0 and len(k.strip()) > 0])
elif isinstance(configs, list):
cleaned_conf = list([v.strip() for v in configs
if len(v.strip()) > 0])
return cleaned_conf
class NewClusterConfigAction(c_flow.GeneralConfigAction):
persist_cluster = forms.BooleanField(
label=_("Persist cluster after job exit"),
required=False)
class Meta:
name = _("Configure Cluster")
help_text_template = (
"clusters/_configure_general_help.html")
class ClusterGeneralConfig(workflows.Step):
action_class = NewClusterConfigAction
contributes = ("hidden_configure_field", )
def contribute(self, data, context):
for k, v in data.items():
context["cluster_general_" + k] = v
return context
class LaunchJob(workflows.Workflow):
slug = "launch_job"
name = _("Launch Job")
finalize_button_name = _("Launch")
success_message = _("Job launched")
failure_message = _("Could not launch job")
success_url = "horizon:sahara:job_executions:index"
default_steps = (JobExecutionExistingGeneralConfig, JobConfig)
def handle(self, request, context):
sahara = saharaclient(request)
sahara.job_executions.create(
context["job_general_job"],
context["job_general_cluster"],
context["job_general_job_input"],
context["job_general_job_output"],
context["job_config"])
return True
class SelectHadoopPluginAction(t_flows.SelectPluginAction):
def __init__(self, request, *args, **kwargs):
super(SelectHadoopPluginAction, self).__init__(request,
*args,
**kwargs)
self.fields["job_id"] = forms.ChoiceField(
label=_("Plugin name"),
required=True,
initial=request.GET.get("job_id") or request.POST.get("job_id"),
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
self.fields["job_configs"] = forms.ChoiceField(
label=_("Job configs"),
required=True,
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
self.fields["job_args"] = forms.ChoiceField(
label=_("Job args"),
required=True,
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
self.fields["job_params"] = forms.ChoiceField(
label=_("Job params"),
required=True,
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
job_ex_id = request.REQUEST.get("job_execution_id")
if job_ex_id is not None:
self.fields["job_execution_id"] = forms.ChoiceField(
label=_("Job Execution Id"),
required=True,
initial=request.REQUEST.get("job_execution_id"),
widget=forms.HiddenInput(
attrs={"class": "hidden_create_field"}))
client = saharaclient(request)
job_ex_id = request.REQUEST.get("job_execution_id")
job_configs = client.job_executions.get(job_ex_id).job_configs
if "configs" in job_configs:
self.fields["job_configs"].initial = (
json.dumps(job_configs["configs"]))
if "params" in job_configs:
self.fields["job_params"].initial = (
json.dumps(job_configs["params"]))
if "args" in job_configs:
self.fields["job_args"].initial = (
json.dumps(job_configs["args"]))
class Meta:
name = _("Select plugin and hadoop version for cluster")
help_text_template = ("clusters/_create_general_help.html")
class SelectHadoopPlugin(workflows.Step):
action_class = SelectHadoopPluginAction
class ChosePluginVersion(workflows.Workflow):
slug = "lunch_job"
name = _("Launch Job")
finalize_button_name = _("Create")
success_message = _("Created")
failure_message = _("Could not create")
success_url = "horizon:sahara:cluster_templates:index"
default_steps = (SelectHadoopPlugin,)
class LaunchJobNewCluster(workflows.Workflow):
slug = "launch_job"
name = _("Launch Job")
finalize_button_name = _("Launch")
success_message = _("Job launched")
failure_message = _("Could not launch job")
success_url = "horizon:sahara:jobs:index"
default_steps = (ClusterGeneralConfig,
JobExecutionGeneralConfig,
JobConfig)
def handle(self, request, context):
sahara = saharaclient(request)
node_groups = None
plugin, hadoop_version = (
whelpers.get_plugin_and_hadoop_version(request))
ct_id = context["cluster_general_cluster_template"] or None
user_keypair = context["cluster_general_keypair"] or None
cluster = sahara.clusters.create(
context["cluster_general_cluster_name"],
plugin, hadoop_version,
cluster_template_id=ct_id,
default_image_id=context["cluster_general_image"],
description=context["cluster_general_description"],
node_groups=node_groups,
user_keypair_id=user_keypair,
is_transient=not(context["cluster_general_persist_cluster"]),
net_id=context.get("cluster_general_neutron_management_network",
None))
sahara.job_executions.create(
context["job_general_job"],
cluster.id,
context["job_general_job_input"],
context["job_general_job_output"],
context["job_config"])
return True

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class NodegroupTemplatesPanel(horizon.Panel):
name = _("Node Group Templates")
slug = 'nodegroup_templates'
dashboard.SaharaDashboard.register(NodegroupTemplatesPanel)

View File

@ -1,91 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django import template
from django.utils.translation import ugettext_lazy as _
from horizon import tables
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class CreateNodegroupTemplate(tables.LinkAction):
name = "create"
verbose_name = _("Create Template")
url = "horizon:sahara:nodegroup_templates:create-nodegroup-template"
classes = ("ajax-modal", "btn-create", "create-nodegrouptemplate-btn")
class ConfigureNodegroupTemplate(tables.LinkAction):
name = "configure"
verbose_name = _("Configure Template")
url = "horizon:sahara:nodegroup_templates:configure-nodegroup-template"
classes = ("ajax-modal", "btn-create", "configure-nodegrouptemplate-btn")
attrs = {"style": "display: none"}
class CopyTemplate(tables.LinkAction):
name = "copy"
verbose_name = _("Copy Template")
url = "horizon:sahara:nodegroup_templates:copy"
classes = ("ajax-modal", )
class DeleteTemplate(tables.BatchAction):
name = "delete_nodegroup_template"
verbose_name = _("Delete")
classes = ("btn-terminate", "btn-danger")
action_present = _("Delete")
action_past = _("Deleted")
data_type_singular = _("Template")
data_type_plural = _("Templates")
def allowed(self, request, template):
return True
def action(self, request, template_id):
sahara = saharaclient(request)
sahara.node_group_templates.delete(template_id)
def render_processes(nodegroup_template):
template_name = 'nodegroup_templates/_processes_list.html'
context = {"processes": nodegroup_template.node_processes}
return template.loader.render_to_string(template_name, context)
class NodegroupTemplatesTable(tables.DataTable):
name = tables.Column("name",
verbose_name=_("Name"),
link=("horizon:sahara:nodegroup_templates:details"))
plugin_name = tables.Column("plugin_name",
verbose_name=_("Plugin"))
hadoop_version = tables.Column("hadoop_version",
verbose_name=_("Hadoop Version"))
node_processes = tables.Column(render_processes,
verbose_name=_("Node Processes"))
class Meta:
name = "nodegroup_templates"
verbose_name = _("Node Group Templates")
table_actions = (CreateNodegroupTemplate,
ConfigureNodegroupTemplate,
DeleteTemplate)
row_actions = (CopyTemplate,
DeleteTemplate,)

View File

@ -1,60 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
from saharadashboard.utils import importutils
# horizon.api is for backward compatibility with folsom
nova = importutils.import_any('openstack_dashboard.api.nova',
'horizon.api.nova')
LOG = logging.getLogger(__name__)
class GeneralTab(tabs.Tab):
name = _("General Info")
slug = "nodegroup_template_details_tab"
template_name = ("nodegroup_templates/_details.html")
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']
sahara = saharaclient(request)
template = sahara.node_group_templates.get(template_id)
flavor = nova.flavor_get(request, template.flavor_id)
return {"template": template, "flavor": flavor}
class ConfigsTab(tabs.Tab):
name = _("Service Configurations")
slug = "nodegroup_template_service_configs_tab"
template_name = ("nodegroup_templates/_service_confs.html")
def get_context_data(self, request):
template_id = self.tab_group.kwargs['template_id']
sahara = saharaclient(request)
template = sahara.node_group_templates.get(template_id)
return {"template": template}
class NodegroupTemplateDetailsTabs(tabs.TabGroup):
slug = "nodegroup_template_details"
tabs = (GeneralTab, ConfigsTab, )
sticky = True

View File

@ -1,44 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import saharadashboard.nodegroup_templates.views as views
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('sahara.nodegroup_templates.views',
url(r'^$', views.NodegroupTemplatesView.as_view(),
name='index'),
url(r'^nodegroup-templates$',
views.NodegroupTemplatesView.as_view(),
name='nodegroup-templates'),
url(r'^create-nodegroup-template$',
views.CreateNodegroupTemplateView.as_view(),
name='create-nodegroup-template'),
url(r'^configure-nodegroup-template$',
views.ConfigureNodegroupTemplateView.as_view(),
name='configure-nodegroup-template'),
url(r'^(?P<template_id>[^/]+)$',
views.NodegroupTemplateDetailsView.as_view(),
name='details'),
url(r'^(?P<template_id>[^/]+)/copy$',
views.CopyNodegroupTemplateView.as_view(),
name='copy')
)

View File

@ -1,91 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from horizon import tables
from horizon import tabs
from horizon import workflows
from saharadashboard.api.client import client as saharaclient
import saharadashboard.nodegroup_templates.tables as _tables
import saharadashboard.nodegroup_templates.tabs as _tabs
import saharadashboard.nodegroup_templates.workflows.copy as copy_flow
import saharadashboard.nodegroup_templates.workflows.create as create_flow
LOG = logging.getLogger(__name__)
class NodegroupTemplatesView(tables.DataTableView):
table_class = _tables.NodegroupTemplatesTable
template_name = 'nodegroup_templates/nodegroup_templates.html'
def get_data(self):
sahara = saharaclient(self.request)
nodegroup_templates = sahara.node_group_templates.list()
return nodegroup_templates
class NodegroupTemplateDetailsView(tabs.TabView):
tab_group_class = _tabs.NodegroupTemplateDetailsTabs
template_name = 'nodegroup_templates/details.html'
def get_context_data(self, **kwargs):
context = super(NodegroupTemplateDetailsView, self)\
.get_context_data(**kwargs)
return context
def get_data(self):
pass
class CreateNodegroupTemplateView(workflows.WorkflowView):
workflow_class = create_flow.CreateNodegroupTemplate
success_url = \
"horizon:sahara:nodegroup_templates:create-nodegroup-template"
classes = ("ajax-modal")
template_name = "nodegroup_templates/create.html"
class ConfigureNodegroupTemplateView(workflows.WorkflowView):
workflow_class = create_flow.ConfigureNodegroupTemplate
success_url = "horizon:sahara:nodegroup_templates"
template_name = "nodegroup_templates/configure.html"
class CopyNodegroupTemplateView(workflows.WorkflowView):
workflow_class = copy_flow.CopyNodegroupTemplate
success_url = "horizon:sahara:nodegroup_templates"
template_name = "nodegroup_templates/configure.html"
def get_context_data(self, **kwargs):
context = super(CopyNodegroupTemplateView, self)\
.get_context_data(**kwargs)
context["template_id"] = kwargs["template_id"]
return context
def get_object(self, *args, **kwargs):
if not hasattr(self, "_object"):
template_id = self.kwargs['template_id']
sahara = saharaclient(self.request)
template = sahara.node_group_templates.get(template_id)
self._object = template
return self._object
def get_initial(self):
initial = super(CopyNodegroupTemplateView, self).get_initial()
initial.update({'template_id': self.kwargs['template_id']})
return initial

View File

@ -1,80 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext as _
from saharadashboard.api.client import client as saharaclient
import saharadashboard.nodegroup_templates.workflows.create as create_flow
LOG = logging.getLogger(__name__)
class CopyNodegroupTemplate(create_flow.ConfigureNodegroupTemplate):
success_message = _("Node Group Template copy %s created")
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
sahara = saharaclient(request)
template_id = context_seed["template_id"]
template = sahara.node_group_templates.get(template_id)
self._set_configs_to_copy(template.node_configs)
plugin = template.plugin_name
hadoop_version = template.hadoop_version
request.GET = request.GET.copy()
request.GET.update({"plugin_name": plugin})
request.GET.update({"hadoop_version": hadoop_version})
super(CopyNodegroupTemplate, self).__init__(request, context_seed,
entry_point, *args,
**kwargs)
for step in self.steps:
if not isinstance(step, create_flow.GeneralConfig):
continue
fields = step.action.fields
fields["nodegroup_name"].initial = template.name + "-copy"
fields["description"].initial = template.description
fields["flavor"].initial = template.flavor_id
storage = "cinder_volume" if template.volumes_per_node > 0 \
else "ephemeral_drive"
volumes_per_node = template.volumes_per_node
volumes_size = template.volumes_size
fields["storage"].initial = storage
fields["volumes_per_node"].initial = volumes_per_node
fields["volumes_size"].initial = volumes_size
if template.floating_ip_pool:
fields['floating_ip_pool'].initial = template.floating_ip_pool
processes_dict = dict()
plugin_details = sahara.plugins.get_version_details(
plugin,
hadoop_version)
plugin_node_processes = plugin_details.node_processes
for process in template.node_processes:
# need to know the service
_service = None
for service, processes in plugin_node_processes.items():
if process in processes:
_service = service
break
processes_dict.update({"%s:%s" % (_service, process): process})
fields["processes"].initial = processes_dict

View File

@ -1,299 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext as _
from horizon import exceptions
from horizon import forms
from horizon import workflows
from saharaclient.api import base as api_base
from saharadashboard.api import client as saharaclient
import saharadashboard.api.helpers as helpers
from saharadashboard.utils import importutils
import saharadashboard.utils.workflow_helpers as whelpers
# horizon.api is for backward compatibility with folsom
nova = importutils.import_any('openstack_dashboard.api.nova',
'horizon.api.nova')
network = importutils.import_any('openstack_dashboard.api.network',
'horizon.api.network')
LOG = logging.getLogger(__name__)
class GeneralConfigAction(workflows.Action):
nodegroup_name = forms.CharField(label=_("Template Name"),
required=True)
description = forms.CharField(label=_("Description"),
required=False,
widget=forms.Textarea)
flavor = forms.ChoiceField(label=_("OpenStack Flavor"),
required=True)
storage = forms.ChoiceField(
label=_("Storage location"),
required=True,
help_text=_("Storage"),
choices=[("ephemeral_drive", "Ephemeral Drive"),
("cinder_volume", "Cinder Volume")],
widget=forms.Select(attrs={"class": "storage_field"}))
volumes_per_node = forms.IntegerField(
label=_("Volumes per node"),
required=False,
initial=1,
widget=forms.TextInput(attrs={"class": "volume_per_node_field"})
)
volumes_size = forms.IntegerField(
label=_("Volumes size (GB)"),
required=False,
initial=10,
widget=forms.TextInput(attrs={"class": "volume_size_field"})
)
hidden_configure_field = forms.CharField(
required=False,
widget=forms.HiddenInput(attrs={"class": "hidden_configure_field"}))
def __init__(self, request, *args, **kwargs):
super(GeneralConfigAction, self).__init__(request, *args, **kwargs)
sahara = saharaclient.client(request)
hlps = helpers.Helpers(sahara)
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
process_choices = []
version_details = sahara.plugins.get_version_details(plugin,
hadoop_version)
for service, processes in version_details.node_processes.items():
for process in processes:
process_choices.append(
(str(service) + ":" + str(process), process))
if not saharaclient.AUTO_ASSIGNMENT_ENABLED:
pools = network.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'] = forms.ChoiceField(
label=_("Floating IP pool"),
choices=pool_choices,
required=False)
self.fields["processes"] = forms.MultipleChoiceField(
label=_("Processes"),
required=True,
widget=forms.CheckboxSelectMultiple(),
help_text=_("Processes to be launched in node group"),
choices=process_choices)
self.fields["plugin_name"] = forms.CharField(
widget=forms.HiddenInput(),
initial=plugin
)
self.fields["hadoop_version"] = forms.CharField(
widget=forms.HiddenInput(),
initial=hadoop_version
)
node_parameters = hlps.get_general_node_group_configs(plugin,
hadoop_version)
for param in node_parameters:
self.fields[param.name] = whelpers.build_control(param)
def populate_flavor_choices(self, request, context):
try:
flavors = nova.flavor_list(request)
flavor_list = [(flavor.id, "%s" % flavor.name)
for flavor in flavors]
except Exception:
flavor_list = []
exceptions.handle(request,
_('Unable to retrieve instance flavors.'))
return sorted(flavor_list)
def get_help_text(self):
extra = dict()
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(self.request)
extra["plugin_name"] = plugin
extra["hadoop_version"] = hadoop_version
return super(GeneralConfigAction, self).get_help_text(extra)
class Meta:
name = _("Configure Node Group Template")
help_text_template = \
("nodegroup_templates/_configure_general_help.html")
class GeneralConfig(workflows.Step):
action_class = GeneralConfigAction
contributes = ("general_nodegroup_name", )
def contribute(self, data, context):
for k, v in data.items():
if "hidden" in k:
continue
context["general_" + k] = v if v != "None" else None
post = self.workflow.request.POST
context['general_processes'] = post.getlist("processes")
return context
class ConfigureNodegroupTemplate(whelpers.ServiceParametersWorkflow,
whelpers.StatusFormatMixin):
slug = "configure_nodegroup_template"
name = _("Create Node Group Template")
finalize_button_name = _("Create")
success_message = _("Created Node Group Template %s")
name_property = "general_nodegroup_name"
success_url = "horizon:sahara:nodegroup_templates:index"
default_steps = (GeneralConfig,)
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
sahara = saharaclient.client(request)
hlps = helpers.Helpers(sahara)
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
general_parameters = hlps.get_general_node_group_configs(
plugin,
hadoop_version)
service_parameters = hlps.get_targeted_node_group_configs(
plugin,
hadoop_version)
self._populate_tabs(general_parameters, service_parameters)
super(ConfigureNodegroupTemplate, self).__init__(request,
context_seed,
entry_point,
*args, **kwargs)
def is_valid(self):
missing = self.depends_on - set(self.context.keys())
if missing:
raise exceptions.WorkflowValidationError(
"Unable to complete the workflow. The values %s are "
"required but not present." % ", ".join(missing))
checked_steps = []
if "general_processes" in self.context:
checked_steps = self.context["general_processes"]
enabled_services = set([])
for process_name in checked_steps:
enabled_services.add(str(process_name).split(":")[0])
steps_valid = True
for step in self.steps:
process_name = str(getattr(step, "process_name", None))
if process_name not in enabled_services and \
not isinstance(step, GeneralConfig):
continue
if not step.action.is_valid():
steps_valid = False
step.has_errors = True
if not steps_valid:
return steps_valid
return self.validate(self.context)
def handle(self, request, context):
try:
sahara = saharaclient.client(request)
processes = []
for service_process in context["general_processes"]:
processes.append(str(service_process).split(":")[1])
configs_dict = whelpers.parse_configs_from_context(context,
self.defaults)
plugin, hadoop_version = whelpers.\
get_plugin_and_hadoop_version(request)
volumes_per_node = None
volumes_size = None
if context["general_storage"] == "cinder_volume":
volumes_per_node = context["general_volumes_per_node"]
volumes_size = context["general_volumes_size"]
sahara.node_group_templates.create(
name=context["general_nodegroup_name"],
plugin_name=plugin,
hadoop_version=hadoop_version,
description=context["general_description"],
flavor_id=context["general_flavor"],
volumes_per_node=volumes_per_node,
volumes_size=volumes_size,
node_processes=processes,
node_configs=configs_dict,
floating_ip_pool=context.get("general_floating_ip_pool", None))
return True
except api_base.APIException as e:
self.error_description = str(e)
return False
except Exception:
exceptions.handle(request)
class SelectPluginAction(workflows.Action,
whelpers.PluginAndVersionMixin):
hidden_create_field = forms.CharField(
required=False,
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
def __init__(self, request, *args, **kwargs):
super(SelectPluginAction, self).__init__(request, *args, **kwargs)
sahara = saharaclient.client(request)
self._generate_plugin_version_fields(sahara)
class Meta:
name = _("Select plugin and hadoop version")
help_text_template = ("nodegroup_templates/_create_general_help.html")
class SelectPlugin(workflows.Step):
action_class = SelectPluginAction
contributes = ("plugin_name", "hadoop_version")
def contribute(self, data, context):
context = super(SelectPlugin, self).contribute(data, context)
context["plugin_name"] = data.get('plugin_name', None)
context["hadoop_version"] = \
data.get(context["plugin_name"] + "_version", None)
return context
class CreateNodegroupTemplate(workflows.Workflow):
slug = "create_nodegroup_template"
name = _("Create Node Group Template")
finalize_button_name = _("Create")
success_message = _("Created")
failure_message = _("Could not create")
success_url = "horizon:sahara:nodegroup_templates:index"
default_steps = (SelectPlugin,)

View File

@ -1,17 +0,0 @@
#
# 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.
import six
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))

View File

@ -1,73 +0,0 @@
# Copyright 2011 OpenStack Foundation.
# All Rights Reserved.
#
# 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.
"""
Import related utilities and helper functions.
"""
import sys
import traceback
def import_class(import_str):
"""Returns a class from a string including module and class."""
mod_str, _sep, class_str = import_str.rpartition('.')
__import__(mod_str)
try:
return getattr(sys.modules[mod_str], class_str)
except AttributeError:
raise ImportError('Class %s cannot be found (%s)' %
(class_str,
traceback.format_exception(*sys.exc_info())))
def import_object(import_str, *args, **kwargs):
"""Import a class and return an instance of it."""
return import_class(import_str)(*args, **kwargs)
def import_object_ns(name_space, import_str, *args, **kwargs):
"""Tries to import object from default namespace.
Imports a class and return an instance of it, first by trying
to find the class in a default namespace, then failing back to
a full path if not found in the default namespace.
"""
import_value = "%s.%s" % (name_space, import_str)
try:
return import_class(import_value)(*args, **kwargs)
except ImportError:
return import_class(import_str)(*args, **kwargs)
def import_module(import_str):
"""Import a module."""
__import__(import_str)
return sys.modules[import_str]
def import_versioned_module(version, submodule=None):
module = 'saharadashboard.v%s' % version
if submodule:
module = '.'.join((module, submodule))
return import_module(module)
def try_import(import_str, default=None):
"""Try to import a module and if it fails return default."""
try:
return import_module(import_str)
except ImportError:
return default

View File

@ -1,27 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 _
import horizon
from saharadashboard import dashboard
class PluginsPanel(horizon.Panel):
name = _("Plugins")
slug = 'plugins'
dashboard.SaharaDashboard.register(PluginsPanel)

View File

@ -1,46 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django import template
from django.utils.translation import ugettext_lazy as _
from horizon import tables
LOG = logging.getLogger(__name__)
def render_versions(plugin):
template_name = 'plugins/_versions_list.html'
context = {"plugin": plugin}
return template.loader.render_to_string(template_name, context)
class PluginsTable(tables.DataTable):
title = tables.Column("title",
verbose_name=_("Name"),
link=("horizon:sahara:plugins:details"))
versions = tables.Column(render_versions,
verbose_name=_("Supported Hadoop Versions"))
description = tables.Column("description",
verbose_name=_("Plugin Description"))
class Meta:
name = "plugins"
verbose_name = _("Plugins")
table_actions = ()
row_actions = ()

View File

@ -1,41 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
LOG = logging.getLogger(__name__)
class DetailsTab(tabs.Tab):
name = _("Details")
slug = "plugin_details_tab"
template_name = ("plugins/_details.html")
def get_context_data(self, request):
plugin_id = self.tab_group.kwargs['plugin_id']
sahara = saharaclient(request)
plugin = sahara.plugins.get(plugin_id)
return {"plugin": plugin}
class PluginDetailsTabs(tabs.TabGroup):
slug = "cluster_details"
tabs = (DetailsTab,)
sticky = True

View File

@ -1,32 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 saharadashboard.plugins.views import PluginDetailsView
from saharadashboard.plugins.views import PluginsView
from saharadashboard.utils import importutils
urls = importutils.import_any('django.conf.urls.defaults',
'django.conf.urls')
patterns = urls.patterns
url = urls.url
urlpatterns = patterns('',
url(r'^$', PluginsView.as_view(), name='index'),
url(r'^$', PluginsView.as_view(), name='plugins'),
url(r'^(?P<plugin_id>[^/]+)$',
PluginDetailsView.as_view(), name='details'),
)

View File

@ -1,46 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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.
import logging
from horizon import tables
from horizon import tabs
from saharadashboard.api.client import client as saharaclient
from saharadashboard.plugins.tables import PluginsTable
from saharadashboard.plugins.tabs import PluginDetailsTabs
LOG = logging.getLogger(__name__)
class PluginsView(tables.DataTableView):
table_class = PluginsTable
template_name = 'plugins/plugins.html'
def get_data(self):
sahara = saharaclient(self.request)
return sahara.plugins.list()
class PluginDetailsView(tabs.TabView):
tab_group_class = PluginDetailsTabs
template_name = 'plugins/details.html'
def get_context_data(self, **kwargs):
context = super(PluginDetailsView, self).get_context_data(**kwargs)
return context
def get_data(self):
pass

View File

@ -1,21 +0,0 @@
<div class="well">
<p>
This Cluster Template will be created for:
<br >
<b>Plugin</b>: {{ plugin_name }}
<br />
<b>Hadoop version</b>: {{ hadoop_version }}
<br />
</p>
<p>
The Cluster Template object should specify Node Group Templates that will be used to build a Hadoop Cluster.
You can add Node Groups using Node Group Templates on a &quotNode Groups&quot tab.
</p>
<p>
You may set <b>cluster</b> scoped Hadoop configurations on corresponding tabs.
</p>
<p>
The Cluster Template object may specify a list of processes in anti-affinity group.
That means these processes may not be launched more than once on a single host.
</p>
</div>

View File

@ -1,3 +0,0 @@
<p class="well">
Select a plugin and Hadoop version for a new Cluster template.
</p>

View File

@ -1,54 +0,0 @@
{% load i18n sizeformat %}
{% load url from future %}
<h3>{% trans "Template Overview" %}</h3>
<div class="status row-fluid detail">
<dl>
<dt>{% trans "Name" %}</dt>
<dd>{{ template.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ template.id }}</dd>
<dt>{% trans "Description" %}</dt>
<dd>{{ template.description|default:"None" }}</dd>
</dl>
<dl>
<dt>{% trans "Plugin" %}</dt>
<dd><a href="{% url 'horizon:sahara:plugins:details' template.plugin_name %}">{{ template.plugin_name }}</a></dd>
<dt>{% trans "Hadoop Version" %}</dt>
<dd>{{ template.hadoop_version }}</dd>
</dl>
<dl>
<dt>{% trans "Anti-affinity enabled for" %}</dt>
{% if template.anti_affinity %}
<dd>
<ul>
{% for process in template.anti_affinity %}
<li>{{ process }}</li>
{% endfor %}
</ul>
</dd>
{% else %}
<h6>{% trans "no processes" %}</h6>
{% endif %}
</dl>
<dl>
<dt>{% trans "Node Configurations" %}</dt>
{% if template.cluster_configs %}
<dd>
{% for service, service_conf in template.cluster_configs.items %}
<h4>{{ service }}</h4>
{% if service_conf %}
<ul>
{% for conf_name, conf_value in service_conf.items %}
<li>{{ conf_name }}:&nbsp{{ conf_value }}</li>
{% endfor %}
</ul>
{% else %}
<h6>No configurations</h6>
{% endif %}
{% endfor %}
</dd>
{% else %}
<dd>{% trans "Cluster configurations are not specified" %}</dd>
{% endif %}
</dl>
</div>

View File

@ -1,55 +0,0 @@
{% load i18n sizeformat %}
{% load url from future %}
<h3>{% trans "Node Groups" %}</h3>
<div class="status row-fluid detail">
{% for node_group in template.node_groups %}
<dl class="well">
<h4>{% trans "Node Group" %}: {{ node_group.name }}</h4>
<dt>{% trans "Nodes Count" %}</dt>
<dd>{{ node_group.count }}</dd>
<dt>{% trans "Flavor" %}</dt>
<dd>{{ node_group.flavor_id|default:"Flavor is not specified" }}</dd>
<dt>{% trans "Template" %}</dt>
{% if node_group.node_group_template_id %}
<dd><a href="{% url 'horizon:sahara:nodegroup_templates:details' node_group.node_group_template_id %}">{{ node_group.node_group_template.name }} </a></dd>
{% else %}
<dd>{% trans "Template not specified" %}</dd>
{% endif %}
<dt>{% trans "Node Processes" %}</dt>
{% if node_group.node_processes %}
<dd>
<ul>
{% for process in node_group.node_processes %}
<li>{{ process }}</li>
{% endfor %}
</ul>
</dd>
{% else %}
<dd>{% trans "Node processes are not specified" %}</dd>
{% endif %}
<dt>{% trans "Node Configurations" %}</dt>
{% if node_group.node_configs %}
<dd>
{% for service, service_conf in node_group.node_configs.items %}
<h6>{{ service }}</h6>
{% if service_conf %}
<ul>
{% for conf_name, conf_value in service_conf.items %}
<li>{{ conf_name }}:&nbsp{{ conf_value }}</li>
{% endfor %}
</ul>
{% else %}
<h6>No configurations</h6>
{% endif %}
{% endfor %}
</dd>
{% else %}
<dd>{% trans "Node configurations are not specified" %}</dd>
{% endif %}
</dl>
{% endfor %}
</div>

View File

@ -1,5 +0,0 @@
<ul>
{% for group in node_groups %}
<li>{{ group.name }}:&nbsp{{ group.count }}</li>
{% endfor %}
</ul>

View File

@ -1,24 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load url from future %}
{% load i18n %}
{% block form_id %}upload_file{% endblock %}
{% block form_action %}{% url 'horizon:sahara:cluster_templates:upload_file' %}{% endblock %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-header %}{% trans "Upload Template" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" id="upload_file_btn" type="submit" value="{% trans "Upload" %}"/>
<a href="{% url 'horizon:sahara:cluster_templates:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,155 +0,0 @@
<script>
var template = '<tr id_attr="$id"><td>' +
'<div class="input control-group" style="padding-right:15px;">' +
'<input id="template_id_$id" value="$template_id" type="hidden" name="template_id_$id">' +
'<input id="group_name_$id" value="$group_name" type="text" name="group_name_$id" class="input-medium">' +
'</div>' +
'</td>' +
'<td>' +
'<div class="input control-group" style="padding-right:15px;"><input disabled value="$template_name" class="input-medium" /></div>' +
'</td>' +
'<td>' +
'<div class="input control-group btn-group input-append" style="float:left;padding-right:5px;">' +
'<input id="count_$id" class="count-field" value="$node_count" type="text" max="4" maxlength="4" name="count_$id" size="4" style="width:50px">' +
'<div class="btn dec-btn" data-count-id="count_$id"><i class="icon-minus"></i></div>' +
'<div class="btn inc-btn" data-count-id="count_$id"><i class="icon-plus"></i></div>' +
'</div>' +
'<div class="input" style="float:left">' +
'</div>' +
'</td>' +
'<td>' +
'<div class="input control-group" style="float:left;padding-right:5px;">' +
'<input type="button" class="btn btn-danger" id="delete_btn_$id" data-toggle="dropdown" onclick="delete_node_group(this)" value="Remove" style="margin-bottom: 10px"/>' +
'</div>' +
'</td>' +
'</tr>';
function mark_element_as_wrong(id){
$("#"+id).parent("div").addClass("error");
}
function get_next_id() {
var max = -1;
$("#node-templates tbody tr").each(function () {
max = Math.max(max, parseInt($(this).attr("id_attr")));
});
return max + 1;
}
function set_nodes_ids() {
var ids = [];
$("#node-templates tbody tr").each(function () {
ids.push(parseInt($(this).attr("id_attr")));
});
$("#forms_ids").val(JSON.stringify(ids));
}
function add_node(node_count, group_name, template_id, id, deletable) {
var template_name = $("select option[value='" + template_id + "']").html();
var tmp = template.
replace(/\$id/g, id).
replace(/\$group_name/g, group_name).
replace(/\$template_id/g, template_id).
replace(/\$node_count/g, node_count).
replace(/\$template_name/g, template_name);
$("#node-templates tbody").append(tmp);
if (!deletable) {
$("#delete_btn_" + id).remove();
$("#group_name_" + id).prop('readonly', true);
}
$("#node-templates").show();
set_nodes_ids();
}
function add_node_group_template(node_count) {
if ($("select option:selected").html() == "Select") {
return;
}
var template_id = $("#template_id option:selected").val();
var template_name = $("#template_id option:selected").html();
add_node(node_count, template_name, template_id, get_next_id(), true);
$(".count-field").change();
}
function delete_node_group(el) {
var tr = $(el).parents("tr")[0];
tr.parentNode.removeChild(tr);
var id = get_next_id();
if (id == 0) {
$("#node-templates").hide();
}
set_nodes_ids();
}
$("#template_id").change(function () {
if ($("select option:selected").html() == "Select") {
$("#add_group_button").addClass("disabled");
} else {
$("#add_group_button").removeClass("disabled");
}
});
$("#node-templates").hide();
</script>
<input type="hidden" value="[]" name="forms_ids" id="forms_ids">
<div class="input">
<select id="template_id" name="template">
<option>Select</option>
{% for template in form.templates %}
<option value="{{ template.id }}">{{ template.name }}</option>
{% endfor %}
</select>
<a id="add_group_button" class="btn disabled btn-inline" onclick="add_node_group_template(1);">+</a>
</div>
<br/>
<div id="node-templates">
<table>
<thead>
<tr>
<td><label style="padding-bottom:5px">Group Name</label></td>
<td><label style="padding-bottom:5px">Template&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label></td>
<td><label style="padding-bottom:5px">Count</label></td>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<script>
{% for group in form.groups %}
add_node("{{ group.count }}", "{{ group.name }}", "{{ group.template_id }}", "{{ group.id }}", {{ group.deletable }});
{% endfor %}
{% for field_id in form.errors_fields %}
mark_element_as_wrong("{{ field_id }}");
{% endfor %}
var handlers_registred;
var lower_limit = 1;
$(function() {
if (!handlers_registred) {
handlers_registred = true;
$(".inc-btn").live("click", function(e) {
var id = $(this).attr("data-count-id");
$("#" + id).val(parseInt($("#" + id).val()) + 1);
$(".count-field").change();
});
$(".dec-btn").live("click", function(e) {
var id = $(this).attr("data-count-id");
var val = parseInt($("#" + id).val());
if (val > lower_limit) {
$("#" + id).val(val - 1);
}
$(".count-field").change();
});
}
$(".count-field").live("change", function() {
var val = $(this).val();
if (val > lower_limit) {
$(this).parent("div").find(".dec-btn").removeClass("disabled");
} else {
$(this).parent("div").find(".dec-btn").addClass("disabled");
}
}).change();
});
</script>

View File

@ -1,66 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Sahara" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Sahara - Cluster Templates") %}
{% endblock page_header %}
{% block main %}
<div class="cluster_templates">
{{ cluster_templates_table.render }}
</div>
<script type="text/javascript">
addHorizonLoadEvent(function () {
horizon.modals.addModalInitFunction(function (modal) {
if ($(modal).find(".nav-tabs").find("li").size() == 1) {
// hide tab bar for plugin/version modal wizard
$('div#modal_wrapper ul.nav-tabs').hide();
}
$(".hidden_nodegroups_field").after("<input type='button' id='add_nodegroup' value='Add Node group'/><br/>");
$("#add_nodegroup").click(function() {
$(".hidden_nodegroups_field").val("create_nodegroup");
$(".hidden_configure_field").val("create_nodegroup");
var form = $(".hidden_nodegroups_field").closest("form");
form.submit();
});
$(".hidden_nodegroups_field").val("");
$(".hidden_configure_field").val("");
if ($(modal).find(".hidden_create_field").length > 0) {
var form = $(".hidden_create_field").closest("form");
var successful = false;
form.submit(function (e) {
var oldHref = $(".configure-clustertemplate-btn")[0].href;
var plugin = $("#id_plugin_name option:selected").val();
var version = $("#id_" + plugin + "_version option:selected").val();
form.find(".close").click();
$(".configure-clustertemplate-btn")[0].href = oldHref +
"?plugin_name=" + encodeURIComponent(plugin) +
"&hadoop_version=" + encodeURIComponent(version);
$(".configure-clustertemplate-btn").click();
$(".configure-clustertemplate-btn")[0].href = oldHref;
return false;
});
$(".plugin_version_choice").closest(".control-group").hide();
}
//display version for selected plugin
$(document).on('change', '.plugin_name_choice', switch_versions);
function switch_versions() {
$(".plugin_version_choice").closest(".control-group").hide();
var plugin = $(this);
$("." + plugin.val() + "_version_choice").closest(".control-group").show();
}
$(".plugin_name_choice").change();
});
});
</script>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Cluster Template" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Sahara - Create Cluster Template") %}
{% endblock page_header %}
{% block main %}
{% include 'horizon/common/_workflow.html' %}
{% endblock %}<!DOCTYPE html>

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Cluster Template" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Sahara - Create Cluster Template") %}
{% endblock page_header %}
{% block main %}
{% include 'horizon/common/_workflow.html' %}
{% endblock %}<!DOCTYPE html>

View File

@ -1,15 +0,0 @@
{% extends 'base.html' %}
{% load i18n sizeformat %}
{% block title %}{% trans "Cluster Template Details" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Sahara - Cluster Template Details") %}
{% endblock page_header %}
{% block main %}
<div class="row-fluid">
<div class="span12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Upload Template" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Upload Template") %}
{% endblock page_header %}
{% block main %}
{% include 'cluster_templates/_upload_file.html' %}
{% endblock %}

View File

@ -1,19 +0,0 @@
<div class="well">
<p>
This Cluster will be started with:
<br >
<b>Plugin</b>: {{ plugin_name }}
<br />
<b>Hadoop version</b>: {{ hadoop_version }}
<br />
</p>
<p>
Cluster can be launched using existing Cluster Templates.
</p>
<p>
The Cluster object should specify OpenStack Image to boot instances for Hadoop Cluster.
</p>
<p>
User has to choose a keypair to have access to clusters instances.
</p>
</div>

View File

@ -1,23 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load url from future %}
{% load i18n %}
{% block form_id %}create_cluster_form{% endblock %}
{% block form_action %}{% url 'horizon:shara:clusters:create' %}{% endblock %}
{% block modal-header %}{% trans "Launch Cluster" %}{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" id="create_cluster_btn" type="submit" value="{% trans " Done" %}"/>
<a href="{% url 'horizon:shara:clusters:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,3 +0,0 @@
<p class="well">
Select a plugin and Hadoop version for a new Cluster.
</p>

View File

@ -1,89 +0,0 @@
{% load i18n sizeformat %}
{% load url from future %}
<h3>{% trans "Cluster Overview" %}</h3>
<div class="status row-fluid detail">
<dl>
<dt>{% trans "Name" %}</dt>
<dd>{{ cluster.name }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ cluster.id }}</dd>
<dt>{% trans "Description" %}</dt>
<dd>{{ cluster.description|default:"None" }}</dd>
<dt>{% trans "Status" %}</dt>
<dd>{{ cluster.status }}</dd>
</dl>
{% if cluster.error_description %}
<h4>{% trans "Error Details" %}</h4>
<p class="well">
{{ cluster.error_description }}
</p>
{% endif %}
<dl>
<dt>{% trans "Plugin" %}</dt>
<dd><a href="{% url 'horizon:sahara:plugins:details' cluster.plugin_name %}">{{ cluster.plugin_name }}</a></dd>
<dt>{% trans "Hadoop Version" %}</dt>
<dd>{{ cluster.hadoop_version }}</dd>
</dl>
<dl>
<dt>{% trans "Template" %}</dt>
{% if cluster_template %}
<dd><a href="{% url 'horizon:sahara:cluster_templates:details' cluster_template.id %}">{{ cluster_template.name }} </a></dd>
{% else %}
<dd>{% trans "Template not specified" %}</dd>
{% endif %}
<dt>{% trans "Base Image" %}</dt>
<dd>{{ base_image.name }}</dd>
{% if network %}
<dt>{% trans "Neutron Management Network" %}</dt>
<dd>{{ network }}</dd>
{% endif %}
<dt>{% trans "Keypair" %}</dt>
<dd>{{ cluster.user_keypair_id }}</dd>
</dl>
<dl>
<dt>{% trans "Anti-affinity enabled for" %}</dt>
{% if cluster.anti_affinity %}
<dd>
<ul>
{% for process in cluster.anti_affinity %}
<li>{{ process }}</li>
{% endfor %}
</ul>
</dd>
{% else %}
<h6>{% trans "no processes" %}</h6>
{% endif %}
</dl>
<dl>
<dt>{% trans "Node Configurations" %}</dt>
{% if cluster.cluster_configs %}
<dd>
{% for service, service_conf in cluster.cluster_configs.items %}
<h4>{{ service }}</h4>
{% if service_conf %}
<ul>
{% for conf_name, conf_value in service_conf.items %}
<li>{{ conf_name }}:&nbsp{{ conf_value }}</li>
{% endfor %}
</ul>
{% else %}
<h6>No configurations</h6>
{% endif %}
{% endfor %}
</dd>
{% else %}
<dd>{% trans "Cluster configurations are not specified" %}</dd>
{% endif %}
</dl>
<dl>
{% for info_key, info_val in cluster.info.items %}
<dt>{{ info_key }}</dt>
{% for key, val in info_val.items %}
<dd>{{ key }}:&nbsp{% autoescape off %}{{ val }}{% endautoescape %}</dd>
{% endfor %}
{% endfor %}
</dl>
</div>

View File

@ -1,5 +0,0 @@
{% load i18n sizeformat %}
<h3>{% trans "Cluster Instances" %}</h3>
<div class="status row-fluid detail">
{{ cluster_instances_table.render }}
</div>

View File

@ -1,63 +0,0 @@
{% load i18n sizeformat %}
{% load url from future %}
<h3>{% trans "Node Groups" %}</h3>
<div class="status row-fluid detail">
{% for node_group in cluster.node_groups %}
<dl class="well">
<h4>{% trans "Name" %}: {{ node_group.name }}</h4>
<dt>{% trans "Number of Nodes" %}</dt>
<dd>{{ node_group.count }}</dd>
<dt>{% trans "Flavor" %}</dt>
<dd>{{ node_group.flavor_name|default:"Flavor is not specified" }}</dd>
{% if node_group.floating_ip_pool %}
<dt>{% trans "Floating IP Pool" %}</dt>
<dd>{{ node_group.floating_ip_pool }}</dd>
{% endif %}
<dt>{% trans "Template" %}</dt>
{% if node_group.node_group_template_id %}
<dd><a href="{% url 'horizon:sahara:nodegroup_templates:details' node_group.node_group_template_id %}">{{ node_group.node_group_template.name }} </a></dd>
{% else %}
<dd>{% trans "Template not specified" %}</dd>
{% endif %}
<dt>{% trans "Node Processes" %}</dt>
{% if node_group.node_processes %}
<dd>
<ul>
{% for process in node_group.node_processes %}
<li>{{ process }}</li>
{% endfor %}
</ul>
</dd>
{% else %}
<dd>{% trans "Node processes are not specified" %}</dd>
{% endif %}
<dt>{% trans "Node Configurations" %}</dt>
{% if node_group.node_configs %}
<dd>
{% for service, service_conf in node_group.node_configs.items %}
<h4>{{ service }}</h4>
{% if service_conf %}
<ul>
{% for conf_name, conf_value in service_conf.items %}
<li>{{ conf_name }}:&nbsp{{ conf_value }}</li>
{% endfor %}
</ul>
{% else %}
<h6>No configurations</h6>
{% endif %}
{% endfor %}
</dd>
{% else %}
<dd>{% trans "Node configurations are not specified" %}</dd>
{% endif %}
</dl>
{% endfor %}
</div>

View File

@ -1,61 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Sahara" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Sahara - Clusters") %}
{% endblock page_header %}
{% block main %}
<div class="cluster_templates">
{{ clusters_table.render }}
</div>
<script type="text/javascript">
addHorizonLoadEvent(function () {
horizon.modals.addModalInitFunction(function (modal) {
if ($(modal).find(".nav-tabs").find("li").size() == 1) {
// hide tab bar for plugin/version modal wizard
$('div#modal_wrapper ul.nav-tabs').hide();
}
$(".hidden_nodegroups_field").val("");
$(".hidden_configure_field").val("");
lower_limit = 0;
$(".count-field").change();
if ($(modal).find(".hidden_create_field").length > 0) {
var form = $(".hidden_create_field").closest("form");
var successful = false;
form.submit(function (e) {
var oldHref = $(".configure-cluster-btn")[0].href;
var plugin = $("#id_plugin_name option:selected").val();
var version = $("#id_" + plugin + "_version option:selected").val();
form.find(".close").click();
$(".configure-cluster-btn")[0].href = oldHref +
"?plugin_name=" + encodeURIComponent(plugin) +
"&hadoop_version=" + encodeURIComponent(version);
$(".configure-cluster-btn").click();
$(".configure-cluster-btn")[0].href = oldHref;
return false;
});
$(".plugin_version_choice").closest(".control-group").hide();
}
//display version for selected plugin
$(document).on('change', '.plugin_name_choice', switch_versions);
function switch_versions() {
$(".plugin_version_choice").closest(".control-group").hide();
var plugin = $(this);
$("." + plugin.val() + "_version_choice").closest(".control-group").show();
}
$(".plugin_name_choice").change();
});
});
</script>
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Launch Cluster" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Sahara - Launch Cluster") %}
{% endblock page_header %}
{% block main %}
{% include 'horizon/common/_workflow.html' %}
{% endblock %}<!DOCTYPE html>

View File

@ -1,11 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Launch Cluster" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Sahara - Launch Cluster") %}
{% endblock page_header %}
{% block main %}
{% include 'horizon/common/_workflow.html' %}
{% endblock %}<!DOCTYPE html>

Some files were not shown because too many files have changed in this diff Show More