Sfoglia il codice sorgente

[impala] Initial commit

Beeswax becomes a lib that provides a reusable query editor. The lib is still in the 'apps'
directory for historical reason and Hive UI is the default option of this lib (so Beeswax itself
does not change).
The new Impala app is basded on this Beeswax lib and contains two ini properties.

Done:
Convert Beeswax urls to 'named' urls
Make each namespace of URL plugable and changed all the views/templates. The name of the current
app is available trought a new context processor adding 'app_name' (e.g. 'beeswax' or 'impala').
In each app: History, Saved Queries, My Queries are only showing query from one type (Beeswax or Impala).
Beeswax ini files are reverted to 2.0 release (only one Beeswaxd server can be specified).
Impala app custom settings inherits Beeswax ones.
Hide non supported Impala options.

Until Impala support DDL:
DDL queries are still sent to Beeswaxd (query from query editor are always sent to Impala tough).
beeswax.py/design.py queries are always of Beeswax type (e.g. 0 or null).
Romain Rigaux 13 anni fa
parent
commit
e299da3e7c
46 ha cambiato i file con 489 aggiunte e 402 eliminazioni
  1. 4 2
      apps/Makefile
  2. 1 30
      apps/beeswax/src/beeswax/conf.py
  3. 13 9
      apps/beeswax/src/beeswax/create_table.py
  4. 1 1
      apps/beeswax/src/beeswax/design.py
  5. 15 36
      apps/beeswax/src/beeswax/forms.py
  6. 1 4
      apps/beeswax/src/beeswax/management/commands/beeswax_server.py
  7. 3 2
      apps/beeswax/src/beeswax/models.py
  8. 24 33
      apps/beeswax/src/beeswax/server/dbms.py
  9. 2 0
      apps/beeswax/src/beeswax/settings.py
  10. 5 3
      apps/beeswax/src/beeswax/templates/choose_delimiter.mako
  11. 7 5
      apps/beeswax/src/beeswax/templates/choose_file.mako
  12. 6 20
      apps/beeswax/src/beeswax/templates/configuration.mako
  13. 13 11
      apps/beeswax/src/beeswax/templates/create_table_index.mako
  14. 6 4
      apps/beeswax/src/beeswax/templates/create_table_manually.mako
  15. 5 3
      apps/beeswax/src/beeswax/templates/define_columns.mako
  16. 1 1
      apps/beeswax/src/beeswax/templates/describe_partitions.mako
  17. 6 6
      apps/beeswax/src/beeswax/templates/describe_table.mako
  18. 28 26
      apps/beeswax/src/beeswax/templates/execute.mako
  19. 8 1
      apps/beeswax/src/beeswax/templates/explain.mako
  20. 12 11
      apps/beeswax/src/beeswax/templates/index.mako
  21. 6 6
      apps/beeswax/src/beeswax/templates/layout.mako
  22. 8 6
      apps/beeswax/src/beeswax/templates/list_designs.mako
  23. 5 5
      apps/beeswax/src/beeswax/templates/list_history.mako
  24. 1 1
      apps/beeswax/src/beeswax/templates/load_table.mako
  25. 10 10
      apps/beeswax/src/beeswax/templates/my_queries.mako
  26. 4 7
      apps/beeswax/src/beeswax/templates/parameterization.mako
  27. 1 1
      apps/beeswax/src/beeswax/templates/save_results.mako
  28. 11 8
      apps/beeswax/src/beeswax/templates/show_tables.mako
  29. 3 3
      apps/beeswax/src/beeswax/templates/util.mako
  30. 4 4
      apps/beeswax/src/beeswax/templates/watch_results.mako
  31. 4 4
      apps/beeswax/src/beeswax/templates/watch_wait.mako
  32. 7 8
      apps/beeswax/src/beeswax/test_base.py
  33. 9 4
      apps/beeswax/src/beeswax/tests.py
  34. 30 31
      apps/beeswax/src/beeswax/urls.py
  35. 60 60
      apps/beeswax/src/beeswax/views.py
  36. 24 0
      apps/impala/Makefile
  37. 2 0
      apps/impala/babel.cfg
  38. 20 0
      apps/impala/hueversion.py
  39. 29 0
      apps/impala/setup.py
  40. 3 0
      apps/impala/static/help/index.html
  41. 13 16
      desktop/conf.dist/hue.ini
  42. 14 16
      desktop/conf/pseudo-distributed.ini.tmpl
  43. 42 0
      desktop/core/src/desktop/context_processors.py
  44. 13 1
      desktop/core/src/desktop/settings.py
  45. 4 2
      desktop/core/src/desktop/templates/error.mako
  46. 1 1
      desktop/core/src/desktop/urls.py

+ 4 - 2
apps/Makefile

@@ -39,7 +39,8 @@ APPS := about \
   oozie \
   oozie \
   proxy \
   proxy \
   shell \
   shell \
-  useradmin
+  useradmin \
+  impala
 
 
 ################################################
 ################################################
 # Install all applications into the Desktop environment
 # Install all applications into the Desktop environment
@@ -107,7 +108,8 @@ I18N_APPS := about \
   jobsub \
   jobsub \
   shell \
   shell \
   useradmin \
   useradmin \
-  oozie
+  oozie \
+  impala
 
 
 COMPILE_LOCALE_TARGETS := $(I18N_APPS:%=.recursive-compile-locales/%)
 COMPILE_LOCALE_TARGETS := $(I18N_APPS:%=.recursive-compile-locales/%)
 compile-locales: $(COMPILE_LOCALE_TARGETS)
 compile-locales: $(COMPILE_LOCALE_TARGETS)

+ 1 - 30
apps/beeswax/src/beeswax/conf.py

@@ -18,7 +18,7 @@ import os.path
 
 
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 
 
-from desktop.lib.conf import Config, UnspecifiedConfigSection, ConfigSection, coerce_bool
+from desktop.lib.conf import Config, coerce_bool
 
 
 
 
 SERVER_INTERFACE = Config(
 SERVER_INTERFACE = Config(
@@ -27,41 +27,12 @@ SERVER_INTERFACE = Config(
   default="beeswax")
   default="beeswax")
 
 
 
 
-QUERY_SERVERS = UnspecifiedConfigSection(
-  "query_servers",
-  help=_("One entry for each Query Server that can execute some queries."),
-  each=ConfigSection(
-    help=_("Information about a single Query Server"),
-    members=dict(
-      SERVER_HOST = Config(
-        key="server_host",
-        help=_("Host where the Query Server Thrift daemon is running."),
-        private=True,
-        default="localhost"),
-      SERVER_PORT = Config(
-        key="server_port",
-        help=_("Configure the port the Query Server Thrift server."),
-        default=8002,
-        type=int),
-      SUPPORT_DDL = Config(
-        key='support_ddl',
-        default=True,
-        type=coerce_bool,
-        help=_('If DDL queries are supported (e.g. DROP can be sent directly to this server).'))
-    )
-  )
-)
-
-# Deprecated! To remove in Hue 3
-# Multiple sections are now available in QUERY_SERVERS
 BEESWAX_SERVER_HOST = Config(
 BEESWAX_SERVER_HOST = Config(
   key="beeswax_server_host",
   key="beeswax_server_host",
   help=_("Host where Beeswax server Thrift daemon is running."),
   help=_("Host where Beeswax server Thrift daemon is running."),
   private=True,
   private=True,
   default="localhost")
   default="localhost")
 
 
-# Deprecated! To remove in Hue 3
-# Multiple sections are now available in QUERY_SERVERS
 BEESWAX_SERVER_PORT = Config(
 BEESWAX_SERVER_PORT = Config(
   key="beeswax_server_port",
   key="beeswax_server_port",
   help=_("Configure the port the Beeswax Thrift server runs on."),
   help=_("Configure the port the Beeswax Thrift server runs on."),

+ 13 - 9
apps/beeswax/src/beeswax/create_table.py

@@ -22,9 +22,10 @@ Views & controls for creating tables
 import logging
 import logging
 import gzip
 import gzip
 
 
-from django.core import urlresolvers
+from django.core.urlresolvers import reverse
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
+from desktop.context_processors import get_app_name
 from desktop.lib import django_mako, i18n
 from desktop.lib import django_mako, i18n
 from desktop.lib.django_util import render
 from desktop.lib.django_util import render
 from desktop.lib.exceptions import PopupException
 from desktop.lib.exceptions import PopupException
@@ -37,7 +38,7 @@ from beeswax.forms import CreateTableForm, ColumnTypeFormSet,\
   PartitionTypeFormSet, CreateByImportFileForm, CreateByImportDelimForm,\
   PartitionTypeFormSet, CreateByImportFileForm, CreateByImportDelimForm,\
   TERMINATOR_CHOICES
   TERMINATOR_CHOICES
 from beeswax.server import dbms
 from beeswax.server import dbms
-from beeswax.views import describe_table, execute_directly
+from beeswax.views import execute_directly
 
 
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
@@ -110,6 +111,7 @@ def import_wizard(request):
     - Does not work with binary data.
     - Does not work with binary data.
   """
   """
   encoding = i18n.get_site_encoding()
   encoding = i18n.get_site_encoding()
+  app_name = get_app_name(request)
 
 
   if request.method == 'POST':
   if request.method == 'POST':
     # Have a while loop to allow an easy way to break
     # Have a while loop to allow an easy way to break
@@ -193,7 +195,7 @@ def import_wizard(request):
 
 
       if do_s2_auto_delim or do_s2_user_delim or cancel_s3_column_def:
       if do_s2_auto_delim or do_s2_user_delim or cancel_s3_column_def:
         return render('choose_delimiter.mako', request, dict(
         return render('choose_delimiter.mako', request, dict(
-          action=urlresolvers.reverse(import_wizard),
+          action=reverse(app_name + ':import_wizard'),
           delim_readable=DELIMITER_READABLE.get(s2_delim_form['delimiter'].data[0], s2_delim_form['delimiter'].data[1]),
           delim_readable=DELIMITER_READABLE.get(s2_delim_form['delimiter'].data[0], s2_delim_form['delimiter'].data[1]),
           initial=delim_is_auto,
           initial=delim_is_auto,
           file_form=s1_file_form,
           file_form=s1_file_form,
@@ -216,7 +218,7 @@ def import_wizard(request):
             ))
             ))
           s3_col_formset = ColumnTypeFormSet(prefix='cols', initial=columns)
           s3_col_formset = ColumnTypeFormSet(prefix='cols', initial=columns)
         return render('define_columns.mako', request, dict(
         return render('define_columns.mako', request, dict(
-          action=urlresolvers.reverse(import_wizard),
+          action=reverse(app_name + ':import_wizard'),
           file_form=s1_file_form,
           file_form=s1_file_form,
           delim_form=s2_delim_form,
           delim_form=s2_delim_form,
           column_formset=s3_col_formset,
           column_formset=s3_col_formset,
@@ -248,7 +250,7 @@ def import_wizard(request):
     s1_file_form = CreateByImportFileForm()
     s1_file_form = CreateByImportFileForm()
 
 
   return render('choose_file.mako', request, dict(
   return render('choose_file.mako', request, dict(
-    action=urlresolvers.reverse(import_wizard),
+    action=reverse(app_name + ':import_wizard'),
     file_form=s1_file_form,
     file_form=s1_file_form,
   ))
   ))
 
 
@@ -257,14 +259,15 @@ def _submit_create_and_load(request, create_hql, table_name, path, do_load):
   """
   """
   Submit the table creation, and setup the load to happen (if ``do_load``).
   Submit the table creation, and setup the load to happen (if ``do_load``).
   """
   """
-  on_success_params = { }
+  on_success_params = {}
+  app_name = get_app_name(request)
 
 
   if do_load:
   if do_load:
     on_success_params['table'] = table_name
     on_success_params['table'] = table_name
     on_success_params['path'] = path
     on_success_params['path'] = path
-    on_success_url = urlresolvers.reverse(load_after_create)
+    on_success_url = reverse(app_name + ':load_after_create')
   else:
   else:
-    on_success_url = urlresolvers.reverse(describe_table, kwargs={'table': table_name})
+    on_success_url = reverse(app_name + ':describe_table', kwargs={'table': table_name})
 
 
   query = hql_query(create_hql)
   query = hql_query(create_hql)
   return execute_directly(request, query,
   return execute_directly(request, query,
@@ -454,6 +457,7 @@ def load_after_create(request):
   LOG.debug("Auto loading data from %s into table %s" % (path, tablename))
   LOG.debug("Auto loading data from %s into table %s" % (path, tablename))
   hql = "LOAD DATA INPATH '%s' INTO TABLE `%s`" % (path, tablename)
   hql = "LOAD DATA INPATH '%s' INTO TABLE `%s`" % (path, tablename)
   query = hql_query(hql)
   query = hql_query(hql)
-  on_success_url = urlresolvers.reverse(describe_table, kwargs={'table': tablename})
+  app_name = get_app_name(request)
+  on_success_url = reverse(app_name + ':describe_table', kwargs={'table': tablename})
 
 
   return execute_directly(request, query, on_success_url=on_success_url)
   return execute_directly(request, query, on_success_url=on_success_url)

+ 1 - 1
apps/beeswax/src/beeswax/design.py

@@ -36,7 +36,7 @@ SERIALIZATION_VERSION = '0.4.0'
 
 
 
 
 def hql_query(hql):
 def hql_query(hql):
-  data_dict = simplejson.loads('{"query": {"email_notify": null, "query": null, "type": null, "is_parameterized": null}, '
+  data_dict = simplejson.loads('{"query": {"email_notify": null, "query": null, "type": 0, "is_parameterized": true}, '
                                '"functions": [], "VERSION": "0.4.0", "file_resources": [], "settings": []}')
                                '"functions": [], "VERSION": "0.4.0", "file_resources": [], "settings": []}')
   if not (isinstance(hql, str) or isinstance(hql, unicode)):
   if not (isinstance(hql, str) or isinstance(hql, unicode)):
     raise Exception('Requires a SQL text query of type <str>, <unicode> and not %s' % type(hql))
     raise Exception('Requires a SQL text query of type <str>, <unicode> and not %s' % type(hql))

+ 15 - 36
apps/beeswax/src/beeswax/forms.py

@@ -18,17 +18,17 @@
 from django import forms
 from django import forms
 from django.utils.translation import ugettext as _, ugettext_lazy as _t
 from django.utils.translation import ugettext as _, ugettext_lazy as _t
 
 
+import hadoop
 import hive_metastore
 import hive_metastore
 
 
 from desktop.lib.django_forms import simple_formset_factory, DependencyAwareForm
 from desktop.lib.django_forms import simple_formset_factory, DependencyAwareForm
 from desktop.lib.django_forms import ChoiceOrOtherField, MultiForm, SubmitButton
 from desktop.lib.django_forms import ChoiceOrOtherField, MultiForm, SubmitButton
-
 from filebrowser.forms import PathField
 from filebrowser.forms import PathField
 
 
-from beeswax import common, conf, models
+from beeswax import common
 from beeswax.design import _strip_trailing_semicolon
 from beeswax.design import _strip_trailing_semicolon
-import hadoop
 from beeswax.server.dbms import NoSuchObjectException
 from beeswax.server.dbms import NoSuchObjectException
+from beeswax.models import SavedQuery
 
 
 
 
 class QueryForm(MultiForm):
 class QueryForm(MultiForm):
@@ -38,28 +38,14 @@ class QueryForm(MultiForm):
       settings=SettingFormSet,
       settings=SettingFormSet,
       file_resources=FileResourceFormSet,
       file_resources=FileResourceFormSet,
       functions=FunctionFormSet,
       functions=FunctionFormSet,
-      saveform=SaveForm,
-      query_servers=QueryServerForm)
-
-
-class QueryServerForm(forms.Form):
-  server = forms.CharField(max_length=128, help_text=_t('Host:port of the query server.'))
-
-  def __init__(self, *args, **kwargs):
-    super(QueryServerForm, self).__init__(*args, **kwargs)
-
-    if conf.QUERY_SERVERS:
-      servers = ((server, server) for server in conf.QUERY_SERVERS.keys())
-    else:
-      servers = (('default', 'default'),)
-    self.fields['server'].widget = forms.Select(choices=servers)
+      saveform=SaveForm)
 
 
 
 
 class SaveForm(forms.Form):
 class SaveForm(forms.Form):
   """Used for saving query design."""
   """Used for saving query design."""
   name = forms.CharField(required=False,
   name = forms.CharField(required=False,
                         max_length=64,
                         max_length=64,
-                        initial=models.SavedQuery.DEFAULT_NEW_DESIGN_NAME,
+                        initial=SavedQuery.DEFAULT_NEW_DESIGN_NAME,
                         help_text=_t('Change the name to save as a new design.'))
                         help_text=_t('Change the name to save as a new design.'))
   desc = forms.CharField(required=False, max_length=1024, label=_t("Description"))
   desc = forms.CharField(required=False, max_length=1024, label=_t("Description"))
   save = forms.BooleanField(widget=SubmitButton, required=False)
   save = forms.BooleanField(widget=SubmitButton, required=False)
@@ -73,15 +59,14 @@ class SaveForm(forms.Form):
     self.fields['saveas'].widget.label = _('Save As')
     self.fields['saveas'].widget.label = _('Save As')
 
 
   def clean_name(self):
   def clean_name(self):
-    name = self.cleaned_data.get('name', '').strip()
-    return name
+    return self.cleaned_data.get('name', '').strip()
 
 
   def clean(self):
   def clean(self):
     if self.errors:
     if self.errors:
       return
       return
     save = self.cleaned_data.get('save')
     save = self.cleaned_data.get('save')
     name = self.cleaned_data.get('name')
     name = self.cleaned_data.get('name')
-    if save and len(name) == 0:
+    if save and not name:
       # Bother with name iff we're saving
       # Bother with name iff we're saving
       raise forms.ValidationError(_('Please enter a name'))
       raise forms.ValidationError(_('Please enter a name'))
     return self.cleaned_data
     return self.cleaned_data
@@ -132,9 +117,10 @@ class SaveResultsForm(DependencyAwareForm):
 class HQLForm(forms.Form):
 class HQLForm(forms.Form):
   query = forms.CharField(label=_t("Query Editor"),
   query = forms.CharField(label=_t("Query Editor"),
                           required=True,
                           required=True,
-                          widget=forms.Textarea(attrs={'class':'beeswax_query'}))
+                          widget=forms.Textarea(attrs={'class': 'beeswax_query'}))
   is_parameterized = forms.BooleanField(required=False, initial=True)
   is_parameterized = forms.BooleanField(required=False, initial=True)
   email_notify = forms.BooleanField(required=False, initial=False)
   email_notify = forms.BooleanField(required=False, initial=False)
+  type = forms.IntegerField(required=False, initial=0)
 
 
   def clean_query(self):
   def clean_query(self):
     return _strip_trailing_semicolon(self.cleaned_data['query'])
     return _strip_trailing_semicolon(self.cleaned_data['query'])
@@ -155,7 +141,7 @@ class FileResourceForm(forms.Form):
       ("FILE", ("file")),
       ("FILE", ("file")),
     ], help_text=_t("Resources to upload with your Hive job." +
     ], help_text=_t("Resources to upload with your Hive job." +
        "  Use 'jar' for UDFs.  Use 'file' and 'archive' for "
        "  Use 'jar' for UDFs.  Use 'file' and 'archive' for "
-       "files to be copied and made locally available duirng MAP/TRANSFORM. " +
+       "files to be copied and made locally available during MAP/TRANSFORM. " +
        "Paths are on HDFS.")
        "Paths are on HDFS.")
   )
   )
 
 
@@ -232,8 +218,7 @@ class CreateTableForm(DependencyAwareForm):
   ]
   ]
 
 
   # External?
   # External?
-  use_default_location = forms.BooleanField(required=False, initial=True,
-    label=_t("Use default location"))
+  use_default_location = forms.BooleanField(required=False, initial=True, label=_t("Use default location"))
   external_location = forms.CharField(required=False, help_text=_t("Path to HDFS directory or file of table data."))
   external_location = forms.CharField(required=False, help_text=_t("Path to HDFS directory or file of table data."))
 
 
   dependencies += [
   dependencies += [
@@ -297,22 +282,16 @@ class CreateByImportDelimForm(forms.Form):
   def clean(self):
   def clean(self):
     # ChoiceOrOtherField doesn't work with required=True
     # ChoiceOrOtherField doesn't work with required=True
     delimiter = self.cleaned_data.get('delimiter')
     delimiter = self.cleaned_data.get('delimiter')
-    if not delimiter:
-      raise forms.ValidationError(_('Delimiter value is required.'))
-    _clean_terminator(delimiter)
-    return self.cleaned_data
-
-  def old(self):
     if delimiter.isdigit():
     if delimiter.isdigit():
       try:
       try:
         chr(int(delimiter))
         chr(int(delimiter))
         return int(delimiter)
         return int(delimiter)
       except ValueError:
       except ValueError:
         raise forms.ValidationError(_('Delimiter value must be smaller than 256.'))
         raise forms.ValidationError(_('Delimiter value must be smaller than 256.'))
-    val = delimiter.decode('string_escape')
-    if len(val) != 1:
-      raise forms.ValidationError(_('Delimiter must be exactly one character.'))
-    return ord(val)
+    if not delimiter:
+      raise forms.ValidationError(_('Delimiter value is required.'))
+    _clean_terminator(delimiter)
+    return self.cleaned_data
 
 
 
 
 # Note, struct is not currently supported.  (Because it's recursive, for example.)
 # Note, struct is not currently supported.  (Because it's recursive, for example.)

+ 1 - 4
apps/beeswax/src/beeswax/management/commands/beeswax_server.py

@@ -60,10 +60,7 @@ class Command(NoArgsCommand):
     if dt_host == '0.0.0.0':
     if dt_host == '0.0.0.0':
       dt_host = '127.0.0.1'
       dt_host = '127.0.0.1'
 
 
-    if 'beeswax_server_port' in beeswax.conf.QUERY_SERVERS['bind_to']:
-      server_port = beeswax.conf.QUERY_SERVERS['default'].SERVER_PORT.get()
-    else:
-      server_port = beeswax.conf.BEESWAX_SERVER_PORT.get()
+    server_port = beeswax.conf.BEESWAX_SERVER_PORT.get()
 
 
     args = [
     args = [
       os.path.basename(bin),
       os.path.basename(bin),

+ 3 - 2
apps/beeswax/src/beeswax/models.py

@@ -97,7 +97,7 @@ class QueryHistory(models.Model):
       return HiveServerQueryHistory.objects.get(id=id)
       return HiveServerQueryHistory.objects.get(id=id)
 
 
 
 
-  def get_query_server(self):
+  def get_query_server_config(self):
     return dict(zip(['server_name', 'server_host', 'server_port', 'server_type'],
     return dict(zip(['server_name', 'server_host', 'server_port', 'server_type'],
                     [self.server_name, self.server_host, self.server_port, self.server_type]))
                     [self.server_name, self.server_host, self.server_port, self.server_type]))
 
 
@@ -216,7 +216,8 @@ class SavedQuery(models.Model):
   """
   """
   DEFAULT_NEW_DESIGN_NAME = _('My saved query')
   DEFAULT_NEW_DESIGN_NAME = _('My saved query')
   AUTO_DESIGN_SUFFIX = _(' (new)')
   AUTO_DESIGN_SUFFIX = _(' (new)')
-  TYPES = (HQL, REPORT) = range(2) # REPORT is unused
+  TYPES = (HQL, IMPALA) = range(2)
+  TYPES_MAPPING = {'beeswax': HQL, 'hql': HQL, 'impala': IMPALA}
 
 
   type = models.IntegerField(null=False)
   type = models.IntegerField(null=False)
   owner = models.ForeignKey(User, db_index=True)
   owner = models.ForeignKey(User, db_index=True)

+ 24 - 33
apps/beeswax/src/beeswax/server/dbms.py

@@ -26,7 +26,9 @@ from filebrowser.views import location_to_url
 
 
 from beeswaxd.ttypes import BeeswaxException
 from beeswaxd.ttypes import BeeswaxException
 
 
-from beeswax import conf
+from beeswax.conf import BEESWAX_SERVER_HOST, BEESWAX_SERVER_PORT,\
+  BROWSE_PARTITIONED_TABLE_LIMIT
+from impala.conf import SERVER_HOST, SERVER_PORT
 from beeswax.design import hql_query
 from beeswax.design import hql_query
 from beeswax.models import QueryHistory, HIVE_SERVER2
 from beeswax.models import QueryHistory, HIVE_SERVER2
 from beeswax.conf import SERVER_INTERFACE
 from beeswax.conf import SERVER_INTERFACE
@@ -41,7 +43,7 @@ def get(user, query_server=None):
   from beeswax.server.beeswax_lib import BeeswaxClient
   from beeswax.server.beeswax_lib import BeeswaxClient
 
 
   if query_server is None:
   if query_server is None:
-    query_server = get_query_server(support_ddl=True)
+    query_server = get_query_server_config(requires_ddl=True)
 
 
   if SERVER_INTERFACE.get() == HIVE_SERVER2:
   if SERVER_INTERFACE.get() == HIVE_SERVER2:
     return Dbms(HiveServerClientCompatible(HiveServerClient(query_server, user)), QueryHistory.SERVER_TYPE[1][0])
     return Dbms(HiveServerClientCompatible(HiveServerClient(query_server, user)), QueryHistory.SERVER_TYPE[1][0])
@@ -50,33 +52,22 @@ def get(user, query_server=None):
 
 
 
 
 
 
-def get_query_server(name='default', support_ddl=False):
-  if 'beeswax_server_host' in conf.QUERY_SERVERS['bind_to'] or 'beeswax_server_port' in conf.QUERY_SERVERS['bind_to']:
-    return {
-          'server_name': 'default',
-          'server_host': conf.BEESWAX_SERVER_HOST.get(),
-          'server_port': conf.BEESWAX_SERVER_PORT.get(),
-          'support_ddl': True,
+def get_query_server_config(name='beeswax', requires_ddl=False):
+  if name == 'impala' and not requires_ddl:
+    query_server = {
+        'server_name': 'impala',
+        'server_host': SERVER_HOST.get(),
+        'server_port': SERVER_PORT.get(),
+        'support_ddl': False,
+    }
+  else:
+    query_server = {
+        'server_name': 'beeswax',
+        'server_host': BEESWAX_SERVER_HOST.get(),
+        'server_port': BEESWAX_SERVER_PORT.get(),
+        'support_ddl': True,
       }
       }
 
 
-  servers = conf.QUERY_SERVERS
-
-  if support_ddl:
-    if not servers[name].SUPPORT_DDL.get():
-      for key in servers.keys():
-        if servers[key].SUPPORT_DDL.get():
-          name = key
-          break
-
-  config = servers[name]
-
-  query_server = {
-      'server_name': name,
-      'server_host': config.SERVER_HOST.get(),
-      'server_port': config.SERVER_PORT.get(),
-      'support_ddl': config.SUPPORT_DDL.get(),
-  }
-
   return query_server
   return query_server
 
 
 
 
@@ -128,7 +119,7 @@ class Dbms:
   def get_sample(self, table):
   def get_sample(self, table):
     """No samples if it's a view (HUE-526)"""
     """No samples if it's a view (HUE-526)"""
     if not table.is_view:
     if not table.is_view:
-      limit = min(100, conf.BROWSE_PARTITIONED_TABLE_LIMIT.get())
+      limit = min(100, BROWSE_PARTITIONED_TABLE_LIMIT.get())
       hql = "SELECT * FROM `%s` LIMIT %s" % (table.name, limit)
       hql = "SELECT * FROM `%s` LIMIT %s" % (table.name, limit)
       query = hql_query(hql)
       query = hql_query(hql)
       handle = self.execute_and_wait(query, timeout_sec=5.0)
       handle = self.execute_and_wait(query, timeout_sec=5.0)
@@ -201,7 +192,7 @@ class Dbms:
     except BeeswaxException, ex: # TODO HS2
     except BeeswaxException, ex: # TODO HS2
       LOG.exception(ex)
       LOG.exception(ex)
       # Kind of expected (hql compile/syntax error, etc.)
       # Kind of expected (hql compile/syntax error, etc.)
-      if ex.handle:
+      if hasattr(ex, 'handle') and ex.handle:
         query_history.server_id = ex.handle.id
         query_history.server_id = ex.handle.id
         query_history.log_context = ex.handle.log_context
         query_history.log_context = ex.handle.log_context
       query_history.save_state(QueryHistory.STATE.failed)
       query_history.save_state(QueryHistory.STATE.failed)
@@ -229,8 +220,8 @@ class Dbms:
 
 
 
 
   def get_partitions(self, db_name, table, max_parts=None):
   def get_partitions(self, db_name, table, max_parts=None):
-    if max_parts is None or max_parts > conf.BROWSE_PARTITIONED_TABLE_LIMIT.get():
-      max_parts = conf.BROWSE_PARTITIONED_TABLE_LIMIT.get()
+    if max_parts is None or max_parts > BROWSE_PARTITIONED_TABLE_LIMIT.get():
+      max_parts = BROWSE_PARTITIONED_TABLE_LIMIT.get()
 
 
     return self.client.get_partitions(db_name, table.name, max_parts)
     return self.client.get_partitions(db_name, table.name, max_parts)
 
 
@@ -254,7 +245,7 @@ class Dbms:
   def _get_browse_limit_clause(self, table):
   def _get_browse_limit_clause(self, table):
     """Get the limit clause when browsing a partitioned table"""
     """Get the limit clause when browsing a partitioned table"""
     if table.partition_keys:
     if table.partition_keys:
-      limit = conf.BROWSE_PARTITIONED_TABLE_LIMIT.get()
+      limit = BROWSE_PARTITIONED_TABLE_LIMIT.get()
       if limit > 0:
       if limit > 0:
         return "LIMIT %d" % (limit,)
         return "LIMIT %d" % (limit,)
     return ""
     return ""
@@ -284,7 +275,7 @@ def expand_exception(exc, db):
     log = db.get_log(exc)
     log = db.get_log(exc)
   except:
   except:
     # Always show something, even if server has died on the job.
     # Always show something, even if server has died on the job.
-    log = _("Could not retrieve log.")
+    log = _("Could not retrieve logs.")
 
 
   if not exc.message:
   if not exc.message:
     error_message = _("Unknown exception.")
     error_message = _("Unknown exception.")

+ 2 - 0
apps/beeswax/src/beeswax/settings.py

@@ -18,3 +18,5 @@ NICE_NAME = "Beeswax (Hive UI)"
 REQUIRES_HADOOP = True
 REQUIRES_HADOOP = True
 ICON = "/beeswax/static/art/icon_beeswax_24.png"
 ICON = "/beeswax/static/art/icon_beeswax_24.png"
 MENU_INDEX = 10
 MENU_INDEX = 10
+
+IS_URL_NAMESPACED = True

+ 5 - 3
apps/beeswax/src/beeswax/templates/choose_delimiter.mako

@@ -17,10 +17,12 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
-${commonheader(_('Create table from file'), "beeswax", user, "100px")}
+
+${commonheader(_('Create table from file'), app_name, user, '100px')}
 ${layout.menubar(section='tables')}
 ${layout.menubar(section='tables')}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
@@ -30,8 +32,8 @@ ${layout.menubar(section='tables')}
             <div class="well sidebar-nav">
             <div class="well sidebar-nav">
                 <ul class="nav nav-list">
                 <ul class="nav nav-list">
                     <li class="nav-header">${_('Actions')}</li>
                     <li class="nav-header">${_('Actions')}</li>
-                    <li><a href="${ url('beeswax.create_table.import_wizard')}">${_('Create a new table from a file')}</a></li>
-                    <li><a href="${ url('beeswax.create_table.create_table')}">${_('Create a new table manually')}</a></li>
+                    <li><a href="${ url(app_name + ':import_wizard')}">${_('Create a new table from a file')}</a></li>
+                    <li><a href="${ url(app_name + ':create_table')}">${_('Create a new table manually')}</a></li>
                 </ul>
                 </ul>
             </div>
             </div>
         </div>
         </div>

+ 7 - 5
apps/beeswax/src/beeswax/templates/choose_file.mako

@@ -17,9 +17,11 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
-${commonheader(_('Create table from file'), "beeswax", user, "100px")}
+
+${commonheader(_('Create table from file'), app_name, user, '100px')}
 ${layout.menubar(section='tables')}
 ${layout.menubar(section='tables')}
 
 
 
 
@@ -30,14 +32,14 @@ ${layout.menubar(section='tables')}
             <div class="well sidebar-nav">
             <div class="well sidebar-nav">
                 <ul class="nav nav-list">
                 <ul class="nav nav-list">
                     <li class="nav-header">${_('Actions')}</li>
                     <li class="nav-header">${_('Actions')}</li>
-                    <li><a href="${ url('beeswax.create_table.import_wizard')}">${_('Create a new table from a file')}</a></li>
-                    <li><a href="${ url('beeswax.create_table.create_table')}">${_('Create a new table manually')}</a></li>
+                    <li><a href="${ url(app_name + ':import_wizard')}">${_('Create a new table from a file')}</a></li>
+                    <li><a href="${ url(app_name + ':create_table')}">${_('Create a new table manually')}</a></li>
                 </ul>
                 </ul>
             </div>
             </div>
         </div>
         </div>
         <div class="span9">
         <div class="span9">
             <ul class="nav nav-pills">
             <ul class="nav nav-pills">
-                <li class="active"><a href="${ url('beeswax.create_table.import_wizard') }">${_('Step 1: Choose File')}</a></li>
+                <li class="active"><a href="${ url(app_name + ':import_wizard') }">${_('Step 1: Choose File')}</a></li>
                 <li><a id="step2" href="#">${_('Step 2: Choose Delimiter')}</a></li>
                 <li><a id="step2" href="#">${_('Step 2: Choose Delimiter')}</a></li>
                 <li><a href="#">${_('Step 3: Define Columns')}</a></li>
                 <li><a href="#">${_('Step 3: Define Columns')}</a></li>
             </ul>
             </ul>
@@ -73,7 +75,7 @@ ${layout.menubar(section='tables')}
                         <div class="controls">
                         <div class="controls">
                             ${comps.field(file_form["path"],
                             ${comps.field(file_form["path"],
                             placeholder="/user/user_name/data_dir",
                             placeholder="/user/user_name/data_dir",
-                            klass="pathChooser",
+                            klass="pathChooser input-xxlarge",
                             file_chooser=True,
                             file_chooser=True,
                             show_errors=False
                             show_errors=False
                             )}
                             )}

+ 6 - 20
apps/beeswax/src/beeswax/templates/configuration.mako

@@ -17,24 +17,21 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
 
 
-${commonheader(_('Configuration Variables'), "beeswax", user, "100px")}
+${commonheader(_('Configuration Variables'), app_name, user, '100px')}
 ${layout.menubar(section='configuration')}
 ${layout.menubar(section='configuration')}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
 	<h1>${_('Configuration Variables')}</h1>
 	<h1>${_('Configuration Variables')}</h1>
 	<div class="well">
 	<div class="well">
 		<form class="form-search" method="POST">
 		<form class="form-search" method="POST">
-		  <span>
-		    ${server_form['server']}
-		    <button type="submit" class="btn btn-primary">${_('Look')}</button>
-		   </span>
-		   <span class="pull-right">
-              ${_('Filter:')} <input type="text" id="filterInput" class="input-xlarge search-query" placeholder="${_('Search for key, value, etc...')}">
-		      <a href="#" id="clearFilterBtn" class="btn">${_('Clear')}</a>
-		   </span>
+		    <span class="pull-left">
+                ${_('Filter:')} <input type="text" id="filterInput" class="input-xlarge search-query" placeholder="${_('Search for key, value, etc...')}">
+		        <a href="#" id="clearFilterBtn" class="btn">${_('Clear')}</a>
+		    </span>
 		</form>
 		</form>
 	</div>
 	</div>
 	<table class="table table-striped table-condensed datatables">
 	<table class="table table-striped table-condensed datatables">
@@ -55,9 +52,6 @@ ${layout.menubar(section='configuration')}
 	</table>
 	</table>
 </div>
 </div>
 
 
-
-<script src="/static/ext/js/jquery/plugins/jquery.cookie.js"></script>
-
 <script type="text/javascript" charset="utf-8">
 <script type="text/javascript" charset="utf-8">
 	$(document).ready(function(){
 	$(document).ready(function(){
 		$(".datatables").dataTable({
 		$(".datatables").dataTable({
@@ -85,14 +79,6 @@ ${layout.menubar(section='configuration')}
 	            $(value).show();
 	            $(value).show();
 	        });
 	        });
 	    });
 	    });
-
-        $("#id_server").change(function(){
-            $.cookie("hueBeeswaxLastQueryServer", $(this).val(), {expires: 90});
-        });
-
-        if ($.cookie("hueBeeswaxLastQueryServer") != null) {
-            $("#id_server").val($.cookie("hueBeeswaxLastQueryServer"));
-        }
 	});
 	});
 </script>
 </script>
 
 

+ 13 - 11
apps/beeswax/src/beeswax/templates/create_table_index.mako

@@ -17,20 +17,22 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
-${commonheader(_('Create Table'), "beeswax", user, "100px")}
+
+${commonheader(_('Create Table'), app_name, user, '100px')}
+
 <div class="container-fluid">
 <div class="container-fluid">
-  <div class="toolbar">
-  </div>
+  <div class="toolbar"></div>
 
 
-<div id="create_table" class="view">
-  <div class="bw-create_table_choice">
-    <h1>${_('How Do You Want to Create Your Table?')}</h1>
-    <ul class="creation_choices">
-       <li class="clearfix"><a href="${ url('beeswax.create_table.import_wizard')}">${_('Create From File')}</a></li>
-       <li class="clearfix"><a href="${ url('beeswax.create_table.create_table')}">${_('Create Manually')}</a></li>
-    </ul>
+  <div id="create_table" class="view">
+    <div class="bw-create_table_choice">
+      <h1>${_('How Do You Want to Create Your Table?')}</h1>
+      <ul class="creation_choices">
+         <li class="clearfix"><a href="${ url(app_name + ':import_wizard')}">${_('Create From File')}</a></li>
+         <li class="clearfix"><a href="${ url(app_name + ':create_table')}">${_('Create Manually')}</a></li>
+      </ul>
+    </div>
   </div>
   </div>
 </div>
 </div>
-</div>
 ${commonfooter(messages)}
 ${commonfooter(messages)}

+ 6 - 4
apps/beeswax/src/beeswax/templates/create_table_manually.mako

@@ -17,9 +17,11 @@
     from desktop.views import commonheader, commonfooter
     from desktop.views import commonheader, commonfooter
     from django.utils.translation import ugettext as _
     from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
-${commonheader(_("Create table manually"), "beeswax", user, "100px")}
+
+${commonheader(_("Create table manually"), app_name, user, '100px')}
 ${layout.menubar(section='tables')}
 ${layout.menubar(section='tables')}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
@@ -29,8 +31,8 @@ ${layout.menubar(section='tables')}
     <div class="well sidebar-nav">
     <div class="well sidebar-nav">
         <ul class="nav nav-list">
         <ul class="nav nav-list">
             <li class="nav-header">${_('Actions')}</li>
             <li class="nav-header">${_('Actions')}</li>
-            <li><a href="${ url('beeswax.create_table.import_wizard')}">${_('Create a new table from a file')}</a></li>
-            <li><a href="${ url('beeswax.create_table.create_table')}">${_('Create a new table manually')}</a></li>
+            <li><a href="${ url(app_name + ':import_wizard')}">${_('Create a new table from a file')}</a></li>
+            <li><a href="${ url(app_name + ':create_table')}">${_('Create a new table manually')}</a></li>
         </ul>
         </ul>
     </div>
     </div>
 </div>
 </div>
@@ -257,7 +259,7 @@ ${layout.menubar(section='tables')}
               <div class="controls">
               <div class="controls">
                   ${comps.field(table_form["external_location"],
                   ${comps.field(table_form["external_location"],
                   placeholder="/user/user_name/data_dir",
                   placeholder="/user/user_name/data_dir",
-                  klass="pathChooser",
+                  klass="pathChooser input-xxlarge",
                   file_chooser=True,
                   file_chooser=True,
                   show_errors=False
                   show_errors=False
                   )}
                   )}

+ 5 - 3
apps/beeswax/src/beeswax/templates/define_columns.mako

@@ -17,10 +17,12 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
-${commonheader(_('Create table from file'), "beeswax", user, "100px")}
+
+${commonheader(_('Create table from file'), app_name, user, '100px')}
 ${layout.menubar(section='tables')}
 ${layout.menubar(section='tables')}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
@@ -30,8 +32,8 @@ ${layout.menubar(section='tables')}
             <div class="well sidebar-nav">
             <div class="well sidebar-nav">
                 <ul class="nav nav-list">
                 <ul class="nav nav-list">
                     <li class="nav-header">${_('Actions')}</li>
                     <li class="nav-header">${_('Actions')}</li>
-                    <li><a href="${ url('beeswax.create_table.import_wizard')}">${_('Create a new table from a file')}</a></li>
-                    <li><a href="${ url('beeswax.create_table.create_table')}">${_('Create a new table manually')}</a></li>
+                    <li><a href="${ url(app_name + ':import_wizard')}">${_('Create a new table from a file')}</a></li>
+                    <li><a href="${ url(app_name + ':create_table')}">${_('Create a new table manually')}</a></li>
                 </ul>
                 </ul>
             </div>
             </div>
         </div>
         </div>

+ 1 - 1
apps/beeswax/src/beeswax/templates/describe_partitions.mako

@@ -21,7 +21,7 @@
 
 
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 
 
-${commonheader(_('Beeswax Table Partitions: %(tableName)s') % dict(tableName=table.name), "beeswax", user, "100px")}
+${commonheader(_('Beeswax Table Partitions: %(tableName)s') % dict(tableName=table.name), app_name, user, '100px')}
 ${layout.menubar(section='tables')}
 ${layout.menubar(section='tables')}
 
 
 <div class="container-fluid">
 <div class="container-fluid">

+ 6 - 6
apps/beeswax/src/beeswax/templates/describe_table.mako

@@ -27,7 +27,7 @@ from django.utils.translation import ugettext as _
   else:
   else:
     view_or_table_noun = _("Table")
     view_or_table_noun = _("Table")
 %>
 %>
-${commonheader(_("%s Metadata: %s") % (view_or_table_noun, table.name), "beeswax", user, "100px")}
+${commonheader(_("%s Metadata: %s") % (view_or_table_noun, table.name), app_name, user, '100px')}
 ${layout.menubar(section='tables')}
 ${layout.menubar(section='tables')}
 
 
 <%def name="column_table(cols)">
 <%def name="column_table(cols)">
@@ -60,7 +60,7 @@ ${layout.menubar(section='tables')}
                 <ul class="nav nav-list">
                 <ul class="nav nav-list">
                     <li class="nav-header">${_('Actions')}</li>
                     <li class="nav-header">${_('Actions')}</li>
                     <li><a href="#importData" data-toggle="modal">${_('Import Data')}</a></li>
                     <li><a href="#importData" data-toggle="modal">${_('Import Data')}</a></li>
-                    <li><a href="${ url("beeswax.views.read_table", table=table.name) }">${_('Browse Data')}</a></li>
+                    <li><a href="${ url("beeswax:read_table", table=table.name) }">${_('Browse Data')}</a></li>
                     <li><a href="#dropTable" data-toggle="modal">${_('Drop')} ${view_or_table_noun}</a></li>
                     <li><a href="#dropTable" data-toggle="modal">${_('Drop')} ${view_or_table_noun}</a></li>
                     <li><a href="${ table.hdfs_link }" rel="${ table.path_location }">${_('View File Location')}</a></li>
                     <li><a href="${ table.hdfs_link }" rel="${ table.path_location }">${_('View File Location')}</a></li>
                 </ul>
                 </ul>
@@ -89,7 +89,7 @@ ${layout.menubar(section='tables')}
                 % if table.partition_keys:
                 % if table.partition_keys:
                   <div class="tab-pane" id="partitionColumns">
                   <div class="tab-pane" id="partitionColumns">
                     ${column_table(table.partition_keys)}
                     ${column_table(table.partition_keys)}
-                    <a href="${ url("beeswax.views.describe_partitions", table=table.name) }">${_('Show Partitions')}</a>
+                    <a href="${ url("beeswax:describe_partitions", table=table.name) }">${_('Show Partitions')}</a>
                   </div>
                   </div>
                 % endif
                 % endif
 
 
@@ -131,7 +131,7 @@ ${layout.menubar(section='tables')}
 
 
 
 
 <div id="dropTable" class="modal hide fade">
 <div id="dropTable" class="modal hide fade">
-    <form id="dropTableForm" method="POST" action="${ url("beeswax.views.drop_table", table=table.name) }">
+    <form id="dropTableForm" method="POST" action="${ url("beeswax:drop_table", table=table.name) }">
     <div class="modal-header">
     <div class="modal-header">
         <a href="#" class="close" data-dismiss="modal">&times;</a>
         <a href="#" class="close" data-dismiss="modal">&times;</a>
         <h3>${_('Drop Table')}</h3>
         <h3>${_('Drop Table')}</h3>
@@ -150,7 +150,7 @@ ${layout.menubar(section='tables')}
 
 
 
 
 <div id="importData" class="modal hide fade">
 <div id="importData" class="modal hide fade">
-    <form method="POST" action="${ url("beeswax.views.load_table", table=table.name) }" class="form-horizontal">
+    <form method="POST" action="${ url("beeswax:load_table", table=table.name) }" class="form-horizontal">
         <div class="modal-header">
         <div class="modal-header">
             <a href="#" class="close" data-dismiss="modal">&times;</a>
             <a href="#" class="close" data-dismiss="modal">&times;</a>
             <h3>${_('Import data')}</h3>
             <h3>${_('Import data')}</h3>
@@ -246,7 +246,7 @@ ${layout.menubar(section='tables')}
        "bFilter":false
        "bFilter":false
      });
      });
 
 
-     $.getJSON("${ url("beeswax.views.drop_table", table=table.name) }", function (data) {
+     $.getJSON("${ url("beeswax:drop_table", table=table.name) }", function (data) {
        $("#dropTableMessage").text(data.title);
        $("#dropTableMessage").text(data.title);
      });
      });
 
 

+ 28 - 26
apps/beeswax/src/beeswax/templates/execute.mako

@@ -21,7 +21,6 @@
 
 
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
-<%namespace name="util" file="util.mako" />
 
 
 <%def name="query()">
 <%def name="query()">
     <h1>${_('Query Editor')}</h1>
     <h1>${_('Query Editor')}</h1>
@@ -54,13 +53,15 @@
         <a id="saveQuery" class="btn">${_('Save')}</a>
         <a id="saveQuery" class="btn">${_('Save')}</a>
         % endif
         % endif
         <a id="saveQueryAs" class="btn">${_('Save as...')}</a>
         <a id="saveQueryAs" class="btn">${_('Save as...')}</a>
+        % if app_name != 'impala':
         <a id="explainQuery" class="btn">${_('Explain')}</a>
         <a id="explainQuery" class="btn">${_('Explain')}</a>
-        &nbsp; ${_('or create a')} &nbsp;<a class="btn" href="${ url('beeswax.views.execute_query') }">${_('New query')}</a>
+        % endif
+        &nbsp; ${_('or create a')} &nbsp;<a class="btn" href="${ url(app_name + ':execute_query') }">${_('New query')}</a>
     </div>
     </div>
 </%def>
 </%def>
 
 
 
 
-${commonheader(_('Query'), "beeswax", user, "100px")}
+${commonheader(_('Query'), app_name, user, '100px')}
 ${layout.menubar(section='query')}
 ${layout.menubar(section='query')}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
@@ -69,10 +70,6 @@ ${layout.menubar(section='query')}
             <div class="well sidebar-nav">
             <div class="well sidebar-nav">
                 <form id="advancedSettingsForm" action="${action}" method="POST" class="form form-horizontal noPadding">
                 <form id="advancedSettingsForm" action="${action}" method="POST" class="form form-horizontal noPadding">
                     <ul class="nav nav-list">
                     <ul class="nav nav-list">
-                        <li class="nav-header">${_('Query server')}</li>
-                        <li>
-                          ${form.query_servers['server']}
-                        </li>
                         <li class="nav-header">${_('settings')}</li>
                         <li class="nav-header">${_('settings')}</li>
                         <li>
                         <li>
                             % for i, f in enumerate(form.settings.forms):
                             % for i, f in enumerate(form.settings.forms):
@@ -108,8 +105,18 @@ ${layout.menubar(section='query')}
                                 <a class="btn btn-small" data-form-prefix="settings">${_('Add')}</a>
                                 <a class="btn btn-small" data-form-prefix="settings">${_('Add')}</a>
                             </div>
                             </div>
                         </li>
                         </li>
-                        <li class="nav-header">${_('File Resources')}</li>
-                        <li>
+                        <li class="nav-header
+                        % if app_name == 'impala':
+                            hide
+                        % endif
+                        ">
+                            ${_('File Resources')}
+                        </li>
+                        <li
+                        % if app_name == 'impala':
+                            class="hide"
+                        % endif
+                        >
                             % for i, f in enumerate(form.file_resources.forms):
                             % for i, f in enumerate(form.file_resources.forms):
                             <div class="param">
                             <div class="param">
                                 <div class="remove">
                                 <div class="remove">
@@ -143,8 +150,18 @@ ${layout.menubar(section='query')}
                                 <a class="btn btn-small" data-form-prefix="file_resources">${_('Add')}</a>
                                 <a class="btn btn-small" data-form-prefix="file_resources">${_('Add')}</a>
                             </div>
                             </div>
                         </li>
                         </li>
-                        <li class="nav-header">${_('User-defined Functions')}</li>
-                        <li>
+                        <li class="nav-header
+                        % if app_name == 'impala':
+                            hide
+                        % endif
+                        ">
+                            ${_('User-defined Functions')}
+                        </li>
+                        <li
+                        % if app_name == 'impala':
+                            class="hide"
+                        % endif
+                        >
                             % for i, f in enumerate(form.functions.forms):
                             % for i, f in enumerate(form.functions.forms):
                                 <div class="param">
                                 <div class="param">
                                     <div class="remove">
                                     <div class="remove">
@@ -328,17 +345,11 @@ ${layout.menubar(section='query')}
 </style>
 </style>
 
 
 
 
-
-<script src="/static/ext/js/jquery/plugins/jquery.cookie.js"></script>
-
 <script type="text/javascript" charset="utf-8">
 <script type="text/javascript" charset="utf-8">
     $(document).ready(function(){
     $(document).ready(function(){
         $("*[rel=tooltip]").tooltip({
         $("*[rel=tooltip]").tooltip({
             placement: 'bottom'
             placement: 'bottom'
         });
         });
-        // hack!!!
-        $("select:not(#id_query_servers-server)").addClass("span8");
-        $("#id_query_servers-server").addClass("span12");
 
 
         $("a[data-form-prefix]").each(function(){
         $("a[data-form-prefix]").each(function(){
             var _prefix = $(this).attr("data-form-prefix");
             var _prefix = $(this).attr("data-form-prefix");
@@ -424,18 +435,9 @@ ${layout.menubar(section='query')}
         });
         });
 
 
         function checkAndSubmit(){
         function checkAndSubmit(){
-            // TODO: client side validation
             $(".query").val($("#queryField").val());
             $(".query").val($("#queryField").val());
             $("#advancedSettingsForm").submit();
             $("#advancedSettingsForm").submit();
         }
         }
-
-        $("#id_query_servers-server").change(function(){
-            $.cookie("hueBeeswaxLastQueryServer", $(this).val(), {expires: 90});
-        });
-
-        if ($.cookie("hueBeeswaxLastQueryServer") != null) {
-            $("#id_query_servers-server").val($.cookie("hueBeeswaxLastQueryServer"));
-        }
     });
     });
 </script>
 </script>
 
 

+ 8 - 1
apps/beeswax/src/beeswax/templates/explain.mako

@@ -17,10 +17,12 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
-${commonheader(_('Query Explanation'), "beeswax", user, "100px")}
+
+${commonheader(_('Query Explanation'), app_name, user, '100px')}
 ${layout.menubar(section='saved queries')}
 ${layout.menubar(section='saved queries')}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
@@ -39,5 +41,10 @@ ${layout.menubar(section='saved queries')}
             <pre>${query.hql_query | h}</pre>
             <pre>${query.hql_query | h}</pre>
         </div>
         </div>
     </div>
     </div>
+
+  <br/>
+  <a class="btn" onclick="history.back()">${ _('Back') }</a>
+
 </div>
 </div>
+
 ${commonfooter(messages)}
 ${commonfooter(messages)}

+ 12 - 11
apps/beeswax/src/beeswax/templates/index.mako

@@ -17,10 +17,11 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 
 
-${commonheader(_('Beeswax'), "beeswax", user, "100px")}
+${commonheader(_('Beeswax'), app_name, user, '100px')}
 ${layout.menubar(section='tables')}
 ${layout.menubar(section='tables')}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
@@ -33,22 +34,22 @@ ${layout.menubar(section='tables')}
                     <li><a href="#installSamples" data-toggle="modal">${_('Install Samples')}</a></li>
                     <li><a href="#installSamples" data-toggle="modal">${_('Install Samples')}</a></li>
                     % endif
                     % endif
                     <li class="nav-header">${_('Tables')}</li>
                     <li class="nav-header">${_('Tables')}</li>
-                    <li><a href="${ url('beeswax.views.show_tables') }">${_('Show Tables')}</a></li>
-                    <li><a href="${ url('beeswax.create_table.create_table') }">${_('Create Table')}</a></li>
+                    <li><a href="${ url(app_name + ':show_tables') }">${_('Show Tables')}</a></li>
+                    <li><a href="${ url(app_name + ':create_table') }">${_('Create Table')}</a></li>
                     <li class="nav-header">${_('Queries')}</li>
                     <li class="nav-header">${_('Queries')}</li>
-                    <li><a href="${ url('beeswax.views.list_designs') }">${_('Saved Queries')}</a></li>
-                    <li><a href="${ url('beeswax.views.execute_query') }">${_('Execute Query')}</a></li>
-                    <li><a href="${ url('beeswax.views.list_query_history') }">${_('Query History')}</a></li>
+                    <li><a href="${ url(app_name + ':list_designs') }">${_('Saved Queries')}</a></li>
+                    <li><a href="${ url(app_name + ':execute_query') }">${_('Execute Query')}</a></li>
+                    <li><a href="${ url(app_name + ':list_query_history') }">${_('Query History')}</a></li>
                     <li class="nav-header">${_('Configuration')}</li>
                     <li class="nav-header">${_('Configuration')}</li>
-                    <li><a href="${ url('beeswax.views.configuration') }">${_('Configuration')}</a></li>
-                    <li><a href="${ url('beeswax.views.configuration') }?include_hadoop=1">${_('Extended Configuration')}</a></li>
+                    <li><a href="${ url(app_name + ':configuration') }">${_('Configuration')}</a></li>
+                    <li><a href="${ url(app_name + ':configuration') }?include_hadoop=1">${_('Extended Configuration')}</a></li>
                 </ul>
                 </ul>
             </div>
             </div>
         </div>
         </div>
         <div class="span9">
         <div class="span9">
             <h1>${_('Welcome to Beeswax for Hive')}</h1>
             <h1>${_('Welcome to Beeswax for Hive')}</h1>
             ${_("To get started with Beeswax you'll first need set up some data:")}
             ${_("To get started with Beeswax you'll first need set up some data:")}
-            <a href="${ url('beeswax.create_table.create_table') }" class='btn'>${_('Import Data')}</a>
+            <a href="${ url(app_name + ':create_table') }" class='btn'>${_('Import Data')}</a>
             ${_("or")} <a href="#installSamples" data-toggle="modal" class='btn'>${_('Install Samples')}</a>
             ${_("or")} <a href="#installSamples" data-toggle="modal" class='btn'>${_('Install Samples')}</a>
         </div>
         </div>
     </div>
     </div>
@@ -71,13 +72,13 @@ ${layout.menubar(section='tables')}
 
 
 <script type="text/javascript" charset="utf-8">
 <script type="text/javascript" charset="utf-8">
     $(document).ready(function(){
     $(document).ready(function(){
-        $.getJSON("${ url('beeswax.views.install_examples') }",function(data){
+        $.getJSON("${ url(app_name + ':install_examples') }",function(data){
             $("#installSamplesMessage").text(data.title);
             $("#installSamplesMessage").text(data.title);
         });
         });
 
 
         $("#installSamplesBtn").click(function(){
         $("#installSamplesBtn").click(function(){
             $.post(
             $.post(
-                "${ url('beeswax.views.install_examples') }",
+                "${ url(app_name + ':install_examples') }",
                 { submit:"Submit" },
                 { submit:"Submit" },
                 function(result){
                 function(result){
                     if (result.creationSucceeded){
                     if (result.creationSucceeded){

+ 6 - 6
apps/beeswax/src/beeswax/templates/layout.mako

@@ -31,12 +31,12 @@ def is_selected(section, matcher):
 <div class="subnav subnav-fixed">
 <div class="subnav subnav-fixed">
 	<div class="container-fluid">
 	<div class="container-fluid">
 		<ul class="nav nav-pills">
 		<ul class="nav nav-pills">
-			<li class="${is_selected(section, 'query')}"><a href="/beeswax/execute">${_('Query Editor')}</a></li>
-			<li class="${is_selected(section, 'my queries')}"><a href="/beeswax/my_queries">${_('My Queries')}</a></li>
-			<li class="${is_selected(section, 'saved queries')}"><a href="/beeswax/list_designs">${_('Saved Queries')}</a></li>
-			<li class="${is_selected(section, 'history')}"><a href="/beeswax/query_history">${_('History')}</a></li>
-			<li class="${is_selected(section, 'tables')}"><a href="/beeswax/tables">${_('Tables')}</a></li>
-			<li class="${is_selected(section, 'configuration')}"><a href="/beeswax/configuration">${_('Settings')}</a></li>
+			<li class="${is_selected(section, 'query')}"><a href="${ url(app_name + ':execute_query') }">${_('Query Editor')}</a></li>
+			<li class="${is_selected(section, 'my queries')}"><a href="${ url(app_name + ':my_queries') }">${_('My Queries')}</a></li>
+			<li class="${is_selected(section, 'saved queries')}"><a href="${ url(app_name + ':list_designs') }">${_('Saved Queries')}</a></li>
+			<li class="${is_selected(section, 'history')}"><a href="${ url(app_name + ':list_query_history') }">${_('History')}</a></li>
+			<li class="${is_selected(section, 'tables')}"><a href="${ url(app_name + ':show_tables') }">${_('Tables')}</a></li>
+			<li class="${is_selected(section, 'configuration')}"><a href="${ url(app_name + ':configuration') }">${_('Settings')}</a></li>
 		</ul>
 		</ul>
 	</div>
 	</div>
 </div>
 </div>

+ 8 - 6
apps/beeswax/src/beeswax/templates/list_designs.mako

@@ -19,9 +19,11 @@
     from desktop.views import commonheader, commonfooter
     from desktop.views import commonheader, commonfooter
     from django.utils.translation import ugettext as _
     from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
-${commonheader(_('Queries'), "beeswax", user, "100px")}
+
+${commonheader(_('Queries'), app_name, user, '100px')}
 ${layout.menubar(section='saved queries')}
 ${layout.menubar(section='saved queries')}
 <div class="container-fluid">
 <div class="container-fluid">
     <h1>${_('Queries')}</h1>
     <h1>${_('Queries')}</h1>
@@ -47,7 +49,7 @@ ${layout.menubar(section='saved queries')}
                 <tr>
                 <tr>
                 <td>
                 <td>
                     % if may_edit:
                     % if may_edit:
-                        <a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a>
+                        <a href="${ url(app_name + ':execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a>
                     % else:
                     % else:
                     ${design.name}
                     ${design.name}
                     % endif
                     % endif
@@ -70,11 +72,11 @@ ${layout.menubar(section='saved queries')}
                     </a>
                     </a>
                     <ul class="dropdown-menu">
                     <ul class="dropdown-menu">
                     % if may_edit:
                     % if may_edit:
-                        <li><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" title="${_('Edit this query.')}" class="contextItem">${_('Edit')}</a></li>
-                        <li><a href="javascript:void(0)" data-confirmation-url="${ url('beeswax.views.delete_design', design_id=design.id) }" title="${_('Delete this query.')}" class="contextItem confirmationModal">${_('Delete')}</a></li>
-                        <li><a href="${ url('beeswax.views.list_query_history') }?design_id=${design.id}" title="${_('View the usage history of this query.')}" class="contextItem">${_('Usage History')}</a></li>
+                        <li><a href="${ url(app_name + ':execute_query', design_id=design.id) }" title="${_('Edit this query.')}" class="contextItem">${_('Edit')}</a></li>
+                        <li><a href="javascript:void(0)" data-confirmation-url="${ url(app_name + ':delete_design', design_id=design.id) }" title="${_('Delete this query.')}" class="contextItem confirmationModal">${_('Delete')}</a></li>
+                        <li><a href="${ url(app_name + ':list_query_history') }?design_id=${design.id}" title="${_('View the usage history of this query.')}" class="contextItem">${_('Usage History')}</a></li>
                     % endif
                     % endif
-                    <li><a href="${ url('beeswax.views.clone_design', design_id=design.id) }" title="${_('Copy this query.')}" class="contextItem">${_('Clone')}</a></li>
+                    <li><a href="${ url(app_name + ':clone_design', design_id=design.id) }" title="${_('Copy this query.')}" class="contextItem">${_('Clone')}</a></li>
                     </ul>
                     </ul>
                 </div>
                 </div>
                 </td>
                 </td>

+ 5 - 5
apps/beeswax/src/beeswax/templates/list_history.mako

@@ -17,19 +17,19 @@
 import time
 import time
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
+from beeswax.views import collapse_whitespace
 %>
 %>
 
 
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
-<%!  from beeswax.views import collapse_whitespace %>
 
 
-${commonheader(_('Query History'), "beeswax", user, "100px")}
+${commonheader(_('Query History'), app_name, user, '100px')}
 ${layout.menubar(section='history')}
 ${layout.menubar(section='history')}
 
 
 <%def name="show_saved_query(design, history)">
 <%def name="show_saved_query(design, history)">
   % if design:
   % if design:
     % if request.user == design.owner:
     % if request.user == design.owner:
-      <a href="${ url('beeswax.views.execute_query', design_id=design.id) }">
+      <a href="${ url(app_name + ':execute_query', design_id=design.id) }">
     % endif
     % endif
     % if design.is_auto:
     % if design.is_auto:
       [ ${_('Unsaved')} ]
       [ ${_('Unsaved')} ]
@@ -40,7 +40,7 @@ ${layout.menubar(section='history')}
     </a>
     </a>
     % else:
     % else:
     ## TODO (bc/nutron): Shouldn't be able to edit someone else's design. Let user clone instead.
     ## TODO (bc/nutron): Shouldn't be able to edit someone else's design. Let user clone instead.
-    <a href="${ url('beeswax.views.clone_design', design_id=design.id) }" title="${_('Copy this query.')}">${_('Clone')}</a>
+    <a href="${ url(app_name + ':clone_design', design_id=design.id) }" title="${_('Copy this query.')}">${_('Clone')}</a>
     % endif
     % endif
   % else:
   % else:
     [ ${_('Auto generated action')} ]
     [ ${_('Auto generated action')} ]
@@ -130,7 +130,7 @@ ${layout.menubar(section='history')}
                 <td>${models.QueryHistory.STATE[query.last_state]}</td>
                 <td>${models.QueryHistory.STATE[query.last_state]}</td>
                 <td>
                 <td>
                   % if qcontext and query.last_state != models.QueryHistory.STATE.expired.index:
                   % if qcontext and query.last_state != models.QueryHistory.STATE.expired.index:
-                    <a href="${ url('beeswax.views.watch_query', id=query.id) }?context=${qcontext|u}" data-row-selector="true">${_('Results')}</a>
+                    <a href="${ url(app_name + ':watch_query', id=query.id) }?context=${qcontext|u}" data-row-selector="true">${_('Results')}</a>
                   % else:
                   % else:
                     ~
                     ~
                   % endif
                   % endif

+ 1 - 1
apps/beeswax/src/beeswax/templates/load_table.mako

@@ -19,7 +19,7 @@ from django.utils.translation import ugettext as _
 %>
 %>
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
-${commonheader(_('Load Data into %(table)s') % dict(table=table), "beeswax", user, "100px")}
+${commonheader(_('Load Data into %(table)s') % dict(table=table), app_name, user, '100px')}
 ${layout.menubar()}
 ${layout.menubar()}
 <div class="container-fluid">
 <div class="container-fluid">
   <h1>${_('Load Data into %(table)s') % dict(table=table)}</h1>
   <h1>${_('Load Data into %(table)s') % dict(table=table)}</h1>

+ 10 - 10
apps/beeswax/src/beeswax/templates/my_queries.mako

@@ -22,7 +22,7 @@ from django.utils.translation import ugettext as _
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%!  from beeswax.views import collapse_whitespace %>
 <%!  from beeswax.views import collapse_whitespace %>
-${commonheader(_('My Queries'), "beeswax", user, "100px")}
+${commonheader(_('My Queries'), app_name, user, '100px')}
 ${layout.menubar(section='my queries')}
 ${layout.menubar(section='my queries')}
 <style>
 <style>
     .tab-content {
     .tab-content {
@@ -67,7 +67,7 @@ ${layout.menubar(section='my queries')}
                       % for design in q_page.object_list:
                       % for design in q_page.object_list:
                         <tr>
                         <tr>
                           <td>
                           <td>
-                            <a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a>
+                            <a href="${ url(app_name + ':execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a>
                           </td>
                           </td>
                           <td>
                           <td>
                             % if design.desc:
                             % if design.desc:
@@ -85,10 +85,10 @@ ${layout.menubar(section='my queries')}
                                   <span class="caret"></span>
                                   <span class="caret"></span>
                                 </a>
                                 </a>
                                 <ul class="dropdown-menu">
                                 <ul class="dropdown-menu">
-                                  <li><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" title="${_('Edit this query.')}" class="contextItem">${_('Edit')}</a></li>
-                                  <li><a href="javascript:void(0)" data-confirmation-url="${ url('beeswax.views.delete_design', design_id=design.id) }" title="${_('Delete this query.')}" class="contextItem confirmationModal">${_('Delete')}</a></li>
-                                  <li><a href="${ url('beeswax.views.list_query_history') }?design_id=${design.id}" title="${_('View the usage history of this query.')}" class="contextItem">${_('Usage History')}</a></li>
-                                  <li><a href="${ url('beeswax.views.clone_design', design_id=design.id) }" title="${_('Copy this query.')}" class="contextItem">${_('Clone')}</a></li>
+                                  <li><a href="${ url(app_name + ':execute_query', design_id=design.id) }" title="${_('Edit this query.')}" class="contextItem">${_('Edit')}</a></li>
+                                  <li><a href="javascript:void(0)" data-confirmation-url="${ url(app_name + ':delete_design', design_id=design.id) }" title="${_('Delete this query.')}" class="contextItem confirmationModal">${_('Delete')}</a></li>
+                                  <li><a href="${ url(app_name + ':list_query_history') }?design_id=${design.id}" title="${_('View the usage history of this query.')}" class="contextItem">${_('Usage History')}</a></li>
+                                  <li><a href="${ url(app_name + ':clone_design', design_id=design.id) }" title="${_('Copy this query.')}" class="contextItem">${_('Clone')}</a></li>
                                 </ul>
                                 </ul>
                             </div>
                             </div>
                           </td>
                           </td>
@@ -97,7 +97,7 @@ ${layout.menubar(section='my queries')}
                       </tbody>
                       </tbody>
                     </table>
                     </table>
                     % if q_page.number != q_page.num_pages():
                     % if q_page.number != q_page.num_pages():
-                      <a href="${ url('beeswax.views.list_designs') }?user=${request.user.username|u}" >${_('View all my queries')} &raquo;</a>
+                      <a href="${ url(app_name + ':list_designs') }?user=${request.user.username|u}" >${_('View all my queries')} &raquo;</a>
                     % endif
                     % endif
             </div>
             </div>
 
 
@@ -127,7 +127,7 @@ ${layout.menubar(section='my queries')}
                     %>
                     %>
                     <tr>
                     <tr>
                       <td data-sort-value="${time.mktime(query.submission_date.timetuple())}">${query.submission_date.strftime("%x %X")}</td>
                       <td data-sort-value="${time.mktime(query.submission_date.timetuple())}">${query.submission_date.strftime("%x %X")}</td>
-                      <td><a href="${ url('beeswax.views.execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a></td>
+                      <td><a href="${ url(app_name + ':execute_query', design_id=design.id) }" data-row-selector="true">${design.name}</a></td>
                       <td>
                       <td>
                         <p>
                         <p>
                           % if len(query.query) > 100:
                           % if len(query.query) > 100:
@@ -140,7 +140,7 @@ ${layout.menubar(section='my queries')}
                       <td>${models.QueryHistory.STATE[query.last_state]}</td>
                       <td>${models.QueryHistory.STATE[query.last_state]}</td>
                       <td>
                       <td>
                         % if qcontext and query.last_state != models.QueryHistory.STATE.expired.index:
                         % if qcontext and query.last_state != models.QueryHistory.STATE.expired.index:
-                          <a href="${ url('beeswax.views.watch_query', id=query.id) }?context=${qcontext|u}">${_('View')}</a>
+                          <a href="${ url(app_name + ':watch_query', id=query.id) }?context=${qcontext|u}">${_('View')}</a>
                         % else:
                         % else:
                           ~
                           ~
                         % endif
                         % endif
@@ -150,7 +150,7 @@ ${layout.menubar(section='my queries')}
                   </tbody>
                   </tbody>
                 </table>
                 </table>
                 % if h_page.number != h_page.num_pages():
                 % if h_page.number != h_page.num_pages():
-                  <a href="${ url('beeswax.views.list_query_history') }">${_('View my entire query history')} &raquo;</a>
+                  <a href="${ url(app_name + ':list_query_history') }">${_('View my entire query history')} &raquo;</a>
                 % endif
                 % endif
             </div>
             </div>
     </div>
     </div>

+ 4 - 7
apps/beeswax/src/beeswax/templates/parameterization.mako

@@ -20,17 +20,17 @@ from django.utils.translation import ugettext as _
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 
 
-${commonheader(_('Parameterize Query'), "beeswax", user, "100px")}
+${commonheader(_('Parameterize Query'), app_name, user, '100px')}
 
 
 ${layout.menubar()}
 ${layout.menubar()}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
     <%
     <%
         if explain:
         if explain:
-            action = url('beeswax.views.explain_parameterized_query', design.id)
+            action = url(app_name + ':explain_parameterized_query', design.id)
             btn = _("Explain query")
             btn = _("Explain query")
         else:
         else:
-            action = url('beeswax.views.execute_parameterized_query', design.id)
+            action = url(app_name + ':execute_parameterized_query', design.id)
             btn = _("Execute query")
             btn = _("Execute query")
     %>
     %>
     <form method="POST" action="${action}" class="form-horizontal">
     <form method="POST" action="${action}" class="form-horizontal">
@@ -44,12 +44,9 @@ ${layout.menubar()}
                     </div>
                     </div>
                 </div>
                 </div>
             % endfor
             % endfor
-            <div class="hide">
-              ${query_servers}
-            </div>
             <div class="form-actions">
             <div class="form-actions">
-                <button type="submit" class="btn btn-primary">${btn}</button>
                 <a class="btn" href="javascript:history.go(-1);">${_('Cancel')}</a>
                 <a class="btn" href="javascript:history.go(-1);">${_('Cancel')}</a>
+                <button type="submit" class="btn btn-primary">${btn}</button>
             </div>
             </div>
         </fieldset>
         </fieldset>
     </form>
     </form>

+ 1 - 1
apps/beeswax/src/beeswax/templates/save_results.mako

@@ -20,7 +20,7 @@ from django.utils.translation import ugettext as _
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
-${commonheader(_('Create table from file'), "beeswax", user, "100px")}
+${commonheader(_('Create table from file'), app_name, user, '100px')}
 ${layout.menubar(section='history')}
 ${layout.menubar(section='history')}
 <div class="container-fluid">
 <div class="container-fluid">
 % if error_msg:
 % if error_msg:

+ 11 - 8
apps/beeswax/src/beeswax/templates/show_tables.mako

@@ -17,10 +17,13 @@
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
+
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
-${commonheader(_('Table List'), "beeswax", user, "100px")}
+
+${commonheader(_('Table List'), app_name, user, '100px')}
 ${layout.menubar(section='tables')}
 ${layout.menubar(section='tables')}
 
 
+
 <div class="container-fluid">
 <div class="container-fluid">
 	<h1>${_('Table List')}</h1>
 	<h1>${_('Table List')}</h1>
 	<div class="row-fluid">
 	<div class="row-fluid">
@@ -31,8 +34,8 @@ ${layout.menubar(section='tables')}
 					% if not examples_installed:
 					% if not examples_installed:
 		        	<li><a href="#installSamples" data-toggle="modal">${_('Install samples')}</a></li>
 		        	<li><a href="#installSamples" data-toggle="modal">${_('Install samples')}</a></li>
 		      		% endif
 		      		% endif
-		      		<li><a href="${ url('beeswax.create_table.import_wizard')}">${_('Create a new table from a file')}</a></li>
-					<li><a href="${ url('beeswax.create_table.create_table')}">${_('Create a new table manually')}</a></li>
+		      		<li><a href="${ url(app_name + ':import_wizard') }">${_('Create a new table from a file')}</a></li>
+					<li><a href="${ url(app_name + ':create_table') }">${_('Create a new table manually')}</a></li>
 				</ul>
 				</ul>
 			</div>
 			</div>
 		</div>
 		</div>
@@ -48,9 +51,9 @@ ${layout.menubar(section='tables')}
 				% for table in tables:
 				% for table in tables:
 					<tr>
 					<tr>
 						<td>
 						<td>
-							<a href="${ url("beeswax.views.describe_table", table=table) }" data-row-selector="true">${ table }</a>
+							<a href="${ url(app_name + ':describe_table', table=table) }" data-row-selector="true">${ table }</a>
 						</td>
 						</td>
-						<td><a href="${ url("beeswax.views.read_table", table=table) }" class="btn">${_('Browse Data')}</a></td>
+						<td><a href="${ url(app_name + ':read_table', table=table) }" class="btn">${_('Browse Data')}</a></td>
 					</tr>
 					</tr>
 				% endfor
 				% endfor
 				</tbody>
 				</tbody>
@@ -95,17 +98,17 @@ ${layout.menubar(section='tables')}
 		$("a[data-row-selector='true']").jHueRowSelector();
 		$("a[data-row-selector='true']").jHueRowSelector();
 
 
 		% if not examples_installed:
 		% if not examples_installed:
-		$.getJSON("${ url('beeswax.views.install_examples') }",function(data){
+		$.getJSON("${ url(app_name + ':install_examples') }",function(data){
 			$("#installSamplesMessage").text(data.title);
 			$("#installSamplesMessage").text(data.title);
 		});
 		});
 
 
         $("#installSamplesBtn").click(function(){
         $("#installSamplesBtn").click(function(){
             $.post(
             $.post(
-                "${ url('beeswax.views.install_examples') }",
+                "${ url(app_name + ':install_examples') }",
                 { submit:"Submit" },
                 { submit:"Submit" },
                 function(result){
                 function(result){
                     if (result.creationSucceeded){
                     if (result.creationSucceeded){
-                        window.location.href = "/beeswax/tables";
+                        window.location.href = "${ url(app_name + ':show_tables') }";
                     }
                     }
                     else {
                     else {
                         var message = "${_('There was an error processing your request:')} " + result.message;
                         var message = "${_('There was an error processing your request:')} " + result.message;

+ 3 - 3
apps/beeswax/src/beeswax/templates/util.mako

@@ -59,13 +59,13 @@ from django.utils.translation import ugettext as _
   % if query_context:
   % if query_context:
     % if query_context[0] == 'table':
     % if query_context[0] == 'table':
       <% tablename = query_context[1] %>
       <% tablename = query_context[1] %>
-      <a href="${ url('beeswax.views.describe_table', tablename) }">${tablename}</a>
+      <a href="${ url(app_name + ':describe_table', tablename) }">${tablename}</a>
     % elif query_context[0] == 'design':
     % elif query_context[0] == 'design':
       <% design = query_context[1] %>
       <% design = query_context[1] %>
       % if design.is_auto:
       % if design.is_auto:
-		<a href="${ url('beeswax.views.execute_query', design.id)}">${_('Unsaved Query')}</a>
+		<a href="${ url(app_name + ':execute_query', design.id)}">${_('Unsaved Query')}</a>
       % else:
       % else:
-        <a href="${ url('beeswax.views.execute_query', design.id)}">${design.name}</a>
+        <a href="${ url(app_name + ':execute_query', design.id)}">${design.name}</a>
       % endif
       % endif
     % else:
     % else:
       ${_('Query Results')}
       ${_('Query Results')}

+ 4 - 4
apps/beeswax/src/beeswax/templates/watch_results.mako

@@ -22,7 +22,7 @@ from django.utils.translation import ugettext as _
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 <%namespace name="comps" file="beeswax_components.mako" />
 
 
-${commonheader(_('Query Results'), "beeswax", user, "100px")}
+${commonheader(_('Query Results'), app_name, user, '100px')}
 ${layout.menubar(section='query')}
 ${layout.menubar(section='query')}
 
 
 <style>
 <style>
@@ -145,10 +145,10 @@ ${layout.menubar(section='query')}
             <div class="pagination pull-right">
             <div class="pagination pull-right">
               <ul>
               <ul>
               % if start_row != 0:
               % if start_row != 0:
-                  <li class="prev"><a title="${_('Beginning of List')}" href="${ url('beeswax.views.view_results', query.id, 0) }${'?context=' + context_param or '' | n}">&larr; ${_('Beginning of List')}</a></li>
+                  <li class="prev"><a title="${_('Beginning of List')}" href="${ url(app_name + ':view_results', query.id, 0) }${'?context=' + context_param or '' | n}">&larr; ${_('Beginning of List')}</a></li>
               % endif
               % endif
               % if has_more and len(results) == 100:
               % if has_more and len(results) == 100:
-                  <li><a title="${_('Next page')}" href="${ url('beeswax.views.view_results', query.id, next_row) }${'?context=' + context_param or '' | n}">${_('Next Page')} &rarr;</a></li>
+                  <li><a title="${_('Next page')}" href="${ url(app_name + ':view_results', query.id, next_row) }${'?context=' + context_param or '' | n}">${_('Next Page')} &rarr;</a></li>
               % endif
               % endif
               </ul>
               </ul>
             </div>
             </div>
@@ -185,7 +185,7 @@ ${layout.menubar(section='query')}
 
 
 %if can_save:
 %if can_save:
 <div id="saveAs" class="modal hide fade">
 <div id="saveAs" class="modal hide fade">
-  <form id="saveForm" action="${url('beeswax.views.save_results', query.id) }" method="POST"
+  <form id="saveForm" action="${url(app_name + ':save_results', query.id) }" method="POST"
         class="form form-inline form-padding-fix">
         class="form form-inline form-padding-fix">
     <div class="modal-header">
     <div class="modal-header">
       <a href="#" class="close" data-dismiss="modal">&times;</a>
       <a href="#" class="close" data-dismiss="modal">&times;</a>

+ 4 - 4
apps/beeswax/src/beeswax/templates/watch_wait.mako

@@ -21,11 +21,11 @@ from django.utils.translation import ugettext as _
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="layout" file="layout.mako" />
 <%namespace name="util" file="util.mako" />
 <%namespace name="util" file="util.mako" />
 
 
-${commonheader(_('Waiting for query...'), "beeswax", user, "100px")}
+${commonheader(_('Waiting for query...'), app_name, user, '100px')}
 ${layout.menubar(section='query')}
 ${layout.menubar(section='query')}
 
 
 ## Required for unit tests
 ## Required for unit tests
-<!-- <meta http-equiv="refresh" content="3;${url('beeswax.views.watch_query', query.id)}?${fwd_params}" /> -->
+<!-- <meta http-equiv="refresh" content="3;${url(app_name + ':watch_query', query.id)}?${fwd_params}" /> -->
 
 
 <div class="container-fluid">
 <div class="container-fluid">
 	<h1>${_('Waiting for query...')} ${util.render_query_context(query_context)}</h1>
 	<h1>${_('Waiting for query...')} ${util.render_query_context(query_context)}</h1>
@@ -71,7 +71,7 @@ ${layout.menubar(section='query')}
 <script>
 <script>
 
 
   $(document).ready(function(){
   $(document).ready(function(){
-    var fwdUrl = "${url('beeswax.views.watch_query', query.id)}?${fwd_params}";
+    var fwdUrl = "${url(app_name + ':watch_query', query.id)}?${fwd_params}";
     var labels = {
     var labels = {
       MRJOB: "${_('MR Job')}",
       MRJOB: "${_('MR Job')}",
       MRJOBS: "${_('MR Jobs')}"
       MRJOBS: "${_('MR Jobs')}"
@@ -82,7 +82,7 @@ ${layout.menubar(section='query')}
     var logsAtEnd = true;
     var logsAtEnd = true;
 
 
     function refreshView() {
     function refreshView() {
-      $.getJSON("${url('beeswax.views.watch_query_refresh_json', query.id)}", function (data) {
+      $.getJSON("${url(app_name + ':watch_query_refresh_json', query.id)}", function (data) {
         if (data.isSuccess || data.isFailure) {
         if (data.isSuccess || data.isFailure) {
           location.href = fwdUrl;
           location.href = fwdUrl;
         }
         }

+ 7 - 8
apps/beeswax/src/beeswax/test_base.py

@@ -16,6 +16,7 @@
 # See the License for the specific language governing permissions and
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # limitations under the License.
 #
 #
+from django.core.urlresolvers import reverse
 
 
 """
 """
 Common infrastructure for beeswax tests
 Common infrastructure for beeswax tests
@@ -39,7 +40,7 @@ import beeswax.conf
 
 
 from beeswax.conf import SERVER_INTERFACE
 from beeswax.conf import SERVER_INTERFACE
 from beeswax.models import HIVE_SERVER2
 from beeswax.models import HIVE_SERVER2
-from beeswax.server.dbms import get_query_server
+from beeswax.server.dbms import get_query_server_config
 from beeswax.server import dbms
 from beeswax.server import dbms
 
 
 
 
@@ -94,9 +95,8 @@ def get_shared_beeswax_server():
     default_xml = file(beeswax.conf.BEESWAX_HIVE_CONF_DIR.get() + "/hive-default.xml.template").read()
     default_xml = file(beeswax.conf.BEESWAX_HIVE_CONF_DIR.get() + "/hive-default.xml.template").read()
 
 
     finish = (
     finish = (
-      beeswax.conf.QUERY_SERVERS['default'].SERVER_HOST.set_for_testing("localhost"),
-      beeswax.conf.QUERY_SERVERS['default'].SERVER_PORT.set_for_testing(BEESWAXD_TEST_PORT),
-      beeswax.conf.QUERY_SERVERS['default'].SUPPORT_DDL.set_for_testing(True),
+      beeswax.conf.BEESWAX_SERVER_HOST.set_for_testing("localhost"),
+      beeswax.conf.BEESWAX_SERVER_PORT.set_for_testing(BEESWAXD_TEST_PORT),
       beeswax.conf.BEESWAX_META_SERVER_HOST.set_for_testing("localhost"),
       beeswax.conf.BEESWAX_META_SERVER_HOST.set_for_testing("localhost"),
       beeswax.conf.BEESWAX_META_SERVER_PORT.set_for_testing(BEESWAXD_TEST_PORT + 1),
       beeswax.conf.BEESWAX_META_SERVER_PORT.set_for_testing(BEESWAXD_TEST_PORT + 1),
       # Use a bogus path to avoid loading the normal hive-site.xml
       # Use a bogus path to avoid loading the normal hive-site.xml
@@ -132,7 +132,7 @@ def get_shared_beeswax_server():
       sleep = 0.001
       sleep = 0.001
       make_logged_in_client()
       make_logged_in_client()
       user = User.objects.get(username='test')
       user = User.objects.get(username='test')
-      query_server = get_query_server(support_ddl=True)
+      query_server = get_query_server_config(requires_ddl=True)
       db = dbms.get(user, query_server)
       db = dbms.get(user, query_server)
 
 
       while not started and time.time() - start < 20.0:
       while not started and time.time() - start < 20.0:
@@ -201,7 +201,7 @@ def wait_for_query_to_finish(client, response, max=30.0):
 def make_query(client, query, submission_type="Execute",
 def make_query(client, query, submission_type="Execute",
                udfs=None, settings=None, resources=None,
                udfs=None, settings=None, resources=None,
                wait=False, name=None, desc=None, local=True,
                wait=False, name=None, desc=None, local=True,
-               is_parameterized=True, max=30.0, server='default', **kwargs):
+               is_parameterized=True, max=30.0, **kwargs):
   """
   """
   Prepares arguments for the execute view.
   Prepares arguments for the execute view.
 
 
@@ -217,7 +217,6 @@ def make_query(client, query, submission_type="Execute",
   # Prepares arguments for the execute view.
   # Prepares arguments for the execute view.
   parameters = {
   parameters = {
     'query-query': query,
     'query-query': query,
-    'query_servers-server': server,
     'query-is_parameterized': is_parameterized and "on"
     'query-is_parameterized': is_parameterized and "on"
   }
   }
 
 
@@ -253,7 +252,7 @@ def make_query(client, query, submission_type="Execute",
     parameters["file_resources-%d-_exists" % i] = 'True'
     parameters["file_resources-%d-_exists" % i] = 'True'
 
 
   kwargs.setdefault('follow', True)
   kwargs.setdefault('follow', True)
-  response = client.post("/beeswax/execute", parameters, **kwargs)
+  response = client.post("/beeswax/execute/", parameters, **kwargs)
 
 
   if wait:
   if wait:
     return wait_for_query_to_finish(client, response, max)
     return wait_for_query_to_finish(client, response, max)

+ 9 - 4
apps/beeswax/src/beeswax/tests.py

@@ -45,7 +45,7 @@ import beeswax.views
 
 
 from beeswax import conf
 from beeswax import conf
 from beeswax.views import collapse_whitespace
 from beeswax.views import collapse_whitespace
-from beeswax.test_base import make_query, wait_for_query_to_finish, verify_history, get_query_server,\
+from beeswax.test_base import make_query, wait_for_query_to_finish, verify_history, get_query_server_config,\
   BEESWAXD_TEST_PORT
   BEESWAXD_TEST_PORT
 from beeswax.design import hql_query, _strip_trailing_semicolon
 from beeswax.design import hql_query, _strip_trailing_semicolon
 from beeswax.data_export import download
 from beeswax.data_export import download
@@ -66,6 +66,7 @@ def _make_query(client, query, submission_type="Execute",
   res = make_query(client, query, submission_type,
   res = make_query(client, query, submission_type,
                    udfs, settings, resources,
                    udfs, settings, resources,
                    wait, name, desc, local, is_parameterized, max, **kwargs)
                    wait, name, desc, local, is_parameterized, max, **kwargs)
+
   # Should be in the history if it's submitted.
   # Should be in the history if it's submitted.
   if submission_type == 'Execute':
   if submission_type == 'Execute':
     fragment = collapse_whitespace(smart_str(query[:20]))
     fragment = collapse_whitespace(smart_str(query[:20]))
@@ -87,7 +88,7 @@ class TestBeeswaxWithHadoop(BeeswaxSampleProvider):
 
 
   def setUp(self):
   def setUp(self):
     user = User.objects.get(username='test')
     user = User.objects.get(username='test')
-    self.db = dbms.get(user, get_query_server())
+    self.db = dbms.get(user, get_query_server_config())
 
 
   def _verify_query_state(self, state):
   def _verify_query_state(self, state):
     """
     """
@@ -1074,10 +1075,10 @@ for x in sys.stdin:
 
 
   def test_select_query_server(self):
   def test_select_query_server(self):
     c = make_logged_in_client()
     c = make_logged_in_client()
-    _make_query(c, 'SELECT bogus FROM test', server='default') # Improvement: mock another server
+    _make_query(c, 'SELECT bogus FROM test', server='beeswax') # Improvement: mock another server
 
 
     history = beeswax.models.QueryHistory.objects.latest('id')
     history = beeswax.models.QueryHistory.objects.latest('id')
-    assert_equal('default', history.server_name)
+    assert_equal('beeswax', history.server_name)
     assert_equal('localhost', history.server_host)
     assert_equal('localhost', history.server_host)
     assert_equal(BEESWAXD_TEST_PORT, history.server_port)
     assert_equal(BEESWAXD_TEST_PORT, history.server_port)
 
 
@@ -1126,6 +1127,7 @@ def test_history_page():
   def do_view(param):
   def do_view(param):
     resp = client.get('/beeswax/query_history?' + param)
     resp = client.get('/beeswax/query_history?' + param)
     assert_true(len(resp.context['page'].object_list) >= 0)     # Make the query run
     assert_true(len(resp.context['page'].object_list) >= 0)     # Make the query run
+    return resp
 
 
   do_view('user=test')
   do_view('user=test')
   do_view('user=:all')
   do_view('user=:all')
@@ -1144,6 +1146,9 @@ def test_history_page():
   do_view('sort=name&user=bogus&design_id=1&auto_query=1')
   do_view('sort=name&user=bogus&design_id=1&auto_query=1')
   do_view('sort=-type&user=:all&type=hql&auto_query=0')
   do_view('sort=-type&user=:all&type=hql&auto_query=0')
 
 
+  # Only show Beeswax queries
+  response = do_view('')
+  assert_equal('beeswax', response.context['filter_params']['type'])
 
 
 def test_strip_trailing_semicolon():
 def test_strip_trailing_semicolon():
   # Note that there are two queries (both an execute and an explain) scattered
   # Note that there are two queries (both an execute and an explain) scattered

+ 30 - 31
apps/beeswax/src/beeswax/urls.py

@@ -18,41 +18,40 @@
 from django.conf.urls.defaults import patterns, url
 from django.conf.urls.defaults import patterns, url
 
 
 urlpatterns = patterns('beeswax.views',
 urlpatterns = patterns('beeswax.views',
-  url(r'^$', 'index'),
-
-  url(r'^tables$', 'show_tables'),
-  url(r'^table/(?P<table>\w+)$', 'describe_table'),
-  url(r'^table/(?P<table>\w+)/partitions$', 'describe_partitions'),
-  url(r'^table/(?P<table>\w+)/load$', 'load_table'),
-  url(r'^table/(?P<table>\w+)/read$', 'read_table'),
-  url(r'^table/(?P<table>\w+)/drop$', 'drop_table'),
-
-  url(r'^execute$', 'execute_query'),
-  url(r'^execute/(?P<design_id>\d+)$', 'execute_query'),
-  url(r'^explain_parameterized/(?P<design_id>\d+)$', 'explain_parameterized_query'),
-  url(r'^execute_parameterized/(?P<design_id>\d+)$', 'execute_parameterized_query'),
-  url(r'^watch/(?P<id>\d+)$', 'watch_query'),
-  url(r'^watch/json/(?P<id>\d+)$', 'watch_query_refresh_json'),
-  url(r'^results/(?P<id>\d+)/(?P<first_row>\d+)$', 'view_results'),
-  url(r'^download/(?P<id>\d+)/(?P<format>\w+)$', 'download'),
-  url(r'^save_results/(?P<id>\d+)$', 'save_results'),
-
-  url(r'^my_queries$', 'my_queries'),
-  url(r'^list_designs$', 'list_designs'),
-  url(r'^delete_design/(?P<design_id>\d+)$', 'delete_design'),
-  url(r'^clone_design/(?P<design_id>\d+)$', 'clone_design'),
-  url(r'^query_history$', 'list_query_history'),
-
-  url(r'^configuration$', 'configuration'),
-  url(r'^install_examples$', 'install_examples'),
-  url(r'^query_cb/done/(?P<server_id>\S+)$', 'query_done_cb'),
+  url(r'^$', 'index', name='index'),
+
+  url(r'^tables$', 'show_tables', name='show_tables'),
+  url(r'^table/(?P<table>\w+)$', 'describe_table', name='describe_table'),
+  url(r'^table/(?P<table>\w+)/partitions$', 'describe_partitions', name='describe_partitions'),
+  url(r'^table/(?P<table>\w+)/load$', 'load_table', name='load_table'),
+  url(r'^table/(?P<table>\w+)/read$', 'read_table', name='read_table'),
+  url(r'^table/(?P<table>\w+)/drop$', 'drop_table', name='drop_table'),
+
+  url(r'^execute/(?P<design_id>\d+)?$', 'execute_query', name='execute_query'),
+  url(r'^explain_parameterized/(?P<design_id>\d+)$', 'explain_parameterized_query', name='explain_parameterized_query'),
+  url(r'^execute_parameterized/(?P<design_id>\d+)$', 'execute_parameterized_query', name='execute_parameterized_query'),
+  url(r'^watch/(?P<id>\d+)$', 'watch_query', name='watch_query'),
+  url(r'^watch/json/(?P<id>\d+)$', 'watch_query_refresh_json', name='watch_query_refresh_json'),
+  url(r'^results/(?P<id>\d+)/(?P<first_row>\d+)$', 'view_results', name='view_results'),
+  url(r'^download/(?P<id>\d+)/(?P<format>\w+)$', 'download', name='download'),
+  url(r'^save_results/(?P<id>\d+)$', 'save_results', name='save_results'),
+
+  url(r'^my_queries$', 'my_queries', name='my_queries'),
+  url(r'^list_designs$', 'list_designs', name='list_designs'),
+  url(r'^delete_design/(?P<design_id>\d+)$', 'delete_design', name='delete_design'),
+  url(r'^clone_design/(?P<design_id>\d+)$', 'clone_design', name='clone_design'),
+  url(r'^query_history$', 'list_query_history', name='list_query_history'),
+
+  url(r'^configuration$', 'configuration', name='configuration'),
+  url(r'^install_examples$', 'install_examples', name='install_examples'),
+  url(r'^query_cb/done/(?P<server_id>\S+)$', 'query_done_cb', name='query_done_cb'),
 )
 )
 
 
 urlpatterns += patterns(
 urlpatterns += patterns(
   'beeswax.create_table',
   'beeswax.create_table',
 
 
   url(r'^create$', 'index'),
   url(r'^create$', 'index'),
-  url(r'^create/create_table$', 'create_table'),
-  url(r'^create/import_wizard$', 'import_wizard'),
-  url(r'^create/auto_load$', 'load_after_create'),
+  url(r'^create/create_table$', 'create_table', name='create_table'),
+  url(r'^create/import_wizard$', 'import_wizard', name='import_wizard'),
+  url(r'^create/auto_load$', 'load_after_create', name='load_after_create'),
 )
 )

+ 60 - 60
apps/beeswax/src/beeswax/views.py

@@ -24,13 +24,13 @@ import re
 
 
 from django import forms
 from django import forms
 from django.contrib import messages
 from django.contrib import messages
-from django.core import urlresolvers
 from django.db.models import Q
 from django.db.models import Q
 from django.http import HttpResponse, QueryDict
 from django.http import HttpResponse, QueryDict
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 
 
+from desktop.context_processors import get_app_name
 from desktop.lib.paginator import Paginator
 from desktop.lib.paginator import Paginator
 from desktop.lib.django_util import copy_query_dict, format_preserving_redirect, render
 from desktop.lib.django_util import copy_query_dict, format_preserving_redirect, render
 from desktop.lib.django_util import login_notrequired, get_desktop_uri_prefix
 from desktop.lib.django_util import login_notrequired, get_desktop_uri_prefix
@@ -45,10 +45,11 @@ import beeswax.design
 import beeswax.management.commands.beeswax_install_examples
 import beeswax.management.commands.beeswax_install_examples
 
 
 from beeswax import common, data_export, models, conf
 from beeswax import common, data_export, models, conf
-from beeswax.forms import QueryServerForm, LoadDataForm, QueryForm
+from beeswax.forms import LoadDataForm, QueryForm
 from beeswax.design import HQLdesign, hql_query
 from beeswax.design import HQLdesign, hql_query
+from beeswax.models import SavedQuery
 from beeswax.server import dbms
 from beeswax.server import dbms
-from beeswax.server.dbms import expand_exception, get_query_server
+from beeswax.server.dbms import expand_exception, get_query_server_config
 
 
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
@@ -90,6 +91,8 @@ def save_design(request, form, type, design, explicit_save):
 
 
   if type == models.SavedQuery.HQL:
   if type == models.SavedQuery.HQL:
     design_cls = beeswax.design.HQLdesign
     design_cls = beeswax.design.HQLdesign
+  elif type == models.SavedQuery.IMPALA:
+    design_cls = beeswax.design.HQLdesign
   else:
   else:
     raise ValueError(_('Invalid design type %(type)s') % {'type': type})
     raise ValueError(_('Invalid design type %(type)s') % {'type': type})
 
 
@@ -114,8 +117,11 @@ def save_design(request, form, type, design, explicit_save):
       design.name = models.SavedQuery.DEFAULT_NEW_DESIGN_NAME
       design.name = models.SavedQuery.DEFAULT_NEW_DESIGN_NAME
     design.is_auto = True
     design.is_auto = True
 
 
+  design.type = type
   design.data = new_data
   design.data = new_data
+
   design.save()
   design.save()
+
   LOG.info('Saved %sdesign "%s" (id %s) for %s' %
   LOG.info('Saved %sdesign "%s" (id %s) for %s' %
            (explicit_save and '' or 'auto ', design.name, design.id, design.owner))
            (explicit_save and '' or 'auto ', design.name, design.id, design.owner))
   if explicit_save:
   if explicit_save:
@@ -135,9 +141,9 @@ def delete_design(request, design_id):
 
 
   if request.method == 'POST':
   if request.method == 'POST':
     design.delete()
     design.delete()
-    return redirect(urlresolvers.reverse(list_designs))
+    return redirect(reverse(get_app_name(request) + ':list_designs'))
   else:
   else:
-    return render('confirm.html', request, dict(url=request.path, title='Delete design?'))
+    return render('confirm.html', request, dict(url=request.path, title=_('Delete design?')))
 
 
 
 
 def clone_design(request, design_id):
 def clone_design(request, design_id):
@@ -154,7 +160,7 @@ def clone_design(request, design_id):
   copy.save()
   copy.save()
   messages.info(request, _('Copied design: %(name)s') % {'name': design.name})
   messages.info(request, _('Copied design: %(name)s') % {'name': design.name})
   return format_preserving_redirect(
   return format_preserving_redirect(
-      request, urlresolvers.reverse(execute_query, kwargs={'design_id': copy.id}))
+      request, reverse(get_app_name(request) + ':execute_query', kwargs={'design_id': copy.id}))
 
 
 
 
 def list_designs(request):
 def list_designs(request):
@@ -198,12 +204,15 @@ def my_queries(request):
   is the ``user`` filter, since this view only shows what belongs to the user.
   is the ``user`` filter, since this view only shows what belongs to the user.
   """
   """
   DEFAULT_PAGE_SIZE = 40
   DEFAULT_PAGE_SIZE = 40
+  app_name= get_app_name(request)
 
 
   # Extract the history list.
   # Extract the history list.
   prefix = 'h-'
   prefix = 'h-'
   querydict_history = _copy_prefix(prefix, request.GET)
   querydict_history = _copy_prefix(prefix, request.GET)
   # Manually limit up the user filter.
   # Manually limit up the user filter.
   querydict_history[ prefix + 'user' ] = request.user.username
   querydict_history[ prefix + 'user' ] = request.user.username
+  querydict_history[ prefix + 'type' ] = app_name
+
   hist_page, hist_filter = _list_query_history(request.user,
   hist_page, hist_filter = _list_query_history(request.user,
                                                querydict_history,
                                                querydict_history,
                                                DEFAULT_PAGE_SIZE,
                                                DEFAULT_PAGE_SIZE,
@@ -213,6 +222,8 @@ def my_queries(request):
   querydict_query = _copy_prefix(prefix, request.GET)
   querydict_query = _copy_prefix(prefix, request.GET)
   # Manually limit up the user filter.
   # Manually limit up the user filter.
   querydict_query[ prefix + 'user' ] = request.user.username
   querydict_query[ prefix + 'user' ] = request.user.username
+  querydict_query[ prefix + 'type' ] = app_name
+
   query_page, query_filter = _list_designs(querydict_query, DEFAULT_PAGE_SIZE, prefix)
   query_page, query_filter = _list_designs(querydict_query, DEFAULT_PAGE_SIZE, prefix)
 
 
   filter_params = hist_filter
   filter_params = hist_filter
@@ -249,6 +260,9 @@ def list_query_history(request):
   if not share_queries:
   if not share_queries:
     querydict_query['user'] = request.user.username
     querydict_query['user'] = request.user.username
 
 
+  app_name= get_app_name(request)
+  querydict_query['type'] = app_name
+
   page, filter_params = _list_query_history(request.user, querydict_query, DEFAULT_PAGE_SIZE)
   page, filter_params = _list_query_history(request.user, querydict_query, DEFAULT_PAGE_SIZE)
 
 
   return render('list_history.mako', request, {
   return render('list_history.mako', request, {
@@ -300,7 +314,7 @@ def drop_table(request, table):
   if request.method == 'POST':
   if request.method == 'POST':
     try:
     try:
       query_history = db.drop_table(table)
       query_history = db.drop_table(table)
-      url = reverse(watch_query, args=[query_history.id]) + '?on_success_url=' + urlresolvers.reverse(show_tables)
+      url = reverse(get_app_name(request) + ':watch_query', args=[query_history.id]) + '?on_success_url=' + reverse(get_app_name(request) + ':show_tables')
       return redirect(url)
       return redirect(url)
     except BeeswaxException, ex:
     except BeeswaxException, ex:
       error_message, log = expand_exception(ex, db)
       error_message, log = expand_exception(ex, db)
@@ -351,7 +365,7 @@ def load_table(request, table):
         hql += ", ".join(vals)
         hql += ", ".join(vals)
         hql += ")"
         hql += ")"
 
 
-      on_success_url = urlresolvers.reverse(describe_table, kwargs={'table': table})
+      on_success_url = reverse(get_app_name(request) + ':describe_table', kwargs={'table': table})
       return confirm_query(request, hql, on_success_url)
       return confirm_query(request, hql, on_success_url)
   else:
   else:
     form = beeswax.forms.LoadDataForm(table_obj)
     form = beeswax.forms.LoadDataForm(table_obj)
@@ -375,7 +389,7 @@ def download(request, id, format):
   assert format in common.DL_FORMATS
   assert format in common.DL_FORMATS
 
 
   query_history = authorized_get_history(request, id, must_exist=True)
   query_history = authorized_get_history(request, id, must_exist=True)
-  db = dbms.get(request.user, query_history.get_query_server())
+  db = dbms.get(request.user, query_history.get_query_server_config())
   LOG.debug('Download results for query %s: [ %s ]' % (query_history.server_id, query_history.query))
   LOG.debug('Download results for query %s: [ %s ]' % (query_history.server_id, query_history.query))
 
 
   return data_export.download(query_history.get_handle(), format, db)
   return data_export.download(query_history.get_handle(), format, db)
@@ -401,7 +415,9 @@ def execute_query(request, design_id=None):
   form = QueryForm()
   form = QueryForm()
   action = request.path
   action = request.path
   log = None
   log = None
-  design = safe_get_design(request, models.SavedQuery.HQL, design_id)
+  app_name = get_app_name(request)
+  query_type = SavedQuery.TYPES_MAPPING[app_name]
+  design = safe_get_design(request, query_type, design_id)
   on_success_url = request.REQUEST.get('on_success_url')
   on_success_url = request.REQUEST.get('on_success_url')
 
 
   if request.method == 'POST':
   if request.method == 'POST':
@@ -421,12 +437,12 @@ def execute_query(request, design_id=None):
 
 
       if to_submit or to_save or to_saveas or to_explain:
       if to_submit or to_save or to_saveas or to_explain:
         explicit_save = to_save or to_saveas
         explicit_save = to_save or to_saveas
-        design = save_design(request, form, models.SavedQuery.HQL, design, explicit_save)
-        action = urlresolvers.reverse(execute_query, kwargs=dict(design_id=design.id))
+        design = save_design(request, form, query_type, design, explicit_save)
+        action = reverse(app_name + ':execute_query', kwargs=dict(design_id=design.id))
 
 
       if to_explain or to_submit:
       if to_explain or to_submit:
         query_str = form.query.cleaned_data["query"]
         query_str = form.query.cleaned_data["query"]
-        query_server = get_query_server(form.query_servers.cleaned_data["server"])
+        query_server = get_query_server_config(app_name, requires_ddl=False)
 
 
         # (Optional) Parameterization.
         # (Optional) Parameterization.
         parameterization = get_parameterization(request, query_str, form, design, to_explain)
         parameterization = get_parameterization(request, query_str, form, design, to_explain)
@@ -496,7 +512,7 @@ def watch_query(request, id):
   context_param = request.GET.get('context', '')
   context_param = request.GET.get('context', '')
 
 
   # GET param: on_success_url. Default to view_results
   # GET param: on_success_url. Default to view_results
-  results_url = urlresolvers.reverse(view_results, kwargs={'id': id, 'first_row': 0})
+  results_url = reverse(get_app_name(request) + ':view_results', kwargs={'id': id, 'first_row': 0})
   on_success_url = request.GET.get('on_success_url')
   on_success_url = request.GET.get('on_success_url')
   if not on_success_url:
   if not on_success_url:
     on_success_url = results_url
     on_success_url = results_url
@@ -517,7 +533,7 @@ def watch_query(request, id):
     return format_preserving_redirect(request, results_url, request.GET)
     return format_preserving_redirect(request, results_url, request.GET)
 
 
   # Still running
   # Still running
-  log = dbms.get(request.user, query_history.get_query_server()).get_log(handle)
+  log = dbms.get(request.user, query_history.get_query_server_config()).get_log(handle)
 
 
   # Keep waiting
   # Keep waiting
   # - Translate context into something more meaningful (type, data)
   # - Translate context into something more meaningful (type, data)
@@ -536,13 +552,12 @@ def watch_query_refresh_json(request, id):
   handle, state = _get_query_handle_and_state(query_history)
   handle, state = _get_query_handle_and_state(query_history)
   query_history.save_state(state)
   query_history.save_state(state)
 
 
-  log = dbms.get(request.user, query_history.get_query_server()).get_log(handle)
+  log = dbms.get(request.user, query_history.get_query_server_config()).get_log(handle)
 
 
   jobs = _parse_out_hadoop_jobs(log)
   jobs = _parse_out_hadoop_jobs(log)
   job_urls = {}
   job_urls = {}
   for job in jobs:
   for job in jobs:
-    job_urls[job] = urlresolvers.reverse('jobbrowser.views.single_job', kwargs=dict(jobid=job))
-
+    job_urls[job] = reverse('jobbrowser.views.single_job', kwargs=dict(jobid=job))
 
 
   result = {
   result = {
     'log': log,
     'log': log,
@@ -551,6 +566,7 @@ def watch_query_refresh_json(request, id):
     'isSuccess': query_history.is_success(),
     'isSuccess': query_history.is_success(),
     'isFailure': query_history.is_failure()
     'isFailure': query_history.is_failure()
   }
   }
+
   return HttpResponse(json.dumps(result), mimetype="application/json")
   return HttpResponse(json.dumps(result), mimetype="application/json")
 
 
 def view_results(request, id, first_row=0):
 def view_results(request, id, first_row=0):
@@ -575,7 +591,7 @@ def view_results(request, id, first_row=0):
   log = ''
   log = ''
 
 
   query_history = authorized_get_history(request, id, must_exist=True)
   query_history = authorized_get_history(request, id, must_exist=True)
-  db = dbms.get(request.user, query_history.get_query_server())
+  db = dbms.get(request.user, query_history.get_query_server_config())
 
 
   handle, state = _get_query_handle_and_state(query_history)
   handle, state = _get_query_handle_and_state(query_history)
   context_param = request.GET.get('context', '')
   context_param = request.GET.get('context', '')
@@ -615,7 +631,7 @@ def view_results(request, id, first_row=0):
     download_urls = {}
     download_urls = {}
     if downloadable:
     if downloadable:
       for format in common.DL_FORMATS:
       for format in common.DL_FORMATS:
-        download_urls[format] = urlresolvers.reverse(download, kwargs=dict(id=str(id), format=format))
+        download_urls[format] = reverse(get_app_name(request) + ':download', kwargs=dict(id=str(id), format=format))
 
 
     save_form = beeswax.forms.SaveResultsForm()
     save_form = beeswax.forms.SaveResultsForm()
     results.start_row = first_row
     results.start_row = first_row
@@ -657,7 +673,7 @@ def save_results(request, id):
         msg = _('The result of this query is not available yet.')
         msg = _('The result of this query is not available yet.')
       raise PopupException(msg)
       raise PopupException(msg)
 
 
-    db = dbms.get(request.user, query_history.get_query_server())
+    db = dbms.get(request.user, query_history.get_query_server_config())
     form = beeswax.forms.SaveResultsForm(request.POST, db=db)
     form = beeswax.forms.SaveResultsForm(request.POST, db=db)
 
 
     # Cancel goes back to results
     # Cancel goes back to results
@@ -692,7 +708,7 @@ def save_results(request, id):
           request.fs.rename_star(result_meta.table_dir, target_dir)
           request.fs.rename_star(result_meta.table_dir, target_dir)
           LOG.debug("Moved results from %s to %s" % (result_meta.table_dir, target_dir))
           LOG.debug("Moved results from %s to %s" % (result_meta.table_dir, target_dir))
           query_history.save_state(models.QueryHistory.STATE.expired)
           query_history.save_state(models.QueryHistory.STATE.expired)
-          return redirect(urlresolvers.reverse('filebrowser.views.view', kwargs={'path': target_dir}))
+          return redirect(reverse('filebrowser.views.view', kwargs={'path': target_dir}))
         elif form.cleaned_data['save_target'] == form.SAVE_TYPE_TBL:
         elif form.cleaned_data['save_target'] == form.SAVE_TYPE_TBL:
           # To new table
           # To new table
           try:
           try:
@@ -711,7 +727,7 @@ def save_results(request, id):
   if error_msg:
   if error_msg:
     error_msg = _('Failed to save results from query: %(error)s') % {'error': error_msg}
     error_msg = _('Failed to save results from query: %(error)s') % {'error': error_msg}
   return render('save_results.mako', request, dict(
   return render('save_results.mako', request, dict(
-    action=urlresolvers.reverse(save_results, kwargs={'id': str(id)}),
+    action=reverse(get_app_name(request) + ':save_results', kwargs={'id': str(id)}),
     form=form,
     form=form,
     error_msg=error_msg,
     error_msg=error_msg,
     log=log,
     log=log,
@@ -723,7 +739,7 @@ def _save_results_ctas(request, query_history, target_table, result_meta):
   Handle saving results as a new table. Returns HTTP response.
   Handle saving results as a new table. Returns HTTP response.
   May raise BeeswaxException, IOError.
   May raise BeeswaxException, IOError.
   """
   """
-  query_server = query_history.get_query_server() # Query server requires DDL support
+  query_server = query_history.get_query_server_config() # Query server requires DDL support
   db = dbms.get(request.user)
   db = dbms.get(request.user)
 
 
   # Case 1: The results are straight from an existing table
   # Case 1: The results are straight from an existing table
@@ -731,7 +747,7 @@ def _save_results_ctas(request, query_history, target_table, result_meta):
     hql = 'CREATE TABLE `%s` AS SELECT * FROM %s' % (target_table, result_meta.in_tablename)
     hql = 'CREATE TABLE `%s` AS SELECT * FROM %s' % (target_table, result_meta.in_tablename)
     query = hql_query(hql)
     query = hql_query(hql)
     # Display the CTAS running. Could take a long time.
     # Display the CTAS running. Could take a long time.
-    return execute_directly(request, query, query_server, on_success_url=urlresolvers.reverse(show_tables))
+    return execute_directly(request, query, query_server, on_success_url=reverse(get_app_name(request) + ':show_tables'))
 
 
   # Case 2: The results are in some temporary location
   # Case 2: The results are in some temporary location
   # 1. Create table
   # 1. Create table
@@ -779,7 +795,7 @@ def _save_results_ctas(request, query_history, target_table, result_meta):
     raise ex
     raise ex
 
 
   # Show tables upon success
   # Show tables upon success
-  return format_preserving_redirect(request, urlresolvers.reverse(show_tables))
+  return format_preserving_redirect(request, reverse(get_app_name(request) + ':show_tables'))
 
 
 
 
 def confirm_query(request, query, on_success_url=None):
 def confirm_query(request, query, on_success_url=None):
@@ -795,7 +811,7 @@ def confirm_query(request, query, on_success_url=None):
   mform.query.initial = dict(query=query)
   mform.query.initial = dict(query=query)
   return render('execute.mako', request, {
   return render('execute.mako', request, {
     'form': mform,
     'form': mform,
-    'action': urlresolvers.reverse(execute_query),
+    'action': reverse(get_app_name(request) + ':execute_query'),
     'error_message': None,
     'error_message': None,
     'design': None,
     'design': None,
     'on_success_url': on_success_url,
     'on_success_url': on_success_url,
@@ -810,18 +826,12 @@ def explain_directly(request, query, design, query_server):
 
 
 
 
 def configuration(request):
 def configuration(request):
-  if request.method == 'POST':
-    server_form = QueryServerForm(request.POST)
-    if server_form.is_valid():
-      query_server = get_query_server(server_form.cleaned_data["server"])
-      config_values = dbms.get(request.user, query_server).get_default_configuration(
-                          bool(request.REQUEST.get("include_hadoop", False)))
-  else:
-    server_form = QueryServerForm()
-    config_values = {}
+  app_name = get_app_name(request)
+  query_server = get_query_server_config(app_name)
+  config_values = dbms.get(request.user, query_server).get_default_configuration(
+                      bool(request.REQUEST.get("include_hadoop", False)))
 
 
-  return render("configuration.mako", request, {'config_values': config_values,
-                                                'server_form': server_form})
+  return render("configuration.mako", request, {'config_values': config_values})
 
 
 
 
 """
 """
@@ -881,7 +891,7 @@ def query_done_cb(request, server_id):
 
 
   link = "%s/#launch=Beeswax:%s" % \
   link = "%s/#launch=Beeswax:%s" % \
             (get_desktop_uri_prefix(),
             (get_desktop_uri_prefix(),
-             urlresolvers.reverse(watch_query, kwargs={'id': history.id}))
+             reverse(get_app_name(request) + ':watch_query', kwargs={'id': history.id}))
   body = _("%(subject)s. You may see the results here: %(link)s\n\nQuery:\n%(query)s") % {'subject': subject, 'link': link, 'query': history.query}
   body = _("%(subject)s. You may see the results here: %(link)s\n\nQuery:\n%(query)s") % {'subject': subject, 'link': link, 'query': history.query}
   try:
   try:
     user.email_user(subject, body)
     user.email_user(subject, body)
@@ -960,8 +970,7 @@ def get_parameterization(request, query_str, form, design, is_explain):
       return render("parameterization.mako", request, dict(
       return render("parameterization.mako", request, dict(
         form=parameters_form(prefix="parameterization"),
         form=parameters_form(prefix="parameterization"),
         design=design,
         design=design,
-        explain=is_explain,
-        query_servers=form.query_servers))
+        explain=is_explain))
   return None
   return None
 
 
 def make_parameterization_form(query_str):
 def make_parameterization_form(query_str):
@@ -998,7 +1007,7 @@ def _run_parameterized_query(request, design_id, explain):
   assert query_form.is_valid()
   assert query_form.is_valid()
 
 
   query_str = query_form.query.cleaned_data["query"]
   query_str = query_form.query.cleaned_data["query"]
-  query_server = get_query_server(query_form.query_servers.cleaned_data["server"])
+  query_server = get_query_server_config(get_app_name(request))
 
 
   parameterization_form_cls = make_parameterization_form(query_str)
   parameterization_form_cls = make_parameterization_form(query_str)
   if not parameterization_form_cls:
   if not parameterization_form_cls:
@@ -1019,15 +1028,14 @@ def _run_parameterized_query(request, design_id, explain):
       db = dbms.get(request.user, query_server)
       db = dbms.get(request.user, query_server)
       error_message, log = expand_exception(ex, db)
       error_message, log = expand_exception(ex, db)
       return render('execute.mako', request, {
       return render('execute.mako', request, {
-        'action': urlresolvers.reverse(execute_query),
+        'action': reverse(get_app_name(request) + ':execute_query'),
         'design': design,
         'design': design,
         'error_message': error_message,
         'error_message': error_message,
         'form': query_form,
         'form': query_form,
         'log': log,
         'log': log,
       })
       })
   else:
   else:
-    return render("parameterization.mako", request, dict(form=parameterization_form, design=design, explain=explain,
-                                                         query_servers=query_form.query_servers))
+    return render("parameterization.mako", request, dict(form=parameterization_form, design=design, explain=explain))
 
 
 
 
 def execute_directly(request, query, query_server=None, design=None, tablename=None,
 def execute_directly(request, query, query_server=None, design=None, tablename=None,
@@ -1067,7 +1075,7 @@ def execute_directly(request, query, query_server=None, design=None, tablename=N
     authorized_get_design(request, design.id)
     authorized_get_design(request, design.id)
 
 
   history_obj = dbms.get(request.user, query_server).execute_query(query, design)
   history_obj = dbms.get(request.user, query_server).execute_query(query, design)
-  watch_url = urlresolvers.reverse(watch_query, kwargs={'id': history_obj.id})
+  watch_url = reverse(get_app_name(request) + ':watch_query', kwargs={'id': history_obj.id})
 
 
   # Prepare the GET params for the watch_url
   # Prepare the GET params for the watch_url
   get_dict = QueryDict(None, mutable=True)
   get_dict = QueryDict(None, mutable=True)
@@ -1101,7 +1109,6 @@ def _list_designs(querydict, page_size, prefix="", user=None):
   """
   """
   DEFAULT_SORT = ('-', 'date')                  # Descending date
   DEFAULT_SORT = ('-', 'date')                  # Descending date
 
 
-  VALID_TYPES = ('hql')               # Design types
   SORT_ATTR_TRANSLATION = dict(
   SORT_ATTR_TRANSLATION = dict(
     date='mtime',
     date='mtime',
     name='name',
     name='name',
@@ -1120,14 +1127,10 @@ def _list_designs(querydict, page_size, prefix="", user=None):
   d_type = querydict.get(prefix + 'type')
   d_type = querydict.get(prefix + 'type')
   if d_type:
   if d_type:
     d_type = str(d_type)
     d_type = str(d_type)
-    if d_type not in VALID_TYPES:
+    if d_type not in SavedQuery.TYPES_MAPPING.keys():
       LOG.warn('Bad parameter to list_designs: type=%s' % (d_type,))
       LOG.warn('Bad parameter to list_designs: type=%s' % (d_type,))
     else:
     else:
-      if d_type == 'hql':
-        d_type = models.SavedQuery.HQL
-      else:
-        d_type = models.SavedQuery.REPORT
-      db_queryset = db_queryset.filter(type=d_type)
+      db_queryset = db_queryset.filter(type=SavedQuery.TYPES_MAPPING[d_type])
 
 
   # Text search
   # Text search
   frag = querydict.get(prefix + 'text')
   frag = querydict.get(prefix + 'text')
@@ -1183,7 +1186,7 @@ def _get_query_handle_and_state(query_history):
   if handle is None:
   if handle is None:
     raise PopupException(_("Failed to retrieve query state from the Beeswax Server."))
     raise PopupException(_("Failed to retrieve query state from the Beeswax Server."))
 
 
-  state = dbms.get(query_history.owner, query_history.get_query_server()).get_state(handle)
+  state = dbms.get(query_history.owner, query_history.get_query_server_config()).get_state(handle)
 
 
   if state is None:
   if state is None:
     raise PopupException(_("Failed to contact Beeswax Server to check query status."))
     raise PopupException(_("Failed to contact Beeswax Server to check query status."))
@@ -1246,7 +1249,6 @@ def _list_query_history(user, querydict, page_size, prefix=""):
   """
   """
   DEFAULT_SORT = ('-', 'date')                  # Descending date
   DEFAULT_SORT = ('-', 'date')                  # Descending date
 
 
-  VALID_TYPES = ('hql')               # Design types
   SORT_ATTR_TRANSLATION = dict(
   SORT_ATTR_TRANSLATION = dict(
     date='submission_date',
     date='submission_date',
     state='last_state',
     state='last_state',
@@ -1276,12 +1278,10 @@ def _list_query_history(user, querydict, page_size, prefix=""):
   # Design type
   # Design type
   d_type = querydict.get(prefix + 'type')
   d_type = querydict.get(prefix + 'type')
   if d_type:
   if d_type:
-    if d_type not in VALID_TYPES:
+    if d_type not in SavedQuery.TYPES_MAPPING.keys():
       LOG.warn('Bad parameter to list_query_history: type=%s' % (d_type,))
       LOG.warn('Bad parameter to list_query_history: type=%s' % (d_type,))
     else:
     else:
-      if d_type == 'hql':
-        d_type = models.SavedQuery.HQL
-      db_queryset = db_queryset.filter(design__type=d_type)
+      db_queryset = db_queryset.filter(design__type=SavedQuery.TYPES_MAPPING[d_type])
 
 
   # Ordering
   # Ordering
   sort_key = querydict.get(prefix + 'sort')
   sort_key = querydict.get(prefix + 'sort')
@@ -1330,7 +1330,7 @@ def _update_query_state(query_history):
   when the user attempts to view results that have expired.
   when the user attempts to view results that have expired.
   """
   """
   if query_history.last_state <= models.QueryHistory.STATE.running.index:
   if query_history.last_state <= models.QueryHistory.STATE.running.index:
-    state_enum = dbms.get(query_history.owner, query_history.get_query_server()).get_state(query_history.get_handle())
+    state_enum = dbms.get(query_history.owner, query_history.get_query_server_config()).get_state(query_history.get_handle())
     if state_enum is None:
     if state_enum is None:
       # Error was logged at the source
       # Error was logged at the source
       return False
       return False

+ 24 - 0
apps/impala/Makefile

@@ -0,0 +1,24 @@
+#
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you 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.
+#
+
+ifeq ($(ROOT),)
+  $(error "Error: Expect the environment variable $$ROOT to point to the Desktop installation")
+endif
+
+APP_NAME = impala
+include $(ROOT)/Makefile.sdk

+ 2 - 0
apps/impala/babel.cfg

@@ -0,0 +1,2 @@
+[python: src/impala/**.py]
+[mako: src/impala/templates/**.mako]

+ 20 - 0
apps/impala/hueversion.py

@@ -0,0 +1,20 @@
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you 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.
+#
+# This file should be the one source of truth for for versions within HUE.
+# It is at least included by each of the default hue app's setup.py.
+
+VERSION="2.1.0"

+ 29 - 0
apps/impala/setup.py

@@ -0,0 +1,29 @@
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you 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 setuptools import setup, find_packages
+from hueversion import VERSION
+
+setup(
+      name = "Impala",
+      version = VERSION,
+      author = "Hue",
+      url = 'http://github.com/cloudera/hue',
+      description = "Real time queries all your Big Data",
+      packages = find_packages('src'),
+      package_dir = {'': 'src'},
+      install_requires = ['setuptools', 'desktop'],
+      entry_points = { 'desktop.sdk.application': 'impala=impala' },
+)

+ 3 - 0
apps/impala/static/help/index.html

@@ -0,0 +1,3 @@
+<html>
+<h1><a name="AboutHue-AboutHue"></a>About impala</h1>
+</html>

+ 13 - 16
desktop/conf.dist/hue.ini

@@ -332,22 +332,18 @@
 
 
 [beeswax]
 [beeswax]
 
 
-  # Deprecated! Will be removed in Hue 3
-  # Multiple sections are now available in query_servers
   # Host where Beeswax internal metastore Thrift daemon is running
   # Host where Beeswax internal metastore Thrift daemon is running
   ## beeswax_meta_server_host=localhost
   ## beeswax_meta_server_host=localhost
 
 
-  # Deprecated! Will be removed in Hue 3
-  # Multiple sections are now available in query_servers  
   # Configure the port the internal metastore daemon runs on. Used only if
   # Configure the port the internal metastore daemon runs on. Used only if
-  # hive.metastore.local is true. 
+  # hive.metastore.local is true.
   ## beeswax_meta_server_port=8003
   ## beeswax_meta_server_port=8003
 
 
   # Host where Beeswax internal metastore Thrift daemon is running
   # Host where Beeswax internal metastore Thrift daemon is running
   ## beeswax_meta_server_host=localhost
   ## beeswax_meta_server_host=localhost
-  
+
   # Configure the port the internal metastore daemon runs on. Used only if
   # Configure the port the internal metastore daemon runs on. Used only if
-  # hive.metastore.local is true. 
+  # hive.metastore.local is true.
   ## beeswax_meta_server_port=8003
   ## beeswax_meta_server_port=8003
 
 
   # Hive home directory
   # Hive home directory
@@ -375,17 +371,18 @@
   # Choices are 'beeswax' (default), 'hiveserver2'.
   # Choices are 'beeswax' (default), 'hiveserver2'.
   ## server_interface=beeswax
   ## server_interface=beeswax
 
 
- # One entry for each Query Server that can execute some SQL queries.
- # This must be in the bottom of the [beewax] section.
- [[query_servers]]
 
 
-   [[[default]]]
-   # Host where the Query Server Thrift daemon is running
+###########################################################################
+# Settings to configure Impala
+###########################################################################
+
+[impala]
+
+   # Host of the Impala Server
    ## server_host=localhost
    ## server_host=localhost
-   # Configure the port the Query Server Thrift server 
-   ## server_port=8002
-   # If DDL queries are supported (e.g. DROP can be sent directly to this server)
-   ## support_ddl=True
+
+   # Port of the Impala Server
+   ## server_port=21000
 
 
 
 
 ###########################################################################
 ###########################################################################

+ 14 - 16
desktop/conf/pseudo-distributed.ini.tmpl

@@ -335,22 +335,18 @@
 
 
 [beeswax]
 [beeswax]
 
 
-  # Deprecated! Will be removed in Hue 3
-  # Multiple sections are now available in query_servers
   # Host where Beeswax internal metastore Thrift daemon is running
   # Host where Beeswax internal metastore Thrift daemon is running
   ## beeswax_meta_server_host=localhost
   ## beeswax_meta_server_host=localhost
 
 
-  # Deprecated! Will be removed in Hue 3
-  # Multiple sections are now available in query_servers  
   # Configure the port the internal metastore daemon runs on. Used only if
   # Configure the port the internal metastore daemon runs on. Used only if
-  # hive.metastore.local is true. 
+  # hive.metastore.local is true.
   ## beeswax_meta_server_port=8003
   ## beeswax_meta_server_port=8003
 
 
   # Host where Beeswax internal metastore Thrift daemon is running
   # Host where Beeswax internal metastore Thrift daemon is running
   ## beeswax_meta_server_host=localhost
   ## beeswax_meta_server_host=localhost
-  
+
   # Configure the port the internal metastore daemon runs on. Used only if
   # Configure the port the internal metastore daemon runs on. Used only if
-  # hive.metastore.local is true. 
+  # hive.metastore.local is true.
   ## beeswax_meta_server_port=8003
   ## beeswax_meta_server_port=8003
 
 
   # Hive home directory
   # Hive home directory
@@ -378,17 +374,19 @@
   # Choices are 'beeswax' (default), 'hiveserver2'.
   # Choices are 'beeswax' (default), 'hiveserver2'.
   ## server_interface=beeswax
   ## server_interface=beeswax
 
 
- # One entry for each Query Server that can execute some SQL queries.
- # This must be in the bottom of the [beewax] section.
- [[query_servers]]
 
 
-   [[[default]]]
-   # Host where the Query Server Thrift daemon is running
+###########################################################################
+# Settings to configure Impala
+###########################################################################
+
+[impala]
+
+   # Host of the Impala Server
    ## server_host=localhost
    ## server_host=localhost
-   # Configure the port the Query Server Thrift server 
-   ## server_port=8002
-   # If DDL queries are supported (e.g. DROP can be sent directly to this server)
-   ## support_ddl=True
+
+   # Port of the Impala Server
+   ## server_port=21000
+
 
 
 ###########################################################################
 ###########################################################################
 # Settings to configure Job Designer
 # Settings to configure Job Designer

+ 42 - 0
desktop/core/src/desktop/context_processors.py

@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you 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 re
+
+
+def app_name(request):
+  """
+  Add the name of the app to the template context.
+  """
+  context = {}
+
+  name = get_app_name(request)
+
+  if name is not None:
+    context.update({'app_name': name})
+
+  return context
+
+
+NAME = re.compile('/([^/]+)')
+
+def get_app_name(request):
+  # No other cleaner way found
+  match = NAME.search(request.path)
+
+  if match is not None:
+    return match.group(1)

+ 13 - 1
desktop/core/src/desktop/settings.py

@@ -148,6 +148,18 @@ INSTALLED_APPS = [
     'desktop'
     'desktop'
 ]
 ]
 
 
+# Keep default values up to date
+TEMPLATE_CONTEXT_PROCESSORS = (
+  'django.contrib.auth.context_processors.auth',
+  'django.core.context_processors.debug',
+  'django.core.context_processors.i18n',
+  'django.core.context_processors.media',
+  'django.contrib.messages.context_processors.messages',
+   # Not default
+  'desktop.context_processors.app_name',
+)
+
+
 # Desktop doesn't use an auth profile module, because
 # Desktop doesn't use an auth profile module, because
 # because it doesn't mesh very well with the notion
 # because it doesn't mesh very well with the notion
 # of having multiple apps.  If your app needs
 # of having multiple apps.  If your app needs
@@ -255,7 +267,7 @@ if SECRET_KEY == "":
 
 
 ############################################################
 ############################################################
 
 
-# Necessary for South to not futz with tests.  Fixed in South 0.7.1
+# Necessary for South to not fuzz with tests.  Fixed in South 0.7.1
 SKIP_SOUTH_TESTS = True
 SKIP_SOUTH_TESTS = True
 
 
 # Set up environment variable so Kerberos libraries look at our private
 # Set up environment variable so Kerberos libraries look at our private

+ 4 - 2
desktop/core/src/desktop/templates/error.mako

@@ -18,7 +18,8 @@ from desktop.views import commonheader, commonfooter
 from desktop.lib.i18n import smart_unicode
 from desktop.lib.i18n import smart_unicode
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 %>
 %>
-${commonheader(_('Error'), "", user)}
+
+${commonheader(_('Error'), app_name, user)}
 
 
   <div class="container-fluid">
   <div class="container-fluid">
     <h1>${_('Error!')}</h1>
     <h1>${_('Error!')}</h1>
@@ -29,7 +30,8 @@ ${commonheader(_('Error'), "", user)}
       ${smart_unicode(traceback) | h}
       ${smart_unicode(traceback) | h}
       </textarea>
       </textarea>
     %endif
     %endif
+
+    <a class="btn" onclick="history.back()">${ _('Back') }</a>
   </div>
   </div>
 
 
 ${commonfooter(messages)}
 ${commonfooter(messages)}
-

+ 1 - 1
desktop/core/src/desktop/urls.py

@@ -85,7 +85,7 @@ for app in appmanager.DESKTOP_APPS:
     static_patterns.append(
     static_patterns.append(
       static_pattern('%s/static' % app.name, app.static_dir))
       static_pattern('%s/static' % app.name, app.static_dir))
 
 
-# TODO this stuff should probably be moved into a "ui" lib or somesuch so it
+# TODO this stuff should probably be moved into a "ui" lib or such so it
 # is autodiscovered
 # is autodiscovered
 def buildpath(d):
 def buildpath(d):
   return os.path.join(os.path.dirname(__file__), "..", '..', '..', d)
   return os.path.join(os.path.dirname(__file__), "..", '..', '..', d)