Browse Source

[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 năm trước cách đây
mục cha
commit
427035a

+ 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);