Forráskód Böngészése

[oozie] Adding Fs action

Romain Rigaux 11 éve
szülő
commit
b9ce6005d3

+ 124 - 0
apps/oozie/src/oozie/models2.py

@@ -742,6 +742,128 @@ class ShellAction(Action):
     return [cls.FIELDS['shell_command']]
 
 
+class SshAction(Action):
+  TYPE = 'shell' 
+  FIELDS = {
+     'host': { 
+          'name': 'shell_command',
+          'label': _('Shell command'),
+          'value': 'script.sh',
+          'help_text': _('The path of the Shell command to execute.')
+     },            
+     'arguments': {
+          'name': 'arguments',
+          'label': _('Arguments'),
+          'value': [],
+          'help_text': _('The arguments of the command can then be specified using one or more argument element.')
+     },    
+     'env_var': { 
+          'name': 'env_var',
+          'label': _('Environment variables'),
+          'value': [],
+          'help_text': _('Environemnt to be passed to the Shell command. env-var should contain only one pair of environment variable and value. '
+                         'If the pair contains the variable such as $PATH, it should follow the Unix convention such as PATH=$PATH:mypath. '
+                         'Don\'t use ${PATH} which will be substitued by Oozie\'s EL evaluator.')
+     },         
+     'capture_output': { 
+          'name': 'capture_output',
+          'label': _('Capture output'),
+          'value': True,
+          'help_text': _('Capture output of the stdout of the %(program)s command execution. The %(program)s '
+                         'command output must be in Java Properties file format and it must not exceed 2KB. '
+                         'From within the workflow definition, the output of an %(program)s action node is accessible '
+                         'via the String action:output(String node, String key) function') % {'program': TYPE}
+     },
+     # Common
+     'files': { 
+          'name': 'files',
+          'label': _('Files'),
+          'value': [],
+          'help_text': _('List of names or paths of files to be added to the distributed cache and the task running directory.')
+     },
+     'archives': { 
+          'name': 'archives',
+          'label': _('Archives'),
+          'value': [],
+          'help_text': _('List of names or paths of the archives to be added to the distributed cache.')
+     },
+     'job_properties': { 
+          'name': 'job_properties',
+          'label': _('Hadoop job properties'),
+          'value': [],
+          'help_text': _('For the job configuration (e.g. mapred.job.queue.name=production).')
+     },
+     'prepares': { 
+          'name': 'prepares',
+          'label': _('Prepares'),
+          'value': [],
+          'help_text': _('List of absolute paths to delete and then to create before starting the application. This should be used exclusively for directory cleanup.')
+     },
+     'job_xml': { 
+          'name': 'job_xml',
+          'label': _('Job XML'),
+          'value': [],
+          'help_text': _('Refer to a Hadoop JobConf job.xml file bundled in the workflow deployment directory. '
+                        'Properties specified in the Job Properties element override properties specified in the '
+                        'files specified in the Job XML element.')
+     }
+  }
+
+  @classmethod
+  def get_mandatory_fields(cls):
+    return [cls.FIELDS['shell_command']]
+
+
+class FsAction(Action):
+  TYPE = 'fs' 
+  FIELDS = {
+     'deletes': { 
+          'name': 'deletes',
+          'label': _('Delete path'),
+          'value': [],
+          'help_text': _('Delete the specified path, if it is a directory it deletes recursively all its content and then deletes the directory.')
+     },
+     'mkdirs': { 
+          'name': 'mkdirs',
+          'label': _('Create directory'),
+          'value': [],
+          'help_text': _('Create the specified directory, it creates all missing directories in the path. If the directory already exist it does a no-op.')
+     },
+     'moves': { 
+          'name': 'moves',
+          'label': _('Move file'),
+          'value': [],
+          'help_text': _('Move a file or directory to another path.')
+     },  
+     'chmods': { 
+          'name': 'chmods',
+          'label': _('Change permissions'),
+          'value': [],
+          'help_text': _('Change the permissions for the specified path. Permissions can be specified using the Unix Symbolic '
+                         'representation (e.g. -rwxrw-rw-) or an octal representation (755).')
+     },
+     'touchzs': { 
+          'name': 'touchzs',
+          'label': _('Create or touch a file'),
+          'value': [],
+          'help_text': _('Creates a zero length file in the specified path if none exists or touch it.')
+     },
+     'chgrps': { 
+          'name': 'chgrps',
+          'label': _('Changes the group for the specified path. '),
+          'value': [],
+          'help_text': _('TWhen doing a chgrp command on a directory, by default the command is applied '
+                         'to the directory and the files one level within the directory. To apply the chgrp command to the directory, without affecting '
+                         'the files within it, the dir-files attribute must be set to false . To apply the chgrp command recursively to all levels within a directory, '
+                         'put a recursive element inside the element.')
+     }
+  }
+
+  @classmethod
+  def get_mandatory_fields(cls):
+    return []
+
+
 class KillAction(Action):
   TYPE = 'kill'
   FIELDS = {
@@ -776,6 +898,8 @@ NODES = {
   'mapreduce-widget': MapReduceAction,  
   'subworkflow-widget': SubWorkflowAction,
   'shell-widget': ShellAction,
+  'ssh-widget': SshAction,
+  'fs-widget': FsAction,    
   'kill-widget': KillAction,
   'join-widget': JoinAction,
 }

+ 25 - 19
apps/oozie/src/oozie/templates/editor/gen2/workflow-fs.xml.mako

@@ -21,35 +21,41 @@
 
 <%namespace name="common" file="workflow-common.xml.mako" />
 
-    <action name="${ node }"${ common.credentials(node.credentials) }>
+    <action name="${ node['name'] }"${ common.credentials(node['properties']['credentials']) }>
         <fs>
-            % for param in node.get_deletes():
-              <delete path='${ smart_path(param['name'], mapping) }'/>
+            % for param in node['properties']['deletes']:
+              <delete path='${ smart_path(param['value'], mapping) }'/>
             % endfor
 
-            % for param in node.get_mkdirs():
-              <mkdir path='${ smart_path(param['name'], mapping) }'/>
+            % for param in node['properties']['mkdirs']:
+              <mkdir path='${ smart_path(param['value'], mapping) }'/>
             % endfor
 
-            % for param in node.get_moves():
-              <move source='${ smart_path(param['source'], mapping) }' target='${ smart_path(param['destination'], mapping) }'/>
+            % for param in node['properties']['moves']:
+              <move source='${ smart_path(param['source'], mapping) }' target='${ smart_path(param['target'], mapping) }'/>
             % endfor
 
-            % for param in node.get_chmods():
-              <%
-                if param['recursive']:
-                  recursive = 'true'
-                else:
-                  recursive = 'false'
-              %>
-              <chmod path='${ smart_path(param['path'], mapping) }' permissions='${ param['permissions'] }' dir-files='${ recursive }'/>
+            % for param in node['properties']['moves']:
+              <chmod path='${ smart_path(param['value'], mapping) }' permissions='${ param['permissions'] }' dir-files='${ 'true' if dir_files else 'false' }'>
+              % if param['recursive']:
+                <recursive/>
+              % endif
+              </chmod>              
             % endfor
 
-            % for param in node.get_touchzs():
-              <touchz path='${ smart_path(param['name'], mapping) }'/>
+            % for param in node['properties']['touchzs']:
+              <touchz path='${ smart_path(param['value'], mapping) }'/>
             % endfor
+            
+            % for param in node['properties']['chgrps']:
+              <chgrp path='${ smart_path(param['value'], mapping) }' group='${ param['group'] }' dir-files='${ 'true' if dir_files else 'false' }'>
+              % if param['recursive']:
+                <recursive/>
+              % endif
+              </chgrp>              
+            % endfor            
         </fs>
-        <ok to="${ node.get_oozie_child('ok') }"/>
-        <error to="${ node.get_oozie_child('error') }"/>
+        <ok to="${ node_mapping[node['children'][0]['to']].name }"/>
+        <error to="${ node_mapping[node['children'][1]['error']].name }"/>
         ${ common.sla(node) }
     </action>

+ 157 - 29
apps/oozie/src/oozie/templates/editor/workflow_editor.mako

@@ -93,14 +93,14 @@ ${ commonheader(_("Workflow Editor"), "Oozie", user) | n,unicode }
     
     <div data-bind="css: { 'draggable-widget': true },
                     draggable: {data: draggableSshAction(), isEnabled: true,
-                    options: {'start': function(event, ui){}}}"
+                    options: {'start': function(event, ui){$root.currentlyDraggedWidget(draggableSshAction());}}}"
          title="${_('Ssh')}" rel="tooltip" data-placement="top">
          <a class="draggable-icon"><i class="fa fa-tty"></i></a>
     </div>
         
     <div data-bind="css: { 'draggable-widget': true },
                     draggable: {data: draggableFsAction(), isEnabled: true,
-                    options: {'start': function(event, ui){}}}"
+                    options: {'start': function(event, ui){$root.currentlyDraggedWidget(draggableFsAction());}}}"
          title="${_('HDFS Fs')}" rel="tooltip" data-placement="top">
          <a class="draggable-icon"><i class="fa fa-file-o"></i></a>
     </div>    
@@ -1091,26 +1091,47 @@ ${ commonheader(_("Workflow Editor"), "Oozie", user) | n,unicode }
 
     <div>
       <ul class="nav nav-tabs">
-        <li class="active"><a href="#action" data-toggle="tab">${ _('Ssh') }</a></li>
-        <li><a href="#properties" data-toggle="tab">${ _('Files') }</a></li>
-        <li><a href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
-        <li><a href="#credentials" data-toggle="tab">${ _('Credentials') }</a></li>
-        <li><a href="#transitions" data-toggle="tab">${ _('Transitions') }</a></li>
+        <li class="active"><a data-bind="attr: { href: '#action-' + id()}" data-toggle="tab">${ _('Ssh') }</a></li>
+        <li><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
       </ul>
       <div class="tab-content">
-        <div class="tab-pane active" id="action">
+        <div class="tab-pane active" data-bind="attr: { id: 'action-' + id() }">
           <i class="fa fa-tty"></i>
         </div>
-        <div class="tab-pane" id="properties">
+
+        <div class="tab-pane" data-bind="attr: { id: 'properties-' + id() }">          
+          <span data-bind="text: $root.workflow_properties.capture_output.label"></span>
+          <input type="checkbox" data-bind="checked: properties.capture_output" />
+          <br/>
+          <span data-bind="text: $root.workflow_properties.env_var.label"></span>
+          <ul data-bind="foreach: properties.env_var">
+            <li>
+              <input data-bind="value: value"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.env_var.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+          <button data-bind="click: function(){ properties.env_var.push({'value': ''}); }">
+            <i class="fa fa-plus"></i>
+          </button>           
+          <br/>
           <span data-bind="template: { name: 'common-action-properties' }"></span>
         </div>
-        <div class="tab-pane" id="sla">
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
         </div>
-        <div class="tab-pane" id="credentials">
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
         </div>
-        <div class="tab-pane" id="transitions">
-          OK --> []
-          KO --> []
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
         </div>
       </div>
     </div>
@@ -1129,26 +1150,133 @@ ${ commonheader(_("Workflow Editor"), "Oozie", user) | n,unicode }
 
     <div>
       <ul class="nav nav-tabs">
-        <li class="active"><a href="#action" data-toggle="tab">${ _('Fs') }</a></li>
-        <li><a href="#properties" data-toggle="tab">${ _('Files') }</a></li>
-        <li><a href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
-        <li><a href="#credentials" data-toggle="tab">${ _('Credentials') }</a></li>
-        <li><a href="#transitions" data-toggle="tab">${ _('Transitions') }</a></li>
+        <li class="active"><a data-bind="attr: { href: '#action-' + id()}" data-toggle="tab">${ _('Fs') }</a></li>
+        <li><a data-bind="attr: { href: '#properties-' + id()}" data-toggle="tab">${ _('Properties') }</a></li>
+        <li><a data-bind="attr: { href: '#sla-' + id()}" href="#sla" data-toggle="tab">${ _('SLA') }</a></li>
+        <li><a data-bind="attr: { href: '#credentials-' + id()}" data-toggle="tab">${ _('Credentials') }</a></li>
+        <li><a data-bind="attr: { href: '#transitions-' + id()}" data-toggle="tab">${ _('Transitions') }</a></li>
       </ul>
       <div class="tab-content">
-        <div class="tab-pane active" id="action">
-          <img src="/oozie/static/art/icon_beeswax_48.png" class="app-icon">
+        <div class="tab-pane active" data-bind="attr: { id: 'action-' + id() }">
+          <span data-bind="text: $root.workflow_properties.deletes.label"></span>
+          <ul data-bind="foreach: properties.deletes">
+            <li>
+              <input data-bind="value: value"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.deletes.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+          <button data-bind="click: function(){ properties.deletes.push({'value': ''}); }">
+            <i class="fa fa-plus"></i>
+          </button>  
+                   
+          <br/>
+          
+          <span data-bind="text: $root.workflow_properties.mkdirs.label"></span>
+          <ul data-bind="foreach: properties.mkdirs">
+            <li>
+              <input data-bind="value: value"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.mkdirs.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+          <button data-bind="click: function(){ properties.mkdirs.push({'value': ''}); }">
+            <i class="fa fa-plus"></i>
+          </button> 
+                    
+          <br/>
+          
+          <span data-bind="text: $root.workflow_properties.touchzs.label"></span>
+          <ul data-bind="foreach: properties.touchzs">
+            <li>
+              <input data-bind="value: value"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.touchzs.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+          <button data-bind="click: function(){ properties.touchzs.push({'value': ''}); }">
+            <i class="fa fa-plus"></i>
+          </button> 
+                    
+          <br/>
+          
+          <span data-bind="text: $root.workflow_properties.moves.label"></span>
+          <span data-bind="text: $root.workflow_properties.moves.label"></span>
+          <ul data-bind="foreach: properties.moves">
+            <li>
+              <input data-bind="value: source"/>
+              <input data-bind="value: target"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.moves.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+          <button data-bind="click: function(){ properties.moves.push({'source': '', 'target': ''}); }">
+            <i class="fa fa-plus"></i>
+          </button>    
+      
+          <br/>
+               
+          <span data-bind="text: $root.workflow_properties.chmods.label"></span>
+          <span data-bind="text: $root.workflow_properties.chmods.label"></span>
+          <ul data-bind="foreach: properties.chmods">
+            <li>
+              ${ _('Path') }
+              <input data-bind="value: value"/>
+              ${ _('Permissions') }
+              <input data-bind="value: permissions"/>
+              ${ _('Also apply to files') }
+              <input type="checkbox" data-bind="value: dir_files"/>
+              ${ _('Recursive') }
+              <input type="checkbox" data-bind="value: recursive"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.chmods.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+          <button data-bind="click: function(){ properties.chmods.push({'value': '', 'permissions': '600', 'dir_files': true, 'recursive': false}); }">
+            <i class="fa fa-plus"></i>
+          </button>
+                     
+          <br/>
+          
+          <span data-bind="text: $root.workflow_properties.chgrps.label"></span>
+          <ul data-bind="foreach: properties.chgrps">
+            <li>
+              ${ _('Path') }
+              <input data-bind="value: value"/>
+              ${ _('Group') }
+              <input data-bind="value: group"/>
+              ${ _('Also apply to files') }
+              <input type="checkbox" data-bind="value: dir_files"/>
+              ${ _('Recursive') }
+              <input type="checkbox" data-bind="value: recursive"/>
+              <a href="#" data-bind="click: function(){ $parent.properties.chgrps.remove(this); }">
+                <i class="fa fa-minus"></i>
+              </a>
+            </li>
+          </ul>
+          <button data-bind="click: function(){ properties.chmods.push({'value': '', 'group': 'mygroup', 'dir_files': false, 'recursive': false}); }">
+            <i class="fa fa-plus"></i>
+          </button>           
         </div>
-        <div class="tab-pane" id="properties">
-          <span data-bind="template: { name: 'common-action-properties' }"></span>
+        
+        <div class="tab-pane" data-bind="attr: { id: 'properties-' + id() }">
         </div>
-        <div class="tab-pane" id="sla">
+
+        <div class="tab-pane" data-bind="attr: { id: 'sla-' + id() }">
+          <span data-bind="template: { name: 'common-action-sla' }"></span>
         </div>
-        <div class="tab-pane" id="credentials">
+
+        <div class="tab-pane" data-bind="attr: { id: 'credentials-' + id() }">
+          <span data-bind="template: { name: 'common-action-credentials' }"></span>
         </div>
-        <div class="tab-pane" id="transitions">
-          OK --> []
-          KO --> []
+
+        <div class="tab-pane" data-bind="attr: { id: 'transitions-' + id() }">
+          <span data-bind="template: { name: 'common-action-transition' }"></span>
         </div>
       </div>
     </div>
@@ -1174,7 +1302,7 @@ ${ commonheader(_("Workflow Editor"), "Oozie", user) | n,unicode }
         <li><a href="#transitions" data-toggle="tab">${ _('Transitions') }</a></li>
       </ul>
       <div class="tab-content">
-        <div class="tab-pane active" id="action">
+        <div class="tab-pane active" data-bind="attr: { id: 'action-' + id() }">
           <i class="fa fa-envelope-o"></i>
         </div>
         <div class="tab-pane" id="properties">