Преглед изворни кода

HUE-1176 [jb] Filter and perform action on coordinator actions

Romain Rigaux пре 8 година
родитељ
комит
79e3b25

+ 1 - 1
apps/jobbrowser/src/jobbrowser/apis/bundle_api.py

@@ -108,5 +108,5 @@ class BundleApi(Api):
     elif status == 'SUCCEEDED':
       return 'SUCCEEDED'
     else:
-      return 'FINISHED' # DONEWITHERROR, KILLED, FAILED
+      return 'FAILED' # DONEWITHERROR, KILLED, FAILED
 

+ 10 - 5
apps/jobbrowser/src/jobbrowser/apis/schedule_api.py

@@ -76,12 +76,14 @@ class ScheduleApi(Api):
         'startTime': format_time(coordinator.startTime),
     }
     common['properties'] = json.loads(response.content)
+    common['properties']['tasks'] = common['properties']['actions']
     common['properties']['xml'] = ''
     common['properties']['properties'] = ''
     common['properties']['bundle_id'] = coordinator.conf_dict.get('oozie.bundle.id')
 
     return common
 
+
   def logs(self, appid, app_type, log_name=None):
     request = MockDjangoRequest(self.user)
     data = get_oozie_job_log(request, job_id=appid)
@@ -92,16 +94,19 @@ class ScheduleApi(Api):
   def profile(self, appid, app_type, app_property, app_filters):
     if app_property == 'xml':
       oozie_api = get_oozie(self.user)
-      workflow = oozie_api.get_coordinator(jobid=appid)
+      coordinator = oozie_api.get_coordinator(jobid=appid)
       return {
-        'xml': workflow.definition,
+        'xml': coordinator.definition,
       }
     elif app_property == 'properties':
       oozie_api = get_oozie(self.user)
-      workflow = oozie_api.get_coordinator(jobid=appid)
+      coordinator = oozie_api.get_coordinator(jobid=appid)
       return {
-        'properties': workflow.conf_dict,
+        'properties': coordinator.conf_dict,
       }
+    elif app_property == 'tasks':
+      coordinator = self.app(appid)
+      return coordinator['properties']['tasks']
 
   def _api_status(self, status):
     if status in ['PREP', 'RUNNING', 'RUNNINGWITHERROR']:
@@ -111,7 +116,7 @@ class ScheduleApi(Api):
     elif status == 'SUCCEEDED':
       return 'SUCCEEDED'
     else:
-      return 'FINISHED' # DONEWITHERROR, KILLED, FAILED
+      return 'FAILED' # DONEWITHERROR, KILLED, FAILED
 
 
 class MockGet():

+ 84 - 46
apps/jobbrowser/src/jobbrowser/templates/job_browser.mako

@@ -49,7 +49,6 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 <script src="${ static('desktop/js/ko.charts.js') }"></script>
 <script src="${ static('desktop/ext/js/knockout-sortable.min.js') }"></script>
 <script src="${ static('desktop/js/ko.editable.js') }"></script>
-<script src="${ static('oozie/js/list-oozie-coordinator.ko.js') }"></script>
 <script src="${ static('desktop/js/ace/ace.js') }"></script>
 
 % if not is_mini:
@@ -279,14 +278,14 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
         <!-- /ko -->
       <!-- /ko -->
       </li>
-      % if not is_mini:
-      <!-- ko if: ['workflows', 'schedules', 'bundles', 'slas'].indexOf($parent.interface()) > -1 -->
-      <li class="pull-right">
+    </ul>
+    % if not is_mini:
+      <!-- ko if: ['workflows', 'schedules', 'bundles', 'slas'].indexOf(interface()) > -1 -->
+      <span class="pull-right">
         <a href="javascript:void(0)" data-bind="click: function() { $root.selectInterface('oozie-info') }">${ _('Configuration') }</a>
-      </li>
+      </span>
       <!-- /ko -->
-      % endif
-    </ul>
+    % endif
   </h3>
 </script>
 
@@ -652,6 +651,13 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       <i class="fa fa-times"></i> ${_('Kill')}
     </button>
     <!-- /ko -->
+
+    <!-- ko if: hasIgnore -->
+    <button class="btn btn-danger" title="${_('Ignore selected')}" data-bind="click: function() { control('ignore'); }, enable: ignoreEnabled">
+      ## TODO confirmation
+      <i class="fa fa-eraser"></i> ${_('Ignore')}
+    </button>
+    <!-- /ko -->
   </div>
 </script>
 
@@ -784,32 +790,27 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
   <br>
 
   <ul class="nav nav-tabs">
-    <li class="active"><a href="#schedule-page-calendar" data-toggle="tab">${ _('Calendar') }</a></li>
+    <li class="active"><a href="#schedule-page-task" data-toggle="tab">${ _('Tasks') }</a></li>
     <li><a href="#schedule-page-logs" data-toggle="tab">${ _('Logs') }</a></li>
-    <li><a href="#schedule-page-tasks" data-toggle="tab">${ _('Tasks') }</a></li>
     <li><a href="#schedule-page-metadata" data-bind="click: function(){ fetchProfile('properties'); $('a[href=\'#schedule-page-metadata\']').tab('show'); }">${ _('Properties') }</a></li>
     <li><a href="#schedule-page-xml" data-bind="click: function(){ fetchProfile('xml'); $('a[href=\'#schedule-page-xml\']').tab('show'); }">${ _('XML') }</a></li>
   </ul>
 
   <div class="tab-content">
     <div class="tab-pane active" id="schedule-page-calendar">
-      <pre data-bind="text: ko.toJSON(properties['actions'], null, 2)"></pre>
-    </div>
-
-    <div class="tab-pane" id="schedule-page-logs">
-      <pre data-bind="html: logs"></pre>
-    </div>
-
-    <div class="tab-pane" id="schedule-page-tasks">
+      <!-- ko with: coordinatorActions() -->
       <div data-bind="template: { name: 'job-actions' }"></div>
 
-      ${_('Filter')} <input type="text" class="input-xlarge search-query"  placeholder="${_('Filter by id, name, user...')}" />
-      <div class="btn-group">
-        <select size="3" multiple="true"></select>
-        <a class="btn btn-status btn-success" data-value="completed">${ _('Succeeded') }</a>
-        <a class="btn btn-status btn-warning" data-value="running">${ _('Running') }</a>
-        <a class="btn btn-status btn-danger disable-feedback" data-value="failed">${ _('Failed') }</a>
-      </div>
+      ${_('Filter')}
+      <input data-bind="value: textFilter" type="text" class="input-xlarge search-query" placeholder="${_('Filter by name')}">
+
+      <span data-bind="foreach: statesValuesFilter">
+        <label class="checkbox">
+          <div data-bind="attr: {'class': 'status-circle ' + klass()}"></div>
+          <input type="checkbox" data-bind="checked: checked, attr: {id: name}">
+          <span data-bind="text: name, attr: {for: name}"></span>
+        </label>
+      </span>
 
       <table id="schedulesTable" class="datatables table table-condensed">
         <thead>
@@ -827,22 +828,29 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
           <th>${_('lastModifiedTime')}</th>
         </tr>
         </thead>
-        <tbody data-bind="foreach: properties['actions']">
-          <tr data-bind="click: function() {  if (externalId()) { $root.job().id(externalId()); $root.job().fetchJob();} }">
-            <td><div class="hueCheckbox fa"></div></td>
-            <td data-bind="text: status"></td>
-            <td data-bind="text: title"></td>
-            <td data-bind="text: type"></td>
-            <td data-bind="text: errorMessage"></td>
-            <td data-bind="text: missingDependencies"></td>
-            <td data-bind="text: number"></td>
-            <td data-bind="text: errorCode"></td>
-            <td data-bind="text: externalId"></td>
-            <td data-bind="text: id"></td>
-            <td data-bind="text: lastModifiedTime"></td>
+        <tbody data-bind="foreach: apps">
+          <tr data-bind="click: function() {  if (properties.externalId()) { $root.job().id(properties.externalId()); $root.job().fetchJob();} }">
+            <td>
+              <div class="hueCheckbox fa" data-bind="click: function() {}, clickBubble: false, multiCheck: '#schedulesTable', value: $data, hueChecked: $parent.selectedJobs"></div>
+            </td>
+            <td data-bind="text: properties.status"></td>
+            <td data-bind="text: properties.title"></td>
+            <td data-bind="text: properties.type"></td>
+            <td data-bind="text: properties.errorMessage"></td>
+            <td data-bind="text: properties.missingDependencies"></td>
+            <td data-bind="text: properties.number"></td>
+            <td data-bind="text: properties.errorCode"></td>
+            <td data-bind="text: properties.externalId"></td>
+            <td data-bind="text: properties.id"></td>
+            <td data-bind="text: properties.lastModifiedTime"></td>
           </tr>
         </tbody>
       </table>
+      <!-- /ko -->
+    </div>
+
+    <div class="tab-pane" id="schedule-page-logs">
+      <pre data-bind="html: logs"></pre>
     </div>
 
     <div class="tab-pane" id="schedule-page-metadata">
@@ -975,11 +983,25 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
       self.logs = ko.observable('');
 
-      //self.coordVM = new RunningCoordinatorModel([]);
-
       self.properties = ko.mapping.fromJS(job.properties || {});
       self.mainType = ko.observable(vm.interface());
 
+      self.coordinatorActions = ko.computed(function() {
+        if (self.mainType() == 'schedules' && self.properties['tasks']) {
+          var apps = [];
+          self.properties['tasks']().forEach(function (instance) {
+            var job = new Job(vm, ko.mapping.toJS(instance));
+            job.resumeEnabled = function() { return false };
+            job.properties = instance;
+            apps.push(job);
+          });
+          var instances = new Jobs(vm);
+          instances.apps(apps)
+          instances.isCoordinator(true);
+          return instances;
+        }
+      });
+
       self.textFilter = ko.observable('').extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 1000 } });
       self.statesValuesFilter = ko.observableArray([
         ko.mapping.fromJS({'value': 'completed', 'name': '${_("Succeeded")}', 'checked': true, 'klass': 'green'}),
@@ -1032,7 +1054,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       });
 
       self.hasRerun = ko.pureComputed(function() {
-        return ['workflow'].indexOf(self.type()) != -1;
+        return ['workflow', 'schedule-task'].indexOf(self.type()) != -1;
       });
       self.rerunEnabled = ko.pureComputed(function() {
         return self.hasRerun() && ! self.isRunning();
@@ -1045,6 +1067,13 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
         return self.hasPause() && self.apiStatus() == 'RUNNING';
       });
 
+      self.hasIgnore = ko.pureComputed(function() {
+        return ['schedule-task'].indexOf(self.type()) != -1;
+      });
+      self.ignoreEnabled = ko.pureComputed(function() {
+        return self.hasIgnore() && ! self.isRunning();
+      });
+
       self.loadingJob = ko.observable(false);
       var lastFetchJobRequest = null;
       var lastUpdateJobRequest = null;
@@ -1246,12 +1275,13 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
 
       self.apps = ko.observableArray().extend({ rateLimit: 50 });
       self.totalApps = ko.observable(null);
+      self.isCoordinator = ko.observable(false);
 
       self.loadingJobs = ko.observable(false);
       self.selectedJobs = ko.observableArray();
 
       self.hasKill = ko.pureComputed(function() {
-        return ['jobs', 'workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1;
+        return ['jobs', 'workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1 && ! self.isCoordinator();
       });
       self.killEnabled = ko.pureComputed(function() {
         return self.hasKill() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
@@ -1260,7 +1290,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       });
 
       self.hasResume = ko.pureComputed(function() {
-        return ['workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1;
+        return ['workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1 && ! self.isCoordinator();
       });
       self.resumeEnabled = ko.pureComputed(function() {
         return self.hasResume() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
@@ -1269,7 +1299,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       });
 
       self.hasRerun = ko.pureComputed(function() {
-        return ['workflows'].indexOf(vm.interface()) != -1;
+        return ['workflows'].indexOf(vm.interface()) != -1 || self.isCoordinator();
       });
       self.rerunEnabled = ko.pureComputed(function() {
         return self.hasRerun() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
@@ -1278,7 +1308,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       });
 
       self.hasPause = ko.pureComputed(function() {
-        return ['workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1;
+        return ['workflows', 'schedules', 'bundles'].indexOf(vm.interface()) != -1 && ! self.isCoordinator();
       });
       self.pauseEnabled = ko.pureComputed(function() {
         return self.hasPause() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
@@ -1286,6 +1316,14 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
         }).length == self.selectedJobs().length;
       });
 
+      self.hasIgnore = ko.pureComputed(function() {
+        return self.isCoordinator();
+      });
+      self.ignoreEnabled = ko.pureComputed(function() {
+        return self.hasIgnore() && self.selectedJobs().length > 0 && $.grep(self.selectedJobs(), function(job) {
+          return job.ignoreEnabled();
+        }).length == self.selectedJobs().length;
+      });
 
       self.textFilter = ko.observable('user:${ user.username } ').extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 1000 } });
       self.statesValuesFilter = ko.observableArray([
@@ -1459,7 +1497,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
       };
     };
 
-    var JobBrowserViewModel = function (RunningCoordinatorModel) {
+    var JobBrowserViewModel = function () {
       var self = this;
 
       self.apiHelper = ApiHelper.getInstance();
@@ -1592,7 +1630,7 @@ ${ commonheader("Job Browser", "jobbrowser", user, request) | n,unicode }
     });
 
     $(document).ready(function () {
-      viewModel = new JobBrowserViewModel(RunningCoordinatorModel);
+      viewModel = new JobBrowserViewModel();
       % if not is_mini:
       ko.applyBindings(viewModel, $('#jobbrowserComponents')[0]);
       % else:

+ 1 - 1
apps/oozie/src/oozie/views/dashboard.py

@@ -1002,7 +1002,7 @@ def massaged_coordinator_actions_for_json(coordinator, oozie_bundle):
       'id': action.id,
       'url': action.externalId and reverse('oozie:list_oozie_workflow', kwargs={'job_id': action.externalId}) + '?%s' % '&'.join(related_job_ids) or '',
       'number': action.actionNumber,
-      'type': action.type,
+      'type': 'schedule-task',
       'status': action.status,
       'externalId': action.externalId or '-',
       'externalIdUrl': action.externalId and reverse('oozie:list_oozie_workflow_action', kwargs={'action': action.externalId}) or '',