Browse Source

[oozie] Sleep worflow and coordinator examples

Install a sleep example
Catch all Oozie errors and display them in a PopupException
Shared jobs can be submitted by another user
UI shows the possible actions for a workflow or coordinator according to permissions
Only workflow or coordinator variables with curly brackets are picked up
Each workflow or coordinator gets a unique deployment directory during its submission
Adding share option to coordinator
Fix History record creation
Remove 'show only mine' Oozie workflow and coordinator button
Romain Rigaux 13 years ago
parent
commit
c371fa841d

BIN
apps/oozie/demo/lib/hadoop-examples.jar


+ 8 - 0
apps/oozie/demo/pig/aggregate.pig

@@ -0,0 +1,8 @@
+
+A = LOAD '$INPUT' AS (word:CHARARRAY, count:INT);
+
+B = FOREACH A GENERATE count, word;
+C = ORDER B BY count DESC;
+
+STORE C INTO '$OUTPUT';
+

+ 15 - 11
apps/oozie/src/oozie/conf.py

@@ -23,24 +23,28 @@ from desktop.lib.conf import Config, coerce_bool
 from desktop.lib import paths
 from desktop.lib import paths
 
 
 
 
-REMOTE_DATA_DIR = Config(
-  key="remote_data_dir",
-  default="/user/hue/oozie",
-  help=_("Location on HDFS where the oozie examples and templates are stored."))
-
-# Where examples are stored.
-LOCAL_DATA_DIR = Config(
+LOCAL_SAMPLE_DIR = Config(
   key="local_data_dir",
   key="local_data_dir",
-  default=os.path.join(os.path.dirname(__file__), "..", "..", "data"),
-  help=_("Location on local FS where examples and template are stored"),
+  default=os.path.join(os.path.dirname(__file__), "..", "..", "examples"),
+  help=_("Location on local FS where the examples are stored"),
   private=True)
   private=True)
 
 
-SAMPLE_DATA_DIR = Config(
+LOCAL_SAMPLE_DATA_DIR = Config(
   key="sample_data_dir",
   key="sample_data_dir",
   default=paths.get_thirdparty_root("sample_data"),
   default=paths.get_thirdparty_root("sample_data"),
-  help=_("Location on local FS where sample data is stored"),
+  help=_("Location on local FS where the data for the examples is stored"),
   private=True)
   private=True)
 
 
+REMOTE_SAMPLE_DIR = Config(
+  key="remote_data_dir",
+  default="/user/hue/oozie/examples",
+  help=_("Location on HDFS where the oozie examples and templates are stored."))
+
+REMOTE_DEPLOYMENT_DIR = Config(
+  key="remote_data_dir",
+  default="/user/hue/oozie/deployments",
+  help=_("Location on HDFS where the workflows/coordinator are deployed when submitted."))
+
 SHARE_JOBS = Config(
 SHARE_JOBS = Config(
   key='share_jobs',
   key='share_jobs',
   default=True,
   default=True,

+ 183 - 0
apps/oozie/src/oozie/fixtures/initial_data.json

@@ -0,0 +1,183 @@
+[
+  {
+    "pk": 5,
+    "model": "oozie.job",
+    "fields": {
+      "is_shared": true,
+      "name": "SleepWorkflow",
+      "deployment_dir": "/user/hue/oozie/demo/sleep",
+      "schema_version": "",
+      "last_modified": "2012-08-20 13:13:34",
+      "owner": 1,
+      "description": "Run a Sleep MapReduce job"
+    }
+  },
+  {
+    "pk": 6,
+    "model": "oozie.job",
+    "fields": {
+      "is_shared": true,
+      "name": "DailySleep",
+      "deployment_dir": "/user/hue/oozie/demo/sleep",
+      "schema_version": "",
+      "last_modified": "2012-08-20 13:56:53",
+      "owner": 1,
+      "description": "Sleep each day for the date of the day milliseconds"
+    }
+  },
+  {
+    "pk": 5,
+    "model": "oozie.workflow",
+    "fields": {
+      "start": 18,
+      "is_single": false,
+      "end": 17
+    }
+  },
+  {
+    "pk": 23,
+    "model": "oozie.link",
+    "fields": {
+      "comment": "",
+      "name": "to",
+      "parent": 18,
+      "child": 19
+    }
+  },
+  {
+    "pk": 24,
+    "model": "oozie.link",
+    "fields": {
+      "comment": "",
+      "name": "related",
+      "parent": 18,
+      "child": 17
+    }
+  },
+  {
+    "pk": 25,
+    "model": "oozie.link",
+    "fields": {
+      "comment": "",
+      "name": "ok",
+      "parent": 19,
+      "child": 17
+    }
+  },
+  {
+    "pk": 26,
+    "model": "oozie.link",
+    "fields": {
+      "comment": "",
+      "name": "error",
+      "parent": 19,
+      "child": 16
+    }
+  },
+  {
+    "pk": 16,
+    "model": "oozie.node",
+    "fields": {
+      "node_type": "kill",
+      "workflow": 5,
+      "name": "kill",
+      "description": ""
+    }
+  },
+  {
+    "pk": 17,
+    "model": "oozie.node",
+    "fields": {
+      "node_type": "end",
+      "workflow": 5,
+      "name": "end",
+      "description": ""
+    }
+  },
+  {
+    "pk": 18,
+    "model": "oozie.node",
+    "fields": {
+      "node_type": "start",
+      "workflow": 5,
+      "name": "start",
+      "description": ""
+    }
+  },
+  {
+    "pk": 19,
+    "model": "oozie.node",
+    "fields": {
+      "node_type": "mapreduce",
+      "workflow": 5,
+      "name": "Sleep",
+      "description": "Sleep for some time"
+    }
+  },
+  {
+    "pk": 19,
+    "model": "oozie.mapreduce",
+    "fields": {
+      "files": "[]",
+      "job_properties": "[{\"name\":\"mapred.reduce.tasks\",\"value\":\"1\"},{\"name\":\"mapred.mapper.class\",\"value\":\"org.apache.hadoop.examples.SleepJob\"},{\"name\":\"mapred.reducer.class\",\"value\":\"org.apache.hadoop.examples.SleepJob\"},{\"name\":\"mapred.mapoutput.key.class\",\"value\":\"org.apache.hadoop.io.IntWritable\"},{\"name\":\"mapred.mapoutput.value.class\",\"value\":\"org.apache.hadoop.io.NullWritable\"},{\"name\":\"mapred.output.format.class\",\"value\":\"org.apache.hadoop.mapred.lib.NullOutputFormat\"},{\"name\":\"mapred.input.format.class\",\"value\":\"org.apache.hadoop.examples.SleepJob$SleepInputFormat\"},{\"name\":\"mapred.partitioner.class\",\"value\":\"org.apache.hadoop.examples.SleepJob\"},{\"name\":\"mapred.speculative.execution\",\"value\":\"false\"},{\"name\":\"sleep.job.map.sleep.time\",\"value\":\"0\"},{\"name\":\"sleep.job.reduce.sleep.time\",\"value\":\"${REDUCER_SLEEP_TIME}\"}]",
+      "jar_path": "/user/hue/oozie/demo/lib/hadoop-examples.jar",
+      "archives": "[]",
+      "node_ptr": 19
+    }
+  },
+  {
+    "pk": 18,
+    "model": "oozie.start",
+    "fields": {
+      "node_ptr": 18
+    }
+  },
+  {
+    "pk": 17,
+    "model": "oozie.end",
+    "fields": {}
+  },
+  {
+    "pk": 16,
+    "model": "oozie.kill",
+    "fields": {
+      "message": "Action failed, error message[${wf:errorMessage(wf:lastErrorNode())}]"
+    }
+  },
+  {
+    "pk": 6,
+    "model": "oozie.coordinator",
+    "fields": {
+      "end": "2012-08-04 00:00:00",
+      "frequency_number": 1,
+      "workflow": 5,
+      "frequency_unit": "days",
+      "start": "2012-08-01 00:00:00",
+      "timezone": "America/Los_Angeles"
+    }
+  },
+  {
+    "pk": 3,
+    "model": "oozie.dataset",
+    "fields": {
+      "name": "sleep_time",
+      "frequency_number": 1,
+      "coordinator": 6,
+      "frequency_unit": "days",
+      "uri": "${DAY}",
+      "start": "2012-08-01 00:00:00",
+      "timezone": "America/Los_Angeles",
+      "done_flag": "",
+      "description": ""
+    }
+  },
+  {
+    "pk": 1,
+    "model": "oozie.dataoutput",
+    "fields": {
+      "coordinator": 6,
+      "name": "REDUCER_SLEEP_TIME",
+      "dataset": 3
+    }
+  }
+]

+ 0 - 0
apps/oozie/src/oozie/management/__init__.py


+ 0 - 0
apps/oozie/src/oozie/management/commands/__init__.py


+ 86 - 0
apps/oozie/src/oozie/management/commands/oozie_demo_setup.py

@@ -0,0 +1,86 @@
+#!/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 os
+import posixpath
+import shutil
+
+from django.core.management.base import NoArgsCommand
+
+from hadoop import cluster
+
+from oozie.conf import LOCAL_SAMPLE_DATA_DIR, LOCAL_SAMPLE_DIR
+from oozie.models import Workflow
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Command(NoArgsCommand):
+  def handle_noargs(self, **options):
+    remote_fs = cluster.get_hdfs()
+    remote_dir = Workflow.objects.create_data_dir(remote_fs)
+
+    # Demo binaries
+    for demo in ('lib', 'pig'):
+      local_dir = posixpath.join(LOCAL_SAMPLE_DIR.get(), demo)
+      remote_data_dir = posixpath.join(remote_dir, demo)
+      self.stdout.write('Copying workflows %s to %s\n' % (local_dir, remote_data_dir))
+      copy_dir(local_dir, remote_fs, remote_data_dir)
+
+    # Demo data
+    local_dir = LOCAL_SAMPLE_DATA_DIR.get()
+    remote_data_dir = posixpath.join(remote_dir, 'data')
+    self.stdout.write('Copying data %s to %s\n' % (local_dir, remote_data_dir))
+    copy_dir(local_dir, remote_fs, remote_data_dir)
+
+  def has_been_setup(self):
+    return False
+
+# TODO refactor with Jobsub and move this to a utility in WebHdfs
+def copy_dir(local_dir, remote_fs, remote_dir):
+  remote_fs.mkdir(remote_dir)
+
+  for f in os.listdir(local_dir):
+    local_src = os.path.join(local_dir, f)
+    remote_dst = posixpath.join(remote_dir, f)
+    copy_file(local_src, remote_fs, remote_dst)
+
+
+CHUNK_SIZE = 65536
+
+def copy_file(local_src, remote_fs, remote_dst):
+  if remote_fs.exists(remote_dst):
+    LOG.info('%s already exists.  Skipping.' % remote_dst)
+    return
+  else:
+    LOG.info('%s does not exist. trying to copy' % remote_dst)
+
+  if os.path.isfile(local_src):
+    src = file(local_src)
+    try:
+      dst = remote_fs.open(remote_dst, 'w')
+      try:
+        shutil.copyfileobj(src, dst, CHUNK_SIZE)
+        LOG.info('Copied %s -> %s' % (local_src, remote_dst))
+      finally:
+        dst.close()
+    finally:
+      src.close()
+  else:
+    LOG.info('Skipping %s (not a file)' % local_src)

+ 6 - 6
apps/oozie/src/oozie/models.py

@@ -36,7 +36,7 @@ from desktop.lib import django_mako
 from hadoop.fs.hadoopfs import Hdfs
 from hadoop.fs.hadoopfs import Hdfs
 from liboozie.submittion import Submission
 from liboozie.submittion import Submission
 
 
-from oozie.conf import REMOTE_DATA_DIR
+from oozie.conf import REMOTE_SAMPLE_DIR
 from timezones import TIMEZONES
 from timezones import TIMEZONES
 
 
 
 
@@ -69,7 +69,7 @@ class Job(models.Model):
     super(Job, self).save()
     super(Job, self).save()
 
 
     if not self.deployment_dir:
     if not self.deployment_dir:
-      default_dir = Hdfs.join(REMOTE_DATA_DIR.get(), '_%s_-oozie-%s' % (self.owner.username, self.id))
+      default_dir = Hdfs.join(REMOTE_SAMPLE_DIR.get(), '_%s_-oozie-%s' % (self.owner.username, self.id))
       self.deployment_dir = default_dir
       self.deployment_dir = default_dir
       super(Job, self).save()
       super(Job, self).save()
 
 
@@ -137,14 +137,14 @@ class WorkflowManager(models.Manager):
     workflow.save()
     workflow.save()
 
 
     WorkflowManager.create_data_dir(fs)
     WorkflowManager.create_data_dir(fs)
-    Submission(workflow, fs, {})._create_deployment_dir()
+    Submission(workflow.owner, workflow, fs, {})._create_deployment_dir()
 
 
     return workflow
     return workflow
 
 
   @classmethod
   @classmethod
   def create_data_dir(cls, fs):
   def create_data_dir(cls, fs):
     # If needed, create the remote home and data directories
     # If needed, create the remote home and data directories
-    remote_data_dir = REMOTE_DATA_DIR.get()
+    remote_data_dir = REMOTE_SAMPLE_DIR.get()
     user = fs.user
     user = fs.user
 
 
     try:
     try:
@@ -993,7 +993,7 @@ class DataOutput(models.Model):
 
 
 class HistoryManager(models.Manager):
 class HistoryManager(models.Manager):
   def create_from_submission(self, submission):
   def create_from_submission(self, submission):
-    History.objects.create(submitter=submission.job.owner,
+    History.objects.create(submitter=submission.user,
                            oozie_job_id=submission.oozie_id,
                            oozie_job_id=submission.oozie_id,
                            job=submission.job,
                            job=submission.job,
                            properties=json.dumps(submission.properties))
                            properties=json.dumps(submission.properties))
@@ -1069,7 +1069,7 @@ def find_parameters(instance, fields=None):
     data = getattr(instance, field)
     data = getattr(instance, field)
     if isinstance(data, basestring):
     if isinstance(data, basestring):
       for match in Template.pattern.finditer(data):
       for match in Template.pattern.finditer(data):
-        name = match.group('named') or match.group('braced')
+        name = match.group('braced')
         if name is not None:
         if name is not None:
           params.append(name)
           params.append(name)
   return params
   return params

+ 1 - 0
apps/oozie/src/oozie/settings.py

@@ -18,6 +18,7 @@ DJANGO_APPS=['oozie']
 NICE_NAME = "Oozie Editor/Dashboard"
 NICE_NAME = "Oozie Editor/Dashboard"
 REQUIRES_HADOOP = True
 REQUIRES_HADOOP = True
 
 
+# Unused
 PERMISSION_ACTIONS = (
 PERMISSION_ACTIONS = (
   ("launch_editor", "Launch the Oozie Editor"),
   ("launch_editor", "Launch the Oozie Editor"),
   ("launch_dashboard", "Launch the Oozie Dashboard"),
   ("launch_dashboard", "Launch the Oozie Dashboard"),

+ 2 - 19
apps/oozie/src/oozie/templates/dashboard/list_oozie_coordinators.mako

@@ -34,13 +34,8 @@ ${layout.menubar(section='dashboard')}
       ${ _('Filter:') } <input id="filterInput" class="input-xlarge search-query" placeholder="Search for username, name, etc...">
       ${ _('Filter:') } <input id="filterInput" class="input-xlarge search-query" placeholder="Search for username, name, etc...">
 
 
       <span class="pull-right">
       <span class="pull-right">
-        <span style="padding-right:10px;float:left">
+        <span style="padding-right:10px;float:left;margin-top:3px">
         ${ _('Show only') }
         ${ _('Show only') }
-         <a class="btn btn-submitter btn-info active">
-          <span class="btn-submitter">${ _('Mine') }</span>
-          <span class="btn-submitter hide">${ _('All') }</span>
-         </a>
-        ${ _('from the last') }
         </span>
         </span>
         <span class="btn-group" style="float:left">
         <span class="btn-group" style="float:left">
           <a class="btn btn-date btn-info">1</a>
           <a class="btn btn-date btn-info">1</a>
@@ -200,12 +195,6 @@ ${layout.menubar(section='dashboard')}
       $("#filterInput").keyup();
       $("#filterInput").keyup();
     });
     });
 
 
-    $("a.btn-submitter").click(function() {
-      $("a.btn-submitter").toggleClass('active');
-      $("span.btn-submitter").toggleClass('hide');
-      $("#filterInput").keyup();
-    });
-
     $.fn.dataTableExt.afnFiltering.push(
     $.fn.dataTableExt.afnFiltering.push(
       function(oSettings, aData, iDataIndex) {
       function(oSettings, aData, iDataIndex) {
 
 
@@ -228,13 +217,7 @@ ${layout.menubar(section='dashboard')}
           dateFilter = Date.parse(aData[0]) >= minAge;
           dateFilter = Date.parse(aData[0]) >= minAge;
         }
         }
 
 
-        submitterBtn = $('a.btn-submitter.active');
-        submitterFilter = true;
-        if (submitterBtn.length > 0) {
-          submitterFilter = aData[4] == '${ user }';
-        }
-
-        return statusFilter && dateFilter && submitterFilter;
+        return statusFilter && dateFilter;
       }
       }
     );
     );
 
 

+ 1 - 1
apps/oozie/src/oozie/templates/dashboard/list_oozie_workflow.mako

@@ -141,7 +141,7 @@ ${ layout.menubar(section='dashboard') }
      % endif
      % endif
 
 
     <div class="tab-pane ${ utils.if_false(hue_workflow, 'active') }" id="actions">
     <div class="tab-pane ${ utils.if_false(hue_workflow, 'active') }" id="actions">
-      % if oozie_workflow.actions:
+      % if oozie_workflow.get_working_actions():
         <table class="table table-striped table-condensed selectable">
         <table class="table table-striped table-condensed selectable">
           <thead>
           <thead>
             <tr>
             <tr>

+ 2 - 20
apps/oozie/src/oozie/templates/dashboard/list_oozie_workflows.mako

@@ -34,13 +34,8 @@ ${ layout.menubar(section='dashboard') }
       ${ _('Filter:') } <input id="filterInput" class="input-xlarge search-query" placeholder="${ _('Search for username, name, etc...') }">
       ${ _('Filter:') } <input id="filterInput" class="input-xlarge search-query" placeholder="${ _('Search for username, name, etc...') }">
 
 
       <span class="pull-right">
       <span class="pull-right">
-        <span style="padding-right:10px;float:left">
+        <span style="padding-right:10px;float:left;margin-top:3px">
         ${ _('Show only') }
         ${ _('Show only') }
-         <a class="btn btn-submitter btn-info active">
-          <span class="btn-submitter">${ _('Mine') }</span>
-          <span class="btn-submitter hide">${ _('All') }</span>
-         </a>
-        ${ _('from the last') }
         </span>
         </span>
         <span class="btn-group" style="float:left">
         <span class="btn-group" style="float:left">
           <a class="btn btn-date btn-info">1</a>
           <a class="btn btn-date btn-info">1</a>
@@ -200,15 +195,8 @@ ${ layout.menubar(section='dashboard') }
       $("#filterInput").keyup();
       $("#filterInput").keyup();
     });
     });
 
 
-    $("a.btn-submitter").click(function() {
-      $("a.btn-submitter").toggleClass('active');
-      $("span.btn-submitter").toggleClass('hide');
-      $("#filterInput").keyup();
-    });
-
     $.fn.dataTableExt.afnFiltering.push(
     $.fn.dataTableExt.afnFiltering.push(
       function(oSettings, aData, iDataIndex) {
       function(oSettings, aData, iDataIndex) {
-
         urlHashes = ""
         urlHashes = ""
 
 
         statusBtn = $('a.btn-status.active');
         statusBtn = $('a.btn-status.active');
@@ -228,13 +216,7 @@ ${ layout.menubar(section='dashboard') }
           dateFilter = Date.parse(aData[0]) >= minAge;
           dateFilter = Date.parse(aData[0]) >= minAge;
         }
         }
 
 
-        submitterBtn = $('a.btn-submitter.active');
-        submitterFilter = true;
-        if (submitterBtn.length > 0) {
-          submitterFilter = aData[4] == '${ user }';
-        }
-
-        return statusFilter && dateFilter && submitterFilter;
+        return statusFilter && dateFilter;
       }
       }
     );
     );
 
 

+ 196 - 196
apps/oozie/src/oozie/templates/editor/edit_coordinator.mako

@@ -27,21 +27,21 @@ ${ layout.menubar(section='coordinators') }
 
 
 
 
 <div class="container-fluid">
 <div class="container-fluid">
-    <h1>${ _('Coordinator') } ${ coordinator.name }</h1>
-
-    <div class="well">
-      ${ _('Description') }: ${ coordinator.description or _("N/A") }
-      % if coordinator.workflow:
-      <br/>
-        ${ _('Workflow') }: <a href="${ coordinator.workflow.get_absolute_url() }">${ coordinator.workflow }</a>
-      % endif
-    </div>
+  <h1>${ _('Coordinator') } ${ coordinator.name }</h1>
+
+  <div class="well">
+    ${ _('Description') }: ${ coordinator.description or _("N/A") }
+    % if coordinator.workflow:
+    <br/>
+      ${ _('Workflow') }: <a href="${ coordinator.workflow.get_absolute_url() }">${ coordinator.workflow }</a>
+    % endif
+  </div>
 
 
-    <ul class="nav nav-tabs">
-        <li class="active"><a href="#editor" data-toggle="tab">${ _('Editor') }</a></li>
-        <li><a href="#datasets" data-toggle="tab">${ _('Datasets') }</a></li>
-        <li><a href="#history" data-toggle="tab">${ _('History') }</a></li>
-    </ul>
+  <ul class="nav nav-tabs">
+    <li class="active"><a href="#editor" data-toggle="tab">${ _('Editor') }</a></li>
+    <li><a href="#datasets" data-toggle="tab">${ _('Datasets') }</a></li>
+    <li><a href="#history" data-toggle="tab">${ _('History') }</a></li>
+  </ul>
 
 
 % if coordinator.id:
 % if coordinator.id:
   <form class="form-horizontal" id="workflowForm" action="${ url('oozie:edit_coordinator', coordinator=coordinator.id) }" method="POST">
   <form class="form-horizontal" id="workflowForm" action="${ url('oozie:edit_coordinator', coordinator=coordinator.id) }" method="POST">
@@ -51,7 +51,7 @@ ${ layout.menubar(section='coordinators') }
 
 
     <div class="tab-content">
     <div class="tab-content">
       <div class="tab-pane active" id="editor">
       <div class="tab-pane active" id="editor">
-      <div class="row-fluid">
+        <div class="row-fluid">
           <div class="span2">
           <div class="span2">
           </div>
           </div>
           <div class="span8">
           <div class="span8">
@@ -60,232 +60,232 @@ ${ layout.menubar(section='coordinators') }
                ${ utils.render_field(coordinator_form['name']) }
                ${ utils.render_field(coordinator_form['name']) }
                ${ utils.render_field(coordinator_form['description']) }
                ${ utils.render_field(coordinator_form['description']) }
                ${ utils.render_field(coordinator_form['workflow']) }
                ${ utils.render_field(coordinator_form['workflow']) }
+               ${ utils.render_field(coordinator_form['is_shared']) }
              </div>
              </div>
 
 
             <hr/>
             <hr/>
             <h2>${ _('Frequency') }</h2>
             <h2>${ _('Frequency') }</h2>
 
 
             <div class="fieldWrapper">
             <div class="fieldWrapper">
-                  <div class="row-fluid">
-                    <div class="span6">
-                    ${ utils.render_field(coordinator_form['frequency_number']) }
-                  </div>
-                  <div class="span6">
-                    ${ utils.render_field(coordinator_form['frequency_unit']) }
-                  </div>
+              <div class="row-fluid">
+                <div class="span6">
+                  ${ utils.render_field(coordinator_form['frequency_number']) }
                 </div>
                 </div>
+                <div class="span6">
+                  ${ utils.render_field(coordinator_form['frequency_unit']) }
+                </div>
+              </div>
             </div>
             </div>
 
 
             <div class="fieldWrapper">
             <div class="fieldWrapper">
-                <div class="row-fluid">
-                  <div class="span6">
+              <div class="row-fluid">
+                 <div class="span6">
                 ${ utils.render_field(coordinator_form['start']) }
                 ${ utils.render_field(coordinator_form['start']) }
               </div>
               </div>
-                <div class="span6">
-                  ${ utils.render_field(coordinator_form['end']) }
-                 </div>
+              <div class="span6">
+                 ${ utils.render_field(coordinator_form['end']) }
               </div>
               </div>
-              ${ utils.render_field(coordinator_form['timezone']) }
             </div>
             </div>
+              ${ utils.render_field(coordinator_form['timezone']) }
+          </div>
 
 
-              ${ dataset_formset.management_form }
-              ${ data_input_formset.management_form }
-              ${ data_output_formset.management_form }
+          ${ dataset_formset.management_form }
+          ${ data_input_formset.management_form }
+          ${ data_output_formset.management_form }
 
 
-            % if coordinator.id:
-              <hr/>
-              <h2>Data</h2>
-                <br/>
-              <p>
-                % if coordinator.workflow:
-                  ${ _('The inputs and outputs of the workflow need to be mapped to some data on the') }
-                  <a href="#" id="datasets-btn" class="btn">${ _('Datasets page') }</a>
-                % endif
-              </p>
-              </br>
-                <div class="row-fluid">
-                  <h3>${ _('Inputs') }</h3>
-
-                  % if data_input_formset.forms:
-                  <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
-                    <thead>
+          % if coordinator.id:
+            <hr/>
+            <h2>Data</h2>
+            <br/>
+            <p>
+              % if coordinator.workflow:
+                ${ _('The inputs and outputs of the workflow need to be mapped to some data on the') }
+                <a href="#" id="datasets-btn" class="btn">${ _('Datasets page') }</a>
+              % endif
+            </p>
+            </br>
+            <div class="row-fluid">
+              <h3>${ _('Inputs') }</h3>
+
+              % if data_input_formset.forms:
+                <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
+                  <thead>
+                    <tr>
+                      <th>${ _('Name') }</th>
+                      <th>${ _('Dataset') }</th>
+                      <th>${ _('Path') }</th>
+                      <th>${ _('Delete') }</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    % for form in data_input_formset.forms:
                       <tr>
                       <tr>
-                        <th>${ _('Name') }</th>
-                        <th>${ _('Dataset') }</th>
-                        <th>${ _('Path') }</th>
-                        <th>${ _('Delete') }</th>
+                         ${ form['id'] }
+                         <td>${ form['name'] }</td>
+                         <td>${ form['dataset'] }</td>
+                         <td>${ form['dataset'].form.instance.dataset.uri }</td>
+                         <td>${ form['DELETE'] }</td>
                       </tr>
                       </tr>
-                    </thead>
-                    <tbody>
-                      % for form in data_input_formset.forms:
-                        <tr>
-                           ${ form['id'] }
-                           <td>${ form['name'] }</td>
-                           <td>${ form['dataset'] }</td>
-                           <td>${ form['dataset'].form.instance.dataset.uri }</td>
-                           <td>${ form['DELETE'] }</td>
-                        </tr>
-                      % endfor
-                    </tbody>
-                  </table>
-                  % else:
-                    <br/>
-                    <div class="alert alert-error">
-                      ${ _('No inputs') }
-                    </div>
-                  % endif
-
-                 <a class="btn" data-toggle="modal" href="#add-data-input-modal">${ _('Add') }</a>
+                    % endfor
+                  </tbody>
+                </table>
+              % else:
+                <br/>
+                <div class="alert alert-error">
+                  ${ _('No inputs') }
                 </div>
                 </div>
+              % endif
 
 
-              <br/>
+             <a class="btn" data-toggle="modal" href="#add-data-input-modal">${ _('Add') }</a>
+            </div>
 
 
-                <div class="row-fluid">
-                  <h3>${ _('Outputs') }</h3>
+            <br/>
 
 
-                  % if data_output_formset.forms:
-                  <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
-                    <thead>
-                      <tr>
-                        <th>${ _('Name') }</th>
-                        <th>${ _('Dataset') }</th>
-                        <th>${ _('Path') }</th>
-                        <th>${ _('Delete') }</th>
-                      </tr>
-                    </thead>
-                    <tbody>
-                      % for form in data_output_formset.forms:
-                        <tr>
-                           ${ form['id'] }
-                           <td>${ form['name'] }</td>
-                           <td>${ form['dataset'] }</td>
-                           <td>${ form['dataset'].form.instance.dataset.uri }</td>
-                           <td>${ form['DELETE'] }</td>
-                        </tr>
-                      % endfor
-                    </tbody>
-                  </table>
-                  % else:
-                    <br/>
-                    <div class="alert alert-error">
-                      ${ _('No outputs') }
-                    </div>
-                  % endif
-
-                  <a class="btn" data-toggle="modal" href="#add-data-output-modal">${ _('Add') }</a>
-                </div>
-            % endif
-         </div>
-      </div>
-      </div>
+            <div class="row-fluid">
+              <h3>${ _('Outputs') }</h3>
 
 
-      <div class="tab-pane" id="datasets">
-        <div class="row-fluid">
-            <div class="span1">
-              <table>
-                <thead>
-                  <tr>
-                    <th>${ _('Add a new dataset') }</th>
-                  </tr>
-                </thead>
-                <tbody>
-                  <tr>
-                    <td><br/><a class="btn" data-toggle="modal" href="#add-dataset-modal">${ _('Create') }</a></td>
-                  </tr>
-                </tbody>
-              </table>
-
-
-            </div>
-            <div class="span10">
-              % if coordinator.id:
-                <div>
+              % if data_output_formset.forms:
               <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
               <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
                 <thead>
                 <thead>
                   <tr>
                   <tr>
-                    <th>${ _('Pick dataset as input/output') }</th>
                     <th>${ _('Name') }</th>
                     <th>${ _('Name') }</th>
-                    <th>${ _('Description') }</th>
-                    <th>${ _('Frequency') }</th>
-                    <th>${ _('Start') }</th>
-                    <th>${ _('Uri') }</th>
-                    <th>${ _('Timezone') }</th>
-                    <th>${ _('Done flag') }</th>
+                    <th>${ _('Dataset') }</th>
+                    <th>${ _('Path') }</th>
                     <th>${ _('Delete') }</th>
                     <th>${ _('Delete') }</th>
                   </tr>
                   </tr>
                 </thead>
                 </thead>
                 <tbody>
                 <tbody>
-                 % for form in dataset_formset.forms:
-                  % for hidden in form.hidden_fields():
-                    ${ hidden }
+                  % for form in data_output_formset.forms:
+                    <tr>
+                       ${ form['id'] }
+                       <td>${ form['name'] }</td>
+                       <td>${ form['dataset'] }</td>
+                       <td>${ form['dataset'].form.instance.dataset.uri }</td>
+                       <td>${ form['DELETE'] }</td>
+                    </tr>
                   % endfor
                   % endfor
-                  <tr>
-                    <td>
-                      <a class="btn" data-toggle="modal" href="#add-data-input-modal">${ _('input') }</a>
-                      <a class="btn" data-toggle="modal" href="#add-data-output-modal">${ _('output') }</a>
-                      </td>
-                    <td>${ form.instance.name }</td>
-                    <td>${ form.instance.description }</td>
-                    <td>${ form.instance.text_frequency }</td>
-                    <td>${ form.instance.start }</td>
-                    <td>${ form.instance.uri }</td>
-                    <td>${ form.instance.timezone }</td>
-                    <td>${ form.instance.done_flag }</td>
-                    <td>${ form['DELETE'] }</td>
-                  </tr>
-
-                   <div class="hide">
-                      % for field in form.visible_fields():
-                              ${ field.errors }
-                              ${ field.label }: ${ field }
-                      % endfor
-                      </div>
-
-                 % endfor
                 </tbody>
                 </tbody>
               </table>
               </table>
-             </div>
-                % if not dataset_formset.forms:
-                  <div class="alert alert-error">
-                    ${ _('No datasets') }
-                  </div>
-                % endif
+              % else:
+                <br/>
+                <div class="alert alert-error">
+                  ${ _('No outputs') }
+                </div>
               % endif
               % endif
+
+              <a class="btn" data-toggle="modal" href="#add-data-output-modal">${ _('Add') }</a>
             </div>
             </div>
-          </div>
+          % endif
+        </div>
       </div>
       </div>
+    </div>
 
 
-      <div class="tab-pane" id="history">
-        <table class="table">
-          <thead>
-            <tr>
-              <th>${ _('Date') }</th>
-              <th>${ _('Id') }</th>
-            </tr>
-          </thead>
-          <tbody>
-            % if not history:
-              ${ _('N/A') }
-            % endif
-            % for record in history:
+    <div class="tab-pane" id="datasets">
+      <div class="row-fluid">
+          <div class="span1">
+            <table>
+              <thead>
                 <tr>
                 <tr>
-                  <td><a href="${ url('oozie:list_history_record', record_id=record.id) }" data-row-selector="true"></a>${ record.submission_date }</td>
-                  <td>${ record.oozie_job_id }</td>
+                  <th>${ _('Add a new dataset') }</th>
                 </tr>
                 </tr>
-            % endfor
-          </tbody>
-        </table>
-      </div>
-
-      <br/>
+              </thead>
+              <tbody>
+                <tr>
+                  <td><br/><a class="btn" data-toggle="modal" href="#add-dataset-modal">${ _('Create') }</a></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
 
 
+          <div class="span10">
+            % if coordinator.id:
+              <div>
+                <table class="table table-striped table-condensed" cellpadding="0" cellspacing="0">
+                  <thead>
+                    <tr>
+                      <th>${ _('Pick dataset as input/output') }</th>
+                      <th>${ _('Name') }</th>
+                      <th>${ _('Description') }</th>
+                      <th>${ _('Frequency') }</th>
+                      <th>${ _('Start') }</th>
+                      <th>${ _('Uri') }</th>
+                      <th>${ _('Timezone') }</th>
+                      <th>${ _('Done flag') }</th>
+                      <th>${ _('Delete') }</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                   % for form in dataset_formset.forms:
+                    % for hidden in form.hidden_fields():
+                      ${ hidden }
+                    % endfor
+                    <tr>
+                      <td>
+                        <a class="btn" data-toggle="modal" href="#add-data-input-modal">${ _('input') }</a>
+                        <a class="btn" data-toggle="modal" href="#add-data-output-modal">${ _('output') }</a>
+                        </td>
+                      <td>${ form.instance.name }</td>
+                      <td>${ form.instance.description }</td>
+                      <td>${ form.instance.text_frequency }</td>
+                      <td>${ form.instance.start }</td>
+                      <td>${ form.instance.uri }</td>
+                      <td>${ form.instance.timezone }</td>
+                      <td>${ form.instance.done_flag }</td>
+                      <td>${ form['DELETE'] }</td>
+                    </tr>
+
+                     <div class="hide">
+                        % for field in form.visible_fields():
+                                ${ field.errors }
+                                ${ field.label }: ${ field }
+                        % endfor
+                        </div>
+
+                   % endfor
+                  </tbody>
+                </table>
+              </div>
+              % if not dataset_formset.forms:
+                <div class="alert alert-error">
+                  ${ _('No datasets') }
+                </div>
+              % endif
+            % endif
+          </div>
+       </div>
     </div>
     </div>
 
 
-    <div class="form-actions center">
-      <a href="${ url('oozie:list_coordinator') }" class="btn">${ _('Back') }</a>
-      <input class="btn btn-primary" type="submit" value="${ _('Save') }"></input>
+    <div class="tab-pane" id="history">
+      <table class="table">
+        <thead>
+          <tr>
+            <th>${ _('Date') }</th>
+            <th>${ _('Id') }</th>
+          </tr>
+        </thead>
+        <tbody>
+          % if not history:
+            ${ _('N/A') }
+          % endif
+          % for record in history:
+              <tr>
+                <td><a href="${ url('oozie:list_history_record', record_id=record.id) }" data-row-selector="true"></a>${ record.submission_date }</td>
+                <td>${ record.oozie_job_id }</td>
+              </tr>
+          % endfor
+        </tbody>
+      </table>
     </div>
     </div>
 
 
+    <br/>
+
+  </div>
+
+  <div class="form-actions center">
+    <a href="${ url('oozie:list_coordinator') }" class="btn">${ _('Back') }</a>
+    <input class="btn btn-primary" type="submit" value="${ _('Save') }"></input>
+  </div>
+
   </form>
   </form>
 
 
 % if coordinator.id:
 % if coordinator.id:

+ 1 - 1
apps/oozie/src/oozie/templates/editor/gen/workflow-graph-status.xml.mako

@@ -42,7 +42,7 @@
           % if not is_fork:
           % if not is_fork:
             ${ form.instance.description }<br/>
             ${ form.instance.description }<br/>
             % if form.instance.__unicode__() in actions:
             % if form.instance.__unicode__() in actions:
-              ${actions[form.instance.__unicode__()].errorMessage }
+              ${ actions[form.instance.__unicode__()].errorMessage or '' }
             % endif
             % endif
           % endif
           % endif
         </div>
         </div>

+ 8 - 23
apps/oozie/src/oozie/templates/editor/list_coordinators.mako

@@ -17,6 +17,8 @@
 <%!
 <%!
   from desktop.views import commonheader, commonfooter
   from desktop.views import commonheader, commonfooter
   from django.utils.translation import ugettext as _
   from django.utils.translation import ugettext as _
+
+  from oozie.views import can_access_job, can_edit_job
 %>
 %>
 
 
 <%namespace name="layout" file="../navigation-bar.mako" />
 <%namespace name="layout" file="../navigation-bar.mako" />
@@ -69,18 +71,20 @@ ${ layout.menubar(section='coordinators') }
         <tr class="action-row">
         <tr class="action-row">
           <td class=".btn-large action-column" data-row-selector-exclude="true" style="background-color: white;">
           <td class=".btn-large action-column" data-row-selector-exclude="true" style="background-color: white;">
             <input type="radio" name="action" data-row-selector-exclude="true"
             <input type="radio" name="action" data-row-selector-exclude="true"
-              % if currentuser.username == coordinator.owner.username:
+              % if can_edit_job(currentuser, coordinator):
                   data-param-url="${ url('oozie:workflow_parameters', workflow=coordinator.id) }"
                   data-param-url="${ url('oozie:workflow_parameters', workflow=coordinator.id) }"
                   data-delete-url="${ url('oozie:delete_coordinator', coordinator=coordinator.id) }"
                   data-delete-url="${ url('oozie:delete_coordinator', coordinator=coordinator.id) }"
               % endif
               % endif
+              % if can_access_job(currentuser, coordinator):
                   data-clone-url="${ url('oozie:clone_coordinator', coordinator=coordinator.id) }"
                   data-clone-url="${ url('oozie:clone_coordinator', coordinator=coordinator.id) }"
                   data-bundle-url="${ url('oozie:create_coordinator') }"
                   data-bundle-url="${ url('oozie:create_coordinator') }"
                   data-submit-url="${ url('oozie:submit_coordinator', coordinator=coordinator.id) }"
                   data-submit-url="${ url('oozie:submit_coordinator', coordinator=coordinator.id) }"
+              % endif
               >
               >
             </input>
             </input>
-            %if currentuser.username == coordinator.owner.username:
+            % if can_access_job(currentuser, coordinator):
               <a href="${ url('oozie:edit_coordinator', coordinator=coordinator.id) }" data-row-selector="true"/>
               <a href="${ url('oozie:edit_coordinator', coordinator=coordinator.id) }" data-row-selector="true"/>
-            %endif%
+            % endif
           </td>
           </td>
           <td>${ coordinator.name }</td>
           <td>${ coordinator.name }</td>
           <td>${ coordinator.description }</td>
           <td>${ coordinator.description }</td>
@@ -91,7 +95,7 @@ ${ layout.menubar(section='coordinators') }
           </td>
           </td>
           <td>${ coordinator.text_frequency }</td>
           <td>${ coordinator.text_frequency }</td>
           <td>
           <td>
-            <span class="label label-info">${ _('production') }</span>
+            <span class="label label-info">${ coordinator.status }</span>
           </td>
           </td>
           <td nowrap="nowrap">${ utils.format_date(coordinator.last_modified) }</td>
           <td nowrap="nowrap">${ utils.format_date(coordinator.last_modified) }</td>
           <td>${ coordinator.owner.username }</td>
           <td>${ coordinator.owner.username }</td>
@@ -134,21 +138,6 @@ ${ layout.menubar(section='coordinators') }
   </form>
   </form>
 </div>
 </div>
 
 
-<div id="installSamples" class="modal hide fade">
-  <form id="installSamplesForm" action="${url('oozie:setup')}" method="POST">
-    <div class="modal-header">
-      <a href="#" class="close" data-dismiss="modal">&times;</a>
-      <h3>${ _('Install sample coordinators?') }</h3>
-    </div>
-    <div class="modal-body">
-      ${ _('It will take a few seconds to install.') }
-    </div>
-    <div class="modal-footer">
-      <input type="submit" class="btn primary" value="${ _('Yes') }"/>
-      <a href="#" class="btn secondary" data-dismiss="modal">${ _('No') }</a>
-    </div>
-  </form>
-</div>
 
 
 <style>
 <style>
   td .btn-large{ cursor: crosshair;  }
   td .btn-large{ cursor: crosshair;  }
@@ -284,10 +273,6 @@ ${ layout.menubar(section='coordinators') }
         oTable.fnFilter($(this).val());
         oTable.fnFilter($(this).val());
     });
     });
 
 
-    $("#installSamplesLink").click(function(){
-        $("#installSamples").modal("show");
-    });
-
     $("a[data-row-selector='true']").jHueRowSelector();
     $("a[data-row-selector='true']").jHueRowSelector();
   });
   });
 </script>
 </script>

+ 10 - 9
apps/oozie/src/oozie/templates/editor/list_workflows.mako

@@ -17,6 +17,8 @@
 <%!
 <%!
   from desktop.views import commonheader, commonfooter
   from desktop.views import commonheader, commonfooter
   from django.utils.translation import ugettext as _
   from django.utils.translation import ugettext as _
+
+  from oozie.views import can_access_job, can_edit_job
 %>
 %>
 
 
 <%namespace name="layout" file="../navigation-bar.mako" />
 <%namespace name="layout" file="../navigation-bar.mako" />
@@ -32,6 +34,7 @@ ${ layout.menubar(section='workflows') }
   <div class="well hueWell">
   <div class="well hueWell">
     <div class="btn-group pull-right">
     <div class="btn-group pull-right">
       <a href="${ url('oozie:create_workflow') }" class="btn">${ _('Create') }</a>
       <a href="${ url('oozie:create_workflow') }" class="btn">${ _('Create') }</a>
+      <a href="#installSamples" data-toggle="modal" class="btn">${ _('Install examples') }</a>
     </div>
     </div>
 
 
     <div class="row-fluid">
     <div class="row-fluid">
@@ -70,15 +73,17 @@ ${ layout.menubar(section='workflows') }
         <tr class="action-row">
         <tr class="action-row">
           <td class=".btn-large action-column" data-row-selector-exclude="true" style="background-color: white;">
           <td class=".btn-large action-column" data-row-selector-exclude="true" style="background-color: white;">
             <input type="radio" name="action" data-row-selector-exclude="true"
             <input type="radio" name="action" data-row-selector-exclude="true"
-              % if currentuser.username == workflow.owner.username:
+              % if can_access_job(currentuser, workflow):
                   data-param-url="${ url('oozie:workflow_parameters', workflow=workflow.id) }"
                   data-param-url="${ url('oozie:workflow_parameters', workflow=workflow.id) }"
                   data-submit-url="${ url('oozie:submit_workflow', workflow=workflow.id) }"
                   data-submit-url="${ url('oozie:submit_workflow', workflow=workflow.id) }"
+                  data-schedule-url="${ url('oozie:create_coordinator', workflow=workflow.id) }"
+              % endif
+              % if can_edit_job(currentuser, workflow):
                   data-delete-url="${ url('oozie:delete_workflow', workflow=workflow.id) }"
                   data-delete-url="${ url('oozie:delete_workflow', workflow=workflow.id) }"
                   data-clone-url="${ url('oozie:clone_workflow', workflow=workflow.id) }"
                   data-clone-url="${ url('oozie:clone_workflow', workflow=workflow.id) }"
-                  data-schedule-url="${ url('oozie:create_coordinator', workflow=workflow.id) }"
               % endif
               % endif
             />
             />
-            % if currentuser.username == workflow.owner.username:
+            % if can_access_job(currentuser, workflow):
               <a href="${ url('oozie:edit_workflow', workflow=workflow.id) }" data-row-selector="true"></a>
               <a href="${ url('oozie:edit_workflow', workflow=workflow.id) }" data-row-selector="true"></a>
             % endif
             % endif
           </td>
           </td>
@@ -134,10 +139,10 @@ ${ layout.menubar(section='workflows') }
 </div>
 </div>
 
 
 <div id="installSamples" class="modal hide fade">
 <div id="installSamples" class="modal hide fade">
-  <form id="installSamplesForm" action="${url('oozie:setup')}" method="POST">
+  <form id="installSamplesForm" action="${url('oozie:install_examples')}" method="POST">
     <div class="modal-header">
     <div class="modal-header">
       <a href="#" class="close" data-dismiss="modal">&times;</a>
       <a href="#" class="close" data-dismiss="modal">&times;</a>
-      <h3>${ _('Install sample job workflows?') }</h3>
+      <h3>${ _('Install samples?') }</h3>
     </div>
     </div>
     <div class="modal-body">
     <div class="modal-body">
       ${ _('It will take a few seconds to install.') }
       ${ _('It will take a few seconds to install.') }
@@ -278,10 +283,6 @@ ${ layout.menubar(section='workflows') }
       oTable.fnFilter($(this).val());
       oTable.fnFilter($(this).val());
     });
     });
 
 
-    $("#installSamplesLink").click(function(){
-      $("#installSamples").modal("show");
-    });
-
     $("a[data-row-selector='true']").jHueRowSelector();
     $("a[data-row-selector='true']").jHueRowSelector();
   });
   });
 </script>
 </script>

+ 1 - 1
apps/oozie/src/oozie/templates/utils.inc.mako

@@ -127,7 +127,7 @@
 <%def name="get_status(status)">
 <%def name="get_status(status)">
    % if status in ('SUCCEEDED', 'OK'):
    % if status in ('SUCCEEDED', 'OK'):
      label-success
      label-success
-   % elif status in ('RUNNING', 'PREP'):
+   % elif status in ('RUNNING', 'PREP', 'WAITING'):
       label-warning
       label-warning
    % elif status == 'READY':
    % elif status == 'READY':
       label-success
       label-success

+ 13 - 5
apps/oozie/src/oozie/tests.py

@@ -32,7 +32,7 @@ from liboozie.types import WorkflowList, Workflow as OozieWorkflow, Coordinator
   CoordinatorList, WorkflowAction
   CoordinatorList, WorkflowAction
 
 
 from oozie.models import Workflow, Node, Job, Coordinator, Fork
 from oozie.models import Workflow, Node, Job, Coordinator, Fork
-from oozie.conf import SHARE_JOBS
+from oozie.conf import SHARE_JOBS, REMOTE_DEPLOYMENT_DIR
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
@@ -55,10 +55,10 @@ class MockOozieApi:
 
 
   def __init__(self, *args, **kwargs):
   def __init__(self, *args, **kwargs):
     pass
     pass
-  
+
   def setuser(self, user):
   def setuser(self, user):
     pass
     pass
-  
+
   def submit_job(self, properties):
   def submit_job(self, properties):
     return 'ONE-OOZIE-ID'
     return 'ONE-OOZIE-ID'
 
 
@@ -104,11 +104,11 @@ class TestEditor:
 
 
   def test_find_paramters(self):
   def test_find_paramters(self):
     jobs = [Job(name="$a"),
     jobs = [Job(name="$a"),
-            Job(name="foo $b $$"),
+            Job(name="foo ${b} $$"),
             Job(name="${foo}", description="xxx ${foo}")]
             Job(name="${foo}", description="xxx ${foo}")]
 
 
     result = [job.find_parameters(['name', 'description']) for job in jobs]
     result = [job.find_parameters(['name', 'description']) for job in jobs]
-    assert_equal(set(["a", "b", "foo"]), reduce(lambda x, y: x | set(y), result, set()))
+    assert_equal(set(["b", "foo"]), reduce(lambda x, y: x | set(y), result, set()))
 
 
 
 
   def test_create_workflow(self):
   def test_create_workflow(self):
@@ -353,17 +353,21 @@ class TestEditor:
 
 
     # Submit
     # Submit
     finish = SHARE_JOBS.set_for_testing(False)
     finish = SHARE_JOBS.set_for_testing(False)
+    finish_deployement = REMOTE_DEPLOYMENT_DIR.set_for_testing('/tmp')
     try:
     try:
       response = client_not_me.post(reverse('oozie:submit_workflow', args=[self.wf.id]))
       response = client_not_me.post(reverse('oozie:submit_workflow', args=[self.wf.id]))
       assert_true('Permission denied' in response.content, response.content)
       assert_true('Permission denied' in response.content, response.content)
     finally:
     finally:
       finish()
       finish()
+      finish_deployement()
     finish = SHARE_JOBS.set_for_testing(True)
     finish = SHARE_JOBS.set_for_testing(True)
+    finish_deployement = REMOTE_DEPLOYMENT_DIR.set_for_testing('/tmp')
     try:
     try:
       response = client_not_me.post(reverse('oozie:submit_workflow', args=[self.wf.id]))
       response = client_not_me.post(reverse('oozie:submit_workflow', args=[self.wf.id]))
       assert_false('Permission denied' in response.content, response.content)
       assert_false('Permission denied' in response.content, response.content)
     finally:
     finally:
       finish()
       finish()
+      finish_deployement()
 
 
     # Delete
     # Delete
     finish = SHARE_JOBS.set_for_testing(False)
     finish = SHARE_JOBS.set_for_testing(False)
@@ -466,17 +470,21 @@ class TestEditor:
 
 
     # Submit
     # Submit
     finish = SHARE_JOBS.set_for_testing(False)
     finish = SHARE_JOBS.set_for_testing(False)
+    finish_deployement = REMOTE_DEPLOYMENT_DIR.set_for_testing('/tmp')
     try:
     try:
       response = client_not_me.post(reverse('oozie:submit_coordinator', args=[coord.id]))
       response = client_not_me.post(reverse('oozie:submit_coordinator', args=[coord.id]))
       assert_true('Permission denied' in response.content, response.content)
       assert_true('Permission denied' in response.content, response.content)
     finally:
     finally:
       finish()
       finish()
+      finish_deployement()
     finish = SHARE_JOBS.set_for_testing(True)
     finish = SHARE_JOBS.set_for_testing(True)
+    finish_deployement = REMOTE_DEPLOYMENT_DIR.set_for_testing('/tmp')
     try:
     try:
       response = client_not_me.post(reverse('oozie:submit_coordinator', args=[coord.id]))
       response = client_not_me.post(reverse('oozie:submit_coordinator', args=[coord.id]))
       assert_false('Permission denied' in response.content, response.content)
       assert_false('Permission denied' in response.content, response.content)
     finally:
     finally:
       finish()
       finish()
+      finish_deployement()
 
 
     # Delete
     # Delete
     finish = SHARE_JOBS.set_for_testing(False)
     finish = SHARE_JOBS.set_for_testing(False)

+ 1 - 1
apps/oozie/src/oozie/urls.py

@@ -51,7 +51,7 @@ urlpatterns = patterns(
   url(r'^workflow_parameters/(?P<workflow>\d+)$', 'get_workflow_parameters', name='workflow_parameters'),
   url(r'^workflow_parameters/(?P<workflow>\d+)$', 'get_workflow_parameters', name='workflow_parameters'),
   url(r'^list_history$', 'list_history', name='list_history'),
   url(r'^list_history$', 'list_history', name='list_history'),
   url(r'^list_history/(?P<record_id>[-\w]+)$', 'list_history_record', name='list_history_record'),
   url(r'^list_history/(?P<record_id>[-\w]+)$', 'list_history_record', name='list_history_record'),
-  url(r'^setup/$', 'setup', name='setup'),
+  url(r'^install_examples/$', 'install_examples', name='install_examples'),
 )
 )
 
 
 urlpatterns += patterns(
 urlpatterns += patterns(

+ 12 - 14
apps/oozie/src/oozie/views/dashboard.py

@@ -31,7 +31,7 @@ from desktop.lib.rest.http_client import RestException
 from liboozie.oozie_api import get_oozie
 from liboozie.oozie_api import get_oozie
 
 
 from oozie.models import History
 from oozie.models import History
-from oozie.views.editor import can_access_job
+from oozie.views.editor import can_access_job_or_exception
 
 
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
@@ -44,17 +44,6 @@ A Workflow/Coordinator can be accessed/submitted/modified only by its owner or a
 
 
 Permissions checking happens by calling check_access_and_get_oozie_job().
 Permissions checking happens by calling check_access_and_get_oozie_job().
 """
 """
-
-
-def show_oozie_error(view_func):
-  def decorate(request, *args, **kwargs):
-    try:
-      return view_func(request, *args, **kwargs)
-    except RestException, ex:
-      raise PopupException(_('Sorry, an error with Oozie happened.'), detail=ex._headers.get('oozie-error-message', ex))
-  return wraps(view_func)(decorate)
-
-
 def manage_oozie_jobs(request, job_id, action):
 def manage_oozie_jobs(request, job_id, action):
   if request.method != 'POST':
   if request.method != 'POST':
     raise PopupException(_('Please use a POST request to manage an Oozie job.'))
     raise PopupException(_('Please use a POST request to manage an Oozie job.'))
@@ -73,6 +62,15 @@ def manage_oozie_jobs(request, job_id, action):
   return HttpResponse(json.dumps(response), mimetype="application/json")
   return HttpResponse(json.dumps(response), mimetype="application/json")
 
 
 
 
+def show_oozie_error(view_func):
+  def decorate(request, *args, **kwargs):
+    try:
+      return view_func(request, *args, **kwargs)
+    except RestException, ex:
+      raise PopupException(_('Sorry, an error with Oozie happened.'), detail=ex._headers.get('oozie-error-message', ex))
+  return wraps(view_func)(decorate)
+
+
 @show_oozie_error
 @show_oozie_error
 def list_oozie_workflows(request):
 def list_oozie_workflows(request):
   kwargs = {'cnt': 50,}
   kwargs = {'cnt': 50,}
@@ -113,8 +111,8 @@ def list_oozie_workflow(request, job_id, coordinator_job_id=None):
   hue_coord = history and history.get_coordinator() or History.get_coordinator_from_config(oozie_workflow.conf_dict)
   hue_coord = history and history.get_coordinator() or History.get_coordinator_from_config(oozie_workflow.conf_dict)
   hue_workflow = (hue_coord and hue_coord.workflow) or (history and history.get_workflow()) or History.get_workflow_from_config(oozie_workflow.conf_dict)
   hue_workflow = (hue_coord and hue_coord.workflow) or (history and history.get_workflow()) or History.get_workflow_from_config(oozie_workflow.conf_dict)
 
 
-  if hue_coord: can_access_job(request, hue_coord.workflow.id)
-  if hue_workflow: can_access_job(request, hue_workflow.id)
+  if hue_coord: can_access_job_or_exception(request, hue_coord.workflow.id)
+  if hue_workflow: can_access_job_or_exception(request, hue_workflow.id)
 
 
   # Add parameters from coordinator to workflow if possible
   # Add parameters from coordinator to workflow if possible
   parameters = {}
   parameters = {}

+ 60 - 61
apps/oozie/src/oozie/views/editor.py

@@ -37,6 +37,7 @@ from hadoop.fs.exceptions import WebHdfsException
 from liboozie.submittion import Submission
 from liboozie.submittion import Submission
 
 
 from oozie.conf import SHARE_JOBS
 from oozie.conf import SHARE_JOBS
+from oozie.management.commands import oozie_setup
 from oozie.models import Workflow, Node, Link, History, Coordinator,\
 from oozie.models import Workflow, Node, Link, History, Coordinator,\
   Dataset, DataInput, DataOutput, Job, _STD_PROPERTIES_JSON
   Dataset, DataInput, DataOutput, Job, _STD_PROPERTIES_JSON
 from oozie.forms import NodeForm, WorkflowForm, CoordinatorForm, DatasetForm,\
 from oozie.forms import NodeForm, WorkflowForm, CoordinatorForm, DatasetForm,\
@@ -57,38 +58,63 @@ A Workflow/Coordinator can be modified only by its owner or a superuser.
 
 
 Permissions checking happens by adding the decorators.
 Permissions checking happens by adding the decorators.
 """
 """
+def can_access_job(user, job):
+  return user.is_superuser or job.owner == user or (SHARE_JOBS.get() and job.is_shared)
 
 
-def can_access_job(request, job_id):
-  """
-  Logic for testing if a user can access a certain Workflow / Coordinator.
-  """
+
+def can_access_job_or_exception(request, job_id):
   if job_id is None:
   if job_id is None:
     return
     return
   try:
   try:
     job = Job.objects.select_related().get(pk=job_id).get_full_node()
     job = Job.objects.select_related().get(pk=job_id).get_full_node()
-    if request.user.is_superuser or job.owner == request.user.username or (SHARE_JOBS.get() and job.is_shared):
+    if can_access_job(request.user, job):
       return job
       return job
     else:
     else:
       message = _("Permission denied. %(username)s don't have the permissions to access job %(id)s") % \
       message = _("Permission denied. %(username)s don't have the permissions to access job %(id)s") % \
           {'username': request.user.username, 'id': job.id}
           {'username': request.user.username, 'id': job.id}
       access_warn(request, message)
       access_warn(request, message)
+      request.error(message)
       raise PopupException(message)
       raise PopupException(message)
 
 
   except Job.DoesNotExist:
   except Job.DoesNotExist:
     raise PopupException(_('job %(id)s not exist') % {'id': job_id})
     raise PopupException(_('job %(id)s not exist') % {'id': job_id})
 
 
 
 
-def can_modify_job(request, job):
+def check_job_access_permission(view_func):
+  """
+  Decorator ensuring that the user has access to the workflow or coordinator.
+
+  Arg: 'workflow' or 'coordinator' id.
+  Return: the workflow of coordinator or raise an exception
+
+  Notice: its gets an id in input and returns the full object in output (not an id).
+  """
+  def decorate(request, *args, **kwargs):
+    if 'workflow' in kwargs:
+      job_type = 'workflow'
+    else:
+      job_type = 'coordinator'
+
+    job = kwargs.get(job_type)
+    if job is not None:
+      job = can_access_job_or_exception(request, job)
+    kwargs[job_type] = job
+
+    return view_func(request, *args, **kwargs)
+  return wraps(view_func)(decorate)
+
+
+def can_edit_job(user, job):
   """Only owners or admins can modify a job."""
   """Only owners or admins can modify a job."""
-  return request.user.is_superuser or job.owner.id == request.user.id
+  return user.is_superuser or job.owner.id == user.id
 
 
 
 
-def check_job_modification(request, job):
-  if not can_modify_job(request, job):
-    raise PopupException(_('Not allowed to modified this job'))
+def can_edit_job_or_exception(request, job):
+  if not can_edit_job(request.user, job):
+    raise PopupException('Not allowed to modified this job')
 
 
 
 
-def check_job_modification_permission(authorize_get=False):
+def check_job_edition_permission(authorize_get=False):
   """
   """
   Decorator ensuring that the user has the permissions to modify a workflow or coordinator.
   Decorator ensuring that the user has the permissions to modify a workflow or coordinator.
 
 
@@ -103,37 +129,13 @@ def check_job_modification_permission(authorize_get=False):
 
 
       job = kwargs.get(job_type)
       job = kwargs.get(job_type)
       if job is not None and not (authorize_get and request.method == 'GET'):
       if job is not None and not (authorize_get and request.method == 'GET'):
-        check_job_modification(request, job)
+        can_edit_job_or_exception(request, job)
 
 
       return view_func(request, *args, **kwargs)
       return view_func(request, *args, **kwargs)
     return wraps(view_func)(decorate)
     return wraps(view_func)(decorate)
   return inner
   return inner
 
 
 
 
-def check_job_access_permission(view_func):
-  """
-  Decorator ensuring that the user has access to the workflow or coordinator.
-
-  Arg: 'workflow' or 'coordinator' id.
-  Return: the workflow of coordinator or raise an exception
-
-  Notice: its gets an id in input and returns the full object in output (not an id).
-  """
-  def decorate(request, *args, **kwargs):
-    if 'workflow' in kwargs:
-      job_type = 'workflow'
-    else:
-      job_type = 'coordinator'
-
-    job = kwargs.get(job_type)
-    if job is not None:
-      job = can_access_job(request, job)
-    kwargs[job_type] = job
-
-    return view_func(request, *args, **kwargs)
-  return wraps(view_func)(decorate)
-
-
 def check_action_access_permission(view_func):
 def check_action_access_permission(view_func):
   """
   """
   Decorator ensuring that the user has access to the workflow action.
   Decorator ensuring that the user has access to the workflow action.
@@ -146,14 +148,14 @@ def check_action_access_permission(view_func):
   def decorate(request, *args, **kwargs):
   def decorate(request, *args, **kwargs):
     action_id = kwargs.get('action')
     action_id = kwargs.get('action')
     action = Node.objects.get(id=action_id).get_full_node()
     action = Node.objects.get(id=action_id).get_full_node()
-    can_access_job(request, action.workflow.id)
+    can_access_job_or_exception(request, action.workflow.id)
     kwargs['action'] = action
     kwargs['action'] = action
 
 
     return view_func(request, *args, **kwargs)
     return view_func(request, *args, **kwargs)
   return wraps(view_func)(decorate)
   return wraps(view_func)(decorate)
 
 
 
 
-def check_action_modification_permission(view_func):
+def check_action_edition_permission(view_func):
   """
   """
   Decorator ensuring that the user has the permissions to modify a workflow action.
   Decorator ensuring that the user has the permissions to modify a workflow action.
 
 
@@ -161,7 +163,7 @@ def check_action_modification_permission(view_func):
   """
   """
   def decorate(request, *args, **kwargs):
   def decorate(request, *args, **kwargs):
     action = kwargs.get('action')
     action = kwargs.get('action')
-    check_job_modification(request, action.workflow)
+    can_edit_job_or_exception(request, action.workflow)
 
 
     return view_func(request, *args, **kwargs)
     return view_func(request, *args, **kwargs)
   return wraps(view_func)(decorate)
   return wraps(view_func)(decorate)
@@ -215,7 +217,7 @@ def edit_workflow(request, workflow):
   WorkflowFormSet = inlineformset_factory(Workflow, Node, form=NodeForm, max_num=0, can_order=False, can_delete=False)
   WorkflowFormSet = inlineformset_factory(Workflow, Node, form=NodeForm, max_num=0, can_order=False, can_delete=False)
   history = History.objects.filter(submitter=request.user, job=workflow)
   history = History.objects.filter(submitter=request.user, job=workflow)
 
 
-  if request.method == 'POST' and can_modify_job(request, workflow):
+  if request.method == 'POST' and can_edit_job_or_exception(request, workflow):
     try:
     try:
       workflow_form = WorkflowForm(request.POST, instance=workflow)
       workflow_form = WorkflowForm(request.POST, instance=workflow)
       actions_formset = WorkflowFormSet(request.POST, request.FILES, instance=workflow)
       actions_formset = WorkflowFormSet(request.POST, request.FILES, instance=workflow)
@@ -245,7 +247,7 @@ def edit_workflow(request, workflow):
 
 
 
 
 @check_job_access_permission
 @check_job_access_permission
-@check_job_modification_permission()
+@check_job_edition_permission()
 def delete_workflow(request, workflow):
 def delete_workflow(request, workflow):
   if request.method != 'POST':
   if request.method != 'POST':
     raise PopupException(_('A POST request is required.'))
     raise PopupException(_('A POST request is required.'))
@@ -253,7 +255,7 @@ def delete_workflow(request, workflow):
   workflow.coordinator_set.update(workflow=None) # In Django 1.3 could do ON DELETE set NULL
   workflow.coordinator_set.update(workflow=None) # In Django 1.3 could do ON DELETE set NULL
   workflow.save()
   workflow.save()
   workflow.delete()
   workflow.delete()
-  Submission(workflow, request.fs, {}).remove_deployment_dir()
+  Submission(request.user, workflow, request.fs, {}).remove_deployment_dir()
   request.info(_('Workflow deleted!'))
   request.info(_('Workflow deleted!'))
 
 
   return redirect(reverse('oozie:list_workflows'))
   return redirect(reverse('oozie:list_workflows'))
@@ -278,7 +280,7 @@ def submit_workflow(request, workflow):
 
 
   try:
   try:
     mapping = dict(request.POST.iteritems())
     mapping = dict(request.POST.iteritems())
-    submission = Submission(workflow, request.fs, mapping)
+    submission = Submission(request.user, workflow, request.fs, mapping)
     job_id = submission.run()
     job_id = submission.run()
   except RestException, ex:
   except RestException, ex:
     raise PopupException(_("Error submitting workflow %s") % (workflow,),
     raise PopupException(_("Error submitting workflow %s") % (workflow,),
@@ -340,7 +342,7 @@ def new_action(request, workflow, node_type, parent_action_id):
 def edit_action(request, action):
 def edit_action(request, action):
   ActionForm = design_form_by_type(action.node_type)
   ActionForm = design_form_by_type(action.node_type)
 
 
-  if request.method == 'POST' and can_modify_job(request, action.workflow):
+  if request.method == 'POST' and can_edit_job_or_exception(request, action.workflow):
     action_form = ActionForm(request.POST, instance=action)
     action_form = ActionForm(request.POST, instance=action)
     if action_form.is_valid():
     if action_form.is_valid():
       action = action_form.save()
       action = action_form.save()
@@ -362,7 +364,7 @@ def edit_action(request, action):
 
 
 
 
 @check_action_access_permission
 @check_action_access_permission
-@check_action_modification_permission
+@check_action_edition_permission
 def edit_workflow_fork(request, action):
 def edit_workflow_fork(request, action):
   fork = action
   fork = action
 
 
@@ -399,7 +401,7 @@ def edit_workflow_fork(request, action):
 
 
 
 
 @check_action_access_permission
 @check_action_access_permission
-@check_action_modification_permission
+@check_action_edition_permission
 def delete_action(request, action):
 def delete_action(request, action):
   if request.method == 'POST':
   if request.method == 'POST':
     action.workflow.delete_action(action)
     action.workflow.delete_action(action)
@@ -419,7 +421,7 @@ def clone_action(request, action):
 
 
 
 
 @check_action_access_permission
 @check_action_access_permission
-@check_action_modification_permission
+@check_action_edition_permission
 def move_up_action(request, action):
 def move_up_action(request, action):
   if request.method == 'POST':
   if request.method == 'POST':
     action.workflow.move_action_up(action)
     action.workflow.move_action_up(action)
@@ -429,7 +431,7 @@ def move_up_action(request, action):
 
 
 
 
 @check_action_access_permission
 @check_action_access_permission
-@check_action_modification_permission
+@check_action_edition_permission
 def move_down_action(request, action):
 def move_down_action(request, action):
   if request.method == 'POST':
   if request.method == 'POST':
     action.workflow.move_action_down(action)
     action.workflow.move_action_down(action)
@@ -461,20 +463,20 @@ def create_coordinator(request, workflow=None):
 
 
 
 
 @check_job_access_permission
 @check_job_access_permission
-@check_job_modification_permission()
+@check_job_edition_permission()
 def delete_coordinator(request, coordinator):
 def delete_coordinator(request, coordinator):
   if request.method != 'POST':
   if request.method != 'POST':
     raise PopupException(_('A POST request is required.'))
     raise PopupException(_('A POST request is required.'))
 
 
   coordinator.delete()
   coordinator.delete()
-  Submission(coordinator, request.fs, {}).remove_deployment_dir()
+  Submission(request.user, coordinator, request.fs, {}).remove_deployment_dir()
   request.info(_('Coordinator deleted!'))
   request.info(_('Coordinator deleted!'))
 
 
-  return redirect(reverse('oozie:list_workflows'))
+  return redirect(reverse('oozie:list_coordinator'))
 
 
 
 
 @check_job_access_permission
 @check_job_access_permission
-@check_job_modification_permission(True)
+@check_job_edition_permission(True)
 def edit_coordinator(request, coordinator):
 def edit_coordinator(request, coordinator):
   history = History.objects.filter(submitter=request.user, job=coordinator)
   history = History.objects.filter(submitter=request.user, job=coordinator)
 
 
@@ -522,7 +524,7 @@ def edit_coordinator(request, coordinator):
 
 
 
 
 @check_job_access_permission
 @check_job_access_permission
-@check_job_modification_permission()
+@check_job_edition_permission()
 def create_coordinator_dataset(request, coordinator):
 def create_coordinator_dataset(request, coordinator):
   """Returns {'status' 0/1, data:html or url}"""
   """Returns {'status' 0/1, data:html or url}"""
 
 
@@ -553,7 +555,7 @@ def create_coordinator_dataset(request, coordinator):
 
 
 
 
 @check_job_access_permission
 @check_job_access_permission
-@check_job_modification_permission()
+@check_job_edition_permission()
 def create_coordinator_data(request, coordinator, data_type):
 def create_coordinator_data(request, coordinator, data_type):
   """Returns {'status' 0/1, data:html or url}"""
   """Returns {'status' 0/1, data:html or url}"""
 
 
@@ -608,7 +610,7 @@ def submit_coordinator(request, coordinator):
 
 
   try:
   try:
     if not coordinator.workflow.is_deployed(request.fs):
     if not coordinator.workflow.is_deployed(request.fs):
-      submission = Submission(coordinator.workflow, request.fs, request.POST)
+      submission = Submission(request.user, coordinator.workflow, request.fs, request.POST)
       wf_dir = submission.deploy()
       wf_dir = submission.deploy()
       coordinator.workflow.deployment_dir = wf_dir
       coordinator.workflow.deployment_dir = wf_dir
       coordinator.workflow.save()
       coordinator.workflow.save()
@@ -617,7 +619,7 @@ def submit_coordinator(request, coordinator):
     properties = {'wf_application_path': coordinator.workflow.deployment_dir}
     properties = {'wf_application_path': coordinator.workflow.deployment_dir}
     properties.update(dict(request.POST.iteritems()))
     properties.update(dict(request.POST.iteritems()))
 
 
-    submission = Submission(coordinator, request.fs, properties=properties)
+    submission = Submission(request.user, coordinator, request.fs, properties=properties)
     job_id = submission.run()
     job_id = submission.run()
   except RestException, ex:
   except RestException, ex:
     raise PopupException(_("Error submitting coordinator %s") % (coordinator,),
     raise PopupException(_("Error submitting coordinator %s") % (coordinator,),
@@ -661,14 +663,11 @@ def list_history_record(request, record_id):
   })
   })
 
 
 
 
-def setup(request):
-  """Installs oozie examples."""
+def install_examples(request):
   if request.method != 'POST':
   if request.method != 'POST':
     raise PopupException(_('A POST request is required.'))
     raise PopupException(_('A POST request is required.'))
   try:
   try:
-    # Warning: below will modify fs.user
-    #oozie_setup.Command().handle_noargs()
-    pass
+    oozie_setup.Command().handle_noargs()
   except WebHdfsException, e:
   except WebHdfsException, e:
     raise PopupException(_('The examples could not be installed.'), detail=e)
     raise PopupException(_('The examples could not be installed.'), detail=e)
   return redirect(reverse('oozie:list_workflows'))
   return redirect(reverse('oozie:list_workflows'))

+ 14 - 10
desktop/libs/liboozie/src/liboozie/submittion.py

@@ -17,20 +17,24 @@
 
 
 import errno
 import errno
 import logging
 import logging
+import time
 
 
 from desktop.lib.django_util import PopupException
 from desktop.lib.django_util import PopupException
 from hadoop import cluster
 from hadoop import cluster
+from hadoop.fs.hadoopfs import Hdfs
 from liboozie.oozie_api import get_oozie
 from liboozie.oozie_api import get_oozie
 
 
+from oozie.conf import REMOTE_DEPLOYMENT_DIR
+
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
 
 
 class Submission(object):
 class Submission(object):
   """Represents one unique Oozie submission"""
   """Represents one unique Oozie submission"""
-  def __init__(self, job, fs, properties=None):
+  def __init__(self, user, job, fs, properties=None):
     self.job = job
     self.job = job
-    self.username = job.owner.username
+    self.user = user
     self.fs = fs
     self.fs = fs
     self.oozie_id = None
     self.oozie_id = None
 
 
@@ -40,7 +44,7 @@ class Submission(object):
       self.properties = {}
       self.properties = {}
 
 
   def __unicode__(self):
   def __unicode__(self):
-    res = "Submission for job '%s' (id %s, owner %s)" % (self.job.name, self.job.id, self.username)
+    res = "Submission for job '%s' (id %s, owner %s)" % (self.job.name, self.job.id, self.user)
     if self.oozie_id:
     if self.oozie_id:
       res += " -- " + self.oozie_id
       res += " -- " + self.oozie_id
     return res
     return res
@@ -58,7 +62,7 @@ class Submission(object):
     deployment_dir = self.deploy()
     deployment_dir = self.deploy()
 
 
     try:
     try:
-      prev = get_oozie().setuser(self.username )
+      prev = get_oozie().setuser(self.user.username)
       self._update_properties(jobtracker, deployment_dir)
       self._update_properties(jobtracker, deployment_dir)
       self.oozie_id = get_oozie().submit_job(self.properties)
       self.oozie_id = get_oozie().submit_job(self.properties)
       LOG.info("Submitted: %s" % (self,))
       LOG.info("Submitted: %s" % (self,))
@@ -80,7 +84,7 @@ class Submission(object):
                            detail=str(ex))
                            detail=str(ex))
 
 
     oozie_xml = self.job.to_xml()
     oozie_xml = self.job.to_xml()
-    self._do_as(self.username , self._copy_files, deployment_dir, oozie_xml)
+    self._do_as(self.user.username , self._copy_files, deployment_dir, oozie_xml)
 
 
     return deployment_dir
     return deployment_dir
 
 
@@ -98,7 +102,7 @@ class Submission(object):
     """
     """
     Return the job deployment directory in HDFS, creating it if necessary.
     Return the job deployment directory in HDFS, creating it if necessary.
     """
     """
-    path = self.job.deployment_dir
+    path = Hdfs.join(REMOTE_DEPLOYMENT_DIR.get(), '_%s_-oozie-%s-%s' % (self.user.username, self.job.id, time.time()))
 
 
     try:
     try:
       statbuf = self.fs.stats(path)
       statbuf = self.fs.stats(path)
@@ -114,7 +118,7 @@ class Submission(object):
         raise IOError(ex.errno, msg)
         raise IOError(ex.errno, msg)
     # The actual deployment dir should be 0711 owned by the user
     # The actual deployment dir should be 0711 owned by the user
     if not self.fs.exists(path):
     if not self.fs.exists(path):
-      self._do_as(self.username , self.fs.mkdir, path, 0711)
+      self._do_as(self.user.username , self.fs.mkdir, path, 0711)
     return path
     return path
 
 
   def _copy_files(self, deployment_dir, oozie_xml):
   def _copy_files(self, deployment_dir, oozie_xml):
@@ -157,9 +161,9 @@ class Submission(object):
     """Delete the workflow deployment directory."""
     """Delete the workflow deployment directory."""
     try:
     try:
       path = self.job.deployment_dir
       path = self.job.deployment_dir
-      if self._do_as(self.username , self.fs.exists, path):
-        self._do_as(self.username , self.fs.rmtree, path)
+      if self._do_as(self.user.username , self.fs.exists, path):
+        self._do_as(self.user.username , self.fs.rmtree, path)
     except Exception, ex:
     except Exception, ex:
       LOG.warn("Failed to clean up workflow deployment directory for "
       LOG.warn("Failed to clean up workflow deployment directory for "
                "%s (owner %s). Caused by: %s",
                "%s (owner %s). Caused by: %s",
-               self.job.name, self.job.owner.username, ex)
+               self.job.name, self.user, ex)