Преглед на файлове

HUE-8980 [jb] Fix coordinator cannot sync with saved documents

Ying Chen преди 6 години
родител
ревизия
47f9a85c75
променени са 2 файла, в които са добавени 235 реда и са изтрити 17 реда
  1. 184 0
      apps/jobbrowser/src/jobbrowser/templates/job_browser.mako
  2. 51 17
      apps/oozie/src/oozie/views/dashboard.py

+ 184 - 0
apps/jobbrowser/src/jobbrowser/templates/job_browser.mako

@@ -50,6 +50,18 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
 <link rel="stylesheet" href="${ static('jobbrowser/css/jobbrowser-embeddable.css') }">
 
+
+<link rel="stylesheet" href="${ static('desktop/ext/css/bootstrap-datepicker.min.css') }">
+<link rel="stylesheet" href="${ static('desktop/ext/css/bootstrap-timepicker.min.css') }">
+<link rel="stylesheet" href="${ static('desktop/css/bootstrap-spinedit.css') }">
+<link rel="stylesheet" href="${ static('desktop/css/bootstrap-slider.css') }">
+
+<script src="${ static('desktop/ext/js/bootstrap-datepicker.min.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/ext/js/bootstrap-timepicker.min.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/js/bootstrap-spinedit.js') }" type="text/javascript" charset="utf-8"></script>
+<script src="${ static('desktop/js/bootstrap-slider.js') }" type="text/javascript" charset="utf-8"></script>
+
+
 <script src="${ static('oozie/js/dashboard-utils.js') }" type="text/javascript" charset="utf-8"></script>
 
 % if ENABLE_QUERY_BROWSER.get():
@@ -474,6 +486,75 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       </div>
     </div>
   <!-- /ko -->
+
+  <!-- ko if: $root.job() && $root.job().type() === 'schedule' -->
+    <div id="syncCoordinatorModal${ SUFFIX }" class="modal hide">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
+        <h2 class="modal-title confirmation_header">${ _('Update Coordinator Job Properties') }</h2>
+      </div>
+      <div id="update-coord" class="span10">
+        <div class="control-group">
+          <label class="control-label">${ _('End Time') }</label>
+          <div class="controls">
+            <div class="input-prepend input-group">
+              <span class="add-on input-group-addon">
+                <i class="fa fa-calendar"></i>
+              </span>
+              <input id="endTimeDateUI" type="text" class="input-small disable-autofocus" data-bind="value: $root.job().syncCoorEndTimeDateUI, datepicker: {}" />
+            </div>
+            <div class="input-prepend input-group">
+              <span class="add-on input-group-addon">
+                <i class="fa fa-clock-o"></i>
+              </span>
+              <input id="endTimeTimeUI" type="text" class="input-mini disable-autofocus" data-bind="value: $root.job().syncCoorEndTimeTimeUI, timepicker: {}" />
+            </div>
+            <span class="help-inline"></span>
+          </div>
+        </div>
+        <div class="control-group">
+          <label class="control-label">${ _('Pause Time') }</label>
+          <div class="controls">
+            <div class="input-prepend input-group">
+              <span class="add-on input-group-addon">
+                <i class="fa fa-calendar"></i>
+              </span>
+              <input id="pauseTimeDateUI" type="text" class="input-small disable-autofocus" data-bind="value: $root.job().syncCoorPauseTimeDateUI, datepicker: {}" />
+            </div>
+            <div class="input-prepend input-group">
+              <span class="add-on input-group-addon">
+                <i class="fa fa-clock-o"></i>
+              </span>
+              <input id="pauseTimeTimeUI" type="text" class="input-mini disable-autofocus" data-bind="value: $root.job().syncCoorPauseTimeTimeUI, timepicker: {}" />
+            </div>
+            <span class="help-inline"></span>
+          </div>
+        </div>
+        <div class="control-group ">
+          <label class="control-label">Clear Pause Time</label>
+          <div class="controls">
+            <input id="id_clearPauseTime" class="disable-autofocus" name="clearPauseTime" type="checkbox">
+          </div>
+        </div>
+        <div class="control-group ">
+          <label class="control-label">Concurrency</label>
+          <div class="controls">
+            <input id="id_concurrency" class="disable-autofocus" name="concurrency" type="number" data-bind="value: $root.job().syncCoorConcurrency">
+          </div>
+        </div>
+      </div>
+      <div class="modal-body">
+          <p class="confirmation_body"></p>
+      </div>
+      <div class="modal-footer update">
+        <a href="#" class="btn" data-dismiss="modal">Cancel</a>
+        <a id="syncCoorBtn" class="btn btn-danger disable-feedback" data-dismiss="modal" data-bind="click: function(){ job().control('sync_coordinator'); }">${_('Update')}</a>
+      </div>
+    </div>
+
+    <div id="syncWorkflowModal${ SUFFIX }" class="modal hide">
+    </div>
+  <!-- /ko -->
 </div>
 
 
@@ -1750,6 +1831,18 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
 
 <script type="text/html" id="job-actions${ SUFFIX }">
+  <div class="btn-group">
+    <!-- ko if: $root.job() && $root.job().type() === 'schedule' -->
+    <button class="btn" title="${ _('Sync Coordinator') }" data-bind="click: function() { $root.job().showSyncCoorModal() }, enable: killEnabled">
+      <i class="fa fa-refresh"></i> <!-- ko ifnot: $root.isMini -->${ _('Coordinator') }<!-- /ko -->
+    </button>
+
+    <button class="btn" title="${ _('Sync Workflow') }" data-bind="click: function(){ control('sync_workflow'); }, enable: killEnabled">
+      <i class="fa fa-refresh"></i> <!-- ko ifnot: $root.isMini -->${ _('Workflow') }<!-- /ko -->
+    </button>
+    <!-- /ko -->
+  </div>
+
   <div class="btn-group">
     <!-- ko if: hasResume -->
     <button class="btn" title="${ _('Resume selected') }" data-bind="click: function() { control('resume'); }, enable: resumeEnabled">
@@ -2465,6 +2558,22 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       self.mainType = ko.observable(vm.interface());
       self.lastEvent = ko.observable(job.lastEvent || '');
 
+      self.syncCoorEndTimeDateUI = ko.observable(null);
+      self.syncCoorEndTimeTimeUI = ko.observable(null);
+      self.syncCoorPauseTimeDateUI = ko.observable(null);
+      self.syncCoorPauseTimeTimeUI = ko.observable(null);
+      self.syncCoorConcurrency = ko.observable(null);
+
+      self.showSyncCoorModal = function () {
+        self.syncCoorEndTimeDateUI(self.properties['endTimeDateUI']());
+        self.syncCoorEndTimeTimeUI(self.properties['endTimeTimeUI']());
+        self.syncCoorPauseTimeDateUI(self.properties['pauseTimeDateUI']());
+        self.syncCoorPauseTimeTimeUI(self.properties['pauseTimeTimeUI']());
+        self.syncCoorConcurrency(self.properties['concurrency']());
+
+        $('#syncCoordinatorModal${ SUFFIX }').modal('show');
+      };
+
       self.coordinatorActions = ko.pureComputed(function() {
         if (self.mainType() == 'schedules' && self.properties['tasks']) {
           var apps = self.properties['tasks']().map(function (instance) {
@@ -2855,6 +2964,49 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
             $('#rerun-modal${ SUFFIX }').modal('show');
             self.rerunModalContent(response);
           });
+        } else if (action == 'sync_coordinator') {
+          var $syncCoordinatorModal = $('#syncCoordinatorModal');
+          var endTimeDateUI = $syncCoordinatorModal.find("#endTimeDateUI").val();
+          var endTimeTimeUI = $syncCoordinatorModal.find("#endTimeTimeUI").val();
+          var endTime = '';
+          if (endTimeDateUI != '' && endTimeTimeUI != '') {
+            endTime = endTimeDateUI + 'T' + endTimeTimeUI;
+          }
+          var pauseTimeDateUI = $syncCoordinatorModal.find("#pauseTimeDateUI").val();
+          var pauseTimeTimeUI = $syncCoordinatorModal.find("#pauseTimeTimeUI").val();
+          var pauseTime = '';
+          if (pauseTimeDateUI != '' && pauseTimeTimeUI != '') {
+            pauseTime = pauseTimeDateUI + 'T' + pauseTimeTimeUI;
+          }
+
+          var clear_pause_time = $syncCoordinatorModal.find("#id_clearPauseTime")[0].checked;
+          var concurrency =  $syncCoordinatorModal.find("#id_concurrency").val();
+
+          $.post('/oozie/manage_oozie_jobs/' + self.id() + '/change', {
+            "end_time": endTime,
+            "pause_time": pauseTime,
+            "clear_pause_time": clear_pause_time,
+            "concurrency": concurrency
+          }, function (data) {
+            if (data.status == 0) {
+              $.jHueNotify.info("${ _('Successfully updated Coordinator Job Properties') }");
+            } else {
+              $(document).trigger("error", data.message);
+            }
+          }).fail(function (xhr, textStatus, errorThrown) {
+            $(document).trigger("error", xhr.responseText);
+          });
+
+        } else if (action == 'sync_workflow') {
+          csrfmiddlewaretoken = "${request and request.COOKIES.get('csrftoken', '')}";
+
+          $.get('/oozie/sync_coord_workflow/' + self.id(), {
+            format: 'json'
+          }, function (data) {
+            $(document).trigger("showSubmitPopup", data);
+          }).fail(function (xhr, textStatus, errorThrown) {
+            $(document).trigger("error", xhr.responseText);
+          });
         } else {
           vm.jobs._control([self.id()], action, function(data) {
             $(document).trigger("info", data.message);
@@ -3721,6 +3873,38 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
           $(dest).find('pre').css('overflow-y', 'auto').height(Math.max(200, $(window).height() - $(dest).find('pre').offset().top - $('.page-content').scrollTop() - 75));
         }
       });
+      $(document).off("showSubmitPopup");
+      $(document).on("showSubmitPopup", function(event, data) {
+        $('#syncWorkflowModal').empty();
+        $('#syncWorkflowModal').html(data);
+        $('#syncWorkflowModal').modal('show');
+        $('#syncWorkflowModal').on('hidden', function () {
+          huePubSub.publish('hide.datepicker');
+        });
+
+        $('#syncWorkflowModal').find(".submit-form").on('submit',function(e){
+          e.preventDefault();
+          $.ajax({
+            type: "POST",
+            cache: false,
+            url: $(this).attr('action'),
+            data: $(this).serialize(),
+            success: function(data) {
+              $('#syncWorkflowModal').modal('hide');
+              if (data && data.status === 0) {
+                $.jHueNotify.info(data.message);
+              } else {
+                $.jHueNotify.error(data.message);
+              }
+            },
+            error: function (data) {
+              $('#syncWorkflowModal').modal('hide');
+              $.jHueNotify.error(data.message);
+            }
+          });
+        });
+
+      });
     });
   })();
 </script>

+ 51 - 17
apps/oozie/src/oozie/views/dashboard.py

@@ -484,9 +484,16 @@ def list_oozie_coordinator(request, job_id):
       'progress': oozie_coordinator.get_progress(),
       'nextTime': format_time(oozie_coordinator.nextMaterializedTime),
       'endTime': format_time(oozie_coordinator.endTime),
+      'endTimeDateUI': get_date_ui_picker(oozie_coordinator.endTime),
+      'endTimeTimeUI': get_time_ui_picker(oozie_coordinator.endTime),
+      'pauseTimeDateUI': get_date_ui_picker(oozie_coordinator.pauseTime),
+      'pauseTimeTimeUI': get_time_ui_picker(oozie_coordinator.pauseTime),
+      'clearPauseTime': True,
+      'concurrency': oozie_coordinator.concurrency,
       'actions': actions,
       'total_actions': oozie_coordinator.total,
       'doc_url': coordinator.get_absolute_url() if coordinator else '',
+      'has_job_edition_permission': has_job_edition_permission(oozie_coordinator, request.user),
     }
     return JsonResponse(return_obj, encoder=JSONEncoderForHTML)
 
@@ -714,30 +721,37 @@ def sync_coord_workflow(request, job_id):
   properties = hue_coord and hue_coord.properties and dict([(param['name'], param['value']) for param in hue_coord.properties]) or None
 
   if request.method == 'POST':
+    response = {'status': -1, 'message': ''}
     params_form = ParametersFormSet(request.POST)
     if params_form.is_valid():
-      mapping = dict([(param['name'], param['value']) for param in params_form.cleaned_data])
+      try:
+        mapping = dict([(param['name'], param['value']) for param in params_form.cleaned_data])
+
+        # Update workflow params in coordinator
+        hue_coord.clear_workflow_params()
+        properties = dict([(param['name'], param['value']) for param in hue_coord.properties])
 
-      # Update workflow params in coordinator
-      hue_coord.clear_workflow_params()
-      properties = dict([(param['name'], param['value']) for param in hue_coord.properties])
+        # Deploy WF XML
+        submission = Submission(user=request.user, job=hue_wf, fs=request.fs, jt=request.jt, properties=properties)
+        submission.deploy(deployment_dir=wf_application_path)
+        submission._create_file(wf_application_path, hue_wf.XML_FILE_NAME, hue_wf.to_xml(mapping=properties), do_as=True)
 
-      # Deploy WF XML
-      submission = Submission(user=request.user, job=hue_wf, fs=request.fs, jt=request.jt, properties=properties)
-      submission.deploy(deployment_dir=wf_application_path)
-      submission._create_file(wf_application_path, hue_wf.XML_FILE_NAME, hue_wf.to_xml(mapping=properties), do_as=True)
+        # Deploy Coordinator XML
+        job.conf_dict.update(mapping)
+        submission = Submission(user=request.user, job=hue_coord, fs=request.fs, jt=request.jt, properties=job.conf_dict, oozie_id=job.id)
+        submission._create_file(coord_application_path, hue_coord.XML_FILE_NAME, hue_coord.to_xml(mapping=job.conf_dict), do_as=True)
+        # Server picks up deployed Coordinator XML changes after running 'update' action
+        submission.update_coord()
 
-      # Deploy Coordinator XML
-      job.conf_dict.update(mapping)
-      submission = Submission(user=request.user, job=hue_coord, fs=request.fs, jt=request.jt, properties=job.conf_dict, oozie_id=job.id)
-      submission._create_file(coord_application_path, hue_coord.XML_FILE_NAME, hue_coord.to_xml(mapping=job.conf_dict), do_as=True)
-      # Server picks up deployed Coordinator XML changes after running 'update' action
-      submission.update_coord()
+        response['status'] = 0
+        response['message'] = _('Successfully updated Workflow definition')
+      except Exception as e:
+        response['message'] = e.message
 
-      request.info(_('Successfully updated Workflow definition'))
-      return redirect(reverse('oozie:list_oozie_coordinator', kwargs={'job_id': job_id}))
     else:
-      request.error(_('Invalid submission form: %s' % params_form.errors))
+      response['message'] = _('Invalid submission form: %s' % params_form.errors)
+
+    return JsonResponse(response)
   else:
     new_params = hue_wf and hue_wf.find_all_parameters() or []
     new_params = dict([(param['name'], param['value']) for param in new_params])
@@ -1100,6 +1114,26 @@ def format_time(st_time):
     return st_time
 
 
+def format_time_ui_picker(str_time):
+  if str_time:
+    return time.strftime('%Y-%m-%dT%H:%M', str_time)
+  return None
+
+
+def get_date_ui_picker(st_time):
+  str_time = format_time_ui_picker(st_time)
+  if str_time and 'T' in str_time:
+    return str_time[:str_time.index("T")]
+  return ''
+
+
+def get_time_ui_picker(st_time):
+  str_time = format_time_ui_picker(st_time)
+  if str_time and 'T' in str_time:
+    return str_time[(str_time.index("T") + 1):]
+  return ''
+
+
 def massaged_oozie_jobs_for_json(oozie_jobs, user, just_sla=False):
   jobs = []