浏览代码

[oozie] Editable names for fork and decision

Decision and fork nodes can now have names.
Removed join and decision end from being displayed.
abec 13 年之前
父节点
当前提交
427035aa53

+ 7 - 1
apps/oozie/src/oozie/forms.py

@@ -90,8 +90,14 @@ class NodeForm(forms.ModelForm):
   class Meta:
     ALWAYS_HIDE = ('workflow', 'children', 'node_type')
     model = Node
-    exclude = ALWAYS_HIDE + ('name', 'description')
+    exclude = ALWAYS_HIDE
+
 
+class NodeMetaForm(forms.ModelForm):
+  class Meta:
+    ALWAYS_HIDE = ('workflow', 'children', 'node_type')
+    model = Node
+    exclude = ALWAYS_HIDE + ('name', 'description')
 
 class JavaForm(forms.ModelForm):
   class Meta:

+ 7 - 5
apps/oozie/src/oozie/models.py

@@ -410,9 +410,9 @@ class Workflow(Job):
       return [node] + self.get_hierarchy_rec(child)
 
   def gen_status_graph(self, oozie_workflow):
-    from oozie.forms import NodeForm  # Circular dependency
+    from oozie.forms import NodeMetaForm  # Circular dependency
     actions = oozie_workflow.get_working_actions()
-    WorkflowFormSet = inlineformset_factory(Workflow, Node, form=NodeForm, max_num=0, can_order=False, can_delete=False)
+    WorkflowFormSet = inlineformset_factory(Workflow, Node, form=NodeMetaForm, max_num=0, can_order=False, can_delete=False)
     forms = WorkflowFormSet(instance=self).forms
     template='editor/gen/workflow-graph-status.xml.mako'
 
@@ -1538,12 +1538,14 @@ ACTION_TYPES = {
   Generic.node_type: Generic,
 }
 
-NODE_TYPES = ACTION_TYPES.copy()
-NODE_TYPES.update({
+CONTROL_TYPES = {
   Fork.node_type: Fork,
   Join.node_type: Join,
   Decision.node_type: Decision,
   DecisionEnd.node_type: DecisionEnd,
   Start.node_type: Start,
   End.node_type: End,
-})
+}
+
+NODE_TYPES = ACTION_TYPES.copy()
+NODE_TYPES.update(CONTROL_TYPES)

+ 92 - 60
apps/oozie/src/oozie/templates/editor/control_utils.mako

@@ -21,14 +21,12 @@
 <%namespace name="utils" file="../utils.inc.mako" />
 
 
-
-
-<%def name="fork_form(node_type, template=True, javascript_attrs={})">
+<%def name="fork_convert_form(node_type, template=True, javascript_attrs={})">
 % if template:
-  <script type="text/html" id="${node_type}EditTemplate">
+  <script type="text/html" id="${node_type}ConvertTemplate">
 % endif
   <div data-bind="with: context().node">
-    <form class="form-horizontal" id="${node_type}-action-form" method="POST">
+    <form class="form-horizontal" id="${node_type}-convert-form" method="POST">
       <div class="modal-header">
         <a href="#" class="close" data-dismiss="modal">&times;</a>
         <h3 class="message" data-bind="text: '${_('Edit Node: ')}' + name()"></h3>
@@ -56,14 +54,46 @@
 </%def>
 
 
+<%def name="fork_edit_form(form, node_type, template=True, javascript_attrs={})">
+% if template:
+  <script type="text/html" id="${node_type}EditTemplate">
+% endif
+  <div data-bind="with: context().node">
+    <form class="form-horizontal" id="${node_type}-action-form" method="POST">
+      <div class="modal-header">
+        <a href="#" class="close" data-dismiss="modal">&times;</a>
+        <h3 class="message" data-bind="text: '${_('Edit Node: ')}' + name()"></h3>
+      </div>
 
+      <div class="modal-content">
+        <fieldset class="span12">
+          % for field in form:
+            % if field.html_name in ('name', 'description'):
+              ${ utils.render_field_with_error_js(field, field.name, extra_attrs={'data-bind': 'disable: $root.context().read_only, value: %s' % field.name}) }
+            % endif
+          % endfor
+        </fieldset>
+      </div>
 
+      <div class="modal-footer">
+        <a class="btn cancelButton" href="javascript:void(0);">Cancel</a>
+        <button class="btn btn-primary doneButton" type="button" data-bind="visible: !$root.context().read_only">${ _('Done')}</button>
+      </div>
 
-<%def name="decision_form(link_form, default_link_form, node_type, template=True, javascript_attrs={})">
+    </form>
+  </div>
+
+% if template:
+  </script>
+% endif
+</%def>
+
+
+<%def name="decision_form(node_form, link_form, default_link_form, node_type, template=True, javascript_attrs={})">
 % if template:
   <script type="text/html" id="${node_type}EditTemplate">
 % endif
-  <div data-bind="with: context">
+  <div data-bind="with: context().node">
     <form class="form-horizontal" id="${node_type}-action-form" method="POST">
       <div class="modal-header">
         <a href="#" class="close" data-dismiss="modal">&times;</a>
@@ -71,11 +101,16 @@
       </div>
 
       <div class="modal-content">
-        <fieldset>
+        <fieldset class="span12">
+          % for field in node_form:
+            % if field.html_name in ('name', 'description'):
+              ${ utils.render_field_with_error_js(field, field.name, extra_attrs={'data-bind': 'disable: $root.context().read_only, value: %s' % field.name}) }
+            % endif
+          % endfor
 
           <div class="control-group">
             <label class="control-label"></label>
-            <div class="controls span8">
+            <div class="controls">
               <div>${ _('Examples of predicates:') }</div>
               <div class="well">
                 ${"${"} fs:fileSize(secondjobOutputDir) gt 10 * GB }
@@ -87,62 +122,58 @@
             </div>
           </div>
 
-          <div class="control-group">
-            <label class="control-label"></label>
-            <div class="controls">
-              <table class="table-condensed">
-                <thead>
-                  <tr>
-                    <th>${ _('Predicate') }</th>
-                    <th/>
-                    <th>${ _('Action') }</th>
-                  </tr>
-                </thead>
-                <tbody>
-                  <!-- ko foreach: links() -->
-                  <tr>
-                    <td>
-                      ${ utils.render_field(link_form['comment'], extra_attrs={'data-bind': 'value: comment'}) }
-                    </td>
-                    <td class="center">
-                      ${ _('go to') }
-                    </td>
-                    <td class="right">
-                      <a class="span3 edit-node-link" data-bind="text: $parent.registry.get(child()).name()"></a>
-                    </td>
-                  </tr>
-                  <!-- /ko -->
-
-                  <!-- ko foreach: meta_links() -->
-                    <!-- ko if: $data.name() == 'default' -->
-                    <tr>
-                      <td>
-                       <div class="control-group">
-                          <label class="control-label"></label>
-                          <div class="controls span8">
-                            <div>${ _('default') }</div>
-                          </div>
-                        </div>
-                      </td>
-                      <td class="center">
-                        ${ _('go to') }
-                      </td>
-                      <td class="right">
-                        ${ utils.render_field(default_link_form['child'], extra_attrs={'data-bind': 'value: child'}) }
-                      </td>
-                    </tr>
-                    <!-- /ko -->
-                  <!-- /ko -->
-                </tbody>
-              </table>
-            </div>
-          </div>
+          <table class="table-condensed">
+            <thead>
+              <tr>
+                <th>${ _('Predicate') }</th>
+                <th/>
+                <th>${ _('Action') }</th>
+              </tr>
+            </thead>
+            <tbody>
+              <!-- ko foreach: links() -->
+              <tr>
+                <td>
+                  ${ utils.render_field(link_form['comment'], extra_attrs={'data-bind': 'value: comment'}) }
+                </td>
+                <td class="center">
+                  ${ _('go to') }
+                </td>
+                <td class="right">
+                  <a class="span3 edit-node-link" data-bind="text: $parent.registry.get(child()).name()"></a>
+                </td>
+              </tr>
+              <!-- /ko -->
+
+              <!-- ko foreach: meta_links() -->
+                <!-- ko if: $data.name() == 'default' -->
+                <tr>
+                  <td>
+                   <div class="control-group">
+                      <label class="control-label"></label>
+                      <div class="controls">
+                        <div>${ _('default') }</div>
+                      </div>
+                    </div>
+                  </td>
+                  <td class="center nowrap">
+                    ${ _('go to') }
+                  </td>
+                  <td class="right">
+                    ${ utils.render_field(default_link_form['child'], extra_attrs={'data-bind': 'value: child'}) }
+                  </td>
+                </tr>
+                <!-- /ko -->
+              <!-- /ko -->
+            </tbody>
+          </table>
 
         </fieldset>
       </div>
 
       <div class="modal-footer">
-        <button data-dismiss="modal" class="btn btn-primary">${ _('Done')}</button>
+        <a class="btn cancelButton" href="javascript:void(0);">Cancel</a>
+        <button class="btn btn-primary doneButton" type="button" data-bind="visible: !$root.context().read_only">${ _('Done')}</button>
       </div>
 
     </form>
@@ -153,6 +184,7 @@
 % endif
 </%def>
 
+
 <%def name="links_form_fields(link_form, default_link_form, javascript_attrs={})">
 
 <div class="control-group" rel="popover" data-original-title="${ field.label }" data-content="${ field.help_text }">

+ 13 - 29
apps/oozie/src/oozie/templates/editor/edit_workflow.mako

@@ -312,9 +312,10 @@ ${ layout.menubar(section='workflows') }
 
 ${ actions.import_jobsub_form(template=True) }
 
-${ controls.fork_form('fork', True, javascript_attrs={'convert': 'function(data, event) { $data.convertToDecision(); $data._workflow.rebuild(); }'}) }
+${ controls.fork_convert_form(node_type='fork', template=True, javascript_attrs={'convert': 'function(data, event) { $data.convertToDecision(); $data._workflow.rebuild(); }'}) }
+${ controls.fork_edit_form(form=node_form, node_type='fork', template=True) }
 
-${ controls.decision_form(link_form, default_link_form, 'decision', True) }
+${ controls.decision_form(node_form, link_form, default_link_form, 'decision', True) }
 
 <script type="text/html" id="emptyTemplate"></script>
 
@@ -360,7 +361,8 @@ ${ controls.decision_form(link_form, default_link_form, 'decision', True) }
 
       <div class="row-fluid node-action-bar">
         <div class="span12" style="text-align:right">
-          <a class="btn btn-mini edit-node-link" title="${ _('Convert to Decision') }" data-bind="attr: { 'data-node-type': node_type() }" rel="tooltip"><i class="icon-wrench"></i></a>
+          <a class="btn btn-mini edit-node-link" title="${ _('Edit') }" rel="tooltip" data-bind="attr: { 'data-node-type': node_type() }"><i class="icon-pencil"></i></a>
+          <a class="btn btn-mini convert-node-link" title="${ _('Convert to Decision') }" data-bind="attr: { 'data-node-type': node_type() }" rel="tooltip"><i class="icon-wrench"></i></a>
           &nbsp;
         </div>
       </div>
@@ -378,18 +380,6 @@ ${ controls.decision_form(link_form, default_link_form, 'decision', True) }
 </script>
 
 <script type="text/html" id="joinTemplate">
-  <div class="node node-join row-fluid">
-    <div class="action span12">
-      <div class="row-fluid">
-        <div class="span12">
-          <h4 data-bind="text: (name()) ? name() : node_type() + '-' + id()"></h4>
-          <span data-bind="text: node_type()" class="muted"></span>
-          <br/>&nbsp;
-        </div>
-      </div>
-    </div>
-
-  </div>
   <div class="row-fluid" data-bind="template: { name: 'linkTemplate', foreach: links() }"></div>
 </script>
 
@@ -424,18 +414,6 @@ ${ controls.decision_form(link_form, default_link_form, 'decision', True) }
 </script>
 
 <script type="text/html" id="decisionEndTemplate">
-  <div class="node node-decisionend row-fluid">
-    <div class="action span12">
-      <div class="row-fluid">
-        <div class="span12">
-          <h4 data-bind="text: 'end-' + id()"></h4>
-          <span data-bind="text: node_type()" class="muted"></span>
-          <br/>&nbsp;
-        </div>
-      </div>
-    </div>
-
-  </div>
   <div class="row-fluid" data-bind="template: { name: 'linkTemplate', foreach: links() }"></div>
 </script>
 
@@ -513,12 +491,12 @@ import_node.loadAvailableNodes({ success: import_load_available_nodes_success })
  * Modals
  */
 // open a modal window for editing a node
-function edit_node_modal(node, save, cancel) {
+function edit_node_modal(node, save, cancel, template) {
   var backup = ko.mapping.toJS(node);
   normalize_model_fields(backup);
 
   modal.hide();
-  modal.setTemplate(node.edit_template);
+  modal.setTemplate(template || node.edit_template);
   modal.show({node: node, read_only: workflow.read_only()});
   modal.recenter(280, 250);
   modal.addDecorations();
@@ -617,6 +595,12 @@ $('#workflow').on('click', '.edit-node-link', function(e) {
   edit_node_modal(node);
 });
 
+// Modal for converting to a decision node
+$('#workflow').on('click', '.convert-node-link', function(e) {
+  var node = ko.contextFor(this).$data;
+  edit_node_modal(node, null, null, 'forkConvertTemplate');
+});
+
 // Modal for cloning a node
 $('#workflow').on('click', '.clone-node-btn', function(e) {
   var node = ko.contextFor(this).$data;

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

@@ -362,9 +362,9 @@ class TestAPI(OozieMockBase):
 
     assert_equal(0, test_response_json_object['status'])
 
-  def test_workflow_validate_action(self):
+  def test_workflow_validate_node(self):
     data = {"files":"[\"hive-site.xml\"]","job_xml":"hive-site.xml","description":"Show databases","workflow":17,"child_links":[{"comment":"","name":"ok","id":106,"parent":76,"child":74},{"comment":"","name":"error","id":107,"parent":76,"child":73}],"job_properties":"[{\"name\":\"oozie.hive.defaults\",\"value\":\"hive-site.xml\"}]","node_type":"hive","params":"[{\"value\":\"INPUT=/user/hue/oozie/workspaces/data\",\"type\":\"param\"}]","archives":"[]","node_ptr":76,"prepares":"[]","script_path":"hive.sql","id":76,"name":"Hive"}
-    response = self.c.post(reverse('oozie:workflow_validate_action', kwargs={'workflow': self.wf.pk, 'node_type': 'hive'}), data={'node': json.dumps(data)}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+    response = self.c.post(reverse('oozie:workflow_validate_node', kwargs={'workflow': self.wf.pk, 'node_type': 'hive'}), data={'node': json.dumps(data)}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
     test_response_json = response.content
     test_response_json_object = json.loads(test_response_json)
 
@@ -388,10 +388,10 @@ class TestAPI(OozieMockBase):
     assert_true('archives' in test_response_json_object['data'], test_response_json_object['data'])
     assert_equal(0, len(test_response_json_object['data']['archives']), test_response_json_object['data'])
 
-  def test_workflow_validate_action_fail(self):
+  def test_workflow_validate_node_fail(self):
     # Empty files field
     data = {"job_xml":"hive-site.xml","description":"Show databases","workflow":17,"child_links":[{"comment":"","name":"ok","id":106,"parent":76,"child":74},{"comment":"","name":"error","id":107,"parent":76,"child":73}],"job_properties":"[{\"name\":\"oozie.hive.defaults\",\"value\":\"hive-site.xml\"}]","node_type":"hive","params":"[{\"value\":\"INPUT=/user/hue/oozie/workspaces/data\",\"type\":\"param\"}]","archives":"[]","node_ptr":76,"prepares":"[]","script_path":"hive.sql","id":76,"name":"Hive"}
-    response = self.c.post(reverse('oozie:workflow_validate_action', kwargs={'workflow': self.wf.pk, 'node_type': 'hive'}), data={'node': json.dumps(data)}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+    response = self.c.post(reverse('oozie:workflow_validate_node', kwargs={'workflow': self.wf.pk, 'node_type': 'hive'}), data={'node': json.dumps(data)}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
     test_response_json = response.content
     test_response_json_object = json.loads(test_response_json)
 
@@ -417,7 +417,7 @@ class TestAPI(OozieMockBase):
 
     # Empty script path
     data = {"files":"[\"hive-site.xml\"]","job_xml":"hive-site.xml","description":"Show databases","workflow":17,"child_links":[{"comment":"","name":"ok","id":106,"parent":76,"child":74},{"comment":"","name":"error","id":107,"parent":76,"child":73}],"job_properties":"[{\"name\":\"oozie.hive.defaults\",\"value\":\"hive-site.xml\"}]","node_type":"hive","params":"[{\"value\":\"INPUT=/user/hue/oozie/workspaces/data\",\"type\":\"param\"}]","archives":"[]","node_ptr":76,"prepares":"[]","script_path":"","id":76,"name":"Hive"}
-    response = self.c.post(reverse('oozie:workflow_validate_action', kwargs={'workflow': self.wf.pk, 'node_type': 'hive'}), data={'node': json.dumps(data)}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+    response = self.c.post(reverse('oozie:workflow_validate_node', kwargs={'workflow': self.wf.pk, 'node_type': 'hive'}), data={'node': json.dumps(data)}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
     test_response_json = response.content
     test_response_json_object = json.loads(test_response_json)
 

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

@@ -56,7 +56,7 @@ urlpatterns += patterns(
 
   url(r'^workflows/(?P<workflow>\d+)$', 'workflow', name='workflow'),
   url(r'^workflows/(?P<workflow>\d+)/save$', 'workflow_save', name='workflow_save'),
-  url(r'^workflows/(?P<workflow>\d+)/actions/(?P<node_type>\w+)/validate$', 'workflow_validate_action', name='workflow_validate_action'),
+  url(r'^workflows/(?P<workflow>\d+)/nodes/(?P<node_type>\w+)/validate$', 'workflow_validate_node', name='workflow_validate_node'),
   url(r'^workflows/(?P<workflow>\d+)/jobsub/actions$', 'workflow_jobsub_actions', name='workflow_jobsub_actions'),
 )
 

+ 12 - 9
apps/oozie/src/oozie/views/api.py

@@ -28,9 +28,9 @@ from desktop.lib.exceptions import StructuredException
 
 from jobsub.models import OozieDesign
 
-from oozie.forms import WorkflowForm, ImportJobsubDesignForm, design_form_by_type
+from oozie.forms import WorkflowForm, ImportJobsubDesignForm, NodeForm, design_form_by_type
 from oozie.import_jobsub import convert_jobsub_design
-from oozie.models import Workflow, Node, Mapreduce, Java, Streaming, Link, NODE_TYPES, ACTION_TYPES
+from oozie.models import Workflow, Node, Mapreduce, Java, Streaming, Link, NODE_TYPES, ACTION_TYPES, CONTROL_TYPES
 from oozie.decorators import check_job_access_permission, check_job_edition_permission
 from oozie.utils import model_to_dict
 
@@ -58,16 +58,19 @@ def format_dict_field_values(dictionary):
   return dictionary
 
 
-def workflow_validate_action_json(node_type, node_dict, errors, user, workflow):
+def workflow_validate_node_json(node_type, node_dict, errors, user, workflow):
   """
-  Validates a single action.
+  Validates a single node.
   node_type is the node type of the action information passed.
   node_dict is a dictionary describing the node.
   errors is a dictionary that will be populated with any found errors.
   Returns Boolean.
   """
   assert isinstance(errors, dict), "errors must be a dict."
-  form_class = design_form_by_type(node_type, user, workflow)
+  if node_type in ACTION_TYPES:
+    form_class = design_form_by_type(node_type, user, workflow)
+  else:
+    form_class = NodeForm
   form = form_class(data=node_dict)
 
   if form.is_valid():
@@ -123,7 +126,7 @@ def update_workflow_nodes(workflow, json_nodes, id_map, user):
   for json_node in json_nodes:
     errors = {}
     if json_node['node_type'] in ACTION_TYPES and \
-        not workflow_validate_action_json(json_node['node_type'], format_dict_field_values(json_node), errors, user, workflow):
+        not workflow_validate_node_json(json_node['node_type'], format_dict_field_values(json_node), errors, user, workflow):
       raise StructuredException(code="INVALID_REQUEST_ERROR", message=_('Invalid action'), data={'errors': errors}, error_code=400)
 
   nodes = []
@@ -158,12 +161,12 @@ def update_workflow_nodes(workflow, json_nodes, id_map, user):
 
 @check_job_access_permission(exception_class=(lambda x: StructuredException(code="UNAUTHORIZED_REQUEST_ERROR", message=x, data=None, error_code=401)))
 @check_job_edition_permission(exception_class=(lambda x: StructuredException(code="UNAUTHORIZED_REQUEST_ERROR", message=x, data=None, error_code=401)))
-def workflow_validate_action(request, workflow, node_type):
+def workflow_validate_node(request, workflow, node_type):
   response = {'status': -1, 'data': {}}
 
-  action_dict = format_dict_field_values(json.loads(str(request.POST.get('node'))))
+  node_dict = format_dict_field_values(json.loads(str(request.POST.get('node'))))
 
-  if workflow_validate_action_json(node_type, action_dict, response['data'], request.user, workflow):
+  if workflow_validate_node_json(node_type, node_dict, response['data'], request.user, workflow):
     response['status'] = 0
   else:
     response['status'] = -1

+ 4 - 2
apps/oozie/src/oozie/views/editor.py

@@ -42,11 +42,12 @@ from oozie.decorators import check_job_access_permission, check_job_edition_perm
 from oozie.import_workflow import import_workflow as _import_workflow
 from oozie.management.commands import oozie_setup
 from oozie.models import Job, Workflow, History, Coordinator,\
-                         Dataset, DataInput, DataOutput, ACTION_TYPES
+                         Dataset, DataInput, DataOutput,\
+                         ACTION_TYPES
 from oozie.forms import WorkflowForm, CoordinatorForm, DatasetForm,\
   DataInputForm, DataOutputForm, LinkForm,\
   DefaultLinkForm, design_form_by_type, ParameterForm,\
-  ImportWorkflowForm
+  ImportWorkflowForm, NodeForm
 
 
 LOG = logging.getLogger(__name__)
@@ -160,6 +161,7 @@ def edit_workflow(request, workflow):
     'job_properties': extract_field_data(workflow_form['job_properties']),
     'link_form': LinkForm(),
     'default_link_form': DefaultLinkForm(action=workflow.start),
+    'node_form': NodeForm(),
     'action_forms': [(node_type, design_form_by_type(node_type, request.user, workflow)())
                      for node_type in ACTION_TYPES.iterkeys()]
   })

+ 1 - 1
apps/oozie/static/js/workflow.js

@@ -1221,7 +1221,7 @@ var NodeModule = function($, IdGeneratorTable) {
 
       var success = false;
       var request = $.extend({
-        url: '/oozie/workflows/' + self._workflow.id() + '/actions/' + self.node_type() + '/validate',
+        url: '/oozie/workflows/' + self._workflow.id() + '/nodes/' + self.node_type() + '/validate',
         type: 'POST',
         data: { node: JSON.stringify(data) },
         success: function(data) {

+ 4 - 0
desktop/core/static/css/hue2.css

@@ -153,6 +153,10 @@ h1 {
     text-align:left!important;
 }
 
+.nowrap {
+    white-space: nowrap;
+}
+
 .btn.primary {
     background-color: #01639c;
     background-image: -moz-linear-gradient(center top , #2c7fb3, #01639c);