浏览代码

HUE-1214 [sqoop] Sqoop App

- Sqoop version 2 compliant app
- The sqoop client manages framework and connector forms
- Front end models for connector, connection, job, and submissions
- create, delete, start, stop, clone, edit jobs
- create, delete, edit connections
Abraham Elmahrek 13 年之前
父节点
当前提交
83626067a6
共有 69 个文件被更改,包括 8293 次插入156 次删除
  1. 3 0
      .gitignore
  2. 4 2
      apps/Makefile
  3. 24 0
      apps/sqoop/Makefile
  4. 2 0
      apps/sqoop/babel.cfg
  5. 1 0
      apps/sqoop/hueversion.py
  6. 29 0
      apps/sqoop/setup.py
  7. 15 0
      apps/sqoop/src/sqoop/__init__.py
  8. 25 0
      apps/sqoop/src/sqoop/api/__init__.py
  9. 165 0
      apps/sqoop/src/sqoop/api/connection.py
  10. 72 0
      apps/sqoop/src/sqoop/api/connector.py
  11. 113 0
      apps/sqoop/src/sqoop/api/decorators.py
  12. 45 0
      apps/sqoop/src/sqoop/api/exception.py
  13. 54 0
      apps/sqoop/src/sqoop/api/framework.py
  14. 237 0
      apps/sqoop/src/sqoop/api/job.py
  15. 56 0
      apps/sqoop/src/sqoop/api/pagination.py
  16. 61 0
      apps/sqoop/src/sqoop/api/submission.py
  17. 20 0
      apps/sqoop/src/sqoop/api/utils.py
  18. 22 0
      apps/sqoop/src/sqoop/client/__init__.py
  19. 212 0
      apps/sqoop/src/sqoop/client/base.py
  20. 190 0
      apps/sqoop/src/sqoop/client/connection.py
  21. 257 0
      apps/sqoop/src/sqoop/client/connector.py
  22. 23 0
      apps/sqoop/src/sqoop/client/exception.py
  23. 142 0
      apps/sqoop/src/sqoop/client/form.py
  24. 197 0
      apps/sqoop/src/sqoop/client/framework.py
  25. 245 0
      apps/sqoop/src/sqoop/client/job.py
  26. 104 0
      apps/sqoop/src/sqoop/client/resource.py
  27. 82 0
      apps/sqoop/src/sqoop/client/submission.py
  28. 26 0
      apps/sqoop/src/sqoop/conf.py
  29. 16 0
      apps/sqoop/src/sqoop/forms.py
  30. 376 0
      apps/sqoop/src/sqoop/locale/de/LC_MESSAGES/django.po
  31. 292 0
      apps/sqoop/src/sqoop/locale/en_US.pot
  32. 376 0
      apps/sqoop/src/sqoop/locale/es/LC_MESSAGES/django.po
  33. 374 0
      apps/sqoop/src/sqoop/locale/fr/LC_MESSAGES/django.po
  34. 352 0
      apps/sqoop/src/sqoop/locale/ja/LC_MESSAGES/django.po
  35. 352 0
      apps/sqoop/src/sqoop/locale/ko/LC_MESSAGES/django.po
  36. 369 0
      apps/sqoop/src/sqoop/locale/pt/LC_MESSAGES/django.po
  37. 373 0
      apps/sqoop/src/sqoop/locale/pt_BR/LC_MESSAGES/django.po
  38. 352 0
      apps/sqoop/src/sqoop/locale/zh_CN/LC_MESSAGES/django.po
  39. 0 0
      apps/sqoop/src/sqoop/migrations/__init__.py
  40. 16 0
      apps/sqoop/src/sqoop/models.py
  41. 22 0
      apps/sqoop/src/sqoop/settings.py
  42. 872 0
      apps/sqoop/src/sqoop/templates/app.mako
  43. 16 0
      apps/sqoop/src/sqoop/tests.py
  44. 41 0
      apps/sqoop/src/sqoop/urls.py
  45. 27 0
      apps/sqoop/src/sqoop/views.py
  46. 二进制
      apps/sqoop/static/art/icon_sqoop_24.png
  47. 20 0
      apps/sqoop/static/css/sqoop.css
  48. 139 0
      apps/sqoop/static/help/index.html
  49. 66 0
      apps/sqoop/static/js/cclass.js
  50. 213 0
      apps/sqoop/static/js/koify.js
  51. 72 0
      apps/sqoop/static/js/sqoop.connections.js
  52. 79 0
      apps/sqoop/static/js/sqoop.connectors.js
  53. 64 0
      apps/sqoop/static/js/sqoop.framework.js
  54. 200 0
      apps/sqoop/static/js/sqoop.jobs.js
  55. 373 0
      apps/sqoop/static/js/sqoop.js
  56. 93 0
      apps/sqoop/static/js/sqoop.models.js
  57. 120 0
      apps/sqoop/static/js/sqoop.submissions.js
  58. 50 0
      apps/sqoop/static/js/sqoop.utils.js
  59. 109 0
      apps/sqoop/static/js/sqoop.wizard.js
  60. 9 0
      desktop/conf.dist/hue.ini
  61. 9 0
      desktop/conf/pseudo-distributed.ini.tmpl
  62. 0 31
      desktop/core/ext-py/python-ldap-2.3.13/Build/build-openbsd/Makefile
  63. 0 1
      desktop/core/ext-py/python-ldap-2.3.13/Build/build-openbsd/pkg/COMMENT
  64. 0 2
      desktop/core/ext-py/python-ldap-2.3.13/Build/build-openbsd/pkg/DESCR
  65. 0 3
      desktop/core/ext-py/python-ldap-2.3.13/Build/build-openbsd/pkg/PLIST
  66. 0 35
      desktop/core/ext-py/python-ldap-2.3.13/Build/setup.cfg.mingw
  67. 0 33
      desktop/core/ext-py/python-ldap-2.3.13/Build/setup.cfg.suse-linux
  68. 0 33
      desktop/core/ext-py/python-ldap-2.3.13/Build/setup.cfg.win32
  69. 25 16
      desktop/core/src/desktop/lib/rest/resource.py

+ 3 - 0
.gitignore

@@ -73,5 +73,8 @@ desktop/logs
 # Maven's target directory
 target
 
+# Maven's dependency reduced pom
+desktop/libs/hadoop/java/dependency-reduced-pom.xml
+
 # Reviewboard config file
 .reviewboardrc

+ 4 - 2
apps/Makefile

@@ -44,7 +44,8 @@ APPS := about \
   impala \
   pig \
   search \
-  hbase
+  hbase \
+  sqoop
 
 ################################################
 # Install all applications into the Desktop environment
@@ -117,7 +118,8 @@ I18N_APPS := about \
   impala \
   pig \
   search \
-  hbase
+  hbase \
+  sqoop
 
 COMPILE_LOCALE_TARGETS := $(I18N_APPS:%=.recursive-compile-locales/%)
 compile-locales: $(COMPILE_LOCALE_TARGETS)

+ 24 - 0
apps/sqoop/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 = sqoop
+include $(ROOT)/Makefile.sdk

+ 2 - 0
apps/sqoop/babel.cfg

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

+ 1 - 0
apps/sqoop/hueversion.py

@@ -0,0 +1 @@
+../../VERSION

+ 29 - 0
apps/sqoop/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 = "sqoop",
+      version = VERSION,
+      author = "Hue",
+      url = 'http://github.com/cloudera/hue',
+      description = "Move big data to and from Hadoop in a few clicks.",
+      packages = find_packages('src'),
+      package_dir = {'': 'src'},
+      install_requires = ['setuptools', 'desktop'],
+      entry_points = { 'desktop.sdk.application': 'sqoop=sqoop' },
+)

+ 15 - 0
apps/sqoop/src/sqoop/__init__.py

@@ -0,0 +1,15 @@
+# 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.

+ 25 - 0
apps/sqoop/src/sqoop/api/__init__.py

@@ -0,0 +1,25 @@
+#!/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.
+
+from connection import get_connections, create_connection, update_connection,\
+                       connection, connections, connection_clone, connection_delete
+from connector import get_connectors, connectors, connector
+from framework import framework
+from job import get_jobs, create_job, update_job,\
+                job, jobs, job_clone, job_delete,\
+                job_start, job_stop, job_status
+from submission import get_submissions, submissions

+ 165 - 0
apps/sqoop/src/sqoop/api/connection.py

@@ -0,0 +1,165 @@
+#!/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.
+
+try:
+  import json
+except ImportError:
+  import simplejson as json
+import logging
+import socket
+
+from django.http import HttpResponse
+from django.utils.translation import ugettext as _
+
+from sqoop import client, conf
+from sqoop.client.connection import SqoopConnectionException
+from decorators import get_connection_or_exception
+from desktop.lib.exceptions import StructuredException
+from desktop.lib.rest.http_client import RestException
+from exception import handle_rest_exception
+from utils import list_to_dict
+
+
+__all__ = ['get_connections', 'create_connection', 'update_connection', 'connection', 'connections', 'connection_clone', 'connection_delete']
+
+
+LOG = logging.getLogger(__name__)
+
+
+def get_connections(request):
+  response = {
+    'status': 0,
+    'errors': None,
+    'connections': []
+  }
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['connections'] = list_to_dict(c.get_connections())
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not get connections.')))
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+def create_connection(request):
+  response = {
+    'status': 0,
+    'errors': None,
+    'connection': None
+  }
+
+  if 'connection' not in request.POST:
+    raise StructuredException(code="INVALID_REQUEST_ERROR", message=_('Error saving connection'), data={'errors': 'Connection is missing.'}, error_code=400)
+
+  d = json.loads(request.POST['connection'])
+  conn = client.Connection.from_dict(d)
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['connection'] = c.create_connection(conn).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not create connection.')))
+  except SqoopConnectionException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+def update_connection(request, connection):
+  response = {
+    'status': 0,
+    'errors': None,
+    'connection': None
+  }
+
+  if 'connection' not in request.POST:
+    raise StructuredException(code="INVALID_REQUEST_ERROR", message=_('Error saving connection'), data={'errors': 'Connection is missing.'}, error_code=400)
+
+  connection.update_from_dict(json.loads(request.POST['connection']))
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['connection'] = c.update_connection(connection).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not update connection.')))
+  except SqoopConnectionException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+def connections(request):
+  if request.method == 'GET':
+    return get_connections(request)
+  elif request.method == 'POST':
+    return create_connection(request)
+  else:
+    raise StructuredException(code="INVALID_METHOD", message=_('GET or POST request required.'), error_code=405)
+
+@get_connection_or_exception()
+def connection(request, connection):
+  response = {
+    'status': 0,
+    'errors': None,
+    'connection': None
+  }
+  if request.method == 'GET':
+    response['connection'] = connection.to_dict()
+    return HttpResponse(json.dumps(response), mimetype="application/json")
+  elif request.method == 'POST':
+    return update_connection(request, connection)
+  else:
+    raise StructuredException(code="INVALID_METHOD", message=_('GET or POST request required.'), error_code=405)
+
+@get_connection_or_exception()
+def connection_clone(request, connection):
+  if request.method != 'POST':
+    raise StructuredException(code="INVALID_METHOD", message=_('POST request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None,
+    'connection': None
+  }
+
+  connection.id = -1
+  connection.name = '%s-copy' % connection.name
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['connection'] = c.create_connection(connection).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not clone connection.')))
+  except SqoopConnectionException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+@get_connection_or_exception()
+def connection_delete(request, connection):
+  if request.method != 'POST':
+    raise StructuredException(code="INVALID_METHOD", message=_('POST request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None
+  }
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    c.delete_connection(connection)
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not delete connection.')))
+  except SqoopConnectionException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")

+ 72 - 0
apps/sqoop/src/sqoop/api/connector.py

@@ -0,0 +1,72 @@
+#!/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.
+
+try:
+  import json
+except ImportError:
+  import simplejson as json
+import logging
+import socket
+
+from django.http import HttpResponse
+from django.utils.translation import ugettext as _
+
+from sqoop import client, conf
+from decorators import get_connector_or_exception
+from desktop.lib.exceptions import StructuredException
+from desktop.lib.rest.http_client import RestException
+from exception import handle_rest_exception
+from utils import list_to_dict
+
+
+__all__ = ['get_connectors', 'connectors', 'connector']
+
+
+LOG = logging.getLogger(__name__)
+
+
+def get_connectors(request):
+  response = {
+    'status': 0,
+    'errors': None,
+    'connectors': []
+  }
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['connectors'] = list_to_dict(c.get_connectors())
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not get connectors.')))
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+def connectors(request):
+  if request.method == 'GET':
+    return get_connectors(request)
+  else:
+    raise StructuredException(code="INVALID_METHOD", message=_('GET request required.'), error_code=405)
+
+@get_connector_or_exception()
+def connector(request, connector):
+  response = {
+    'status': 0,
+    'errors': None,
+    'connector': None
+  }
+  if request.method == 'GET':
+    response['connector'] = connector.to_dict()
+    return HttpResponse(json.dumps(response), mimetype="application/json")
+  else:
+    raise StructuredException(code="INVALID_METHOD", message=_('GET request required.'), error_code=405)

+ 113 - 0
apps/sqoop/src/sqoop/api/decorators.py

@@ -0,0 +1,113 @@
+#!/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.
+
+try:
+  import json
+except ImportError:
+  import simplejson as json
+import logging
+
+from django.http import HttpResponse
+from django.utils.translation import ugettext as _
+from django.utils.functional import wraps
+
+from desktop.lib.django_util import render
+from desktop.lib.exceptions_renderable import PopupException
+from desktop.lib.rest.http_client import RestException
+from exception import handle_rest_exception
+from sqoop import client, conf
+
+
+__all__ = ['get_job_or_exception']
+
+LOG = logging.getLogger(__name__)
+
+
+def get_connector_or_exception(exception_class=PopupException):
+  """
+  Decorator ensuring that the user has access to the Connector.
+
+  :param connector_id: Connector ID
+  :returns: Connector
+  """
+  def inner(view_func):
+    def decorate(request, connector_id, *args, **kwargs):
+      try:
+        c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+        connector = c.get_connector(int(connector_id))
+      except RestException, e:
+        handle_rest_exception(e, _('Could not get connector.'))
+      return view_func(request, connector=connector, *args, **kwargs)
+    return wraps(view_func)(decorate)
+  return inner
+
+
+def get_connection_or_exception(exception_class=PopupException):
+  """
+  Decorator ensuring that the user has access to the connection.
+
+  :param connection_id: Connection ID
+  :returns: Connection
+  """
+  def inner(view_func):
+    def decorate(request, connection_id, *args, **kwargs):
+      try:
+        c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+        connection = c.get_connection(int(connection_id))
+      except RestException, e:
+        handle_rest_exception(e, _('Could not get connection.'))
+      return view_func(request, connection=connection, *args, **kwargs)
+    return wraps(view_func)(decorate)
+  return inner
+
+
+def get_job_or_exception(exception_class=PopupException):
+  """
+  Decorator ensuring that the user has access to the job.
+
+  :param job_id: Job ID
+  :returns: Job
+  """
+  def inner(view_func):
+    def decorate(request, job_id, *args, **kwargs):
+      try:
+        c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+        job = c.get_job(int(job_id))
+      except RestException, e:
+        handle_rest_exception(e, _('Could not get job.'))
+      return view_func(request, job=job, *args, **kwargs)
+    return wraps(view_func)(decorate)
+  return inner
+
+
+def get_submission_or_exception(exception_class=PopupException):
+  """
+  Decorator ensuring that the user has access to the submission.
+
+  :param submission_id: Submission ID
+  :returns: Submission
+  """
+  def inner(view_func):
+    def decorate(request, submission_id, *args, **kwargs):
+      try:
+        c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+        submission = c.get_submission(int(submission_id))
+      except RestException, e:
+        handle_rest_exception(e, _('Could not get submission.'))
+      return view_func(request, submission=submission, *args, **kwargs)
+    return wraps(view_func)(decorate)
+  return inner

+ 45 - 0
apps/sqoop/src/sqoop/api/exception.py

@@ -0,0 +1,45 @@
+#!/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 logging
+import socket
+
+from django.utils.translation import ugettext as _
+from django.utils.encoding import smart_str
+
+from desktop.lib.exceptions import StructuredException
+from desktop.lib.rest.http_client import RestException
+
+
+LOG = logging.getLogger(__name__)
+
+
+def handle_rest_exception(e, msg):
+  reason = e.get_parent_ex().reason
+  if isinstance(reason, socket.error):
+    LOG.error(smart_str('Could not connect to sqoop server: %s (%s)' % (reason[0], reason[1])))
+    return {
+      'status': -1,
+      'errors': [_('Could not connect to sqoop server. %s (%s)') % (reason[0], reason[1])]
+    }
+  else:
+    LOG.error(smart_str(msg))
+    LOG.error(smart_str(e.message))
+    return {
+      'status': 1,
+      'errors': [msg]
+    }

+ 54 - 0
apps/sqoop/src/sqoop/api/framework.py

@@ -0,0 +1,54 @@
+#!/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.
+
+try:
+  import json
+except ImportError:
+  import simplejson as json
+import logging
+import socket
+
+from django.http import HttpResponse
+from django.utils.translation import ugettext as _
+
+from sqoop import client, conf
+from desktop.lib.exceptions import StructuredException
+from desktop.lib.rest.http_client import RestException
+from exception import handle_rest_exception
+
+
+__all__ = ['framework']
+
+
+LOG = logging.getLogger(__name__)
+
+
+def framework(request):
+  response = {
+    'status': 0,
+    'errors': None,
+    'framework': None
+  }
+  if request.method == 'GET':
+    try:
+      c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+      response['framework'] = c.get_framework().to_dict()
+    except RestException, e:
+      response.update(handle_rest_exception(e, _('Could not get framework.')))
+    return HttpResponse(json.dumps(response), mimetype="application/json")
+  else:
+    raise StructuredException(code="INVALID_METHOD", message=_('GET request required.'), error_code=405)

+ 237 - 0
apps/sqoop/src/sqoop/api/job.py

@@ -0,0 +1,237 @@
+#!/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.
+
+try:
+  import json
+except ImportError:
+  import simplejson as json
+import logging
+import socket
+
+from django.http import HttpResponse
+from django.utils.translation import ugettext as _
+
+from sqoop import client, conf
+from sqoop.client.exception import SqoopException
+from decorators import get_job_or_exception
+from desktop.lib.exceptions import StructuredException
+from desktop.lib.rest.http_client import RestException
+from exception import handle_rest_exception
+from utils import list_to_dict
+
+
+__all__ = ['get_jobs', 'create_job', 'update_job', 'job', 'jobs', 'job_clone', 'job_delete', 'job_start', 'job_stop', 'job_status']
+
+
+LOG = logging.getLogger(__name__)
+
+
+def get_jobs(request):
+  response = {
+    'status': 0,
+    'errors': None,
+    'jobs': []
+  }
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    jobs = c.get_jobs()
+    response['jobs'] = list_to_dict(jobs)
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not get jobs.')))
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+def create_job(request):
+  if request.method != 'POST':
+    raise StructuredException(code="INVALID_METHOD", message=_('POST request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None,
+    'job': None
+  }
+
+  if 'job' not in request.POST:
+    raise StructuredException(code="INVALID_REQUEST_ERROR", message=_('Error saving job'), data={'errors': 'job is missing.'}, error_code=400)
+
+  d = json.loads(request.POST['job'])
+  job = client.Job.from_dict(d)
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['job'] = c.create_job(job).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not create job.')))
+  except SqoopException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+def update_job(request, job):
+  if request.method != 'POST':
+    raise StructuredException(code="INVALID_METHOD", message=_('POST request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None,
+    'job': None
+  }
+
+  if 'job' not in request.POST:
+    raise StructuredException(code="INVALID_REQUEST_ERROR", message=_('Error saving job'), data={'errors': 'job is missing.'}, error_code=400)
+
+  job.update_from_dict(json.loads(request.POST['job']))
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['job'] = c.update_job(job).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not update job.')))
+  except SqoopException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+
+def jobs(request):
+  if request.method == 'GET':
+    return get_jobs(request)
+  elif request.method == 'POST':
+    return create_job(request)
+  else:
+    raise StructuredException(code="INVALID_METHOD", message=_('GET or POST request required.'), error_code=405)
+
+@get_job_or_exception()
+def job(request, job):
+  response = {
+    'status': 0,
+    'errors': None,
+    'job': None
+  }
+  if request.method == 'GET':
+    response['job'] = job.to_dict()
+    return HttpResponse(json.dumps(response), mimetype="application/json")
+  elif request.method == 'POST':
+    return update_job(request, job)
+  else:
+    raise StructuredException(code="INVALID_METHOD", message=_('GET or POST request required.'), error_code=405)
+
+@get_job_or_exception()
+def job_clone(request, job):
+  if request.method != 'POST':
+    raise StructuredException(code="INVALID_METHOD", message=_('POST request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None,
+    'job': None
+  }
+
+  job.id = -1
+  job.name = '%s-copy' % job.name
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['job'] = c.create_job(job).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not clone job.')))
+  except SqoopException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+@get_job_or_exception()
+def job_delete(request, job):
+  if request.method != 'POST':
+    raise StructuredException(code="INVALID_METHOD", message=_('POST request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None,
+    'job': None
+  }
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    c.delete_job(job)
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not delete job.')))
+  except SqoopException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+@get_job_or_exception()
+def job_start(request, job):
+  if request.method != 'POST':
+    raise StructuredException(code="INVALID_METHOD", message=_('POST request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None,
+    'submission': None
+  }
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['submission'] = c.start_job(job).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not start job.')))
+  except SqoopException, e:
+    response['status'] = 100
+    response['errors'] = [e.to_dict()]
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+@get_job_or_exception()
+def job_stop(request, job):
+  if request.method != 'POST':
+    raise StructuredException(code="INVALID_METHOD", message=_('POST request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None,
+    'submission': None
+  }
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['submission'] = c.stop_job(job).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not stop job.')))
+  except SqoopException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+@get_job_or_exception()
+def job_status(request, job):
+  if request.method != 'GET':
+    raise StructuredException(code="INVALID_METHOD", message=_('GET request required.'), error_code=405)
+
+  response = {
+    'status': 0,
+    'errors': None,
+    'submission': None
+  }
+
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    response['submission'] = c.get_job_status(job).to_dict()
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not get job status.')))
+  except SqoopException, e:
+    response['status'] = 100
+    response['errors'] = e.to_dict()
+  return HttpResponse(json.dumps(response), mimetype="application/json")

+ 56 - 0
apps/sqoop/src/sqoop/api/pagination.py

@@ -0,0 +1,56 @@
+#!/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.
+
+
+class ContinuationToken(object):
+  def __init__(self, page, page_size, **kwargs):
+    """ Page starting at 1 """
+    self.page = page
+    self.page_size = page_size
+
+  @property
+  def start(self):
+    return (self.page - 1) * self.page_size
+
+  @property
+  def end(self):
+    return self.page * self.page_size
+
+  @classmethod
+  def from_dict(cls, ct_dict):
+    ct['page_size'] = ct_dict.get('page-size', 10)
+    return ContinuationToken(**ct_dict)
+
+  def to_dict(self):
+    return {
+      'page': self.page,
+      'page-size': self.page_size
+    }
+
+  def next(self, li=None):
+    if self.end > len(li):
+      return None
+    return ContinuationToken(self.page + 1, self.page_size)
+
+  def paginate_list(self, li):
+    start = self.start
+    if start <= len(li):
+      end = self.end
+      if end > len(li):
+        end = len(li)
+      return (li[start:end], end - start)
+    return ([], 0)

+ 61 - 0
apps/sqoop/src/sqoop/api/submission.py

@@ -0,0 +1,61 @@
+#!/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.
+
+try:
+  import json
+except ImportError:
+  import simplejson as json
+import logging
+import socket
+
+from django.http import HttpResponse
+from django.utils.translation import ugettext as _
+
+from sqoop import client, conf
+from decorators import get_submission_or_exception
+from desktop.lib.exceptions import StructuredException
+from desktop.lib.rest.http_client import RestException
+from exception import handle_rest_exception
+from utils import list_to_dict
+
+
+__all__ = ['get_submissions', 'submissions']
+
+
+LOG = logging.getLogger(__name__)
+
+
+def get_submissions(request):
+  response = {
+    'status': 0,
+    'errors': None,
+    'submissions': []
+  }
+  status = request.GET.get('status', 'all').split(',')
+  try:
+    c = client.SqoopClient(conf.SERVER_URL.get(), request.user.username, request.LANGUAGE_CODE)
+    submissions = c.get_submissions()
+    response['submissions'] = list_to_dict(submissions)
+  except RestException, e:
+    response.update(handle_rest_exception(e, _('Could not get submissions.')))
+  return HttpResponse(json.dumps(response), mimetype="application/json")
+
+def submissions(request):
+  if request.method == 'GET':
+    return get_submissions(request)
+  else:
+    raise StructuredException(code="INVALID_METHOD", message=_('GET request required.'), error_code=405)

+ 20 - 0
apps/sqoop/src/sqoop/api/utils.py

@@ -0,0 +1,20 @@
+#!/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.
+
+
+def list_to_dict(sqoop_objects):
+  return [sqoop_object.to_dict() for sqoop_object in sqoop_objects]

+ 22 - 0
apps/sqoop/src/sqoop/client/__init__.py

@@ -0,0 +1,22 @@
+# 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 base import SqoopClient
+from connection import Connection
+from connector import Connector
+from framework import Framework
+from job import Job
+from submission import Submission

+ 212 - 0
apps/sqoop/src/sqoop/client/base.py

@@ -0,0 +1,212 @@
+# 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 logging
+import posixpath
+import threading
+import time
+try:
+  import json
+except ImportError:
+  import simplejson as json
+
+from django.utils.translation import ugettext as _
+
+from desktop.conf import TIME_ZONE
+from desktop.lib.rest.http_client import HttpClient
+
+from connection import Connection, SqoopConnectionException
+from connector import Connector
+from framework import Framework
+from job import Job, SqoopJobException
+from submission import Submission, SqoopSubmissionException
+from resource import SqoopResource
+
+
+LOG = logging.getLogger(__name__)
+DEFAULT_USER = 'hue'
+API_VERSION = 'v1'
+
+_JSON_CONTENT_TYPE = 'application/json'
+
+
+class SqoopClient(object):
+  """
+  Sqoop client
+  """
+  STATUS_GOOD = ('FINE', 'ACCEPTABLE')
+  STATUS_BAD = ('UNACCEPTABLE', 'FAILURE_ON_SUBMIT')
+
+  def __init__(self, url, username, language='en'):
+    self._url = url
+    self._client = HttpClient(self._url, logger=LOG)
+    self._root = SqoopResource(self._client)
+    self._language = language
+    self._username = username
+
+  def __str__(self):
+    return "SqoopClient at %s" % self._url
+
+  @property
+  def url(self):
+    return self._url
+
+  @property
+  def headers(self):
+    return {
+      'Accept': 'application/json',
+      'Accept-Language': self._language,
+      'sqoop-user-name': self._username
+    }
+
+  def get_version(self):
+    return self._root.get('version', headers=self.headers)
+
+  def get_framework(self):
+    resp_dict = self._root.get('%s/framework/all' % API_VERSION, headers=self.headers)
+    framework = Framework.from_dict(resp_dict)
+    return framework
+
+  def get_connectors(self):
+    resp_dict = self._root.get('%s/connector/all' % API_VERSION, headers=self.headers)
+    connectors = [ Connector.from_dict(connector_dict, resp_dict['resources-connector']) for connector_dict in resp_dict['all'] ]
+    return connectors
+
+  def get_connector(self, connector_id):
+    resp_dict = self._root.get('%s/connector/%d/' % (API_VERSION, connector_id), headers=self.headers)
+    if resp_dict['all']:
+      return Connector.from_dict(resp_dict['all'][0], resp_dict['resources-connector'])
+    return None
+
+  def get_connections(self):
+    resp_dict = self._root.get('%s/connection/all' % API_VERSION, headers=self.headers)
+    connections = [Connection.from_dict(conn_dict) for conn_dict in resp_dict['all']]
+    return connections
+
+  def get_connection(self, connection_id):
+    resp_dict = self._root.get('%s/connection/%d/' % (API_VERSION, connection_id), headers=self.headers)
+    if resp_dict['all']:
+      return Connection.from_dict(resp_dict['all'][0])
+    return None
+
+  def create_connection(self, connection):
+    if not connection.connector:
+      connection.connector = self.get_connectors()[0].con_forms
+    if not connection.framework:
+      connection.framework = self.get_framework().con_forms
+    connection.created = int( round(time.time() * 1000) )
+    connection.updated = connection.created
+    connection_dict = connection.to_dict()
+    request_dict = {
+      'all': [connection_dict]
+    }
+    resp = self._root.post('%s/connection/' % API_VERSION, data=json.dumps(request_dict), headers=self.headers)
+    if 'id' not in resp:
+      raise SqoopConnectionException.from_dict(resp)
+    connection.id = resp['id']
+    return connection
+
+  def update_connection(self, connection):
+    """ Update a connection """
+    if not connection.connector:
+      connection.connector = self.get_connectors()[0].con_forms
+    if not connection.framework:
+      connection.framework = self.get_framework().con_forms
+    connection.updated = int( round(time.time() * 1000) )
+    connection_dict = connection.to_dict()
+    request_dict = {
+      'all': [connection_dict]
+    }
+    resp = self._root.put('%s/connection/%d/' % (API_VERSION, connection.id), data=json.dumps(request_dict), headers=self.headers)
+    if resp['connector']['status'] in SqoopClient.STATUS_BAD or resp['framework']['status'] in SqoopClient.STATUS_BAD:
+      raise SqoopConnectionException.from_dict(resp)
+    return connection
+
+  def delete_connection(self, connection):
+    resp = self._root.delete('%s/connection/%d/' % (API_VERSION, connection.id), headers=self.headers)
+    return None
+
+  def get_jobs(self):
+    resp_dict = self._root.get('%s/job/all' % API_VERSION, headers=self.headers)
+    jobs = [Job.from_dict(job_dict) for job_dict in resp_dict['all']]
+    return jobs
+
+  def get_job(self, job_id):
+    resp_dict = self._root.get('%s/job/%d/' % (API_VERSION, job_id), headers=self.headers)
+    if resp_dict['all']:
+      return Job.from_dict(resp_dict['all'][0])
+    return None
+
+  def create_job(self, job):
+    if not job.connector:
+      job.connector = self.get_connectors()[0].job_forms[job.type.upper()]
+    if not job.framework:
+      job.framework = self.get_framework().job_forms[job.type.upper()]
+    job.created = int( round(time.time() * 1000) )
+    job.updated = job.created
+    job_dict = job.to_dict()
+    request_dict = {
+      'all': [job_dict]
+    }
+    resp = self._root.post('%s/job/' % API_VERSION, data=json.dumps(request_dict), headers=self.headers)
+    if 'id' not in resp:
+      raise SqoopJobException.from_dict(resp)
+    job.id = resp['id']
+    return job
+
+  def update_job(self, job):
+    if not job.connector:
+      job.connector = self.get_connectors()[0].job_forms[job.type.upper()]
+    if not job.framework:
+      job.framework = self.get_framework().job_forms[job.type.upper()]
+    job.updated = int( round(time.time() * 1000) )
+    job_dict = job.to_dict()
+    request_dict = {
+      'all': [job_dict]
+    }
+    resp = self._root.put('%s/job/%d/' % (API_VERSION, job.id), data=json.dumps(request_dict), headers=self.headers)
+    if resp['connector']['status'] in SqoopClient.STATUS_BAD or resp['framework']['status'] in SqoopClient.STATUS_BAD:
+      raise SqoopJobException.from_dict(resp)
+    return job
+
+  def delete_job(self, job):
+    resp_dict = self._root.delete('%s/job/%d/' % (API_VERSION, job.id), headers=self.headers)
+    return None
+
+  def get_job_status(self, job):
+    resp_dict = self._root.get('%s/submission/action/%d/' % (API_VERSION, job.id), headers=self.headers)
+    return Submission.from_dict(resp_dict['all'][0])
+
+  def start_job(self, job):
+    resp_dict = self._root.post('%s/submission/action/%d/' % (API_VERSION, job.id), headers=self.headers)
+    if resp_dict['all'][0]['status'] in SqoopClient.STATUS_BAD:
+      raise SqoopSubmissionException.from_dict(resp_dict['all'][0])
+    return Submission.from_dict(resp_dict['all'][0])
+
+  def stop_job(self, job):
+    resp_dict = self._root.delete('%s/submission/action/%d/' % (API_VERSION, job.id), headers=self.headers)
+    return Submission.from_dict(resp_dict['all'][0])
+
+  def get_submissions(self):
+    resp_dict = self._root.get('%s/submission/history/all' % API_VERSION, headers=self.headers)
+    submissions = [Submission.from_dict(submission_dict) for submission_dict in resp_dict['all']]
+    return submissions
+
+  def set_user(self, user):
+    self._user = user
+
+  def set_language(self, language):
+    self._language = language

+ 190 - 0
apps/sqoop/src/sqoop/client/connection.py

@@ -0,0 +1,190 @@
+# 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 logging
+
+from exception import SqoopException
+from form import Form
+
+
+class Connection(object):
+  """
+  Sqoop connection object.
+
+  Example of sqoop connection dictionary received by server: {
+    "id": -1,
+    "updated": 1371245829436,
+    "created": 1371245829436,
+    "name": "test1",
+    "connector": [
+      {
+        "id": 1,
+        "inputs": [
+          {
+            "id": 1,
+            "name": "connection.jdbcDriver",
+            "value": "org.apache.derby.jdbc.EmbeddedDriver",
+            "type": "STRING",
+            "size": 128,
+            "sensitive": false
+          },
+          {
+            "id": 2,
+            "name": "connection.connectionString",
+            "value": "jdbc%3Aderby%3A%2Ftmp%2Ftest",
+            "type": "STRING",
+            "size": 128,
+            "sensitive": false
+          },
+          {
+            "id": 3,
+            "name": "connection.username",
+            "type": "STRING",
+            "size": 40,
+            "sensitive": false
+          },
+          {
+            "id": 4,
+            "name": "connection.password",
+            "type": "STRING",
+            "size": 40,
+            "sensitive": true
+          },
+          {
+            "id": 5,
+            "name": "connection.jdbcProperties",
+            "type": "MAP",
+            "sensitive": false
+          }
+        ],
+        "name": "connection",
+        "type": "CONNECTION"
+      }
+    ],
+    "connector-id": 1,
+    "framework": [
+      {
+        "id": 4,
+        "inputs": [
+          {
+            "id": 16,
+            "name": "security.maxConnections",
+            "type": "INTEGER",
+            "sensitive": false
+          }
+        ],
+        "name": "security",
+        "type": "CONNECTION"
+      }
+    ]
+  }
+
+  Some of the key-value pairs are structured and others are not.
+  For example, every connection will have a name, id, and connector-id key,
+  but the values of the ``connector`` key will vary given the chosen connector.
+  The same is true for the ``framework`` key.
+
+  The connection object will have a single framework component
+  and a single connector, for the moment.
+
+  @see sqoop.client.form for more information on unstructured forms in sqoop.
+  """
+  SKIP = ('id', 'created', 'updated')
+
+  def __init__(self, name, connector_id, connector=None, framework=None, **kwargs):
+    self.id = kwargs.setdefault('id', -1)
+    self.created = kwargs.setdefault('created', 0)
+    self.updated = kwargs.setdefault('updated', 0)
+    self.name = name
+    self.connector_id = connector_id
+    self.connector = connector
+    self.framework = framework
+
+  @staticmethod
+  def from_dict(connection_dict):
+    connection_dict.setdefault('connector', [])
+    connection_dict['connector'] = [ Form.from_dict(con_form_dict) for con_form_dict in connection_dict['connector'] ]
+
+    connection_dict.setdefault('framework', [])
+    connection_dict['framework'] = [ Form.from_dict(framework_form_dict) for framework_form_dict in connection_dict['framework'] ]
+
+    if not 'connector_id' in connection_dict:
+      connection_dict['connector_id'] = connection_dict.setdefault('connector-id', -1)
+
+    return Connection(**connection_dict)
+
+  def to_dict(self):
+    d = {
+      'id': self.id,
+      'name': self.name,
+      'created': self.created,
+      'updated': self.updated,
+      'connector-id': self.connector_id,
+      'connector': [ connector.to_dict() for connector in self.connector ],
+      'framework': [ framework.to_dict() for framework in self.framework ]
+    }
+    return d
+
+  def update_from_dict(self, connection_dict):
+    self.update(Connection.from_dict(connection_dict))
+
+  def update(self, connection):
+    for key in self.__dict__:
+      if key not in Connection.SKIP:
+        if hasattr(connection, key):
+          setattr(self, key, getattr(connection, key))
+
+
+class SqoopConnectionException(SqoopException):
+  """
+  This is what the server generally responds with:
+  {
+    "connector": {
+      "status": "UNACCEPTABLE",
+      "messages": {
+        "connection": {
+          "message": "Can't connect to the database with given credentials: No suitable driver found for test",
+          "status": "ACCEPTABLE"
+        },
+        "connection.connectionString": {
+          "message": "This do not seem as a valid JDBC URL",
+          "status": "UNACCEPTABLE"
+        }
+      }
+    },
+    "framework": {
+      "status": "FINE",
+      "messages": {}
+    }
+  }
+  """
+  def __init__(self, connector, framework):
+    self.connector = connector
+    self.framework = framework
+
+  @classmethod
+  def from_dict(cls, error_dict):
+    return SqoopConnectionException(**error_dict)
+
+  def to_dict(self):
+    return {
+      'connector': self.connector,
+      'framework': self.framework
+    }
+
+  def __str__(self):
+    return 'Connector: %s\nFramework: %s\n' % (self.connector, self.framework)
+

+ 257 - 0
apps/sqoop/src/sqoop/client/connector.py

@@ -0,0 +1,257 @@
+# 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 logging
+
+from form import Form
+
+
+class Connector(object):
+  """
+  Sqoop connector object.
+
+  Example of sqoop connector dictionary received by server: {
+      "id": 1,
+      "name": "generic-jdbc-connector",
+      "class": "org.apache.sqoop.connector.jdbc.GenericJdbcConnector",
+      "job-forms": {
+        "IMPORT": [
+          {
+            "id": 3,
+            "inputs": [
+              {
+                "id": 10,
+                "name": "table.schemaName",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              },
+              {
+                "id": 11,
+                "name": "table.tableName",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              },
+              {
+                "id": 12,
+                "name": "table.sql",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              },
+              {
+                "id": 13,
+                "name": "table.columns",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              },
+              {
+                "id": 14,
+                "name": "table.partitionColumn",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              },
+              {
+                "id": 15,
+                "name": "table.boundaryQuery",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              }
+            ],
+            "name": "table",
+            "type": "CONNECTION"
+          }
+        ],
+        "EXPORT": [
+          {
+            "id": 2,
+            "inputs": [
+              {
+                "id": 6,
+                "name": "table.schemaName",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              },
+              {
+                "id": 7,
+                "name": "table.tableName",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              },
+              {
+                "id": 8,
+                "name": "table.sql",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              },
+              {
+                "id": 9,
+                "name": "table.columns",
+                "type": "STRING",
+                "size": 50,
+                "sensitive": false
+              }
+            ],
+            "name": "table",
+            "type": "CONNECTION"
+          }
+        ]
+      },
+      "con-forms": [
+        {
+          "id": 1,
+          "inputs": [
+            {
+              "id": 1,
+              "name": "connection.jdbcDriver",
+              "type": "STRING",
+              "size": 128,
+              "sensitive": false
+            },
+            {
+              "id": 2,
+              "name": "connection.connectionString",
+              "type": "STRING",
+              "size": 128,
+              "sensitive": false
+            },
+            {
+              "id": 3,
+              "name": "connection.username",
+              "type": "STRING",
+              "size": 40,
+              "sensitive": false
+            },
+            {
+              "id": 4,
+              "name": "connection.password",
+              "type": "STRING",
+              "size": 40,
+              "sensitive": true
+            },
+            {
+              "id": 5,
+              "name": "connection.jdbcProperties",
+              "type": "MAP",
+              "sensitive": false
+            }
+          ],
+          "name": "connection",
+          "type": "CONNECTION"
+        }
+      ],
+      "version": "2.0.0-SNAPSHOT"
+    }
+
+  Some of the key-value pairs are structured and others are not.
+  For example, every connector will have a name, id, version, class,
+  con-forms, and job-forms key, but the values of the ``con-forms``
+  and ``job-forms`` keys will vary given the connector.
+
+  The ``job-forms`` and ``con-forms`` keys hold forms.
+  The ``job-forms`` key will hold 2 sets of forms: IMPORT and EXPORT.
+
+  Connector APIs will also return a resource dictionary: {
+    "1": {
+      "ignored.label": "Ignored",
+      "table.partitionColumn.help": "A specific column for data partition",
+      "table.label": "Database configuration",
+      "table.boundaryQuery.label": "Boundary query",
+      "ignored.help": "This is completely ignored",
+      "ignored.ignored.label": "Ignored",
+      "connection.jdbcProperties.help": "Enter any JDBC properties that should be supplied during the creation of connection.",
+      "table.tableName.help": "Table name to process data in the remote database",
+      "connection.username.help": "Enter the username to be used for connecting to the database.",
+      "connection.jdbcDriver.label": "JDBC Driver Class",
+      "table.help": "You must supply the information requested in order to create a job object.",
+      "table.partitionColumn.label": "Partition column name",
+      "ignored.ignored.help": "This is completely ignored",
+      "table.warehouse.label": "Data warehouse",
+      "table.boundaryQuery.help": "The boundary query for data partition",
+      "connection.username.label": "Username",
+      "connection.jdbcDriver.help": "Enter the fully qualified class name of the JDBC driver that will be used for establishing this connection.",
+      "connection.label": "Connection configuration",
+      "table.columns.label": "Table column names",
+      "connection.password.label": "Password",
+      "table.warehouse.help": "The root directory for data",
+      "table.dataDirectory.label": "Data directory",
+      "table.sql.label": "Table SQL statement",
+      "table.sql.help": "SQL statement to process data in the remote database",
+      "table.schemaName.help": "Schema name to process data in the remote database",
+      "connection.jdbcProperties.label": "JDBC Connection Properties",
+      "table.columns.help": "Specific columns of a table name or a table SQL",
+      "connection.connectionString.help": "Enter the value of JDBC connection string to be used by this connector for creating connections.",
+      "table.schemaName.label": "Schema name",
+      "table.dataDirectory.help": "The sub-directory under warehouse for data",
+      "connection.connectionString.label": "JDBC Connection String",
+      "connection.help": "You must supply the information requested in order to create a connection object.",
+      "connection.password.help": "Enter the password to be used for connecting to the database.",
+      "table.tableName.label": "Table name"
+    }
+  }
+
+  The keys of the dictionary are the IDs of the connector.
+  The keys of each entry are names associated with inputs in connector forms.
+
+  @see sqoop.client.form for more information on unstructured forms in sqoop.
+  """
+  def __init__(self, id, name, version, job_forms, con_forms, resources={}, **kwargs):
+    self.id = id
+    self.name = name
+    self.version = version
+    self.job_forms = job_forms
+    self.con_forms = con_forms
+    self.resources = resources
+    setattr(self, 'class', kwargs['class'])
+
+  @staticmethod
+  def from_dict(connector_dict, resources_dict={}):
+    connector_dict.setdefault('job-forms', {})
+    connector_dict['job_forms'] = {}
+    if 'IMPORT' in connector_dict['job-forms']:
+      connector_dict['job_forms']['IMPORT'] = [ Form.from_dict(job_form_dict) for job_form_dict in connector_dict['job-forms']['IMPORT'] ]
+    if 'EXPORT' in connector_dict['job-forms']:
+      connector_dict['job_forms']['EXPORT'] = [ Form.from_dict(job_form_dict) for job_form_dict in connector_dict['job-forms']['EXPORT'] ]
+
+    connector_dict.setdefault('con-forms', [])
+    connector_dict['con_forms'] = [ Form.from_dict(con_form_dict) for con_form_dict in connector_dict['con-forms'] ]
+
+    connector_dict['resources'] = resources_dict.setdefault(unicode(connector_dict['id']), {})
+
+    return Connector(**connector_dict)
+
+  def to_dict(self):
+    d = {
+      'id': self.id,
+      'name': self.name,
+      'version': self.version,
+      'class': getattr(self, 'class'),
+      'con-forms': [ con_form.to_dict() for con_form in self.con_forms ],
+      'job-forms': {},
+      'resources': self.resources
+    }
+    if 'IMPORT' in self.job_forms:
+      d['job-forms']['IMPORT'] = [ job_form.to_dict() for job_form in self.job_forms['IMPORT'] ]
+    if 'EXPORT' in self.job_forms:
+      d['job-forms']['EXPORT'] = [ job_form.to_dict() for job_form in self.job_forms['EXPORT'] ]
+    return d

+ 23 - 0
apps/sqoop/src/sqoop/client/exception.py

@@ -0,0 +1,23 @@
+# 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 logging
+
+from form import Form
+
+
+class SqoopException(Exception):
+  pass

+ 142 - 0
apps/sqoop/src/sqoop/client/form.py

@@ -0,0 +1,142 @@
+# 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 logging
+
+
+class Form(object):
+  """
+  Represents a form in sqoop.
+
+  Example sqoop form dictionary received by server: [
+    {
+      "id": 1,
+      "inputs": [
+        {
+          "id": 1,
+          "name": "connection.jdbcDriver",
+          "value": "org.apache.derby.jdbc.EmbeddedDriver",
+          "type": "STRING",
+          "size": 128,
+          "sensitive": false
+        },
+        {
+          "id": 2,
+          "name": "connection.connectionString",
+          "value": "jdbc%3Aderby%3A%2Ftmp%2Ftest",
+          "type": "STRING",
+          "size": 128,
+          "sensitive": false
+        },
+        {
+          "id": 3,
+          "name": "connection.username",
+          "type": "STRING",
+          "size": 40,
+          "sensitive": false
+        },
+        {
+          "id": 4,
+          "name": "connection.password",
+          "type": "STRING",
+          "size": 40,
+          "sensitive": true
+        },
+        {
+          "id": 5,
+          "name": "connection.jdbcProperties",
+          "type": "MAP",
+          "sensitive": false
+        }
+      ],
+      "name": "connection",
+      "type": "CONNECTION"
+    }
+  ],
+
+  These forms are relatively unstructured. They will always have an ID, name, type, and inputs.
+  The number of inputs will vary.
+  Their definitions are dynamically generated from  annotations on classes in sqoop.
+  The ID identifies the form in the sqoop metadata reprository.
+  The ID could vary.
+  The ID is unique per type.
+  """
+  def __init__(self, id, name, type, inputs=[]):
+    self.id = id
+    self.name = name
+    self.type = type
+    self.inputs = inputs
+
+  @staticmethod
+  def from_dict(form_dict):
+    form_dict['inputs'] = [Input.from_dict(input_dict) for input_dict in form_dict.setdefault('inputs', [])]
+    return Form(**form_dict)
+
+  def to_dict(self):
+    return {
+      'id': self.id,
+      'type': self.type,
+      'name': self.name,
+      'inputs': [input.to_dict() for input in self.inputs]
+    }
+
+
+class Input(object):
+  """
+  Represents an input in a sqoop form.
+
+  Example sqoop input dictionary received by server: {
+    "id": 2,
+    "name": "connection.connectionString",
+    "values": "jdbc%3Aderby%3A%2Ftmp%2Ftest",
+    "type": "STRING",
+    "size": 128,
+    "sensitive": false
+  }
+
+  The ID identifies the input in the sqoop metadata repository.
+  The ID could vary.
+  The ID is unique per type and per form.
+  """
+  def __init__(self, id, type, name, value=None, values=None, sensitive=False, size=-1):
+    self.id = id
+    self.type = type
+    self.name = name
+    self.value = value
+    self.values = values
+    self.sensitive = sensitive
+    self.size = size
+
+  @staticmethod
+  def from_dict(input_dict):
+    if 'values' in input_dict and isinstance(input_dict['values'], basestring):
+      input_dict['values'] = input_dict['values'].split(',')
+    return Input(**input_dict)
+
+  def to_dict(self):
+    d = {
+      'id': self.id,
+      'type': self.type,
+      'name': self.name,
+      'sensitive': self.sensitive
+    }
+    if self.value:
+      d['value'] = self.value
+    if self.values:
+      d['values'] = ','.join(self.values)
+    if self.size != -1:
+      d['size'] = self.size
+    return d

+ 197 - 0
apps/sqoop/src/sqoop/client/framework.py

@@ -0,0 +1,197 @@
+# 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 logging
+
+from form import Form
+
+
+class Framework(object):
+  """
+  Sqoop framework object.
+
+  Example of sqoop framework dictionary received by server: {
+    "id": 1,
+    "resources": {
+      "output.label": "Output configuration",
+      "security.maxConnections.help": "Maximal number of connections that this connection object can use at one point in time",
+      "output.storageType.label": "Storage type",
+      "output.ignored.help": "This value is ignored",
+      "input.label": "Input configuration",
+      "security.help": "You must supply the information requested in order to create a job object.",
+      "output.storageType.help": "Target on Hadoop ecosystem where to store data",
+      "input.inputDirectory.help": "Directory that should be exported",
+      "output.outputFormat.label": "Output format",
+      "output.ignored.label": "Ignored",
+      "output.outputFormat.help": "Format in which data should be serialized",
+      "output.help": "You must supply the information requested in order to get information where you want to store your data.",
+      "throttling.help": "Set throttling boundaries to not overload your systems",
+      "input.inputDirectory.label": "Input directory",
+      "throttling.loaders.label": "Loaders",
+      "input.help": "Specifies information required to get data from Hadoop ecosystem",
+      "throttling.extractors.label": "Extractors",
+      "throttling.extractors.help": "Number of extractors that Sqoop will use",
+      "security.label": "Security related configuration options",
+      "throttling.label": "Throttling resources",
+      "throttling.loaders.help": "Number of loaders that Sqoop will use",
+      "output.outputDirectory.help": "Output directory for final data",
+      "security.maxConnections.label": "Max connections",
+      "output.outputDirectory.label": "Output directory"
+    },
+    "job-forms": {
+      "IMPORT": [
+        {
+          "id": 7,
+          "inputs": [
+            {
+              "id": 20,
+              "values": "HDFS",
+              "name": "output.storageType",
+              "type": "ENUM",
+              "sensitive": false
+            },
+            {
+              "id": 21,
+              "values": "TEXT_FILE,SEQUENCE_FILE",
+              "name": "output.outputFormat",
+              "type": "ENUM",
+              "sensitive": false
+            },
+            {
+              "id": 22,
+              "name": "output.outputDirectory",
+              "type": "STRING",
+              "size": 255,
+              "sensitive": false
+            }
+          ],
+          "name": "output",
+          "type": "CONNECTION"
+        },
+        {
+          "id": 8,
+          "inputs": [
+            {
+              "id": 23,
+              "name": "throttling.extractors",
+              "type": "INTEGER",
+              "sensitive": false
+            },
+            {
+              "id": 24,
+              "name": "throttling.loaders",
+              "type": "INTEGER",
+              "sensitive": false
+            }
+          ],
+          "name": "throttling",
+          "type": "CONNECTION"
+        }
+      ],
+      "EXPORT": [
+        {
+          "id": 5,
+          "inputs": [
+            {
+              "id": 17,
+              "name": "input.inputDirectory",
+              "type": "STRING",
+              "size": 255,
+              "sensitive": false
+            }
+          ],
+          "name": "input",
+          "type": "CONNECTION"
+        },
+        {
+          "id": 6,
+          "inputs": [
+            {
+              "id": 18,
+              "name": "throttling.extractors",
+              "type": "INTEGER",
+              "sensitive": false
+            },
+            {
+              "id": 19,
+              "name": "throttling.loaders",
+              "type": "INTEGER",
+              "sensitive": false
+            }
+          ],
+          "name": "throttling",
+          "type": "CONNECTION"
+        }
+      ]
+    },
+    "con-forms": [
+      {
+        "id": 4,
+        "inputs": [
+          {
+            "id": 16,
+            "name": "security.maxConnections",
+            "type": "INTEGER",
+            "sensitive": false
+          }
+        ],
+        "name": "security",
+        "type": "CONNECTION"
+      }
+    ]
+  }
+
+  The ``job-forms`` and ``con-forms`` keys hold forms.
+  The ``job-forms`` key will hold 2 sets of forms: IMPORT and EXPORT.
+  ``id`` is for look up in metadata repository.
+
+  The framework API will return resource information.
+  The keys are names associated with inputs in the various forms.
+
+  @see sqoop.client.form for more information on unstructured forms in sqoop.
+  """
+  def __init__(self, id, job_forms, con_forms, resources, **kwargs):
+    self.id = id
+    self.job_forms = job_forms
+    self.con_forms = con_forms
+    self.resources = resources
+
+  @staticmethod
+  def from_dict(framework_dict):
+    framework_dict.setdefault('job-forms', {})
+    framework_dict['job_forms'] = {}
+    if 'IMPORT' in framework_dict['job-forms']:
+      framework_dict['job_forms']['IMPORT'] = [ Form.from_dict(job_form_dict) for job_form_dict in framework_dict['job-forms']['IMPORT'] ]
+    if 'EXPORT' in framework_dict['job-forms']:
+      framework_dict['job_forms']['EXPORT'] = [ Form.from_dict(job_form_dict) for job_form_dict in framework_dict['job-forms']['EXPORT'] ]
+
+    framework_dict.setdefault('con-forms', [])
+    framework_dict['con_forms'] = [ Form.from_dict(con_form_dict) for con_form_dict in framework_dict['con-forms'] ]
+
+    return Framework(**framework_dict)
+
+  def to_dict(self):
+    d = {
+      'id': self.id,
+      'con-forms': [ con_form.to_dict() for con_form in self.con_forms ],
+      'job-forms': {},
+      'resources': self.resources
+    }
+    if 'IMPORT' in self.job_forms:
+      d['job-forms']['IMPORT'] = [ job_form.to_dict() for job_form in self.job_forms['IMPORT'] ]
+    if 'EXPORT' in self.job_forms:
+      d['job-forms']['EXPORT'] = [ job_form.to_dict() for job_form in self.job_forms['EXPORT'] ]
+    return d

+ 245 - 0
apps/sqoop/src/sqoop/client/job.py

@@ -0,0 +1,245 @@
+# 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 logging
+
+from exception import SqoopException
+from form import Form
+
+
+class Job(object):
+  """
+  Sqoop job object.
+
+  Example of sqoop job dictionary received by server: {
+    "connection-id": 1,
+    "id": 1,
+    "updated": 1371246055277,
+    "created": 1371246055277,
+    "name": "import1",
+    "connector": [
+      {
+        "id": 3,
+        "inputs": [
+          {
+            "id": 10,
+            "name": "table.schemaName",
+            "type": "STRING",
+            "size": 50,
+            "sensitive": false
+          },
+          {
+            "id": 11,
+            "name": "table.tableName",
+            "value": "derbyDB",
+            "type": "STRING",
+            "size": 50,
+            "sensitive": false
+          },
+          {
+            "id": 12,
+            "name": "table.sql",
+            "type": "STRING",
+            "size": 50,
+            "sensitive": false
+          },
+          {
+            "id": 13,
+            "name": "table.columns",
+            "value": "addr",
+            "type": "STRING",
+            "size": 50,
+            "sensitive": false
+          },
+          {
+            "id": 14,
+            "name": "table.partitionColumn",
+            "value": "num",
+            "type": "STRING",
+            "size": 50,
+            "sensitive": false
+          },
+          {
+            "id": 15,
+            "name": "table.boundaryQuery",
+            "type": "STRING",
+            "size": 50,
+            "sensitive": false
+          }
+        ],
+        "name": "table",
+        "type": "CONNECTION"
+      }
+    ],
+    "connector-id": 1,
+    "type": "IMPORT",
+    "framework": [
+      {
+        "id": 7,
+        "inputs": [
+          {
+            "id": 20,
+            "values": "HDFS",
+            "name": "output.storageType",
+            "value": "HDFS",
+            "type": "ENUM",
+            "sensitive": false
+          },
+          {
+            "id": 21,
+            "values": "TEXT_FILE,SEQUENCE_FILE",
+            "name": "output.outputFormat",
+            "value": "TEXT_FILE",
+            "type": "ENUM",
+            "sensitive": false
+          },
+          {
+            "id": 22,
+            "name": "output.outputDirectory",
+            "value": "%2Ftmp%2Fimport1-out",
+            "type": "STRING",
+            "size": 255,
+            "sensitive": false
+          }
+        ],
+        "name": "output",
+        "type": "CONNECTION"
+      },
+      {
+        "id": 8,
+        "inputs": [
+          {
+            "id": 23,
+            "name": "throttling.extractors",
+            "type": "INTEGER",
+            "sensitive": false
+          },
+          {
+            "id": 24,
+            "name": "throttling.loaders",
+            "type": "INTEGER",
+            "sensitive": false
+          }
+        ],
+        "name": "throttling",
+        "type": "CONNECTION"
+      }
+    ]
+  }
+
+  Some of the key-value pairs are structured and others are not.
+  For example, every job will have a name, id, type, connection-id, and connector-id key,
+  but the values of the ``connector`` and ``connection`` keys
+  will vary given the chosen connector and connection.
+  The same is true for the ``framework`` key.
+
+  The job object will have two framework components
+  and a single connector, for the moment.
+
+  @see sqoop.client.form for more information on unstructured forms in sqoop.
+  """
+  SKIP = ('id', 'created', 'updated')
+
+  def __init__(self, type, name, connection_id, connector_id, connector=None, framework=None, **kwargs):
+    self.id = kwargs.setdefault('id', -1)
+    self.created = kwargs.setdefault('created', 0)
+    self.updated = kwargs.setdefault('updated', 0)
+    self.type = type
+    self.name = name
+    self.connection_id = connection_id
+    self.connector_id = connector_id
+    self.connector = connector
+    self.framework = framework
+
+  @staticmethod
+  def from_dict(job_dict):
+    job_dict.setdefault('connector', [])
+    job_dict['connector'] = [ Form.from_dict(con_form_dict) for con_form_dict in job_dict['connector'] ]
+
+    job_dict.setdefault('framework', [])
+    job_dict['framework'] = [ Form.from_dict(framework_form_dict) for framework_form_dict in job_dict['framework'] ]
+
+    if not 'connection_id' in job_dict:
+      job_dict['connection_id'] = job_dict['connection-id']
+
+    if not 'connector_id' in job_dict:
+      job_dict['connector_id'] = job_dict['connector-id']
+
+    return Job(**job_dict)
+
+  def to_dict(self):
+    d = {
+      'id': self.id,
+      'type': self.type,
+      'name': self.name,
+      'created': self.created,
+      'updated': self.updated,
+      'connection-id': self.connection_id,
+      'connector-id': self.connector_id,
+      'connector': [ connector.to_dict() for connector in self.connector ],
+      'framework': [ framework.to_dict() for framework in self.framework ]
+    }
+    return d
+
+  def update_from_dict(self, job_dict):
+    self.update(Job.from_dict(job_dict))
+
+  def update(self, job):
+    for key in self.__dict__:
+      if key not in Job.SKIP:
+        setattr(self, key, getattr(job, key, getattr(self, key)))
+
+class SqoopJobException(SqoopException):
+  """
+  This is what the sqoop server generally responds with:
+  {
+    "connector": {
+      "status": "UNACCEPTABLE",
+      "messages": {
+        "table": {
+          "message": "Either table name or SQL must be specified",
+          "status": "UNACCEPTABLE"
+        }
+      }
+    },
+    "framework": {
+      "status": "UNACCEPTABLE",
+      "messages": {
+        "output.outputDirectory": {
+          "message": "Output directory is empty",
+          "status": "UNACCEPTABLE"
+        }
+      }
+    }
+  }
+  """
+  def __init__(self, connector, framework):
+    self.connector = connector
+    self.framework = framework
+
+  @classmethod
+  def from_dict(cls, error_dict):
+    return SqoopJobException(**error_dict)
+
+  def to_dict(self):
+    return {
+      'connector': self.connector,
+      'framework': self.framework
+    }
+
+  def __str__(self):
+    return 'Connector: %s\nFramework: %s\n' % (self.connector, self.framework)
+

+ 104 - 0
apps/sqoop/src/sqoop/client/resource.py

@@ -0,0 +1,104 @@
+# 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.
+
+try:
+  import json
+except ImportError:
+  import simplejson as json
+
+from django.utils.translation import ugettext as _
+
+from desktop.lib.rest.resource import Resource
+
+
+class SqoopResource(Resource):
+  """
+  Sqoop resources provide extra response headers.
+  @see desktop.lib.rest.resource
+  """
+
+  def invoke(self, method, relpath=None, params=None, data=None, headers=None):
+    """
+    Invoke an API method.
+    Look for sqoop-error-code and sqoop-error-message.
+    @return: Raw body or JSON dictionary (if response content type is JSON).
+    """
+    path = self._join_uri(relpath)
+    resp = self._client.execute(method,
+                                path,
+                                params=params,
+                                data=data,
+                                headers=headers)
+    body = self._get_body(resp)
+
+    if resp.getcode() == 200:
+      self._client.logger.debug(
+          "%(method)s Got response:\n%(headers)s\n%(body)s" % {
+            'method': method,
+            'headers': resp.headers,
+            'body': body
+      })
+      # Sqoop always uses json
+      return self._format_body(resp, body)
+    else:
+      # Body will probably be a JSON formatted stacktrace
+      body = self._format_body(resp, body)
+      msg_format = "%(method)s Sqoop Error (%s): %s\n\t%s"
+      args = (resp.headers['sqoop-error-code'], resp.headers['sqoop-error-message'], body)
+      self._client.logger.error(msg_format % args)
+      raise IOError(_(msg_format) % args)
+
+  def delete(self, relpath=None, params=None, headers=None):
+    """
+    Invoke the DELETE method on a resource.
+    @param relpath: Optional. A relative path to this resource's path.
+    @param params: Key-value data.
+
+    @return: A dictionary of the JSON result.
+    """
+    return self.invoke("DELETE", relpath, params, None, headers)
+
+
+  def post(self, relpath=None, params=None, data=None, headers=None):
+    """
+    Invoke the POST method on a resource.
+    @param relpath: Optional. A relative path to this resource's path.
+    @param params: Key-value data.
+    @param data: Optional. Body of the request.
+    @param contenttype: Optional.
+
+    @return: A dictionary of the JSON result.
+    """
+    return self.invoke("POST", relpath, params, data, headers)
+
+
+  def put(self, relpath=None, params=None, data=None, headers=None):
+    """
+    Invoke the PUT method on a resource.
+    @param relpath: Optional. A relative path to this resource's path.
+    @param params: Key-value data.
+    @param data: Optional. Body of the request.
+    @param contenttype: Optional.
+
+    @return: A dictionary of the JSON result.
+    """
+    return self.invoke("PUT", relpath, params, data, headers)
+
+
+  def _make_headers(self, contenttype=None):
+    if contenttype:
+      return { 'Content-Type': contenttype }
+    return None

文件差异内容过多而无法显示
+ 82 - 0
apps/sqoop/src/sqoop/client/submission.py


+ 26 - 0
apps/sqoop/src/sqoop/conf.py

@@ -0,0 +1,26 @@
+#!/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.
+
+from django.utils.translation import ugettext as _, ugettext_lazy as _t
+
+from desktop.lib.conf import Config
+
+
+SERVER_URL = Config(
+  key="server_url",
+  default='http://localhost:12000/sqoop',
+  help=_t("The sqoop server URL."))

+ 16 - 0
apps/sqoop/src/sqoop/forms.py

@@ -0,0 +1,16 @@
+#!/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.

+ 376 - 0
apps/sqoop/src/sqoop/locale/de/LC_MESSAGES/django.po

@@ -0,0 +1,376 @@
+# German translations for Hue.
+# Copyright (C) 2012 Cloudera
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: 2012-07-30 18:56-0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: de <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+
+#~ msgid ""
+#~ "bin/hadoop return non-zero %(retcode)d "
+#~ "while trying to merge credentials."
+#~ msgstr ""
+#~ "bin/hadoop gibt Nicht-Null %(retcode)d "
+#~ "zurück beim Versuch, Benutzerinformationen "
+#~ "zusammenzuführen."
+
+#~ msgid "Hue Shell"
+#~ msgstr "Hue-Shell"
+
+#~ msgid ""
+#~ "Failed to create a shell of the"
+#~ " given type. The possible reasons for"
+#~ " this are:"
+#~ msgstr ""
+#~ "Eine Shell des gegebenen Typs konnte "
+#~ "nicht erstellt werden. Die möglichen "
+#~ "Gründe dafür sind:"
+
+#~ msgid "The system is out of PTYs."
+#~ msgstr "Das System hat keine PTYs mehr."
+
+#~ msgid "The system cannot create more subprocesses."
+#~ msgstr "Das System kann keine weiteren Subprozesse erstellen."
+
+#~ msgid "You do not have permission to create shells of this type."
+#~ msgstr ""
+#~ "Sie haben nicht die erforderlichen "
+#~ "Rechte, um Shells dieser Art zu "
+#~ "erstellen."
+
+#~ msgid "There is no shell with that name."
+#~ msgstr "Es gibt keine Shell mit diesem Namen."
+
+#~ msgid "There is no Unix user account for you."
+#~ msgstr "Es gibt kein Unix-Benutzerkonto für Sie."
+
+#~ msgid "Select one of the available shells from the toolbar above."
+#~ msgstr "Wählen Sie eine der verfügbaren Shells aus der Symbolleiste oben aus."
+
+#~ msgid ""
+#~ "You do not have permission to "
+#~ "access any shell or there is no"
+#~ " configured shell."
+#~ msgstr ""
+#~ "Sie haben nicht die erforderlichen "
+#~ "Rechte, um auf eine Shell zuzugreifen,"
+#~ " oder es gibt keine konfigurierte "
+#~ "Shell."
+
+#~ msgid "The shell is not present on the Hue host."
+#~ msgstr "Die Shell ist auf dem Hue-Host nicht vorhanden."
+
+#~ msgid ""
+#~ "The Shell application requires a Unix"
+#~ " user account for every user of "
+#~ "Hue on the remote web server."
+#~ msgstr ""
+#~ "Die Shell-Anwendung erfordert ein "
+#~ "Unix-Benutzerkonto für jeden Benutzer von"
+#~ " Hue auf dem Remote-Webserver."
+
+#~ msgid ""
+#~ "Ask your administrator to create a "
+#~ "user account for you on the remote"
+#~ " web server as described in the "
+#~ "Shell documentation."
+#~ msgstr ""
+#~ "Bitten Sie Ihren Administrator, für Sie"
+#~ " ein Benutzerkonto auf dem Remote-"
+#~ "Webserver zu erstellen, wie in der "
+#~ "Shell-Dokumentation beschrieben."
+
+#~ msgid ""
+#~ "The webserver currently running Hue does"
+#~ " not support the Shell application. "
+#~ "Please contact your administrator."
+#~ msgstr ""
+#~ "Der Webserver, auf dem derzeit Hue "
+#~ "ausgeführt wird, unterstützt die Shell-"
+#~ "Anwendung nicht. Wenden Sie sich an "
+#~ "Ihren Administrator."
+

+ 292 - 0
apps/sqoop/src/sqoop/locale/en_US.pot

@@ -0,0 +1,292 @@
+# Translations template for Hue.
+# Copyright (C) 2013 Cloudera, Inc
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Hue VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+

+ 376 - 0
apps/sqoop/src/sqoop/locale/es/LC_MESSAGES/django.po

@@ -0,0 +1,376 @@
+# Spanish translations for Hue.
+# Copyright (C) 2012 Cloudera
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: 2012-07-30 18:56-0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: es <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+
+#~ msgid ""
+#~ "bin/hadoop return non-zero %(retcode)d "
+#~ "while trying to merge credentials."
+#~ msgstr ""
+#~ "bin/hadoop devuelve %(retcode)d que no "
+#~ "es cero al intentar fusionar "
+#~ "credenciales."
+
+#~ msgid "Hue Shell"
+#~ msgstr "Shell de Hue"
+
+#~ msgid ""
+#~ "Failed to create a shell of the"
+#~ " given type. The possible reasons for"
+#~ " this are:"
+#~ msgstr ""
+#~ "Error al crear un shell del tipo"
+#~ " determinado. Las posibles razones de "
+#~ "esto son:"
+
+#~ msgid "The system is out of PTYs."
+#~ msgstr "El sistema no tiene PTY suficientes."
+
+#~ msgid "The system cannot create more subprocesses."
+#~ msgstr "El sistema no puede crear más subprocesos."
+
+#~ msgid "You do not have permission to create shells of this type."
+#~ msgstr "No tiene permiso para crear shells de este tipo."
+
+#~ msgid "There is no shell with that name."
+#~ msgstr "No hay ningún shell con ese nombre."
+
+#~ msgid "There is no Unix user account for you."
+#~ msgstr "No hay ninguna cuenta de usuario de Unix para usted."
+
+#~ msgid "Select one of the available shells from the toolbar above."
+#~ msgstr ""
+#~ "Seleccione uno de los shells disponibles"
+#~ " de la barra de herramientas "
+#~ "anterior."
+
+#~ msgid ""
+#~ "You do not have permission to "
+#~ "access any shell or there is no"
+#~ " configured shell."
+#~ msgstr ""
+#~ "No tiene permiso para acceder a "
+#~ "ningún shell o no hay ningún shell"
+#~ " configurado."
+
+#~ msgid "The shell is not present on the Hue host."
+#~ msgstr "El shell no está presente en el host de Hue."
+
+#~ msgid ""
+#~ "The Shell application requires a Unix"
+#~ " user account for every user of "
+#~ "Hue on the remote web server."
+#~ msgstr ""
+#~ "La aplicación del shell necesita una "
+#~ "cuenta de usuario de Unix para "
+#~ "todos los usuarios de Hue en el"
+#~ " servidor web remoto."
+
+#~ msgid ""
+#~ "Ask your administrator to create a "
+#~ "user account for you on the remote"
+#~ " web server as described in the "
+#~ "Shell documentation."
+#~ msgstr ""
+#~ "Solicite al administrador que cree una"
+#~ " cuenta de usuario para usted en "
+#~ "el servidor web remoto como se "
+#~ "describe en la documentación del shell."
+
+#~ msgid ""
+#~ "The webserver currently running Hue does"
+#~ " not support the Shell application. "
+#~ "Please contact your administrator."
+#~ msgstr ""
+#~ "El servidor web que actualmente ejecuta"
+#~ " Hue no es compatible con la "
+#~ "aplicación del shell. Póngase en "
+#~ "contacto con su administrador."
+

+ 374 - 0
apps/sqoop/src/sqoop/locale/fr/LC_MESSAGES/django.po

@@ -0,0 +1,374 @@
+# French translations for Hue.
+# Copyright (C) 2012 Cloudera
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: 2012-07-30 18:56-0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: fr <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+
+#~ msgid ""
+#~ "bin/hadoop return non-zero %(retcode)d "
+#~ "while trying to merge credentials."
+#~ msgstr ""
+#~ "bin/hadoop renvoient %(retcode)d différent de"
+#~ " zéro lors de la tentative de "
+#~ "fusion des informations de connexion."
+
+#~ msgid "Hue Shell"
+#~ msgstr "Shell Hue"
+
+#~ msgid ""
+#~ "Failed to create a shell of the"
+#~ " given type. The possible reasons for"
+#~ " this are:"
+#~ msgstr ""
+#~ "Echec de création d'un shell du "
+#~ "type donné. Raisons possibles de cet "
+#~ "échec :"
+
+#~ msgid "The system is out of PTYs."
+#~ msgstr "Le système manque de PTY."
+
+#~ msgid "The system cannot create more subprocesses."
+#~ msgstr "Le système ne peut pas créer d'autres sous-processus."
+
+#~ msgid "You do not have permission to create shells of this type."
+#~ msgstr "Vous n'êtes pas autorisé à créer des shells de ce type."
+
+#~ msgid "There is no shell with that name."
+#~ msgstr "Aucun shell ne porte ce nom."
+
+#~ msgid "There is no Unix user account for you."
+#~ msgstr "Aucun compte utilisateur Unix ne vous a été attribué."
+
+#~ msgid "Select one of the available shells from the toolbar above."
+#~ msgstr ""
+#~ "Sélectionnez l'un des shells disponibles "
+#~ "dans la barre d'outils ci-dessus."
+
+#~ msgid ""
+#~ "You do not have permission to "
+#~ "access any shell or there is no"
+#~ " configured shell."
+#~ msgstr ""
+#~ "Vous n'êtes pas autorisé à accéder "
+#~ "à un shell, ou aucun shell n'est"
+#~ " configuré."
+
+#~ msgid "The shell is not present on the Hue host."
+#~ msgstr "Absence de Shell sur l'hôte Hue."
+
+#~ msgid ""
+#~ "The Shell application requires a Unix"
+#~ " user account for every user of "
+#~ "Hue on the remote web server."
+#~ msgstr ""
+#~ "L'application Shell nécessite un compte "
+#~ "utilisateur Unix pour chaque utilisateur "
+#~ "Hue sur le serveur Web distant."
+
+#~ msgid ""
+#~ "Ask your administrator to create a "
+#~ "user account for you on the remote"
+#~ " web server as described in the "
+#~ "Shell documentation."
+#~ msgstr ""
+#~ "Demandez à votre administrateur de vous"
+#~ " créer un compte utilisateur sur le"
+#~ " serveur Web distant comme décrit "
+#~ "dans la documentation du Shell."
+
+#~ msgid ""
+#~ "The webserver currently running Hue does"
+#~ " not support the Shell application. "
+#~ "Please contact your administrator."
+#~ msgstr ""
+#~ "Le serveur Web qui exécute actuellement"
+#~ " Hue ne prend pas en charge "
+#~ "l'application Shell. Veuillez contacter votre"
+#~ " administrateur."
+

+ 352 - 0
apps/sqoop/src/sqoop/locale/ja/LC_MESSAGES/django.po

@@ -0,0 +1,352 @@
+# Japanese translations for Hue.
+# Copyright (C) 2012 Cloudera
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: 2012-07-30 18:56-0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: ja <LL@li.org>\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+
+#~ msgid ""
+#~ "bin/hadoop return non-zero %(retcode)d "
+#~ "while trying to merge credentials."
+#~ msgstr "bin/hadoop は、資格情報をマージしようとしている間に、ゼロではない %(retcode)d を返します。"
+
+#~ msgid "Hue Shell"
+#~ msgstr "Hue シェル"
+
+#~ msgid ""
+#~ "Failed to create a shell of the"
+#~ " given type. The possible reasons for"
+#~ " this are:"
+#~ msgstr "指定されたタイプのシェルを作成できませんでした。考えられる原因は次のとおりです。"
+
+#~ msgid "The system is out of PTYs."
+#~ msgstr "システムが PTY 外です。"
+
+#~ msgid "The system cannot create more subprocesses."
+#~ msgstr "サブプロセスをこれ以上作成できません。"
+
+#~ msgid "You do not have permission to create shells of this type."
+#~ msgstr "このタイプのシェルを作成するパーミッションがありません。"
+
+#~ msgid "There is no shell with that name."
+#~ msgstr "指定された名前のシェルがありません。"
+
+#~ msgid "There is no Unix user account for you."
+#~ msgstr "使用できる UNIX ユーザーアカウントがありません。"
+
+#~ msgid "Select one of the available shells from the toolbar above."
+#~ msgstr "上のツールバーにある使用可能なシェルの中から 1 つ選択してください。"
+
+#~ msgid ""
+#~ "You do not have permission to "
+#~ "access any shell or there is no"
+#~ " configured shell."
+#~ msgstr "どのシェルにもアクセスするパーミッションがないか、シェルが 1 つも設定されていません。"
+
+#~ msgid "The shell is not present on the Hue host."
+#~ msgstr "シェルが Hue ホストに存在しません。"
+
+#~ msgid ""
+#~ "The Shell application requires a Unix"
+#~ " user account for every user of "
+#~ "Hue on the remote web server."
+#~ msgstr "このシェルアプリケーションでは、リモート Web サーバー上にある Hue のすべてのユーザーに Unix のユーザーアカウントが必要です。"
+
+#~ msgid ""
+#~ "Ask your administrator to create a "
+#~ "user account for you on the remote"
+#~ " web server as described in the "
+#~ "Shell documentation."
+#~ msgstr "シェルのマニュアルに従ってリモート Web サーバー上にユーザーアカウントを作成することを担当の管理者に依頼してください。"
+
+#~ msgid ""
+#~ "The webserver currently running Hue does"
+#~ " not support the Shell application. "
+#~ "Please contact your administrator."
+#~ msgstr "現在 Hue を実行している Web サーバーは、Shell アプリケーションをサポートしていません。管理者に問い合わせてください。"
+

+ 352 - 0
apps/sqoop/src/sqoop/locale/ko/LC_MESSAGES/django.po

@@ -0,0 +1,352 @@
+# Korean translations for Hue.
+# Copyright (C) 2012 Cloudera
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: 2012-07-30 18:56-0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: ko <LL@li.org>\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+
+#~ msgid ""
+#~ "bin/hadoop return non-zero %(retcode)d "
+#~ "while trying to merge credentials."
+#~ msgstr "자격 증명을 병합하는 동안 bin/hadoop이 0이 아닌 값 %(retcode)d을(를) 반환했습니다."
+
+#~ msgid "Hue Shell"
+#~ msgstr "Hue Shell"
+
+#~ msgid ""
+#~ "Failed to create a shell of the"
+#~ " given type. The possible reasons for"
+#~ " this are:"
+#~ msgstr "주어진 유형의 Shell을 생성하지 못했습니다. 가능한 원인:"
+
+#~ msgid "The system is out of PTYs."
+#~ msgstr "이 시스템이 PTY 밖에 있습니다."
+
+#~ msgid "The system cannot create more subprocesses."
+#~ msgstr "이 시스템은 추가 하위 프로세스를 생성할 수 없습니다."
+
+#~ msgid "You do not have permission to create shells of this type."
+#~ msgstr "이 유형의 Shell을 생성할 권한이 없습니다."
+
+#~ msgid "There is no shell with that name."
+#~ msgstr "이 이름을 갖는 Shell이 없습니다."
+
+#~ msgid "There is no Unix user account for you."
+#~ msgstr "귀하의 Unix 사용자 계정이 없습니다."
+
+#~ msgid "Select one of the available shells from the toolbar above."
+#~ msgstr "위 도구 모음에서 사용 가능한 Shell 하나를 선택하십시오."
+
+#~ msgid ""
+#~ "You do not have permission to "
+#~ "access any shell or there is no"
+#~ " configured shell."
+#~ msgstr "Shell에 대한 액세스 권한이 없거나 구성된 Shell이 없습니다."
+
+#~ msgid "The shell is not present on the Hue host."
+#~ msgstr "Shell이 Hue 호스트에 없습니다."
+
+#~ msgid ""
+#~ "The Shell application requires a Unix"
+#~ " user account for every user of "
+#~ "Hue on the remote web server."
+#~ msgstr "Shell 애플리케이션에서는 원격 웹 서버에 있는 Hue의 모든 사용자에 대한  Unix 사용자 계정이 필요합니다."
+
+#~ msgid ""
+#~ "Ask your administrator to create a "
+#~ "user account for you on the remote"
+#~ " web server as described in the "
+#~ "Shell documentation."
+#~ msgstr "Shell 설명서에 나와 있는 대로 관리자에게 원격 웹 서버에 대한 사용자 계정을 생성해주도록 요청하십시오."
+
+#~ msgid ""
+#~ "The webserver currently running Hue does"
+#~ " not support the Shell application. "
+#~ "Please contact your administrator."
+#~ msgstr "현재 Hue를 실행하는 웹 서버가 Shell 애플리케이션을 지원하지 않습니다. 관리자에게 문의하십시오."
+

+ 369 - 0
apps/sqoop/src/sqoop/locale/pt/LC_MESSAGES/django.po

@@ -0,0 +1,369 @@
+# Portuguese translations for Hue.
+# Copyright (C) 2012 Cloudera
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: 2012-07-30 18:56-0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: pt <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+
+#~ msgid ""
+#~ "bin/hadoop return non-zero %(retcode)d "
+#~ "while trying to merge credentials."
+#~ msgstr "bin/hadoop devolveu %(retcode)d não zero ao tentar unir credenciais."
+
+#~ msgid "Hue Shell"
+#~ msgstr "Shell da Hue"
+
+#~ msgid ""
+#~ "Failed to create a shell of the"
+#~ " given type. The possible reasons for"
+#~ " this are:"
+#~ msgstr ""
+#~ "Falha ao criar uma shell do tipo"
+#~ " indicado. Os motivos possíveis para "
+#~ "tal são:"
+
+#~ msgid "The system is out of PTYs."
+#~ msgstr "O sistema já não tem mais PTYs."
+
+#~ msgid "The system cannot create more subprocesses."
+#~ msgstr "O sistema não pode criar mais subprocessos."
+
+#~ msgid "You do not have permission to create shells of this type."
+#~ msgstr "Não tem permissão para criar shells deste tipo."
+
+#~ msgid "There is no shell with that name."
+#~ msgstr "Não existe uma shell com o nome indicado."
+
+#~ msgid "There is no Unix user account for you."
+#~ msgstr "Não existe qualquer conta de utilizador de Unix associada a si."
+
+#~ msgid "Select one of the available shells from the toolbar above."
+#~ msgstr "Seleccione uma das shells disponíveis na barra de ferramentas acima."
+
+#~ msgid ""
+#~ "You do not have permission to "
+#~ "access any shell or there is no"
+#~ " configured shell."
+#~ msgstr ""
+#~ "Não tem permissão para aceder a "
+#~ "qualquer shell ou não existe qualquer"
+#~ " shell configurada."
+
+#~ msgid "The shell is not present on the Hue host."
+#~ msgstr "A shell não está presente no anfitrião do Hue."
+
+#~ msgid ""
+#~ "The Shell application requires a Unix"
+#~ " user account for every user of "
+#~ "Hue on the remote web server."
+#~ msgstr ""
+#~ "A aplicação Shell requer uma conta "
+#~ "de utilizador Unix para cada utilizador"
+#~ " da Hue no servidor web remoto."
+
+#~ msgid ""
+#~ "Ask your administrator to create a "
+#~ "user account for you on the remote"
+#~ " web server as described in the "
+#~ "Shell documentation."
+#~ msgstr ""
+#~ "Solicite ao seu administrador que lhe"
+#~ " crie uma conta de utilizador no "
+#~ "servidor web remoto, conforme descrito "
+#~ "na documentação da Shell."
+
+#~ msgid ""
+#~ "The webserver currently running Hue does"
+#~ " not support the Shell application. "
+#~ "Please contact your administrator."
+#~ msgstr ""
+#~ "O servidor de Internet que está a"
+#~ " executar actualmente o Hue não "
+#~ "suporta a aplicação Shell. Contacte o"
+#~ " seu administrador."
+

+ 373 - 0
apps/sqoop/src/sqoop/locale/pt_BR/LC_MESSAGES/django.po

@@ -0,0 +1,373 @@
+# Portuguese (Brazil) translations for Hue.
+# Copyright (C) 2012 Cloudera
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: 2012-07-30 18:56-0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: pt_BR <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+
+#~ msgid ""
+#~ "bin/hadoop return non-zero %(retcode)d "
+#~ "while trying to merge credentials."
+#~ msgstr ""
+#~ "Retorno do bin/hadoop %(retcode)d diferente"
+#~ " de zero enquanto tenta mesclar "
+#~ "credenciais."
+
+#~ msgid "Hue Shell"
+#~ msgstr "Hue Shell"
+
+#~ msgid ""
+#~ "Failed to create a shell of the"
+#~ " given type. The possible reasons for"
+#~ " this are:"
+#~ msgstr ""
+#~ "Falha ao criar um programa shell "
+#~ "de determinado tipo. As razões possíveis"
+#~ " são:"
+
+#~ msgid "The system is out of PTYs."
+#~ msgstr "O sistema está fora do PTYs."
+
+#~ msgid "The system cannot create more subprocesses."
+#~ msgstr "O sistema não pode criar mais subprocessos."
+
+#~ msgid "You do not have permission to create shells of this type."
+#~ msgstr "Você não tem permissão para criar shells deste tipo."
+
+#~ msgid "There is no shell with that name."
+#~ msgstr "Não existe shell para aquele nome."
+
+#~ msgid "There is no Unix user account for you."
+#~ msgstr "Não existe conta de usuário Unix para você."
+
+#~ msgid "Select one of the available shells from the toolbar above."
+#~ msgstr "Selecione um dos shells disponíveis na barra de ferramentas acima."
+
+#~ msgid ""
+#~ "You do not have permission to "
+#~ "access any shell or there is no"
+#~ " configured shell."
+#~ msgstr ""
+#~ "Você não tem permissão para acessar "
+#~ "qualquer shell ou não existe um "
+#~ "shell configurado."
+
+#~ msgid "The shell is not present on the Hue host."
+#~ msgstr "O shell não está presente no host do Hue."
+
+#~ msgid ""
+#~ "The Shell application requires a Unix"
+#~ " user account for every user of "
+#~ "Hue on the remote web server."
+#~ msgstr ""
+#~ "O aplicativo Shell necessita de uma "
+#~ "conta de usuário Unix para cada "
+#~ "usuário do Hue no servidor Web "
+#~ "remoto."
+
+#~ msgid ""
+#~ "Ask your administrator to create a "
+#~ "user account for you on the remote"
+#~ " web server as described in the "
+#~ "Shell documentation."
+#~ msgstr ""
+#~ "Peça a seu administrador para criar "
+#~ "uma conta de usuário para você no"
+#~ " servidor Web remoto conforme descrito "
+#~ "na documentação do shell."
+
+#~ msgid ""
+#~ "The webserver currently running Hue does"
+#~ " not support the Shell application. "
+#~ "Please contact your administrator."
+#~ msgstr ""
+#~ "O servidor da Web que atualmente "
+#~ "executa o Hue não oferece suporte "
+#~ "ao aplicativo Shell. Entre em contato"
+#~ " com o seu administrador."
+

+ 352 - 0
apps/sqoop/src/sqoop/locale/zh_CN/LC_MESSAGES/django.po

@@ -0,0 +1,352 @@
+# Chinese (China) translations for Hue.
+# Copyright (C) 2012 Cloudera
+# This file is distributed under the same license as the Hue project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2013-07-13 20:19-0700\n"
+"PO-Revision-Date: 2012-07-30 18:56-0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: zh_CN <LL@li.org>\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+#: src/sqoop/conf.py:26
+msgid "The sqoop server URL."
+msgstr ""
+
+#: src/sqoop/api/connection.py:53
+msgid "Could not get connections."
+msgstr ""
+
+#: src/sqoop/api/connection.py:64 src/sqoop/api/connection.py:87
+msgid "Error saving connection"
+msgstr ""
+
+#: src/sqoop/api/connection.py:73
+msgid "Could not create connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:95
+msgid "Could not update connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:107 src/sqoop/api/connection.py:122
+#: src/sqoop/api/job.py:115 src/sqoop/api/job.py:130
+msgid "GET or POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:127 src/sqoop/api/connection.py:150
+#: src/sqoop/api/job.py:59 src/sqoop/api/job.py:85 src/sqoop/api/job.py:135
+#: src/sqoop/api/job.py:158 src/sqoop/api/job.py:179 src/sqoop/api/job.py:200
+msgid "POST request required."
+msgstr ""
+
+#: src/sqoop/api/connection.py:141
+msgid "Could not clone connection."
+msgstr ""
+
+#: src/sqoop/api/connection.py:161
+msgid "Could not delete connection."
+msgstr ""
+
+#: src/sqoop/api/connector.py:52
+msgid "Could not get connectors."
+msgstr ""
+
+#: src/sqoop/api/connector.py:59 src/sqoop/api/connector.py:72
+#: src/sqoop/api/framework.py:54 src/sqoop/api/job.py:221
+#: src/sqoop/api/submission.py:61
+msgid "GET request required."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:53
+msgid "Could not get connector."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:72
+msgid "Could not get connection."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:91
+msgid "Could not get job."
+msgstr ""
+
+#: src/sqoop/api/decorators.py:110
+msgid "Could not get submission."
+msgstr ""
+
+#: src/sqoop/api/exception.py:37
+#, python-format
+msgid "Could not connect to sqoop server. %s (%s)"
+msgstr ""
+
+#: src/sqoop/api/framework.py:51
+msgid "Could not get framework."
+msgstr ""
+
+#: src/sqoop/api/job.py:54
+msgid "Could not get jobs."
+msgstr ""
+
+#: src/sqoop/api/job.py:68 src/sqoop/api/job.py:94
+msgid "Error saving job"
+msgstr ""
+
+#: src/sqoop/api/job.py:77
+msgid "Could not create job."
+msgstr ""
+
+#: src/sqoop/api/job.py:102
+msgid "Could not update job."
+msgstr ""
+
+#: src/sqoop/api/job.py:149
+msgid "Could not clone job."
+msgstr ""
+
+#: src/sqoop/api/job.py:170
+msgid "Could not delete job."
+msgstr ""
+
+#: src/sqoop/api/job.py:191
+msgid "Could not start job."
+msgstr ""
+
+#: src/sqoop/api/job.py:212 src/sqoop/templates/app.mako:667
+msgid "Could not stop job."
+msgstr ""
+
+#: src/sqoop/api/job.py:233
+msgid "Could not get job status."
+msgstr ""
+
+#: src/sqoop/api/submission.py:54
+msgid "Could not get submissions."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:27
+msgid "Sqoop error"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:36
+msgid "Search for job name or content"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "Create a new job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:42
+msgid "New job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:49 src/sqoop/templates/app.mako:72
+msgid "Click to edit"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:51 src/sqoop/templates/app.mako:115
+msgid "Stop"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:52 src/sqoop/templates/app.mako:74
+#: src/sqoop/templates/app.mako:110
+msgid "Run"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:53 src/sqoop/templates/app.mako:75
+#: src/sqoop/templates/app.mako:120
+msgid "Copy"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:54 src/sqoop/templates/app.mako:76
+#: src/sqoop/templates/app.mako:125
+msgid "Delete"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "There are currently no jobs."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:87
+msgid "Please click on \"New Job\" to add one."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:103 src/sqoop/templates/app.mako:104
+msgid "Back to jobs list"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:107
+msgid "Actions"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:109
+msgid "Run the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:114
+msgid "Stop the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:119
+msgid "Copy the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:124
+msgid "Delete the job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:133
+msgid "Create a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:147
+msgid "Previous"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:151
+msgid "Next"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:154 src/sqoop/templates/app.mako:206
+msgid "Save"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:167
+msgid "Connector"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:181 src/sqoop/templates/app.mako:224
+msgid "Back to job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:187
+msgid "Create a connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:190 src/sqoop/templates/app.mako:284
+msgid "Name"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:205
+msgid "Cancel"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:216 src/sqoop/templates/app.mako:218
+msgid "Logs"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:222
+msgid "Back to editing a job"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:246
+msgid "Select job type"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:255
+msgid "Select connection"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:569
+msgid "Step 1: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:575
+msgid "Step 2: Sqoop specific"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:582
+msgid "Step 1: Manage connections"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:588
+msgid "Step 2: Job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:594
+msgid "Step 3: Sqoop specific job info"
+msgstr ""
+
+#: src/sqoop/templates/app.mako:650
+msgid "Cannot connect to sqoop server."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:655
+msgid "Started job."
+msgstr ""
+
+#: src/sqoop/templates/app.mako:659
+msgid "Could not start job: "
+msgstr ""
+
+#: src/sqoop/templates/app.mako:663
+msgid "Stopped job."
+msgstr ""
+
+#~ msgid ""
+#~ "bin/hadoop return non-zero %(retcode)d "
+#~ "while trying to merge credentials."
+#~ msgstr "尝试合并凭据期间,bin/hadoop 返回了非零 %(retcode)d。"
+
+#~ msgid "Hue Shell"
+#~ msgstr "Hue Shell"
+
+#~ msgid ""
+#~ "Failed to create a shell of the"
+#~ " given type. The possible reasons for"
+#~ " this are:"
+#~ msgstr "无法创建给定类型的 shell。造成这种情况的原因可能是:"
+
+#~ msgid "The system is out of PTYs."
+#~ msgstr "系统已超出 PTY。"
+
+#~ msgid "The system cannot create more subprocesses."
+#~ msgstr "系统无法创建更多子进程。"
+
+#~ msgid "You do not have permission to create shells of this type."
+#~ msgstr "您无权创建此类型的 shell。"
+
+#~ msgid "There is no shell with that name."
+#~ msgstr "不存在该名称的 shell。"
+
+#~ msgid "There is no Unix user account for you."
+#~ msgstr "您没有 Unix 用户帐户。"
+
+#~ msgid "Select one of the available shells from the toolbar above."
+#~ msgstr "请从上面的工具栏中选择其中一个可用的 shell。"
+
+#~ msgid ""
+#~ "You do not have permission to "
+#~ "access any shell or there is no"
+#~ " configured shell."
+#~ msgstr "您无权访问任何 shell,或者不存在配置 shell。"
+
+#~ msgid "The shell is not present on the Hue host."
+#~ msgstr "在 Hue 主机上不存在 shell。"
+
+#~ msgid ""
+#~ "The Shell application requires a Unix"
+#~ " user account for every user of "
+#~ "Hue on the remote web server."
+#~ msgstr "对于远程 Web 服务器上的每个 Hue 用户,Shell 应用程序都需要一个 Unix 用户帐户。"
+
+#~ msgid ""
+#~ "Ask your administrator to create a "
+#~ "user account for you on the remote"
+#~ " web server as described in the "
+#~ "Shell documentation."
+#~ msgstr "请要求您的管理员根据 Shell 文档中的描述,为您在远程 Web 服务器上创建一个用户帐户。"
+
+#~ msgid ""
+#~ "The webserver currently running Hue does"
+#~ " not support the Shell application. "
+#~ "Please contact your administrator."
+#~ msgstr "当前正在运行 Hue 的 Web 服务器不支持 Shell 应用程序。请与您的管理员联系。"
+

+ 0 - 0
apps/sqoop/src/sqoop/migrations/__init__.py


+ 16 - 0
apps/sqoop/src/sqoop/models.py

@@ -0,0 +1,16 @@
+#!/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.

+ 22 - 0
apps/sqoop/src/sqoop/settings.py

@@ -0,0 +1,22 @@
+# 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.
+DJANGO_APPS = ['sqoop']
+NICE_NAME = 'Sqoop'
+MENU_INDEX = 20
+ICON = '/sqoop/static/art/icon_sqoop_24.png'
+
+REQUIRES_HADOOP = False
+IS_URL_NAMESPACED = True

+ 872 - 0
apps/sqoop/src/sqoop/templates/app.mako

@@ -0,0 +1,872 @@
+## 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 desktop.views import commonheader, commonfooter
+  from django.utils.translation import ugettext as _
+%>
+
+<%namespace name="actionbar" file="actionbar.mako" />
+
+${ commonheader(None, "sqoop", user, "60px") | n,unicode }
+
+<div class="container-fluid">
+  <div id="sqoop-error" class="row-fluid mainSection hide">
+    <h2>${_('Sqoop error')}</h2>
+    <div class="error message well"></div>
+  </div>
+
+  <div id="jobs" class="row-fluid mainSection hide">
+    <div id="jobs-list" class="row-fluid section hide">
+      <div class="row-fluid" data-bind="if: !isLoading()">
+        <%actionbar:render>
+          <%def name="search()">
+            <input id="filter" type="text" class="input-xlarge search-query" placeholder="${_('Search for job name or content')}">
+          </%def>
+
+          <%def name="actionbar()"></%def>
+
+          <%def name="creation()">
+            <a class="btn fileToolbarBtn" title="${_('Create a new job')}" href="#job/new"><i class="icon-plus-sign"></i> ${_('New job')}</a>
+          </%def>
+        </%actionbar:render>
+
+        <!-- ko if: filteredJobs().length > 0 -->
+        <ul class="major-list" data-bind="foreach: filteredJobs">
+          <!-- ko if: submission() -->
+          <li data-bind="routie: 'job/edit/' + id()" title="${ _('Click to edit') }">
+            <div class="pull-right" style="padding-top: 5px">
+              <a data-bind="visible: $.inArray(submission().status(), ['BOOTING', 'RUNNING']) > -1, routie: 'job/stop/' + id()"><i class="icon-stop"></i> ${_('Stop')}</a> &nbsp;
+              <a data-bind="visible: $.inArray(submission().status(), ['BOOTING', 'RUNNING']) == -1, routie: 'job/run/' + id()"><i class="icon-play"></i> ${_('Run')}</a> &nbsp;
+              <a data-bind="routie: 'job/copy/' + id()"><i class="icon-copy"></i> ${_('Copy')}</a> &nbsp;
+              <a data-bind="visible: $.inArray(submission().status(), ['BOOTING', 'RUNNING']) == -1, routie: 'job/delete/' + id()"><i class="icon-remove"></i> ${_('Delete')}</a>
+            </div>
+            <h4 style="display: inline-block">
+              <i class="icon-list"></i> <span data-bind="text: name"></span>
+            </h4>
+            &nbsp;
+            <span class="label label-success" data-bind="visible: $.inArray(submission().status(), ['BOOTING', 'RUNNING', 'SUCCEEDED']) > -1">
+              <span data-bind="visible: submission().status() != 'SUCCEEDED', text: submission().status, routie: 'job/status/' + id()"></span>
+              <span data-bind="visible: submission().status() == 'SUCCEEDED', text: submission().createdFormatted, routie: 'job/status/' + id()"></span>
+            </span>
+            <span class="label label-warning" style="display: inline-block" data-bind="visible: $.inArray(submission().status(), ['FAILURE_ON_SUBMIT', 'FAILED', 'UNKNOWN']) > -1">
+              <span data-bind="visible: submission().status() == 'UNKNOWN', text: submission().status"></span>
+              <span data-bind="visible: submission().status() != 'UNKNOWN', text: submission().createdFormatted, routie: 'job/status/' + id()"></span>
+            </span>
+          </li>
+          <!-- /ko -->
+
+          <!-- ko ifnot: submission() -->
+          <li data-bind="routie: 'job/edit/' + id()" title="${ _('Click to edit') }">
+            <div class="pull-right" style="padding-top: 5px">
+              <a data-bind="routie: 'job/run/' + id()"><i class="icon-play"></i> ${_('Run')}</a> &nbsp;
+              <a data-bind="routie: 'job/copy/' + id()"><i class="icon-copy"></i> ${_('Copy')}</a> &nbsp;
+              <a data-bind="routie: 'job/delete/' + id()"><i class="icon-remove"></i> ${_('Delete')}</a>
+            </div>
+            <h4 style="display: inline-block">
+              <i class="icon-list"></i> <span data-bind="text: name"></span>
+            </h4>
+          </li>
+          <!-- /ko -->
+        </ul>
+        <!-- /ko -->
+
+        <div class="span10 offset1 center" data-bind="if: filteredJobs().length == 0">
+          <h1 class="emptyMessage">${ _('There are currently no jobs.') }<br/>${ _('Please click on "New Job" to add one.') }</h1>
+        </div>
+      </div>
+
+      <div class="row-fluid" data-bind="if: isLoading">
+        <div class="span10 offset1 center">
+          <i class="icon-spinner icon-spin" style="font-size: 60px; color: #DDD"></i>
+        </div>
+      </div>
+    </div>
+
+    <div id="job-editor" class="row-fluid section hide" data-bind="with: job">
+      <div class="well sidebar-nav span2">
+        <form id="advanced-settings" method="POST" class="form form-horizontal noPadding">
+          <ul class="nav nav-list">
+            <li>
+              <a data-placement="right" rel="tooltip" title="${_('Back to jobs list')}" href="#jobs">
+                <i class="icon-share-alt"></i> ${_('Back to jobs list')}
+              </a>
+            </li>
+            <li class="nav-header" data-bind="visible: $root.job().persisted">${_('Actions')}</li>
+            <li data-bind="visible: $root.job().persisted() && !$root.job().isRunning()">
+              <a data-placement="right" rel="tooltip" title="${_('Run the job')}" href="#job/save-and-run">
+                <i class="icon-play"></i> ${_('Run')}
+              </a>
+            </li>
+            <li data-bind="visible: $root.job().isRunning()">
+              <a data-placement="right" rel="tooltip" title="${_('Stop the job')}" href="#job/stop">
+                <i class="icon-stop"></i> ${_('Stop')}
+              </a>
+            </li>
+            <li data-bind="visible: $root.job().persisted">
+              <a data-placement="right" rel="tooltip" title="${_('Copy the job')}" href="#job/copy">
+                <i class="icon-copy"></i> ${_('Copy')}
+              </a>
+            </li>
+            <li data-bind="visible: $root.job().persisted">
+              <a data-placement="right" rel="tooltip" title="${_('Delete the job')}" href="#job/delete">
+                <i class="icon-remove"></i> ${_('Delete')}
+              </a>
+            </li>
+          </ul>
+        </form>
+      </div>
+
+      <div id="job-forms" class="span10">
+        <h3>${_('Create a job')}</h3>
+          <!-- ko if: $root.jobWizard.page -->
+            <!-- ko with: $root.jobWizard -->
+            <ul class="nav nav-pills" data-bind="foreach: pages">
+              <li data-bind="css: {'active': $parent.index() == $index()}">
+                <a href="javascript:void(0);" data-bind="routie: 'job/edit/wizard/' + identifier(), text: caption"></a>
+              </li>
+            </ul>
+            <form method="POST" class="form form-horizontal noPadding" data-bind="with: page">
+              <div data-bind="template: {'name': template(), 'data': node}">
+              </div>
+
+              <div class="form-actions">
+                <!-- ko if: $parent.hasPrevious -->
+                <a class="btn" data-bind="routie: 'job/edit/wizard/' + $parent.previousIndex()">${_('Previous')}</a>
+                &nbsp;
+                <!-- /ko -->
+                <!-- ko if: $parent.hasNext -->
+                <a class="btn btn-primary" data-bind="routie: 'job/edit/wizard/' + $parent.nextIndex()">${_('Next')}</a>
+                <!-- /ko -->
+                <!-- ko ifnot: $parent.hasNext -->
+                <a class="btn btn-primary" href="#job/save">${_('Save')}</a>
+                <!-- /ko -->
+              </div>
+            </form>
+            <!-- /ko -->
+          <!-- /ko -->
+      </div>
+    </div>
+
+    <div id="connection-editor" class="row-fluid section hide" data-bind="with: editConnection">
+      <div class="well sidebar-nav span2">
+        <form id="advanced-settings" method="POST" class="form form-horizontal noPadding">
+          <ul class="nav nav-list">
+            <li class="nav-header">${_('Connector')}</li>
+            <li data-bind="if: !persisted()">
+              <select name="connector"
+                      data-bind="'options': $root.connectors,
+                                 'optionsText': function(item) {
+                                   return item.name();
+                                 },
+                                 'optionsValue': function(item) {
+                                   return item.id();
+                                 },
+                                 'value': connector_id"
+                      class="span12"></select>
+            </li>
+            <li>&nbsp;</li>
+            <li><a href="#connection/edit-cancel">${_('Back to job')}</a></li>
+          </ul>
+        </form>
+      </div>
+
+      <div id="connection-forms" class="span10">
+        <h3>${_('Create a connection')}</h3>
+        <form method="POST" class="form form-horizontal noPadding">
+          <div class="control-group">
+            <label class="control-label">${ _('Name') }</label>
+            <div class="controls">
+              <input type="text" name="connection-name" data-bind="value: name">
+            </div>
+          </div>
+          <fieldset data-bind="foreach: connector">
+            <div data-bind="foreach: inputs">
+              <div data-bind="template: 'connector-' + type().toLowerCase()"></div>
+            </div>
+          </fieldset>
+          <fieldset data-bind="foreach: framework">
+            <div data-bind="foreach: inputs">
+              <div data-bind="template: 'framework-' + type().toLowerCase()"></div>
+            </div>
+          </fieldset>
+          <a href="#connection/edit-cancel" class="btn">${_('Cancel')}</a>
+          <a href="#connection/save" class="btn btn-primary">${_('Save')}</a>
+        </form>
+      </div>
+    </div>
+
+    <div id="job-status" class="row-fluid section hide" data-bind="with: job">
+      <div class="well sidebar-nav span2">
+        <form id="advanced-settings" method="POST" class="form form-horizontal noPadding">
+          <ul class="nav nav-list">
+            <li>
+              <a rel="tooltip" title="${_('Logs')}" href="javascript:void(0);" target="_new" data-bind="attr: {href: '/jobbrowser/jobs/' + $root.job().submission().external_id()}">
+                <i class="icon-list"></i>
+                ${_('Logs')}
+              </a>
+            </li>
+            <li>
+              <a rel="tooltip" title="${_('Back to editing a job')}" href="#job/edit">
+                <i class="icon-arrow-left"></i>
+                ${_('Back to job')}
+              </a>
+            </li>
+          </ul>
+        </form>
+      </div>
+
+      <div class="span10">
+        <div class="progress center" data-bind="if: submission()">
+          <div class="bar center" data-bind="style: {width: submission().progressFormatted, height: '100%'}, text: submission().progressFormatted">
+            
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+
+<script type="text/html" id="job-editor-type-and-connection">
+<fieldset style="width: 500px;">
+  <div class="control-group">
+    <label class="control-label">${ _('Select job type') }</label>
+    <div class="controls">
+      <select data-bind="'options': ['IMPORT', 'EXPORT'], 'value': type"
+              name="type"
+              class="span12"></select>
+    </div>
+  </div>
+
+  <div class="control-group">
+    <label class="control-label">${ _('Select connection') }</label>
+    <div class="controls">
+      <select data-bind="'options': $root.persistedConnections,
+                         'optionsText': function(item) {
+                           return item.name();
+                         },
+                         'value': $root.connection"
+              name="connection"
+              class="span8"></select>
+      <div class="pull-right span4">
+        <a data-bind="routie: 'connection/new'" href="javascript:void(0);">
+          <i class="icon-plus">&nbsp;</i>
+        </a>
+        <!-- ko if: $root.connection() -->
+        <a data-bind="routie: 'connection/edit/' + $root.connection().id()" href="javascript:void(0);" class="subbtn">
+          <i class="icon-reorder">&nbsp;</i>
+        </a>
+        <a data-bind="routie: 'connection/delete/' + $root.connection().id()" href="javascript:void(0);" class="subbtn">
+          <i class="icon-trash">&nbsp;</i>
+        </a>
+        <!-- /ko -->
+      </div>
+    </div>
+  </div>
+</fieldset>
+</script>
+
+<script type="text/html" id="job-editor-name-and-connector">
+<div class="control-group">
+  <label class="control-label">${ _('Name') }</label>
+  <div class="controls">
+    <input type="text" name="connection-name" data-bind="value: name">
+  </div>
+</div>
+
+<fieldset data-bind="foreach: connector">
+  <div data-bind="foreach: inputs">
+    <div data-bind="template: 'connector-' + type().toLowerCase()"></div>
+  </div>
+</fieldset>
+</script>
+
+<script type="text/html" id="job-editor-framework">
+<fieldset data-bind="foreach: framework">
+  <div data-bind="foreach: inputs">
+    <div data-bind="template: 'framework-' + type().toLowerCase()"></div>
+  </div>
+</fieldset>
+</script>
+
+<script type="text/html" id="framework-enum">
+<div data-bind="css: {
+                  warning: name() in $root.warnings(),
+                  error: name() in $root.errors()
+                }" class="control-group">
+  <label class="control-label" data-bind="text: $root.label('framework', name())"></label>
+  <div class="controls">
+    <select data-bind="'options': values,
+                       'value': value,
+                       'optionsCaption': 'Choose...',
+                       'attr': {
+                         'name': name,
+                         'title': $root.help('framework', name())
+                        }" rel="tooltip">
+    <!-- ko if: name() in $root.errors() -->
+      <!-- ko foreach: $root.errors()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+    <!-- ko if: name() in $root.warnings() -->
+      <!-- ko foreach: $root.warnings()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+  </div>
+</div>
+</script>
+
+<script type="text/html" id="framework-map">
+<div data-bind="css: {
+                  warning: name() in $root.warnings(),
+                  error: name() in $root.errors()
+                }" class="control-group">
+  <label class="control-label" data-bind="text: $root.label('framework', name()),
+                                          attr: {
+                                            'title': $root.help('framework', name())
+                                          }" rel="tooltip"></label>
+  <div class="controls">
+    <input data-bind="value: value,
+                      attr: {'type': (sensitive() ? 'password' : 'text'),
+                        'name': name,
+                        'title': $root.help('framework', name())
+                      }" rel="tooltip">
+    <!-- ko if: name() in $root.errors() -->
+      <!-- ko foreach: $root.errors()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+    <!-- ko if: name() in $root.warnings() -->
+      <!-- ko foreach: $root.warnings()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+  </div>
+</div>
+</script>
+
+<script type="text/html" id="framework-string">
+<div data-bind="css: {
+                  warning: name() in $root.warnings(),
+                  error: name() in $root.errors()
+                }" class="control-group">
+  <label class="control-label" data-bind="text: $root.label('framework', name()),
+                                          attr: {
+                                            'title': $root.help('framework', name())
+                                          }" rel="tooltip"></label>
+  <div class="controls">
+    <input data-bind="value: value,
+                      attr: {
+                        'type': (sensitive() ? 'password' : 'text'),
+                        'name': name,
+                        'title': $root.help('framework', name())
+                      }" rel="tooltip">
+    <!-- ko if: name() in $root.errors() -->
+      <!-- ko foreach: $root.errors()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+    <!-- ko if: name() in $root.warnings() -->
+      <!-- ko foreach: $root.warnings()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+  </div>
+</div>
+</script>
+
+<script type="text/html" id="framework-integer">
+<div data-bind="css: {
+                  warning: name() in $root.warnings(),
+                  error: name() in $root.errors()
+                }" class="control-group">
+  <label class="control-label" data-bind="text: $root.label('framework', name()),
+                                          attr: {
+                                            'title': $root.help('framework', name())
+                                          }" rel="tooltip"></label>
+  <div class="controls">
+    <input data-bind="value: value,
+                      attr: {
+                        'type': (sensitive() ? 'password' : 'text'), 'name': name,
+                        'title': $root.help('framework', name())
+                      }" rel="tooltip">
+    <!-- ko if: name() in $root.errors() -->
+      <!-- ko foreach: $root.errors()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+    <!-- ko if: name() in $root.warnings() -->
+      <!-- ko foreach: $root.warnings()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+  </div>
+</div>
+</script>
+
+<script type="text/html" id="connector-enum">
+<div data-bind="css: {
+                  warning: name() in $root.warnings(),
+                  error: name() in $root.errors()
+                }" class="control-group">
+  <label class="control-label" data-bind="text: $root.label('connector', name()),
+                                          attr: {
+                                            'title': $root.help('connector', name())
+                                          }" rel="tooltip"></label>
+  <div class="controls">
+    <select data-bind="'options': values,
+                       'value': value,
+                       'optionsCaption': 'Choose...',
+                       attr: {
+                        'name': name,
+                        'title': $root.help('connector', name())
+                       }" rel="tooltip">
+    <!-- ko if: name() in $root.errors() -->
+      <!-- ko foreach: $root.errors()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+    <!-- ko if: name() in $root.warnings() -->
+      <!-- ko foreach: $root.warnings()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+  </div>
+</div>
+</script>
+
+<script type="text/html" id="connector-map">
+<div data-bind="css: {
+                  warning: name() in $root.warnings(),
+                  error: name() in $root.errors()
+                }" class="control-group">
+  <label class="control-label" data-bind="text: $root.label('connector', name()),
+                                          attr: {
+                                            'title': $root.help('connector', name())
+                                          }" rel="tooltip"></label>
+  <div class="controls">
+    <input data-bind="value: value,
+                      attr: {
+                        'type': (sensitive() ? 'password' : 'text'),
+                        'name': name,
+                        'title': $root.help('connector', name())
+                      }" rel="tooltip">
+    <!-- ko if: name() in $root.errors() -->
+      <!-- ko foreach: $root.errors()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+    <!-- ko if: name() in $root.warnings() -->
+      <!-- ko foreach: $root.warnings()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+  </div>
+</div>
+</script>
+
+<script type="text/html" id="connector-string">
+<div data-bind="css: {
+                  warning: name() in $root.warnings(),
+                  error: name() in $root.errors()
+                }" class="control-group">
+  <label class="control-label" data-bind="text: $root.label('connector', name()),
+                                          attr: {
+                                            'title': $root.help('connector', name())
+                                          }" rel="tooltip"></label>
+  <div class="controls">
+    <input data-bind="value: value,
+                      attr: {
+                        'type': (sensitive() ? 'password' : 'text'),
+                        'name': name,
+                        'title': $root.help('connector', name())
+                      }" rel="tooltip">
+    <!-- ko if: name() in $root.errors() -->
+      <!-- ko foreach: $root.errors()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+    <!-- ko if: name() in $root.warnings() -->
+      <!-- ko foreach: $root.warnings()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+  </div>
+</div>
+</script>
+
+<script type="text/html" id="connector-integer">
+<div data-bind="css: {
+                  warning: name() in $root.warnings(),
+                  error: name() in $root.errors()
+                }" class="control-group">
+  <label class="control-label" data-bind="text: $root.label('connector', name()),
+                                          attr: {
+                                            'title': $root.help('connector', name())
+                                          }" rel="tooltip"></label>
+  <div class="controls">
+    <input data-bind="value: value, 
+                      attr: {
+                        'type': (sensitive() ? 'password' : 'text'),
+                        'name': name,
+                        'title': $root.help('connector', name())
+                      }" rel="tooltip">
+    <!-- ko if: name() in $root.errors() -->
+      <!-- ko foreach: $root.errors()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+    <!-- ko if: name() in $root.warnings() -->
+      <!-- ko foreach: $root.warnings()[name()]  -->
+        <span data-bind="text: message" class="help-inline"></span>
+      <!-- /ko -->
+    <!-- /ko -->
+  </div>
+</div>
+</script>
+
+<script src="/static/ext/js/datatables-paging-0.1.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/routie-0.3.0.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/moment.min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/knockout-min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/knockout.mapping-2.3.2.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/cclass.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/koify.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.utils.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.wizard.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.models.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.framework.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.connectors.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.connections.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.jobs.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.submissions.js" type="text/javascript" charset="utf-8"></script>
+<script src="/sqoop/static/js/sqoop.js" type="text/javascript" charset="utf-8"></script>
+
+<link rel="stylesheet" href="/sqoop/static/css/sqoop.css">
+
+<script type="text/javascript" charset="utf-8">
+//// Job Wizard
+viewModel.job.subscribe(function(job) {
+  if (job) {
+    viewModel.jobWizard.clearPages();
+    if (job.persisted()) {
+      viewModel.jobWizard.addPage(new wizard.Page({
+        'identifier': 'job-editor-name-and-connector',
+        'caption': '${_("Step 1: Job info")}',
+        'node': job,
+        'template': 'job-editor-name-and-connector',
+      }));
+      viewModel.jobWizard.addPage(new wizard.Page({
+        'identifier': 'job-editor-framework',
+        'caption': '${_("Step 2: Sqoop specific")}',
+        'node': job,
+        'template': 'job-editor-framework',
+      }));
+    } else {
+      viewModel.jobWizard.addPage(new wizard.Page({
+        'identifier': 'job-editor-type-and-connection',
+        'caption': '${_("Step 1: Manage connections")}',
+        'node': job,
+        'template': 'job-editor-type-and-connection',
+      }));
+      viewModel.jobWizard.addPage(new wizard.Page({
+        'identifier': 'job-editor-name-and-connector',
+        'caption': '${_("Step 2: Job info")}',
+        'node': job,
+        'template': 'job-editor-name-and-connector',
+      }));
+      viewModel.jobWizard.addPage(new wizard.Page({
+        'identifier': 'job-editor-framework',
+        'caption': '${_("Step 3: Sqoop specific job info")}',
+        'node': job,
+        'template': 'job-editor-framework',
+      }));
+    }
+  }
+});
+
+//// Render all data
+ko.applyBindings(viewModel, $('#jobs')[0]);
+
+//// Events
+function handle_form_errors(e, node, options, data) {
+  // Add errors and warnings to viewModel.errors and viewModel.warnings
+  var errors = data.errors;
+  viewModel.errors({});
+  viewModel.warnings({});
+  switch(data.status) {
+    case 1:
+    $.each(errors, function(component, err) {
+      $.jHueNotify.error(err);
+    });
+    break;
+    case 100:
+    $.each(errors, function(component, dict) {
+      $.each(dict['messages'], function(resource, message_dict) {
+        var name = '*[name="' + resource + '"]';
+        var el = $(name);
+        if (el.length > 0) {
+          var err_node = ko.dataFor(el[0]);
+          switch(message_dict.status) {
+            case 'ACCEPTABLE':
+            if (!(resource in viewModel.errors())) {
+              viewModel.warnings()[resource] = [];
+            }
+            viewModel.warnings()[resource].push(message_dict);
+            err_node.name.valueHasMutated();
+            break;
+
+            default:
+            case 'UNACCEPTABLE':
+            if (!(resource in viewModel.errors())) {
+              viewModel.errors()[resource] = [];
+            }
+            viewModel.errors()[resource].push(message_dict);
+            err_node.name.valueHasMutated();
+            break;
+          }
+        }
+      });
+    });
+    break;
+  }
+}
+
+$(document).on('connection_error.jobs', function(e, name, options, jqXHR) {
+  $('#sqoop-error .message').text("${ _('Cannot connect to sqoop server.') }");
+  routie('error');
+});
+
+$(document).on('started.job', function(e, job, options, submission_dict) {
+  $.jHueNotify.info("${ _('Started job.') }");
+});
+
+$(document).on('start_fail.job', function(e, job, options, message) {
+  $.jHueNotify.error("${ _('Could not start job: ') }" + message);
+});
+
+$(document).on('stopped.job', function(e, job, options, submission_dict) {
+  $.jHueNotify.info("${ _('Stopped job.') }");
+});
+
+$(document).on('stop_fail.job', function(e, job, options, submission_dict) {
+  $.jHueNotify.info("${ _('Could not stop job.') }");
+});
+
+$(document).on('save_fail.job', handle_form_errors);
+$(document).on('save_fail.connection', handle_form_errors);
+$(document).on('delete_fail.job', handle_form_errors);
+
+$(document).on('keyup', 'input#filter', function() {
+  viewModel.filter($('#filter').val());
+});
+
+$("#jobs-list tbody").on('click', 'tr', function() {
+  var job = ko.dataFor(this);
+  job.selected(!job.selected());
+});
+
+//// Load all the data
+var framework = new framework.Framework({modelDict: {}});
+$(document).one('loaded.jobs', function() {
+  framework.load();
+  connectors.fetchConnectors();
+  connections.fetchConnections();
+  submissions.fetchSubmissions();
+});
+jobs.fetchJobs();
+
+
+//// Routes
+$(document).ready(function () {
+  window.location.hash = 'jobs';
+  routie({
+    "error": function() {
+      showMainSection("sqoop-error");
+      routie.removeAll();
+    },
+    "jobs": function() {
+      showSection("jobs", "jobs-list");
+    },
+    "job/edit": function() {
+      if (!viewModel.job()) {
+        routie('jobs');
+      }
+      showSection("jobs", "job-editor");
+      $("*[rel=tooltip]").tooltip({
+        placement: 'right'
+      });
+    },
+    "job/edit/wizard/:page": function(page) {
+      if ($.isNumeric(page)) {
+        viewModel.jobWizard.index(parseInt(page));
+      } else {
+        viewModel.jobWizard.index(viewModel.jobWizard.getIndex(page));
+      }
+      $("*[rel=tooltip]").tooltip({
+        placement: 'right'
+      });
+    },
+    "job/edit/:id": function(id) {
+      viewModel.chooseJobById(id);
+      showSection("jobs", "job-editor");
+      $("*[rel=tooltip]").tooltip({
+        placement: 'right'
+      });
+    },
+    "job/new": function() {
+      viewModel.newJob();
+      showSection("jobs", "job-editor");
+      $("*[rel=tooltip]").tooltip({
+        placement: 'right'
+      });
+    },
+    "job/save": function() {
+      viewModel.saveJob();
+      $(document).one('saved.job', function(){
+        routie('jobs');
+      });
+      $(document).one('save_fail.job', function(){
+        routie('job/edit');
+      });
+    },
+    "job/run": function() {
+      if (viewModel.job()) {
+        viewModel.job().start();
+      }
+      routie('jobs');
+    },
+    "job/run/:id": function(id) {
+      viewModel.chooseJobById(id);
+      viewModel.job().start();
+      routie('jobs');
+    },
+    "job/save-and-run": function() {
+      viewModel.saveJob();
+      $(document).one('saved.job', function(){
+        routie('job/run');
+      });
+      $(document).one('save_fail.connection', function(){
+        routie('job/edit');
+      });
+    },
+    "job/stop": function() {
+      if (viewModel.job()) {
+        viewModel.job().stop();
+      }
+      routie('jobs');
+    },
+    "job/stop/:id": function(id) {
+      viewModel.chooseJobById(id);
+      routie('job/stop');
+    },
+    "job/status": function() {
+      showSection("jobs", "job-status");
+    },
+    "job/status/:id": function(id) {
+      viewModel.chooseJobById(id);
+      routie('job/status');
+    },
+    "job/copy": function() {
+      if (viewModel.job()) {
+        viewModel.job().clone();
+      }
+      routie('jobs');
+    },
+    "job/copy/:id": function(id) {
+      viewModel.chooseJobById(id);
+      routie('job/copy');
+    },
+    "job/delete": function() {
+      if (viewModel.job()) {
+        $(document).one('deleted.job', function(){routie('jobs');});
+        viewModel.job().delete();
+      } else {
+        routie('jobs');
+      }
+    },
+    "job/delete/:id": function(id) {
+      viewModel.chooseJobById(id);
+      routie('job/delete');
+    },
+    "connection/edit": function() {
+      if (viewModel.connection()) {
+        routie('')
+      }
+      showSection("jobs", "connection-editor");
+      $("*[rel=tooltip]").tooltip({
+        placement: 'right'
+      });
+    },
+    "connection/edit/:id": function(id) {
+      viewModel.chooseConnectionById(id);
+      showSection("jobs", "connection-editor");
+      $("*[rel=tooltip]").tooltip({
+        placement: 'right'
+      });
+    },
+    "connection/edit-cancel": function() {
+      if (!viewModel.connection().persisted()) {
+        viewModel.connections.pop();
+      }
+      routie('job/edit');
+    },
+    "connection/new": function() {
+      viewModel.newConnection();
+      routie('connection/edit');
+    },
+    "connection/save": function() {
+      viewModel.saveConnection();
+      $(document).one('saved.connection', function(){
+        routie('job/edit');
+      });
+      $(document).one('save_fail.connection', function(){
+        routie('connection/edit');
+      });
+    },
+    "connection/copy": function() {
+      if (viewModel.connection()) {
+        viewModel.connection().clone();
+      }
+      routie('job/edit');
+    },
+    "connection/delete": function() {
+      if (viewModel.connection()) {
+        viewModel.connection().delete();
+        $(document).one('deleted.connection', function(){
+          routie('job/edit');
+        });
+      } else {
+        routie('job/edit');
+      }
+    },
+    "connection/delete/:id": function(id) {
+      viewModel.chooseConnectionById(id);
+      viewModel.connection().delete();
+      $(document).one('deleted.connection', function(){
+        routie('job/edit');
+      });
+    }
+  });
+
+  $("*[rel=tooltip]").tooltip({
+    placement: 'right'
+  });
+});
+</script>
+
+${ commonfooter(messages) | n,unicode }

+ 16 - 0
apps/sqoop/src/sqoop/tests.py

@@ -0,0 +1,16 @@
+#!/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.

+ 41 - 0
apps/sqoop/src/sqoop/urls.py

@@ -0,0 +1,41 @@
+#!/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.
+
+from django.conf.urls.defaults import patterns, url
+
+urlpatterns = patterns('sqoop.views',
+  url(r'^$', 'app', name='index')
+)
+
+urlpatterns += patterns('sqoop.api',
+  url(r'^api/framework/?$', 'framework', name='framework'),
+  url(r'^api/connectors/?$', 'connectors', name='connectors'),
+  url(r'^api/connectors/resources/?$', 'connectors_resources', name='connectors_resources'),
+  url(r'^api/connectors/(?P<connector_id>\d+)/?$', 'connector', name='connector'),
+  url(r'^api/connections/?$', 'connections', name='connections'),
+  url(r'^api/connections/(?P<connection_id>\d+)/?$', 'connection', name='connection'),
+  url(r'^api/connections/(?P<connection_id>\d+)/clone/?$', 'connection_clone', name='connection_clone'),
+  url(r'^api/connections/(?P<connection_id>\d+)/delete/?$', 'connection_delete', name='connection_delete'),
+  url(r'^api/jobs/?$', 'jobs', name='jobs'),
+  url(r'^api/jobs/(?P<job_id>\d+)/?$', 'job', name='job'),
+  url(r'^api/jobs/(?P<job_id>\d+)/clone/?$', 'job_clone', name='job_clone'),
+  url(r'^api/jobs/(?P<job_id>\d+)/delete/?$', 'job_delete', name='job_delete'),
+  url(r'^api/jobs/(?P<job_id>\d+)/start/?$', 'job_start', name='job_start'),
+  url(r'^api/jobs/(?P<job_id>\d+)/stop/?$', 'job_stop', name='job_stop'),
+  url(r'^api/jobs/(?P<job_id>\d+)/status/?$', 'job_status', name='job_status'),
+  url(r'^api/submissions/?$', 'submissions', name='submissions')
+)

+ 27 - 0
apps/sqoop/src/sqoop/views.py

@@ -0,0 +1,27 @@
+#!/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 logging
+
+from desktop.lib.django_util import render
+
+
+LOG = logging.getLogger(__name__)
+
+
+def app(request):
+  return render('app.mako', request, {})

二进制
apps/sqoop/static/art/icon_sqoop_24.png


+ 20 - 0
apps/sqoop/static/css/sqoop.css

@@ -0,0 +1,20 @@
+.row-selectable tr {
+  cursor: pointer;
+}
+
+ul.major-list {
+  margin: 0px;
+  padding: 0px;
+}
+
+ul.major-list li {
+  cursor: pointer;
+  list-style: none;
+  margin: 3px;
+  padding: 5px;
+}
+
+ul.major-list h4 {
+  margin: 5px 0px;
+  padding: 0px;
+}

+ 139 - 0
apps/sqoop/static/help/index.html

@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html
+  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html lang="en-us" xml:lang="en-us">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<link rel="stylesheet" type="text/css" href="commonltr.css"/>
+<title>Pig Editor</title>
+</head>
+<body id="xd_583c10bfdbd326ba-3ca24a24-13d80143249--7f9c"><a name="xd_583c10bfdbd326ba-3ca24a24-13d80143249--7f9c"><!-- --></a>
+
+
+  <h1 class="title topictitle1">Pig Editor</h1>
+
+
+  <div class="body conbody">
+    <p class="p">The Pig Editor application allows you to define Pig scripts, run scripts,
+      and view the status of jobs. For information about Pig, see <a class="xref" href="http://archive.cloudera.com/cdh4/cdh/4/pig/" target="_blank">Pig Documentation</a>. </p>
+
+  </div>
+
+  <div class="topic concept nested1" id="concept_u25_g2v_yj"><a name="concept_u25_g2v_yj"><!-- --></a>
+    <h2 class="title topictitle2">Pig Editor Installation and Configuration </h2>
+
+    <div class="body conbody">
+      <p class="p">Pig Editor is one of the applications installed as part of Hue. <span class="ph">For information about installing and configuring Hue,
+        see Hue Installation in <a class="xref" href="http://www.cloudera.com/content/cloudera-content/cloudera-docs/CDH4/latest/CDH4-Installation-Guide/CDH4-Installation-Guide.html" target="_blank">http://www.cloudera.com/content/cloudera-content/cloudera-docs/CDH4/latest/CDH4-Installation-Guide/CDH4-Installation-Guide.html</a>.</span> Pig Editor assumes an existing Pig installation. The Hue installation instructions
+        include the configuration necessary for Pig Editor to access Pig.</p>
+
+    </div>
+
+  </div>
+
+  <div class="topic concept nested1" id="concept_e2q_xn5_wj"><a name="concept_e2q_xn5_wj"><!-- --></a>
+    <h2 class="title topictitle2">Starting Pig Editor</h2>
+
+    <div class="body conbody">
+      <div class="p">Click the <strong class="ph b">Pig Editor</strong> icon (<a name="concept_e2q_xn5_wj__image_fgk_4n5_wj"><!-- --></a><img class="image" id="concept_e2q_xn5_wj__image_fgk_4n5_wj" src="/static/art/help/icon_pig_24.png"/>) in the navigation bar at the top of the Hue
+        browser page. The Pig Editor opens with three tabs:<a name="concept_e2q_xn5_wj__ul_itr_clx_2k"><!-- --></a><ul class="ul" id="concept_e2q_xn5_wj__ul_itr_clx_2k">
+          <li class="li">Editor - editor where you can create, edit, run, save, copy, and
+            delete scripts and edit script properties.</li>
+
+          <li class="li">Scripts - script manager where you can create, open, run, copy,
+            and delete scripts. </li>
+
+          <li class="li">Dashboard - dashboard where you can view running and completed
+            scripts and view the log of a job. </li>
+
+        </ul>
+</div>
+
+    </div>
+
+  </div>
+
+  <div class="topic concept nested1" id="concept_lng_kjt_yj"><a name="concept_lng_kjt_yj"><!-- --></a>
+    <h2 class="title topictitle2">Installing the Sample Pig Scripts </h2>
+
+    <div class="body conbody">
+      <div class="note note"><span class="notetitle"><img src="/static/art/help/note.jpg"/> 
+      <b>Note</b>:</span> You must be a superuser to perform this task.</div>
+
+      <a name="concept_lng_kjt_yj__ol_zys_pjt_yj"><!-- --></a><ol class="ol" id="concept_lng_kjt_yj__ol_zys_pjt_yj">
+        <li class="li">Click <a name="concept_lng_kjt_yj__d45e102"><!-- --></a><img class="image" id="concept_lng_kjt_yj__d45e102" src="/static/art/help/quick_start.png"/>. The Quick Start Wizard opens.</li>
+<li class="li">Click <strong class="ph b">Step 2:
+            Examples</strong>.</li>
+
+        <li class="li">Click <strong class="ph b">Pig Editor</strong>.</li>
+
+      </ol>
+
+    </div>
+
+    <div class="topic concept nested2" id="concept_b5z_23x_2k"><a name="concept_b5z_23x_2k"><!-- --></a>
+      <h3 class="title topictitle3">Scripts</h3>
+
+      <div class="body conbody">
+        <div class="section" id="concept_b5z_23x_2k__section_s35_5lx_2k"><a name="concept_b5z_23x_2k__section_s35_5lx_2k"><!-- --></a><h4 class="title sectiontitle">Creating a Script</h4>
+
+          <a name="concept_b5z_23x_2k__ol_mwq_z2y_2k"><!-- --></a><ol class="ol" id="concept_b5z_23x_2k__ol_mwq_z2y_2k">
+            <li class="li">In either the Editor or Scripts screen, click <span class="ph uicontrol">New
+                script</span>. Edit the script as desired.</li>
+
+            <li class="li">Click <span class="ph uicontrol">Edit
+              Properties</span>. In the Script name field, type a name for the script.</li>
+
+            <li class="li">Click <span class="ph uicontrol">Save</span>.</li>
+
+          </ol>
+
+        </div>
+
+        <div class="section" id="concept_b5z_23x_2k__section_tvv_5lx_2k"><a name="concept_b5z_23x_2k__section_tvv_5lx_2k"><!-- --></a><h4 class="title sectiontitle">Opening a Script</h4>
+
+          <a name="concept_b5z_23x_2k__ol_nvn_2fy_2k"><!-- --></a><ol class="ol" id="concept_b5z_23x_2k__ol_nvn_2fy_2k">
+            <li class="li">Click the <span class="ph uicontrol">Scripts</span> tab.</li>
+
+            <li class="li">In the list of scripts, click a script.</li>
+
+          </ol>
+
+        </div>
+
+        <div class="section" id="concept_b5z_23x_2k__section_gjr_rfy_2k"><a name="concept_b5z_23x_2k__section_gjr_rfy_2k"><!-- --></a><h4 class="title sectiontitle">Running a Script</h4>
+
+          <a name="concept_b5z_23x_2k__ol_r2q_sfy_2k"><!-- --></a><ol class="ol" id="concept_b5z_23x_2k__ol_r2q_sfy_2k">
+            <li class="li">Do one of the following:<a name="concept_b5z_23x_2k__ul_gj2_5fy_2k"><!-- --></a><ul class="ul" id="concept_b5z_23x_2k__ul_gj2_5fy_2k">
+                <li class="li">In the Editor screen, click the <span class="ph uicontrol">Run</span> button.</li>
+
+                <li class="li">In the Scripts screen,  check the checkbox next to a script and click the
+                    <span class="ph uicontrol">Run</span> button.</li>
+
+              </ul>
+</li>
+
+          </ol>
+
+        </div>
+
+        <div class="section" id="concept_b5z_23x_2k__section_kf2_zfy_2k"><a name="concept_b5z_23x_2k__section_kf2_zfy_2k"><!-- --></a><h4 class="title sectiontitle">Viewing the Result of Running a Script</h4>
+
+          <a name="concept_b5z_23x_2k__ol_jvm_2gy_2k"><!-- --></a><ol class="ol" id="concept_b5z_23x_2k__ol_jvm_2gy_2k">
+            <li class="li">Click the <span class="ph uicontrol">Dashboard</span> tab.</li>
+
+            <li class="li">Click a job.</li>
+
+          </ol>
+
+        </div>
+
+      </div>
+
+    </div>
+
+  </div>
+
+
+</body>
+</html>

+ 66 - 0
apps/sqoop/static/js/cclass.js

@@ -0,0 +1,66 @@
+// 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.
+
+/**
+ * cclass is a javascript Class inheritance implementation
+ *
+ * Example Usage:
+ * var A = cclass.create(function() {
+ *   this.instance_member = 'foo';
+ * }, {
+ *  test: function() {;
+ *    // do something!!!
+ *  }});
+ *
+ * var B = A.extend(function() {
+ *   this.__proto__.constructor();
+ * }, {
+ *   static_member: 'bob',
+ *   test: function(){
+ *     this.parent.test();
+ *   }
+ * });
+ *
+ * var a = new B();
+ * a.test();
+ *
+ */
+var cclass = (function($, undefined) {
+  function extend(ext_fn, attrs) {
+    function parent() {};
+    function fn() {
+      ext_fn.apply(this, arguments);
+    };
+    parent.prototype = this.prototype;
+    fn.prototype = new parent();
+    fn.prototype.constructor = fn;
+    fn.extend = extend;
+    $.extend(fn.prototype, attrs || {}, {
+        parent: parent.prototype
+    });
+    return fn;
+  }
+
+  return {
+    create: function(fn, attrs) {
+      $.extend(fn.prototype, attrs || {});
+      fn.extend = extend;
+      fn.parent = undefined;
+      return fn;
+    },
+    extend: extend
+  };
+})($, undefined);

+ 213 - 0
apps/sqoop/static/js/koify.js

@@ -0,0 +1,213 @@
+// 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.
+
+
+var koify = (function($, undefined) {
+  function KOClass(fn, attrs) {
+    var _cclass = cclass.create(fn, attrs);
+
+    function reset_extends(fn, cclass) {
+      cclass._extend = cclass.extend;
+      cclass.extend = function() {
+        var args = [fn];
+        args.push.apply(args, arguments);
+        var _fn = reset_extends(fn, cclass._extend.apply(cclass, args));
+        return _fn;
+      }
+      return cclass;
+    }
+
+    return reset_extends(fn, _cclass);
+  }
+
+  var Model = KOClass(function(attrs) {
+    var self = this;
+    var attrs = attrs || {};
+    $.extend(self, self.initialize(attrs));
+    $.extend(self, self);  // Add attributes in prototype chain
+  }, {
+    initialize: function(attrs) {
+      return attrs;
+    }
+  });
+
+
+  var Node = KOClass(function(options) {
+    var self = this;
+    var options = options || {};
+
+    self.options = options;
+    self.initialize(options);
+  }, {
+    persist: true,
+    identifier: undefined,
+    modelClass: undefined,
+
+    initialize: function(options) {
+      var self = this;
+
+      if (options.modelDict) {
+        self.model = new self.modelClass(options.modelDict);
+      } else {
+        self.model = $.extend(true, options.model, {});
+      }
+
+      if ('__ko_mapping__' in self) {
+        ko.mapping.fromJS(self.model, {
+          'ignore': ['parent', 'initialize']
+        }, self);
+      } else {
+        var mapped = ko.mapping.fromJS(self.model, {
+          'ignore': ['parent', 'initialize']
+        });
+        $.extend(self, mapped);
+      }
+    },
+    request: function(url, options) {
+      var self = this;
+      var request = $.extend({
+        url: url,
+        dataType: 'json',
+        type: 'GET',
+        success: $.noop,
+        error: $.noop
+      }, options || {});
+      $.ajax(request);
+    },
+    load: function(options) {
+      var self = this;
+      $(document).trigger('load.' + self.identifier, [self, options]);
+      var options = $.extend({
+        success: function(data) {
+          switch(data.status) {
+            case 0:
+              self.initialize({modelDict: data[self.identifier]});
+              $(document).trigger('loaded.' + self.identifier, [self, options]);
+            break;
+            default:
+            case 1:
+              $(document).trigger('load_fail.'  + self.identifier, [self, options, data]);
+            break;
+          }
+        }
+      }, options);
+      self.request(self.loadUrl(), options);
+    },
+    save: function(options) {
+      if (!this.persist) {
+        return;
+      }
+
+      var self = this;
+      $(document).trigger('save.' + self.identifier, [self, options]);
+      var options = $.extend({
+        type: 'POST',
+        data: self.getData(),
+        success: function(data) {
+          switch(data.status) {
+            case 0:
+              $(document).trigger('saved.' + self.identifier, [self, options, data]);
+            break;
+            default:
+            case 1:
+              $(document).trigger('save_fail.' + self.identifier, [self, options, data]);
+            break;
+          }
+        }
+      }, options);
+      self.request(self.saveUrl(), options);
+    },
+    clone: function(options) {
+      if (!this.persist) {
+        return;
+      }
+
+      var self = this;
+      $(document).trigger('clone.' + self.identifier, [self, options]);
+      var options = $.extend({
+        type: 'POST',
+        success: function(data) {
+          switch(data.status) {
+            case 0:
+              $(document).trigger('cloned.' + self.identifier, [self, options, data]);
+            break;
+            default:
+            case 1:
+              $(document).trigger('clone_fail.' + self.identifier, [self, options, data]);
+            break;
+          }
+        }
+      }, options);
+      self.request(self.cloneUrl(), options);
+    },
+    delete: function(options) {
+      if (!this.persist) {
+        return;
+      }
+
+      var self = this;
+      $(document).trigger('delete.' + self.identifier, [self, options]);
+      var options = $.extend({
+        type: 'POST',
+        success: function(data) {
+          switch(data.status) {
+            case 0:
+              $(document).trigger('deleted.' + self.identifier, [self, options, data]);
+            break;
+            default:
+            case 1:
+              $(document).trigger('delete_fail.' + self.identifier, [self, options, data]);
+            break;
+          }
+        }
+      }, options);
+      self.request(self.deleteUrl(), options);
+    },
+    getData: function() {
+      var self = this;
+      data = {};
+      data[self.identifier] = ko.mapping.toJSON(self);
+      return data;
+    },
+    loadUrl: function(persist) {
+      var self = this;
+      return self.base_url + ((persist) ? self.id() : '');
+    },
+    saveUrl: function() {
+      var self = this;
+      if (self.id() > -1) {
+        return self.base_url +  self.id();
+      } else {
+        return self.base_url;
+      }
+    },
+    cloneUrl: function() {
+      var self = this;
+      return self.base_url +  self.id() + '/clone';
+    },
+    deleteUrl: function() {
+      var self = this;
+      return self.base_url +  self.id() + '/delete';
+    }
+  });
+
+
+  return {
+    KOClass: KOClass,
+    Node: Node,
+    Model: Model
+  }
+})($, undefined);

+ 72 - 0
apps/sqoop/static/js/sqoop.connections.js

@@ -0,0 +1,72 @@
+// 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.
+
+
+var connections = (function($) {
+  var ConnectionModel = koify.Model.extend({
+    'id': -1,
+    'updated': null,
+    'created': null,
+    'name': null,
+    'connector': [],
+    'connector_id': 0,
+    'framework': [],
+    'initialize': function(attrs) {
+      var self = this;
+      var attrs = $.extend(true, attrs, {});
+      attrs = transform_keys(attrs, {
+        'connector-id': 'connector_id'
+      });
+      attrs = transform_values(attrs, {
+        'connector': to_forms,
+        'framework': to_forms
+      });
+      return attrs;
+    }
+  });
+
+  var Connection = koify.Node.extend({
+    'identifier': 'connection',
+    'persists': true,
+    'modelClass': ConnectionModel,
+    'base_url': '/sqoop/api/connections/',
+    'initialize': function() {
+      var self = this;
+      self.parent.initialize.apply(self, arguments);
+      self.selected = ko.observable();
+      self.persisted = ko.computed(function() {
+        return self.id() > -1;
+      });
+    }
+  });
+
+  function fetch_connections(options) {
+    $(document).trigger('load.connections', [options]);
+    var request = $.extend({
+      url: '/sqoop/api/connections/',
+      dataType: 'json',
+      type: 'GET',
+      success: fetcher_success('connections', Connection, options)
+    }, options || {});
+    $.ajax(request);
+  }
+
+  return {
+    'ConnectionModel': ConnectionModel,
+    'Connection': Connection,
+    'fetchConnections': fetch_connections
+  }
+})($);

+ 79 - 0
apps/sqoop/static/js/sqoop.connectors.js

@@ -0,0 +1,79 @@
+// 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.
+
+
+var connectors = (function($) {
+  var ConnectorModel = koify.Model.extend({
+    'id': -1,
+    'name': null,
+    'class': null,
+    'job_forms': {
+      'IMPORT': [],
+      'EXPORT': []
+    },
+    'con_forms': [],
+    'version': null,
+    'resources': {},
+    'initialize': function(attrs) {
+      var self = this;
+      var attrs = $.extend(true, attrs, {});
+      attrs = transform_keys(attrs, {
+        'job-forms': 'job_forms',
+        'con-forms': 'con_forms'
+      });
+      attrs = transform_values(attrs, {
+        'con_forms': to_forms,
+        'job_forms': function(key, value) {
+          transform_values(value, {
+            'IMPORT': to_forms,
+            'EXPORT': to_forms
+          });
+          return value;
+        }
+      });
+      return attrs;
+    }
+  });
+
+  var Connector = koify.Node.extend({
+    'identifier': 'connector',
+    'persist': false,
+    'modelClass': ConnectorModel,
+    'base_url': '/sqoop/api/connectors/',
+    'initialize': function() {
+      var self = this;
+      self.parent.initialize.apply(self, arguments);
+      self.selected = ko.observable();
+    }
+  });
+
+  function fetch_connectors(options) {
+    $(document).trigger('load.connectors', [options]);
+    var request = $.extend({
+      url: '/sqoop/api/connectors/',
+      dataType: 'json',
+      type: 'GET',
+      success: fetcher_success('connectors', Connector, options)
+    }, options || {});
+    $.ajax(request);
+  }
+
+  return {
+    'ConnectorModel': ConnectorModel,
+    'Connector': Connector,
+    'fetchConnectors': fetch_connectors
+  };
+})($);

+ 64 - 0
apps/sqoop/static/js/sqoop.framework.js

@@ -0,0 +1,64 @@
+// 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.
+
+
+
+var framework = (function($) {
+  var FrameworkModel = koify.Model.extend({
+    'id': 1,
+    'job_forms': {
+      'IMPORT': [],
+      'EXPORT': []
+    },
+    'con_forms': [],
+    'resources': {},
+    'initialize': function(attrs) {
+      var self = this;
+      var attrs = $.extend(true, attrs, {});
+      attrs = transform_keys(attrs, {
+        'job-forms': 'job_forms',
+        'con-forms': 'con_forms'
+      });
+      attrs = transform_values(attrs, {
+        'con_forms': to_forms,
+        'job_forms': function(key, value) {
+          transform_values(value, {
+            'IMPORT': to_forms,
+            'EXPORT': to_forms
+          });
+          return value;
+        }
+      });
+      return attrs;
+    }
+  });
+
+  var Framework = koify.Node.extend({
+    'identifier': 'framework',
+    'persists': false,
+    'modelClass': FrameworkModel,
+    'base_url': '/sqoop/api/framework/',
+    'initialize': function() {
+      var self = this;
+      self.parent.initialize.apply(self, arguments);
+    }
+  });
+
+  return {
+    'FrameworkModel': FrameworkModel,
+    'Framework': Framework
+  }
+})($);

+ 200 - 0
apps/sqoop/static/js/sqoop.jobs.js

@@ -0,0 +1,200 @@
+// 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.
+
+
+
+var jobs = (function($) {
+  var job_registry = {};
+
+  var JobModel = koify.Model.extend({
+    'id': -1,
+    'name': null,
+    'type': 'IMPORT',
+    'connector_id': 0,
+    'connection_id': 0,
+    'connector': [],
+    'framework': [],
+    'created': null,
+    'updated': null,
+    'initialize': function(attrs) {
+      var self = this;
+      var attrs = $.extend(true, attrs, {});
+      attrs = transform_keys(attrs, {
+        'connector-id': 'connector_id',
+        'connection-id': 'connection_id'
+      });
+      attrs = transform_values(attrs, {
+        'connector': to_forms,
+        'framework': to_forms
+      });
+      return attrs;
+    }
+  });
+
+  var Job = koify.Node.extend({
+    'identifier': 'job',
+    'persists': true,
+    'modelClass': JobModel,
+    'base_url': '/sqoop/api/jobs/',
+    'initialize': function() {
+      var self = this;
+      self.parent.initialize.apply(self, arguments);
+      self.createdFormatted = ko.computed(function() {
+        if (self.created()) {
+          return moment(self.created()).format('MM/DD/YYYY hh:mm A');
+        } else {
+          return 0;
+        }
+      });
+      self.updatedFormatted = ko.computed(function() {
+        if (self.updated()) {
+          return moment(self.updated()).format('MM/DD/YYYY hh:mm A');
+        } else {
+          return 0;
+        }
+      });
+      self.selected = ko.observable();
+      self.submission = ko.computed({
+        owner: self,
+        read: function () {
+          return submissions.setDefaultSubmission(self.id());
+        },
+        write: function (submission) {
+          submissions.putSubmission(submission);
+          if (self.runningInterval == 0 && $.inArray(submission.status(), ['BOOTING', 'RUNNING']) != -1) {
+            self.runningInterval = setInterval(function() {
+              if (!self.isRunning()) {
+                clearInterval(self.runningInterval);
+                self.runningInterval = 0;
+              }
+
+              self.getStatus();
+            }, 1000);
+          }
+
+          self.id.valueHasMutated();
+        }
+      });
+      self.persisted = ko.computed(function() {
+        return self.id() > -1;
+      });
+      self.isRunning = ko.computed(function() {
+        return self.submission() && $.inArray(self.submission().status(), ['BOOTING', 'RUNNING']) > -1;
+      });
+
+      self.runningInterval = 0;
+    },
+    'start': function(options) {
+      var self = this;
+      $(document).trigger('start.job', [options, self]);
+      var options = $.extend({
+        type: 'POST',
+        success: function(data) {
+          switch(data.status) {
+            case 0:
+              self.submission(new submissions.Submission({modelDict: data.submission}));
+              $(document).trigger('started.job', [self, options, data]);
+            break;
+            default:
+            case 1:
+              var error = data.errors[0];
+              $(document).trigger('start_fail.job', [self, options, error.exception]);
+            break;
+          }
+        }
+      }, options);
+      self.request('/sqoop/api/jobs/' + self.id() + '/start', options);
+    },
+    'stop': function(options) {
+      var self = this;
+      $(document).trigger('start.job', [options, self]);
+      var options = $.extend({
+        type: 'POST',
+        success: function(data) {
+          switch(data.status) {
+            case 0:
+              self.submission(new submissions.Submission({modelDict: data.submission}));
+              $(document).trigger('stopped.job', [self, options, data.submission]);
+            break;
+            default:
+            case 1:
+              $(document).trigger('stop_fail.job', [self, options, data]);
+            break;
+          }
+        }
+      }, options);
+      self.request('/sqoop/api/jobs/' + self.id() + '/stop', options);
+    },
+    'getStatus': function(options) {
+      var self = this;
+      $(document).trigger('get_status.job', [self, options]);
+      var options = $.extend({
+        type: 'GET',
+        success: function(data) {
+          switch(data.status) {
+            case 0:
+              self.submission(new submissions.Submission({modelDict: data.submission}));
+              $(document).trigger('got_status.job', [self, options, data.submission]);
+            break;
+            default:
+            case 1:
+              $(document).trigger('get_status_fail.job', [self, options, data]);
+            break;
+          }
+        }
+      }, options);
+      self.request('/sqoop/api/jobs/' + self.id() + '/status', options);
+    }
+  });
+
+  function fetch_jobs(options) {
+    $(document).trigger('load.jobs', [options]);
+    var request = $.extend({
+      url: '/sqoop/api/jobs/',
+      dataType: 'json',
+      type: 'GET',
+      success: fetcher_success('jobs', Job, options)
+    }, options || {});
+    $.ajax(request);
+  }
+
+  function put_job(job) {
+    job_registry[job.id()] = job;
+  }
+
+  function get_job(id) {
+    return job_registry[id];
+  }
+
+  function sync_jobs(jobs) {
+    job_registry = {};
+    $.each(jobs, function(index, job) {
+      put_job(job);
+    });
+  }
+
+  $(document).on('loaded.jobs', function(e, nodes, options) {
+    sync_jobs(nodes);
+  });
+
+  return {
+    'JobModel': JobModel,
+    'Job': Job,
+    'fetchJobs': fetch_jobs,
+    'putJob': put_job,
+    'getJob': get_job,
+  }
+})($);

+ 373 - 0
apps/sqoop/static/js/sqoop.js

@@ -0,0 +1,373 @@
+// 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.
+
+
+//// Menu items
+function highlightMainMenu(mainSection) {
+  $(".nav-pills li").removeClass("active");
+  $("a[href='#" + mainSection + "']").parent().addClass("active");
+}
+
+function highlightMenu(section) {
+  $(".nav-list li").removeClass("active");
+  $("li[data-section='" + section + "']").addClass("active");
+}
+
+function showMainSection(mainSection) {
+  if ($("#" + mainSection).is(":hidden")) {
+    $(".mainSection").hide();
+    $("#" + mainSection).show();
+    highlightMainMenu(mainSection);
+  }
+}
+
+function showSection(mainSection, section) {
+  showMainSection(mainSection);
+  if ($("#" + section).is(":hidden")) {
+    $(".section").hide();
+    $("#" + section).show();
+    highlightMenu(section);
+  }
+}
+
+function showSubsection(mainSection, section, subSection) {
+  showSection(mainSection, section);
+  if ($("#" + subSection).is(":hidden")) {
+    $(".subSection").hide();
+    $("#" + subSection).show();
+  }
+}
+
+
+//// Constructors
+function create_connection(attrs, options) {
+  var options = options || {};
+  options.modelDict = attrs || {};
+  var node = new connections.Connection(options);
+  // Need a copy of the forms so that when editing
+  // we don't re-use forms.
+  $.each(viewModel.connector().con_forms(), function(index, form) {
+    node.connector.push($.extend(true, form, {}));
+  });
+  $.each(viewModel.framework().con_forms(), function(index, form) {
+    node.framework.push($.extend(true, form, {}));
+  });
+  return node;
+}
+
+
+function create_job(attrs, options) {
+  var options = options || {};
+  options.modelDict = attrs || {};
+  var node = new jobs.Job(options);
+  return node;
+}
+
+
+//// View Models
+var viewModel = new (function() {
+  var self = this;
+
+  self.jobWizard = new wizard.Wizard();
+  self.errors = ko.observable({});
+  self.warnings = ko.observable({});
+  self.framework = ko.observable();
+  self.connectors = ko.observableArray();
+  self.connections = ko.observableArray();
+  self.jobs = ko.observableArray();
+  self.connection = ko.observable();
+  self.editConnection = ko.observable();
+  self.filter = ko.observable("");
+  self.isDirty = ko.observable(false);
+  // Must always have a value.
+  self.connector = ko.computed(function() {
+    // Fall back to first connector so that a connector is selected when we are creating a connection.
+    if (!self.connection()) {
+      return self.connectors()[0];
+    }
+    var connectorArr = ko.utils.arrayFilter(self.connectors(), function (connector) {
+      return connector.id() == self.connection().connector();
+    });
+    return (connectorArr.length > 0) ? connectorArr[0] : self.connectors()[0];
+  });
+  self.persistedJobs = ko.computed(function() {
+    return ko.utils.arrayFilter(self.jobs(), function (job) {
+      return job.persisted();
+    }); 
+  });
+  self.persistedConnections = ko.computed(function() {
+    return ko.utils.arrayFilter(self.connections(), function (connection) {
+      return connection.persisted();
+    });
+  });
+  self.filteredJobs = ko.computed(function() {
+    var filter = self.filter().toLowerCase();
+    return ko.utils.arrayFilter(self.persistedJobs(), function (job) {
+      if (job.name()) {
+        return job.name().toLowerCase().indexOf(filter) > -1;
+      } else {
+        return false;
+      }
+    });
+  });
+  self.selectedJobs = ko.computed(function() {
+    return ko.utils.arrayFilter(self.jobs(), function (job) {
+      return job.selected();
+    });
+  });
+  self.job = ko.computed(function() {
+    if (self.selectedJobs().length > 0) {
+      return self.selectedJobs()[0];
+    }
+    return null;
+  });
+  self.allJobsSelected = ko.computed(function () {
+    return self.selectedJobs().length > 0 && self.selectedJobs().length == self.jobs().length;
+  });
+  self.isLoading = ko.computed(function() {
+    return !self.framework() && self.connectors().length == 0;
+  });
+
+
+  // Update forms for connectors, jobs, and connections.
+  // In sqoop, the connector and framework provides
+  // attributes that need to be filled in for connections
+  // and jobs. The framework and connector will provide
+  // different forms for IMPORT and EXPORT jobs.
+  self.framework.subscribe(function(value) {
+    // We assume that the framework components
+    // are not going to change so we do not update connection
+    // and job objects unless they lack forms.
+    if (value) {
+      if (self.editConnection() && self.editConnection().framework().length == 0) {
+        self.editConnection().framework(value.con_forms());
+      }
+      if (self.job() && self.job().framework().length == 0) {
+        var type = self.job().type().toUpperCase();
+        self.job().framework(value.job_forms[type]());
+      }
+    }
+  });
+
+  self.connector.subscribe(function(value) {
+    // We assume that the connectors component
+    // are not going to change so we do not update connection
+    // and job objects unless they lack forms.
+    if (value) {
+      if (self.editConnection() && self.editConnection().connector().length == 0) {
+        self.editConnection().connector(value.con_forms());
+      }
+      if (self.job() && self.job().connector().length == 0) {
+        var type = self.job().type().toUpperCase();
+        self.job().connector(value.job_forms[type]());
+      }
+    }
+  });
+
+  // Forms are swapped between IMPORT and EXPORT types.
+  // Use of "beforeChange" subscription event to
+  // remove subscriptions and help with swapping.
+  var job_type_subscriptions = [];
+  var old_connector_forms = {
+    'IMPORT': null,
+    'EXPORT': null
+  };
+  var old_framework_forms = {
+    'IMPORT': null,
+    'EXPORT': null
+  };
+  self.job.subscribe(function(old_job) {
+    if (job_type_subscriptions) {
+      $.each(job_type_subscriptions, function(index, subscription) {
+        subscription.dispose();
+      });
+    }
+  }, self, "beforeChange");
+  self.job.subscribe(function(job) {
+    if (job) {
+      var type = job.type().toUpperCase();
+
+      if (self.connector() && job.connector().length == 0) {
+        job.connector(self.connector().job_forms[type]());
+      }
+
+      if (self.framework() && job.framework().length == 0) {
+        job.framework(self.framework().job_forms[type]());
+      }
+
+      job_type_subscriptions.push(job.type.subscribe(function(new_type) {
+        var connector = old_connector_forms[new_type] || self.connector().job_forms[new_type]();
+        var framework = old_framework_forms[new_type] || self.framework().job_forms[new_type]();
+        old_connector_forms[new_type] = null;
+        old_framework_forms[new_type] = null;
+        job.connector(connector);
+        job.framework(framework);
+      }));
+
+      job_type_subscriptions.push(job.type.subscribe(function(old_type) {
+        if (job.connector().length > 0) {
+          old_connector_forms[old_type] = job.connector();
+        }
+        if (job.framework().length > 0) {
+          old_framework_forms[old_type] = job.framework();
+        }
+      }, self, "beforeChange"));
+    }
+  });
+
+  self.editConnection.subscribe(function() {
+    if (self.editConnection()) {
+      if (self.connector() && self.editConnection().connector().length == 0) {
+        self.editConnection().connector(self.connector().con_forms());
+      }
+      if (self.framework() && !self.editConnection().framework().length == 0) {
+        self.editConnection().framework(self.framework().con_forms());
+      }
+    }
+  });
+
+  self.newConnection = function() {
+    var self = this;
+    if (!self.connection() || self.connection().persisted()) {
+      var conn = create_connection();
+      self.editConnection(conn);
+    }
+  };
+
+  self.saveConnection = function() {
+    var connection = self.editConnection();
+    if (connection) {
+      connection.connector_id(self.connector().id());
+      connection.save();
+    }
+  };
+
+  self.chooseConnectionById = function(id) {
+    $.each(self.connections(), function(index, conn) {
+      if (conn.id() == id) {
+        self.editConnection(conn);
+      }
+    });
+  };
+
+  self.deselectAllConnections = function() {
+    $.each(self.connections(), function(index, value) {
+      value.selected(false);
+    });
+  };
+
+  self.newJob = function() {
+    var self = this;
+    if (!self.job() || self.job().persisted()) {
+      var job = create_job();
+      self.jobs.push(job);
+      self.deselectAllJobs();
+      job.selected(true);
+    }
+  };
+
+  self.saveJob = function() {
+    var job = self.job();
+    if (job) {
+      job.connector_id(self.connector().id());
+      job.connection_id(self.connection().id());
+      job.save();
+    }
+  };
+
+  self.chooseJobById = function(id) {
+    $.each(self.jobs(), function(index, job) {
+      if (job.id() == id) {
+        job.selected(true);
+      } else {
+        job.selected(false);
+      }
+    });
+  };
+
+  self.toggleAllJobsSelected = function() {
+    if (!self.allJobsSelected()) {
+      self.selectAllJobs();
+    } else {
+      self.deselectAllJobs();
+    }
+  };
+
+  self.selectAllJobs = function() {
+    $.each(self.jobs(), function(index, value) {
+      value.selected(true);
+    });
+  };
+
+  self.deselectAllJobs = function() {
+    $.each(self.jobs(), function(index, value) {
+      value.selected(false);
+    });
+  };
+
+  self.label = function(component, name) {
+    var self = this;
+    return self[component]().resources[name + '.label'];
+  };
+
+  self.help = function(component, name) {
+    var self = this;
+    return self[component]().resources[name + '.help'];
+  }
+})();
+
+//// Event handling
+function set_framework(e, framework, options) {
+  viewModel.framework(framework);
+}
+
+function set_connectors(e, connectors, options) {
+  viewModel.connectors(connectors);
+}
+
+function set_connections(e, connections, options) {
+  viewModel.connections(connections);
+  if (viewModel.connections().length > 0) {
+    viewModel.connection(viewModel.connections()[0]);
+  }
+}
+
+function set_jobs(e, jobs, options) {
+  viewModel.jobs.removeAll();
+  viewModel.jobs(jobs);
+}
+
+function update_job_submissions(e, submissions, options) {
+  $.each(submissions, function(index, submission) {
+    var job = jobs.getJob(submission.job());
+    if (job) {
+      job.submission(submission);
+    }
+  });
+}
+
+$(document).on('loaded.framework', set_framework);
+$(document).on('loaded.connectors', set_connectors);
+$(document).on('loaded.connections', set_connections);
+$(document).on('loaded.jobs', set_jobs);
+$(document).on('loaded.submissions', update_job_submissions);
+
+$(document).on('saved.connection', function(){ connections.fetchConnections(); });
+$(document).on('saved.job', function() { jobs.fetchJobs(); });
+$(document).on('cloned.connection', function(){ connections.fetchConnections(); });
+$(document).on('cloned.job', function() { jobs.fetchJobs(); });
+$(document).on('deleted.connection', function(){ connections.fetchConnections(); });
+$(document).on('deleted.job', function() { jobs.fetchJobs(); });

+ 93 - 0
apps/sqoop/static/js/sqoop.models.js

@@ -0,0 +1,93 @@
+// 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.
+
+
+function transform_keys(model, keys_dict) {
+  $.each(keys_dict, function(key, new_key) {
+    if (key in model) {
+      model[new_key] = model[key];
+      delete model[key];
+    }
+  });
+  return model;
+}
+
+function transform_values(model, func_dict) {
+  $.each(func_dict, function(key, f) {
+    if (key in model) {
+      model[key] = f(key, model[key]);
+    }
+  });
+  return model;
+}
+
+function to_form(value) {
+  return new FormModel(value);
+}
+
+function to_forms(key, value) {
+  $.each(value, function(index, form_dict) {
+    value[index] = to_form(form_dict);
+  });
+  return value;
+}
+
+function to_input(value) {
+  return new InputModel(value);
+}
+
+function to_inputs(key, value) {
+  $.each(value, function(index, input_dict) {
+    value[index] = to_input(input_dict);
+  });
+  return value;
+}
+
+var FormModel = koify.Model.extend({
+  'id': -1,
+  'inputs': [],
+  'name': null,
+  'type': null,
+  'initialize': function(attrs) {
+    var self = this;
+    var attrs = $.extend(true, attrs, {});
+    attrs = transform_values(attrs, {
+      'inputs': to_inputs
+    });
+    return attrs;
+  }
+});
+
+var InputModel = koify.Model.extend({
+  'id': -1,
+  'name': null,
+  'type': null,
+  'size': -1,
+  'sensitive': false,
+  'values': null,
+  'value': null,
+  'initialize': function(attrs) {
+    var self = this;
+    var attrs = $.extend(true, attrs, {});
+    if ('values' in attrs) {
+      attrs['values'] = attrs['values'].split(',');
+    }
+    if ('value' in attrs) {
+      attrs.value = decodeURIComponent(attrs.value);
+    }
+    return attrs;
+  }
+});

+ 120 - 0
apps/sqoop/static/js/sqoop.submissions.js

@@ -0,0 +1,120 @@
+// 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.
+
+
+var submissions = (function($) {
+  var submission_registry = {};
+
+  var SubmissionModel = koify.Model.extend({
+    'job': -1,
+    'progress': 0.0,
+    'status': 'NEVER_EXECUTED',
+    'creation_date': 0,
+    'last_update_date': 0,
+    'external_id': null,
+    'external_link': null,
+    'initialize': function(attrs) {
+      var self = this;
+      var attrs = $.extend(true, attrs, {});
+      attrs = transform_keys(attrs, {
+        'creation-date': 'creation_date',
+        'last-update-date': 'last_update_date',
+        'external-id': 'external_id',
+        'external-link': 'external_link'
+      });
+      return attrs;
+    }
+  });
+
+
+  var Submission = koify.Node.extend({
+    'identifier': 'submission',
+    'persists': false,
+    'modelClass': SubmissionModel,
+    'base_url': '/sqoop/api/submissions/',
+    'initialize': function() {
+      var self = this;
+      self.parent.initialize.apply(self, arguments);
+      self.createdFormatted = ko.computed(function() {
+        if (self.creation_date()) {
+          return moment(self.creation_date()).format('MM/DD/YYYY hh:mm A');
+        } else {
+          return 0;
+        }
+      });
+      self.updatedFormatted = ko.computed(function() {
+        if (self.last_update_date()) {
+          return moment(self.last_update_date()).format('MM/DD/YYYY hh:mm A');
+        } else {
+          return 0;
+        }
+      });
+      self.progressFormatted = ko.computed(function() {
+        return (self.progress() * 100) + "%";
+      });
+      self.selected = ko.observable();
+    }
+  });
+
+  function fetch_submissions(options) {
+    $(document).trigger('load.submissions', [options]);
+    var request = $.extend({
+      url: '/sqoop/api/submissions/',
+      dataType: 'json',
+      type: 'GET',
+      success: fetcher_success('submissions', Submission, options)
+    }, options || {});
+    $.ajax(request);
+  }
+
+  function put_submission(submission) {
+    if (submission_registry[submission.job()]) {
+      if (submission_registry[submission.job()].creation_date() < submission.creation_date() || 
+          (submission_registry[submission.job()].creation_date() == submission.creation_date() && submission_registry[submission.job()].last_update_date() < submission.last_update_date())) {
+        submission_registry[submission.job()] = submission;
+      }
+    } else {
+      submission_registry[submission.job()] = submission;
+    }
+  }
+
+  function get_submission(job_id) {
+    return submission_registry[job_id];
+  }
+
+  function set_default_submission(job_id) {
+    var submission = get_submission(job_id);
+    if (!submission) {
+      put_submission(new Submission({modelDict: {job: job_id}}));
+    }
+    return get_submission(job_id);
+  }
+
+  $(document).on('loaded.submissions', function(e, nodes, options) {
+    $.each(nodes, function(index, submission) {
+      put_submission(submission);
+    });
+  });
+
+  return {
+    'SubmissionModel': SubmissionModel,
+    'Submission': Submission,
+    'fetchSubmissions': fetch_submissions,
+    'putSubmission': put_submission,
+    'getSubmission': get_submission,
+    'setDefaultSubmission': set_default_submission
+  }
+})($);

+ 50 - 0
apps/sqoop/static/js/sqoop.utils.js

@@ -0,0 +1,50 @@
+// 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.
+
+
+//// Get collections utils
+function fetcher_success(name, Node, options) {
+  return function(data) {
+    switch(data.status) {
+      case -1:
+      $(document).trigger('connection_error.' + name, [options, data])
+      break;
+      case 0:
+        var nodes = [];
+        $.each(data[name], function(index, model_dict) {
+          var node = new Node({modelDict: model_dict});
+          nodes.push(node);
+        });
+        $(document).trigger('loaded.' + name, [nodes, options]);
+      break;
+      default:
+      case 1:
+        $(document).trigger('load_error.' + name, [options, data]);
+      break;
+    }
+  };
+}
+
+//// KO utils
+ko.bindingHandlers.routie = {
+  init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
+    $(element).click(function() {
+      var url = ko.utils.unwrapObservable(valueAccessor());
+      routie(url);
+      return false;
+    });
+  }
+};

+ 109 - 0
apps/sqoop/static/js/sqoop.wizard.js

@@ -0,0 +1,109 @@
+// 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.
+
+
+var wizard = (function($) {
+  var Wizard = koify.KOClass(function(options) {
+    var self = this;
+    var options = options || {};
+
+    self.page_lookup = {};
+    self.pages = ko.observableArray();
+    self.index = ko.observable(0);
+    self.page = ko.computed(function() {
+      if (self.pages().length > 0 && self.index() < self.pages().length && self.index() >= 0) {
+        return self.pages()[self.index()];
+      } else {
+        return null;
+      }
+    });
+    self.hasNext = ko.computed(function() {
+      var next_index = self.index() + 1;
+      return self.pages().length > 0 && next_index < self.pages().length;
+    });
+    self.hasPrevious = ko.computed(function() {
+      var previous_index = self.index() - 1;
+      return self.pages().length > 0 && previous_index >= 0;
+    });
+    self.nextIndex = ko.computed(function() {
+      if (self.hasNext()) {
+        return self.index() + 1;
+      } else {
+        return -1;
+      }
+    });
+    self.previousIndex = ko.computed(function() {
+      if (self.hasPrevious()) {
+        return self.index() - 1;
+      } else {
+        return -1;
+      }
+    });
+
+    self.initialize(options);
+  }, {
+    initialize: function(options) {
+      var self = this;
+      self.options = options || {};
+      if (self.options.pages) {
+        self.pages(options.pages);
+      }
+      if (self.options.index) {
+        self.index(self.options.index);
+      }
+    },
+    addPage: function(page) {
+      var self = this;
+      self.page_lookup[page.identifier()] = self.pages().length;
+      self.pages.push(page);
+    },
+    getIndex: function(identifier) {
+      var self = this;
+      return self.page_lookup[identifier];
+    },
+    clearPages: function() {
+      var self = this;
+      self.pages([]);
+      self.index(0);
+    }
+  });
+
+  var Page = koify.KOClass(function(options) {
+    var self = this;
+    var options = options || {};
+
+    self.identifier = ko.observable();
+    self.caption = ko.observable();
+    self.template = ko.observable();
+    self.node = ko.observable();
+
+    self.initialize(options);
+  }, {
+    initialize: function(options) {
+      var self = this;
+      self.options = options || {};
+      self.identifier(self.options.identifier);
+      self.caption(self.options.caption);
+      self.node(self.options.node);
+      self.template(self.options.template);
+    }
+  });
+
+  return {
+    'Wizard': Wizard,
+    'Page': Page
+  };
+})($);

+ 9 - 0
desktop/conf.dist/hue.ini

@@ -448,6 +448,15 @@
   ## remote_data_dir=/user/hue/pig/examples
 
 
+###########################################################################
+# Settings to configure Sqoop
+###########################################################################
+
+[sqoop]
+  # Sqoop server URL
+  ## server_url=http://localhost:12000/sqoop
+
+
 ###########################################################################
 # Settings to configure Proxy
 ###########################################################################

+ 9 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -452,6 +452,15 @@
   ## remote_data_dir=/user/hue/pig/examples
 
 
+###########################################################################
+# Settings to configure Sqoop
+###########################################################################
+
+[sqoop]
+  # Sqoop server URL
+  ## server_url=http://localhost:12000/sqoop
+
+
 ###########################################################################
 # Settings to configure Proxy
 ###########################################################################

+ 0 - 31
desktop/core/ext-py/python-ldap-2.3.13/Build/build-openbsd/Makefile

@@ -1,31 +0,0 @@
-# $Id: Makefile,v 1.3 2000/08/20 15:04:23 leonard Exp $
-
-WRKDIST=		${.CURDIR}/../..
-VERSION!=		sh ${WRKDIST}/Build/version.sh
-
-PKGNAME=		python-ldap-${VERSION}
-RUN_DEPENDS=		python:lang/python
-CATEGORIES=		misc
-MAINATINER=		leonard@users.sourceforge.net
-HOMEPAGE=		http://python-ldap.sourceforge.net/
-
-FAKE=			Yes
-CONFIGURE_STYLE=	gnu
-SEPARATE_BUILD=		Yes
-EXTRACT_ONLY=	
-
-CONFIGURE_ARGS+=	--with-ldap=${LOCALBASE}
-
-do-fetch:;
-
-#-- put package on sourceforge ftp server
-export-ftp: ${PKGFILE}
-	scp ${PKGFILE} python-ldap.sourceforge.net:/home/groups/ftp/pub/python-ldap/${PKGNAME}-${OPSYS}-${ARCH}${PKG_SUFX}
-
-post-build:
-	sed -e 's,^${PREFIX}/,,' <${WRKBUILD}/filelist >${WRKBUILD}/filelist2
-
-SED_PLIST+=	|sed -e '/^%%filelist/r${WRKBUILD}/filelist2' -e '//d'
-ALL_TARGET=	build filelist
-
-.include <bsd.port.mk>

+ 0 - 1
desktop/core/ext-py/python-ldap-2.3.13/Build/build-openbsd/pkg/COMMENT

@@ -1 +0,0 @@
-Python interface to LDAP

+ 0 - 2
desktop/core/ext-py/python-ldap-2.3.13/Build/build-openbsd/pkg/DESCR

@@ -1,2 +0,0 @@
-This Python library provides access to the LDAP (Lightweight Directory Access 
-Protocol) RFC1823 C interface.

+ 0 - 3
desktop/core/ext-py/python-ldap-2.3.13/Build/build-openbsd/pkg/PLIST

@@ -1,3 +0,0 @@
-@comment $OpenBSD$
-%%filelist
-@dirrm lib/python1.6/site-packages/python-ldap

+ 0 - 35
desktop/core/ext-py/python-ldap-2.3.13/Build/setup.cfg.mingw

@@ -1,35 +0,0 @@
-# Win32 setup.cfg
-# You have to edit this file to reflect your system configuration
-#
-# $Id: setup.cfg.mingw,v 1.1 2005/01/25 18:54:46 stroeder Exp $
-
-# Section for compiling the C extension module
-# for wrapping OpenLDAP 2 libs
-
-# Platform: Win32
-# Compile environment: MinGW
-[_ldap]
-class = OpenLDAP2
-maintainer = Mauro Cicognini
-defines = WIN32
-
-# modify these to fit your local configuration
-library_dirs = C:/msys/1.0/home/mcicogni/openldap-mingw-build-4/openldap-2.2.18/libraries/libldap_r/.libs C:/msys/1.0/home/mcicogni/openldap-mingw-build-4/openldap-2.2.18/libraries/liblber/.libs C:\msys\1.0\home\mcicogni\openldap-mingw-build-4\openssl-0.9.7e
-include_dirs = C:/msys/1.0/home/mcicogni/openldap-mingw-build-4/openldap-2.2.18/include
-
-extra_compile_args = 
-extra_objects = 
-
-libs = ldap_r lber ssl crypto ws2_32 gdi32
-
-[build]
-compiler = mingw32
-
-# Installation options
-[install]
-compile = 1
-optimize = 1
-record = python-ldap_install.log
-
-[bdist_wininst]
-target_version = 2.4

+ 0 - 33
desktop/core/ext-py/python-ldap-2.3.13/Build/setup.cfg.suse-linux

@@ -1,33 +0,0 @@
-# Example for setup.cfg
-# You have to edit this file to reflect your system configuation
-# $Id: setup.cfg.suse-linux,v 1.1 2003/08/20 10:04:34 stroeder Exp $
-
-[_ldap]
-# Section for compiling the C extension module
-# for wrapping OpenLDAP 2 libs
-
-library_dirs = /usr/lib/sasl2
-include_dirs = /usr/include/sasl
-
-extra_compile_args = 
-extra_objects =
-
-# Example for full-featured SuSE build:
-# Support for StartTLS/LDAPS, SASL bind and reentrant libldap_r.
-# This needs recent OpenLDAP 2.0.26+ or 2.1.3+ built with
-# ./configure --with-cyrus-sasl --with-tls
-libs = ldap_r lber sasl2 ssl crypto
-
-[install]
-# Installation options
-compile = 1
-optimize = 1
-
-# For SuSE Linux 8.2
-[bdist_rpm]
-provides = python-ldap
-requires = python openldap2-client openssl cyrus-sasl2
-distribution_name = SuSE Linux 8.2
-release = 1
-packager = Michael Stroeder <michael@stroeder.com>
-doc_files = CHANGES README INSTALL TODO Demo/

+ 0 - 33
desktop/core/ext-py/python-ldap-2.3.13/Build/setup.cfg.win32

@@ -1,33 +0,0 @@
-# Section for compiling the C extension module
-# for wrapping OpenLDAP 2 libs
-
-# Platform: Win32
-# Compile environment: Microsoft Visual Studio .NET 2003
-[_ldap]
-class = OpenLDAP2
-defines = WIN32
-
-# Cannot have SSL/TLS support under Win32 for the moment
-# (OpenLDAP 2.x port is incomplete)
-libs = olber32 oldap_r ws2_32 libsasl
-
-# Set these to your correct Openldap and Cyrus-sasl paths
-library_dirs = ../openldap/openldap-2.1.22/Release ../openldap/cyrus-sasl/lib
-include_dirs = ../openldap/openldap-2.1.22/include ../openldap/cyrus-sasl/include
-
-# Needs to compile as /MT ("MS libs to use: multithreaded statically-linked")
-# instead of /MD ("MS libs to use: multithreaded DLL") which is distutils' default
-# because OpenLDAP libs compile that way, too
-# This may change, however
-extra_compile_args = /MT
-extra_link_args = /NODEFAULTLIB:msvcrt.lib
-
-# Pull in SASL DLL as a convenience to end-user (which almost never will have it)
-# Destination path is a rather crude hack, but site-packages would be created anyway
-# Set source path to your Cyrus-sasl lib path
-extra_files = Lib/site-packages:../openldap/cyrus-sasl/lib/libsasl.dll
-
-# Installation options
-[install]
-compile = 1
-record = python-ldap_install.log

+ 25 - 16
desktop/core/src/desktop/lib/rest/resource.py

@@ -45,28 +45,18 @@ class Resource(object):
       return self._path
     return self._path + posixpath.normpath('/' + relpath)
 
-  def invoke(self, method, relpath=None, params=None, data=None, headers=None):
-    """
-    Invoke an API method.
-    @return: Raw body or JSON dictionary (if response content type is JSON).
-    """
-    path = self._join_uri(relpath)
-    resp = self._client.execute(method,
-                                path,
-                                params=params,
-                                data=data,
-                                headers=headers)
+  def _get_body(self, resp):
     try:
       body = resp.read()
     except Exception, ex:
       raise Exception("Command '%s %s' failed: %s" %
                       (method, path, ex))
+    return body
 
-    self._client.logger.debug(
-        "%s Got response: %s%s" %
-        (method, body[:32], len(body) > 32 and "..." or ""))
-
-    # Is the response application/json?
+  def _format_body(self, resp, body):
+    """
+    Decide whether the body should be a json dict or string
+    """
     if len(body) != 0 and \
           resp.info().getmaintype() == "application" and \
           resp.info().getsubtype() == "json":
@@ -79,6 +69,25 @@ class Resource(object):
     else:
       return body
 
+  def invoke(self, method, relpath=None, params=None, data=None, headers=None):
+    """
+    Invoke an API method.
+    @return: Raw body or JSON dictionary (if response content type is JSON).
+    """
+    path = self._join_uri(relpath)
+    resp = self._client.execute(method,
+                                path,
+                                params=params,
+                                data=data,
+                                headers=headers)
+    body = self._get_body(resp)
+
+    self._client.logger.debug(
+        "%s Got response: %s%s" %
+        (method, body[:32], len(body) > 32 and "..." or ""))
+
+    return self._format_body(resp, body)
+
 
   def get(self, relpath=None, params=None, headers=None):
     """

部分文件因为文件数量过多而无法显示