Skip to content

Commit

Permalink
Merge pull request #2110 from variacode/prerelease-2.7.0
Browse files Browse the repository at this point in the history
Feature - override node filter (GUI enhancement)
  • Loading branch information
gschueler committed Oct 17, 2016
2 parents 885fa36 + b519a25 commit 483764f
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ class ReportService {
}

if(query.execnodeFilter){
if(query.execnodeFilter.startsWith('name:')){
def node = (query.execnodeFilter.split("name:")[1]).stripIndent()
if(query.execnodeFilter.startsWith('name:') || !(query.execnodeFilter.contains(":") || query.execnodeFilter.contains(".*"))){
def node = query.execnodeFilter.startsWith('name:')?(query.execnodeFilter.split("name:")[1]).stripIndent():query.execnodeFilter;
or {
ilike("failedNodeList", '%' + node + '%')
ilike("succeededNodeList", '%' + node + '%')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2965,10 +2965,7 @@ class ScheduledExecutionService implements ApplicationContextAware, Initializing
}
}
if (scheduledExecution.doNodedispatch) {
if (!scheduledExecution.asFilter()) {
scheduledExecution.errors.rejectValue('filter', 'scheduledExecution.filter.blank.message')
failed = true
} else if (!scheduledExecution.nodeThreadcount) {
if (!scheduledExecution.nodeThreadcount) {
scheduledExecution.nodeThreadcount = 1
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@


<span class="input-group-btn">
<a class="btn btn-default" data-toggle='popover-for' data-target="#${filterFieldId ? enc(attr: filterFieldId) : 'schedJobNodeFilter'}">
<a class="btn btn-default" data-toggle='popover-for' data-target="#${filterFieldId ? enc(attr: filterFieldId) : 'schedJobNodeFilter'}" onclick="jQuery('#${filterFieldId ? enc(attr: filterFieldId) : 'schedJobNodeFilter'}').popover('toggle')">
<i class="glyphicon glyphicon-question-sign"></i>
</a>
<a class="btn btn-default" data-bind="click: $data.newFilterText, css: {disabled: !filter()}" href="#">
Expand Down
4 changes: 2 additions & 2 deletions rundeckapp/grails-app/views/reports/_baseReport.gsp
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,9 @@


<td class=" user autoclickable" style="white-space: nowrap">
<g:if test="${it?.nodeList}">
<g:if test="${it?.filterApplied}">
<em><g:message code="activity.jobs.executed.node"/>:</em>
${it?.nodeList}
${it?.filterApplied}
</g:if>
<g:else>
<em><g:message code="activity.jobs.executed.local"/></em>
Expand Down
159 changes: 134 additions & 25 deletions rundeckapp/grails-app/views/scheduledExecution/_execOptionsForm.gsp
Original file line number Diff line number Diff line change
Expand Up @@ -33,31 +33,69 @@
</div>
</g:if>
<g:set var="project" value="${scheduledExecution?.project ?: params.project?:request.project?: projects?.size() == 1 ? projects[0].name : ''}"/>
<g:embedJSON id="filterParamsJSON" data="${[filterName: params.filterName, filter: query?.filter, filterAll: params.showall in ['true', true]]}"/>
<script lang="text/javascript">
var nodeSummary = new NodeSummary({baseUrl: appLinks.frameworkNodes});
function setFilter(id,value){
$('filterradio').checked=true;
var tmp = '';
nodeSummary.filters().forEach(function (item, index) {
if(value == item.name()) {
$(id).writeAttribute('value', item.filter());
}
function init() {
var pageParams = loadJsonData('pageParams');
jQuery('body').on('click', '.nodefilterlink', function (evt) {
evt.preventDefault();
nodeFilter.selectNodeFilterLink(this);
$('filterradio').checked=true;
});
jQuery('#nodesContent').on('click', '.closeoutput', function (evt) {
evt.preventDefault();
closeOutputArea();
});
}
function pageinit() {
//setup node filters knockout bindings
var filterParams = loadJsonData('filterParamsJSON');
var nodeSummary = new NodeSummary({baseUrl:appLinks.frameworkNodes});
nodeFilter = new NodeFilters(
appLinks.frameworkAdhoc,
appLinks.scheduledExecutionCreate,
appLinks.frameworkNodes,
Object.extend(filterParams, {
nodeSummary:nodeSummary,
view: 'embed',
maxShown: 100,
emptyMode: 'blank',
project: pageParams.project,
nodesTitleSingular: message('Node'),
nodesTitlePlural: message('Node.plural')
}));
ko.applyBindings(nodeFilter, document.getElementById('nodefilterViewArea'));
//show selected named filter
nodeFilter.filterName.subscribe(function (val) {
if (val) {
jQuery('a[data-node-filter-name]').removeClass('active');
jQuery('a[data-node-filter-name=\'' + val + '\']').addClass('active');
}
});
nodeSummary.reload();
nodeFilter.updateMatchedNodes();
var tmpfilt = {};
jQuery.data( tmpfilt, "node-filter-name", "" );
jQuery.data( tmpfilt, "node-filter", "${nodefilter}" );
nodeFilter.selectNodeFilterLink(tmpfilt);
}
jQuery(pageinit);
jQuery(document).ready(init);
</script>
<div class=" collapse" id="queryFilterHelp">
<div class="help-block">
<g:render template="/common/nodefilterStringHelp"/>
</div>
</div>
<div class="list-group list-group-tab-content">
<div class="list-group-item">
<div class="row">
<div class="${hideHead?'col-sm-9':'col-sm-12'}">
<g:render template="editOptions" model="${[scheduledExecution:scheduledExecution, selectedoptsmap:selectedoptsmap, selectedargstring:selectedargstring,authorized:authorized,jobexecOptionErrors:jobexecOptionErrors, optiondependencies: optiondependencies, dependentoptions: dependentoptions, optionordering: optionordering]}"/>
<g:if test="${scheduledExecution.nodeFilterEditable}">

<div class="form-group" style="${wdgt.styleVisible(if: nodesetvariables && !failedNodes || nodesetempty || nodes)}">
<div class="col-sm-2 control-label text-form-label">
Nodes
Expand Down Expand Up @@ -86,7 +124,7 @@

<g:set var="selectedNodes"
value="${failedNodes? failedNodes.split(',').findAll{it}:selectedNodes!=null? selectedNodes.split(',').findAll{it}:null}"/>
<div class="container">

<div class="row">
<div class="col-sm-12 checkbox">
<label >
Expand All @@ -100,7 +138,7 @@
</div>

</div>
</div>
<div class="container">
<div class=" matchednodes embed jobmatchednodes group_section collapse ${selectedNodes!=null? 'in' : ''}" id="nodeSelect">
<%--
split node names into groups, in several patterns
Expand All @@ -109,7 +147,7 @@
--%>
<g:if test="${!nodesetvariables && nodes}">
<g:if test="${namegroups}">
<label for="cherrypickradio">
<label for=" ">
<div class=" group_select_control" style="${wdgt.styleVisible(if: selectedNodes !=null)}">
<input id="cherrypickradio"
type="radio"
Expand Down Expand Up @@ -198,6 +236,7 @@
</g:each>
</g:else>
</g:if>
<g:if test="${scheduledExecution.nodeFilterEditable || nodefilter == ''}">
<div class="subfields nodeFilterFields ">
%{-- filter text --}%
<div class="">
Expand All @@ -206,28 +245,98 @@
<input id="filterradio"
type="radio"
name="extra.nodeoverride"
${(!nodesetvariables && nodes)?'':'checked=true'}
value="filter"
/>
<spans>
<span>
<g:if test="${!nodesetvariables && nodes}"><g:message code="or"/> </g:if>
<g:message code="job.run.override.node"/>: </span>
<g:if test="${session.user && User.findByLogin(session.user)?.nodefilters}">
<g:set var="filterset" value="${User.findByLogin(session.user)?.nodefilters.findAll{it.project == project}}"/>
</g:if>

<g:if test="${filterset}">
<g:render template="/common/selectFilter" model="[noSelection:'-Saved Filters-',filterset:filterset,filterName:filterName,prefName:'txtNodeFilter']"/>
<!--<span class="info note">Filter:</span>-->
</g:if>
<input type='text' name="extra.nodefilter" class="schedJobNodeFilter form-control" id="txtNodeFilter"
placeholder="${queryFieldPlaceholderText?:g.message(code:'enter.a.node.filter')}"
value="${nodefilter}" id="execnodefilter" onclick="$('filterradio').checked=true;"/>
<div id="nodefilterViewArea">
<div class="${emptyQuery ? 'active' : ''}" id="nodeFilterInline">
<div class="spacing">
<div class="">
<g:form action="adhoc" class="form form-horizontal" name="searchForm" >
<g:hiddenField name="max" value="${max}"/>
<g:hiddenField name="offset" value="${offset}"/>
<g:hiddenField name="formInput" value="true"/>



<div class="form-group">
<div class="col-sm-10">
<span class=" input-group" >
<g:render template="/framework/nodeFilterInputGroup"
model="[filterFieldName: 'extra.nodefilter',filterset: filterset, filtvalue: filtvalue, filterName: filterName]"/>
</span>
</div>
</div>
</g:form>

<div class=" collapse" id="queryFilterHelp">
<div class="help-block">
<g:render template="/common/nodefilterStringHelp"/>
</div>
</div>
</div>
</div>

</div>

<div class="row row-space">
<div class="col-sm-10">
<div class="spacing text-warning" id="emptyerror"
style="display: none"
data-bind="visible: !loading() && !error() && (!total() || total()==0)">
<span class="errormessage">
<g:message code="no.nodes.selected.match.nodes.by.selecting.or.entering.a.filter" />
</span>
</div>
<div class="spacing text-danger" id="loaderror2"
style="display: none"
data-bind="visible: error()">
<i class="glyphicon glyphicon-warning-sign"></i>
<span class="errormessage" data-bind="text: error()">

</span>
</div>
<div data-bind="visible: total()>0 || loading()" class="well well-sm inline">
<span data-bind="if: loading()" class="text-info">
<i class="glyphicon glyphicon-time"></i>
<g:message code="loading.matched.nodes" />
</span>
<span data-bind="if: !loading() && !error()">

<span data-bind="messageTemplate: [ total(), nodesTitle() ]"><g:message code="count.nodes.matched" /></span>.

<span data-bind="if: total()>maxShown()">
<span data-bind="messageTemplate: [maxShown(), total()]" class="text-muted"><g:message code="count.nodes.shown" /></span>
</span>
<a class="textbtn textbtn-default pull-right" data-bind="click: nodesPageView">
<g:message code="view.in.nodes.page.prompt" />
</a>
</span>
</div>
<span >
<g:render template="/framework/nodesEmbedKO"/>
</span>
</div>
</div>
</div>



</label>
%{-- filter text --}%
</div>


</div>
</g:if>
</div>
</div>
<g:javascript>
var updateSelectCount = function (evt) {
Expand Down Expand Up @@ -342,7 +451,7 @@

</div>
</div>
</g:if>

<div class="error note" id="formerror" style="display:none">

</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ abstract class ExecutionContext extends BaseNodeFilters{
Integer nodeThreadcount=1
String nodeRankAttribute
Boolean nodeRankOrderAscending=true
Boolean nodeFilterEditable = true
Boolean nodeFilterEditable = false
}

16 changes: 11 additions & 5 deletions rundeckapp/test/integration/ReportServiceTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class ReportServiceTests extends GroovyTestCase {
assertQueryResult([reportIdFilter: 'blah4'], [])
}
void testgetExecNodeFilterReportIdFilter(){
def r1,r2,r3
def r1,r2,r3, r4

ExecReport.withNewSession {
r1=proto(reportId:'blah', jcExecId: '123', succeededNodeList:'test')
Expand All @@ -84,23 +84,29 @@ class ReportServiceTests extends GroovyTestCase {
r3 = proto(reportId: 'blah3', jcExecId: '125', filterApplied:'tags: monkey')
assert r3.validate()
println r3.save(flush: true)
r4 = proto(reportId: 'blah4', jcExecId: '126', filterApplied:'.*',succeededNodeList:'test')
assert r4.validate()
println r4.save(flush: true)

sessionFactory.currentSession.flush()
}
r1=r1.refresh()
r2=r2.refresh()
r3=r3.refresh()
assertEquals(3,ExecReport.count())
r4=r4.refresh()
assertEquals(4,ExecReport.count())
def query = new ExecQuery(execnodeFilter: 'name: test')

def result=reportService.getExecutionReports(query,true)
assert result.total==2
assert result.reports.size()==2
assert result.total==3
assert result.reports.size()==3
assert result.reports.contains(r1)

assertQueryResult([execnodeFilter: 'name: test'], [r1,r2])
assertQueryResult([execnodeFilter: 'name: test'], [r1,r2,r4])
assertQueryResult([execnodeFilter: 'test'], [r1,r2,r4])
assertQueryResult([execnodeFilter: 'tags: monkey'], [r3])
assertQueryResult([execnodeFilter: 'tags: test'], [])
assertQueryResult([execnodeFilter: '.*'], [r4])
}

void testgetExecReportsProjFilterIsExact(){
Expand Down

0 comments on commit 483764f

Please sign in to comment.