http://git-wip-us.apache.org/repos/asf/ignite/blob/1367bc98/modules/web-console/frontend/views/sql/sql.tpl.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/views/sql/sql.tpl.pug 
b/modules/web-console/frontend/views/sql/sql.tpl.pug
deleted file mode 100644
index 98b4d68..0000000
--- a/modules/web-console/frontend/views/sql/sql.tpl.pug
+++ /dev/null
@@ -1,381 +0,0 @@
-//-
-    Licensed to the Apache Software Foundation (ASF) under one or more
-    contributor license agreements.  See the NOTICE file distributed with
-    this work for additional information regarding copyright ownership.
-    The ASF licenses this file to You under the Apache License, Version 2.0
-    (the "License"); you may not use this file except in compliance with
-    the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
-
-include /app/helpers/jade/mixins
-
-mixin btn-toolbar(btn, click, tip, focusId)
-    i.btn.btn-default.fa(class=btn ng-click=click bs-tooltip='' data-title=tip 
ignite-on-click-focus=focusId data-trigger='hover' data-placement='bottom')
-
-mixin btn-toolbar-data(btn, kind, tip)
-    i.btn.btn-default.fa(class=btn ng-click=`setResult(paragraph, '${kind}')` 
ng-class=`{active: resultEq(paragraph, '${kind}')}` bs-tooltip='' 
data-title=tip data-trigger='hover' data-placement='bottom')
-
-mixin result-toolbar
-    .btn-group(ng-model='paragraph.result' ng-click='$event.stopPropagation()' 
style='left: 50%; margin: 0 0 0 -70px;display: block;')
-        +btn-toolbar-data('fa-table', 'table', 'Show data in tabular form')
-        +btn-toolbar-data('fa-bar-chart', 'bar', 'Show bar chart<br/>By 
default first column - X values, second column - Y values<br/>In case of one 
column it will be treated as Y values')
-        +btn-toolbar-data('fa-pie-chart', 'pie', 'Show pie chart<br/>By 
default first column - pie labels, second column - pie values<br/>In case of 
one column it will be treated as pie values')
-        +btn-toolbar-data('fa-line-chart', 'line', 'Show line chart<br/>By 
default first column - X values, second column - Y values<br/>In case of one 
column it will be treated as Y values')
-        +btn-toolbar-data('fa-area-chart', 'area', 'Show area chart<br/>By 
default first column - X values, second column - Y values<br/>In case of one 
column it will be treated as Y values')
-
-mixin chart-settings
-    .total.row
-        .col-xs-7
-            .chart-settings-link(ng-show='paragraph.chart && 
paragraph.chartColumns.length > 0')
-                a(title='Click to show chart settings dialog' 
ng-click='$event.stopPropagation()' bs-popover data-template-url='{{ 
$ctrl.chartSettingsTemplateUrl }}' data-placement='bottom' data-auto-close='1' 
data-trigger='click')
-                    i.fa.fa-bars
-                    | Chart settings
-                div(ng-show='paragraphTimeSpanVisible(paragraph)')
-                    label Show
-                    
button.select-manual-caret.btn.btn-default(ng-model='paragraph.timeLineSpan' 
ng-change='applyChartSettings(paragraph)' bs-options='item for item in 
timeLineSpans' bs-select data-caret-html='<span class="caret"></span>')
-                    label min
-
-                div
-                    label Duration: #[b {{paragraph.duration | duration}}]
-                    label.margin-left-dflt(ng-show='paragraph.localQueryMode') 
NodeID8: #[b {{paragraph.resNodeId | id8}}]
-        .col-xs-2
-            +result-toolbar
-
-mixin notebook-rename
-    .docs-header.notebook-header
-        h1.col-sm-6(ng-hide='notebook.edit')
-            label(style='max-width: calc(100% - 60px)') {{notebook.name}}
-            .btn-group(ng-if='!demo')
-                +btn-toolbar('fa-pencil', 'notebook.edit = 
true;notebook.editName = notebook.name', 'Rename notebook')
-                +btn-toolbar('fa-trash', 'removeNotebook(notebook)', 'Remove 
notebook')
-        h1.col-sm-6(ng-show='notebook.edit')
-            i.btn.fa.fa-floppy-o(ng-show='notebook.editName' 
ng-click='renameNotebook(notebook.editName)' bs-tooltip data-title='Save 
notebook name' data-trigger='hover')
-            .input-tip
-                input.form-control(ng-model='notebook.editName' required 
ignite-on-enter='renameNotebook(notebook.editName)' 
ignite-on-escape='notebook.edit = false;')
-        h1.pull-right
-            a.dropdown-toggle(style='margin-right: 20px' 
data-toggle='dropdown' bs-dropdown='scrollParagraphs' 
data-placement='bottom-right') Scroll to query
-                span.caret
-            button.btn.btn-default(style='margin-top: 2px' 
ng-click='addQuery()' ignite-on-click-focus=focusId)
-                i.fa.fa-fw.fa-plus
-                | Add query
-
-            button.btn.btn-default(style='margin-top: 2px' 
ng-click='addScan()' ignite-on-click-focus=focusId)
-                i.fa.fa-fw.fa-plus
-                | Add scan
-
-mixin notebook-error
-    h2 Failed to load notebook
-    label.col-sm-12 Notebook not accessible any more. Go back to configuration 
or open to another notebook.
-    
button.h3.btn.btn-primary(ui-sref='base.configuration.tabs.advanced.clusters') 
Back to configuration
-
-mixin paragraph-rename
-    .col-sm-6(ng-hide='paragraph.edit')
-        i.fa(ng-class='paragraphExpanded(paragraph) ? "fa-chevron-circle-down" 
: "fa-chevron-circle-right"')
-        label {{paragraph.name}}
-
-        .btn-group(ng-hide='notebook.paragraphs.length > 1')
-            +btn-toolbar('fa-pencil', 'paragraph.edit = true; 
paragraph.editName = paragraph.name; $event.stopPropagation();', 'Rename 
query', 'paragraph-name-{{paragraph.id}}')
-
-        .btn-group(ng-show='notebook.paragraphs.length > 1' 
ng-click='$event.stopPropagation();')
-            +btn-toolbar('fa-pencil', 'paragraph.edit = true; 
paragraph.editName = paragraph.name;', 'Rename query', 
'paragraph-name-{{paragraph.id}}')
-            +btn-toolbar('fa-remove', 'removeParagraph(paragraph)', 'Remove 
query')
-
-    .col-sm-6(ng-show='paragraph.edit')
-        i.tipLabel.fa(style='float: left;' 
ng-class='paragraphExpanded(paragraph) ? "fa-chevron-circle-down" : 
"fa-chevron-circle-right"')
-        i.tipLabel.fa.fa-floppy-o(style='float: right;' 
ng-show='paragraph.editName' ng-click='renameParagraph(paragraph, 
paragraph.editName); $event.stopPropagation();' bs-tooltip data-title='Save 
query name' data-trigger='hover')
-        .input-tip
-            input.form-control(id='paragraph-name-{{paragraph.id}}' 
ng-model='paragraph.editName' required ng-click='$event.stopPropagation();' 
ignite-on-enter='renameParagraph(paragraph, paragraph.editName)' 
ignite-on-escape='paragraph.edit = false')
-
-mixin query-settings
-    .panel-top-align
-        label.tipLabel(bs-tooltip data-placement='bottom' 
data-title='Configure periodical execution of last successfully executed 
query') Refresh rate:
-            
button.btn.btn-default.fa.fa-clock-o.tipLabel(ng-class='{"btn-info": 
paragraph.rate && paragraph.rate.installed}' bs-popover data-template-url='{{ 
$ctrl.paragraphRateTemplateUrl }}' data-placement='left' data-auto-close='1' 
data-trigger='click') {{rateAsString(paragraph)}}
-
-        label.tipLabel(bs-tooltip data-placement='bottom' data-title='Max 
number of rows to show in query result as one page') Page size:
-            
button.btn.btn-default.select-toggle.tipLabel(ng-model='paragraph.pageSize' 
bs-select bs-options='item for item in pageSizes')
-
-        label.tipLabel(bs-tooltip data-placement='bottom' data-title='Limit 
query max results to specified number of pages') Max pages:
-            
button.btn.btn-default.select-toggle.tipLabel(ng-model='paragraph.maxPages' 
bs-select bs-options='item.value as item.label for item in maxPages')
-
-        .panel-tip-container
-            .row(ng-if='nonCollocatedJoinsAvailable(paragraph)')
-                label.tipLabel(bs-tooltip data-placement='bottom' 
data-title='Non-collocated joins is a special mode that allow to join data 
across cluster without collocation.<br/>\
-                    Nested joins are not supported for now.<br/>\
-                    <b>NOTE</b>: In some cases it may consume more heap memory 
or may take a long time than collocated joins.' data-trigger='hover')
-                    input(type='checkbox' 
ng-model='paragraph.nonCollocatedJoins')
-                    span Allow non-collocated joins
-            .row(ng-if='enforceJoinOrderAvailable(paragraph)')
-                label.tipLabel(bs-tooltip data-placement='bottom' 
data-title='Enforce join order of tables in the query.<br/>\
-                    If <b>set</b>, then query optimizer will not reorder 
tables within join.<br/>\
-                    <b>NOTE:</b> It is not recommended to enable this property 
unless you have verified that\
-                    indexes are not selected in optimal order.' 
data-trigger='hover')
-                    input(type='checkbox' 
ng-model='paragraph.enforceJoinOrder')
-                    span Enforce join order
-            .row(ng-if='lazyQueryAvailable(paragraph)')
-                label.tipLabel(bs-tooltip data-placement='bottom' 
data-title='By default Ignite attempts to fetch the whole query result set to 
memory and send it to the client.<br/>\
-                    For small and medium result sets this provides optimal 
performance and minimize duration of internal database locks, thus increasing 
concurrency.<br/>\
-                    If result set is too big to fit in available memory this 
could lead to excessive GC pauses and even OutOfMemoryError.<br/>\
-                    Use this flag as a hint for Ignite to fetch result set 
lazily, thus minimizing memory consumption at the cost of moderate performance 
hit.' data-trigger='hover')
-                    input(type='checkbox' ng-model='paragraph.lazy')
-                    span Lazy result set
-
-mixin query-actions
-    button.btn.btn-primary(ng-disabled='!queryAvailable(paragraph)' 
ng-click='execute(paragraph)')
-        div
-            i.fa.fa-fw.fa-play(ng-hide='paragraph.executionInProgress(false)')
-            
i.fa.fa-fw.fa-refresh.fa-spin(ng-show='paragraph.executionInProgress(false)')
-            span.tipLabelExecute Execute
-    button.btn.btn-primary(ng-disabled='!queryAvailable(paragraph)' 
ng-click='execute(paragraph, true)')
-        div
-            i.fa.fa-fw.fa-play(ng-hide='paragraph.executionInProgress(true)')
-            
i.fa.fa-fw.fa-refresh.fa-spin(ng-show='paragraph.executionInProgress(true)')
-            span.tipLabelExecute Execute on selected node
-
-
-    a.btn.btn-default(ng-disabled='!queryAvailable(paragraph)' 
ng-click='explain(paragraph)' data-placement='bottom' bs-tooltip='' 
data-title='{{queryTooltip(paragraph, "explain query")}}') Explain
-
-mixin table-result-heading-query
-    .total.row
-        .col-xs-7
-            grid-column-selector(grid-api='paragraph.gridOptions.api')
-                .fa.fa-bars.icon
-            label Page: #[b {{paragraph.page}}]
-            label.margin-left-dflt Results so far: #[b {{paragraph.rows.length 
+ paragraph.total}}]
-            label.margin-left-dflt Duration: #[b {{paragraph.duration | 
duration}}]
-            label.margin-left-dflt(ng-show='paragraph.localQueryMode') 
NodeID8: #[b {{paragraph.resNodeId | id8}}]
-        .col-xs-2
-            div(ng-if='paragraph.qryType === "query"')
-                +result-toolbar
-        .col-xs-3
-            .pull-right
-                .btn-group.panel-tip-container
-                    button.btn.btn-primary.btn--with-icon(
-                        ng-click='exportCsv(paragraph)'
-
-                        ng-disabled='paragraph.loading'
-
-                        bs-tooltip=''
-                        ng-attr-title='{{ queryTooltip(paragraph, "export 
query results") }}'
-
-                        data-trigger='hover'
-                        data-placement='bottom'
-                    )
-                        svg(ignite-icon='csv' 
ng-if='!paragraph.csvIsPreparing')
-                        
i.fa.fa-fw.fa-refresh.fa-spin(ng-if='paragraph.csvIsPreparing')
-                        span Export
-
-                    -var options = [{ text: 'Export', click: 
'exportCsv(paragraph)' }, { text: 'Export all', click: 
'exportCsvAll(paragraph)' }, { divider: true }, { text: '<span title="Copy 
current result page to clipboard">Copy to clipboard</span>', click: 
'exportCsvToClipBoard(paragraph)' }]
-                    button.btn.dropdown-toggle.btn-primary(
-                        ng-disabled='paragraph.loading'
-
-                        bs-dropdown=`${JSON.stringify(options)}`
-
-                        data-toggle='dropdown'
-                        data-container='body'
-                        data-placement='bottom-right'
-                        data-html='true'
-                    )
-                        span.caret
-
-
-
-mixin table-result-heading-scan
-    .total.row
-        .col-xs-7
-            grid-column-selector(grid-api='paragraph.gridOptions.api')
-                .fa.fa-bars.icon
-            label Page: #[b {{paragraph.page}}]
-            label.margin-left-dflt Results so far: #[b {{paragraph.rows.length 
+ paragraph.total}}]
-            label.margin-left-dflt Duration: #[b {{paragraph.duration | 
duration}}]
-            label.margin-left-dflt(ng-show='paragraph.localQueryMode') 
NodeID8: #[b {{paragraph.resNodeId | id8}}]
-        .col-xs-2
-            div(ng-if='paragraph.qryType === "query"')
-                +result-toolbar
-        .col-xs-3
-            .pull-right
-                .btn-group.panel-tip-container
-                    // TODO: replace this logic for exporting under one 
component
-                    button.btn.btn-primary.btn--with-icon(
-                        ng-click='exportCsv(paragraph)'
-
-                        ng-disabled='paragraph.loading || 
paragraph.csvIsPreparing'
-
-                        bs-tooltip=''
-                        ng-attr-title='{{ scanTooltip(paragraph) }}'
-
-                        data-trigger='hover'
-                        data-placement='bottom'
-                    )
-                        svg(ignite-icon='csv' 
ng-if='!paragraph.csvIsPreparing')
-                        
i.fa.fa-fw.fa-refresh.fa-spin(ng-if='paragraph.csvIsPreparing')
-                        span Export
-
-                    -var options = [{ text: "Export", click: 
'exportCsv(paragraph)' }, { text: 'Export all', click: 
'exportCsvAll(paragraph)' }, { divider: true }, { text: '<span title="Copy 
current result page to clipboard">Copy to clipboard</span>', click: 
'exportCsvToClipBoard(paragraph)' }]
-                    button.btn.dropdown-toggle.btn-primary(
-                        ng-disabled='paragraph.loading || 
paragraph.csvIsPreparing'
-
-                        bs-dropdown=`${JSON.stringify(options)}`
-
-                        data-toggle='dropdown'
-                        data-container='body'
-                        data-placement='bottom-right'
-                        data-html='true'
-                    )
-                        span.caret
-
-mixin table-result-body
-    .grid(ui-grid='paragraph.gridOptions' ui-grid-resize-columns 
ui-grid-exporter)
-
-mixin chart-result
-    div(ng-hide='paragraph.scanExplain()')
-        +chart-settings
-        .empty(ng-show='paragraph.chartColumns.length > 0 && 
!paragraph.chartColumnsConfigured()') Cannot display chart. Please configure 
axis using #[b Chart settings]
-        .empty(ng-show='paragraph.chartColumns.length == 0') Cannot display 
chart. Result set must contain Java build-in type columns. Please change query 
and execute it again.
-        div(ng-show='paragraph.chartColumnsConfigured()')
-            div(ng-show='paragraph.timeLineSupported() || 
!paragraph.chartTimeLineEnabled()')
-                div(ng-repeat='chart in paragraph.charts')
-                    nvd3(options='chart.options' data='chart.data' 
api='chart.api')
-            .empty(ng-show='!paragraph.timeLineSupported() && 
paragraph.chartTimeLineEnabled()') Pie chart does not support 'TIME_LINE' 
column for X-axis. Please use another column for X-axis or switch to another 
chart.
-    .empty(ng-show='paragraph.scanExplain()')
-        .row
-            .col-xs-4.col-xs-offset-4
-                +result-toolbar
-        label.margin-top-dflt Charts do not support #[b Explain] and #[b Scan] 
query
-
-mixin paragraph-scan
-    .panel-heading(bs-collapse-toggle)
-        .row
-            +paragraph-rename
-    .panel-collapse(role='tabpanel' bs-collapse-target)
-        .col-sm-12.sql-controls
-            .col-sm-3
-                +dropdown-required('Cache:', 'paragraph.cacheName', '"cache"', 
'true', 'false', 'Choose cache', 'caches')
-            .col-sm-3
-                +text-enabled('Filter:', 'paragraph.filter', '"filter"', true, 
false, 'Enter filter')
-                    
label.btn.btn-default.ignite-form-field__btn(ng-click='paragraph.caseSensitive 
= !paragraph.caseSensitive')
-                        input(type='checkbox' 
ng-model='paragraph.caseSensitive')
-                        span(bs-tooltip data-title='Select this checkbox for 
case sensitive search') Cs
-            label.tipLabel(bs-tooltip data-placement='bottom' data-title='Max 
number of rows to show in query result as one page') Page size:
-                
button.btn.btn-default.select-toggle.tipLabel(ng-model='paragraph.pageSize' 
bs-select bs-options='item for item in pageSizes')
-
-        .col-sm-12.sql-controls
-            button.btn.btn-primary(ng-disabled='!scanAvailable(paragraph)' 
ng-click='scan(paragraph)')
-                div
-                    
i.fa.fa-fw.fa-play(ng-hide='paragraph.checkScanInProgress(false)')
-                    
i.fa.fa-fw.fa-refresh.fa-spin(ng-show='paragraph.checkScanInProgress(false)')
-                    span.tipLabelExecute Scan
-
-            button.btn.btn-primary(ng-disabled='!scanAvailable(paragraph)' 
ng-click='scan(paragraph, true)')
-                    
i.fa.fa-fw.fa-play(ng-hide='paragraph.checkScanInProgress(true)')
-                    
i.fa.fa-fw.fa-refresh.fa-spin(ng-show='paragraph.checkScanInProgress(true)')
-                    span.tipLabelExecute Scan on selected node
-
-        .col-sm-12.sql-result(ng-if='paragraph.queryExecuted()' 
ng-switch='paragraph.resultType()')
-            .error(ng-switch-when='error') Error: {{paragraph.error.message}}
-            .empty(ng-switch-when='empty') Result set is empty. Duration: #[b 
{{paragraph.duration | duration}}]
-            .table(ng-switch-when='table')
-                +table-result-heading-scan
-                +table-result-body
-            .footer.clearfix()
-                .pull-left
-                    | Showing results for scan of #[b {{ 
paragraph.queryArgs.cacheName | defaultName }}]
-                    span(ng-if='paragraph.queryArgs.filter') &nbsp; with 
filter: #[b {{ paragraph.queryArgs.filter }}]
-                    span(ng-if='paragraph.queryArgs.localNid') &nbsp; on node: 
#[b {{ paragraph.queryArgs.localNid | limitTo:8 }}]
-
-                -var nextVisibleCondition = 'paragraph.resultType() != "error" 
&& paragraph.queryId && paragraph.nonRefresh() && (paragraph.table() || 
paragraph.chart() && !paragraph.scanExplain())'
-
-                .pull-right(ng-show=`${nextVisibleCondition}` 
ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && 
nextPage(paragraph)')
-                    i.fa.fa-chevron-circle-right
-                    a Next
-
-mixin paragraph-query
-    .row.panel-heading(bs-collapse-toggle)
-        +paragraph-rename
-    .panel-collapse(role='tabpanel' bs-collapse-target)
-        .col-sm-12
-            .col-xs-8.col-sm-9(style='border-right: 1px solid #eee')
-                .sql-editor(ignite-ace='{onLoad: aceInit(paragraph), theme: 
"chrome", mode: "sql", require: ["ace/ext/language_tools"],' +
-                'advanced: {enableSnippets: false, enableBasicAutocompletion: 
true, enableLiveAutocompletion: true}}'
-                ng-model='paragraph.query')
-            .col-xs-4.col-sm-3
-                div(ng-show='caches.length > 0' style='padding: 5px 10px' 
st-table='displayedCaches' st-safe-src='caches')
-                    lable.labelField.labelFormField Caches:
-                    i.fa.fa-database.tipField(title='Click to show cache types 
metadata dialog' bs-popover data-template-url='{{ 
$ctrl.cacheMetadataTemplateUrl }}' data-placement='bottom' data-trigger='click' 
data-container='#{{ paragraph.id }}')
-                    .input-tip
-                        input.form-control(type='text' st-search='label' 
placeholder='Filter caches...')
-                    table.links
-                        tbody.scrollable-y(style='max-height: 15em; display: 
block;')
-                            tr(ng-repeat='cache in displayedCaches track by 
cache.name')
-                                td(style='width: 100%')
-                                    input.labelField(id='cache_{{ 
[paragraph.id, $index].join("_") }}' type='radio' value='{{cache.name}}' 
ng-model='paragraph.cacheName')
-                                    label(for='cache_{{ [paragraph.id, 
$index].join("_") }} ' ng-bind-html='cache.label')
-                    .settings-row
-                        .row(ng-if='ddlAvailable(paragraph)')
-                            label.tipLabel.use-cache(bs-tooltip 
data-placement='bottom'
-                                data-title=
-                                    'Use selected cache as default schema 
name.<br/>\
-                                    This will allow to execute query on 
specified cache without specify schema name.<br/>\
-                                    <b>NOTE:</b> In future version of Ignite 
this feature will be removed.'
-                                data-trigger='hover')
-                                input(type='checkbox' 
ng-model='paragraph.useAsDefaultSchema')
-                                span Use selected cache as default schema name
-                .empty-caches(ng-show='displayedCaches.length == 0 && 
caches.length != 0')
-                    label Wrong caches filter
-                .empty-caches(ng-show='caches.length == 0')
-                    label No caches
-        .col-sm-12.sql-controls
-            +query-actions
-
-            .pull-right
-                +query-settings
-        .col-sm-12.sql-result(ng-if='paragraph.queryExecuted()' 
ng-switch='paragraph.resultType()')
-            .error(ng-switch-when='error')
-                label Error: {{paragraph.error.message}}
-                br
-                a(ng-show='paragraph.resultType() === "error"' 
ng-click='showStackTrace(paragraph)') Show more
-            .empty(ng-switch-when='empty') Result set is empty. Duration: #[b 
{{paragraph.duration | duration}}]
-            .table(ng-switch-when='table')
-                +table-result-heading-query
-                +table-result-body
-            .chart(ng-switch-when='chart')
-                +chart-result
-            .footer.clearfix(ng-show='paragraph.resultType() !== "error"')
-                a.pull-left(ng-click='showResultQuery(paragraph)') Show query
-
-                -var nextVisibleCondition = 'paragraph.resultType() !== 
"error" && paragraph.queryId && paragraph.nonRefresh() && (paragraph.table() || 
paragraph.chart() && !paragraph.scanExplain())'
-
-                .pull-right(ng-show=`${nextVisibleCondition}` 
ng-class='{disabled: paragraph.loading}' ng-click='!paragraph.loading && 
nextPage(paragraph)')
-                    i.fa.fa-chevron-circle-right
-                    a Next
-
-.row
-    .docs-content
-        .row(ng-if='notebook' bs-affix style='margin-bottom: 20px;')
-            +notebook-rename
-
-        ignite-information(data-title='With query notebook you can' 
style='margin-top: 0; margin-bottom: 30px')
-            ul
-                li Create any number of queries
-                li Execute and explain SQL queries
-                li Execute scan queries
-                li View data in tabular form and as charts
-
-        div(ng-if='notebookLoadFailed' style='text-align: center')
-            +notebook-error
-
-        div(ng-if='notebook' ignite-loading='sqlLoading' 
ignite-loading-text='{{ loadingText }}' ignite-loading-position='top')
-            .docs-body.paragraphs
-                .panel-group(bs-collapse 
ng-model='notebook.expandedParagraphs' data-allow-multiple='true' 
data-start-collapsed='false')
-
-                    .panel-paragraph(ng-repeat='paragraph in 
notebook.paragraphs' id='{{paragraph.id}}' ng-form='form_{{paragraph.id}}')
-                        .panel.panel-default(ng-if='paragraph.qryType === 
"scan"')
-                            +paragraph-scan
-                        .panel.panel-default(ng-if='paragraph.qryType === 
"query"')
-                            +paragraph-query

http://git-wip-us.apache.org/repos/asf/ignite/blob/1367bc98/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
index 8eed3dd..86b9ea5 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/ClusterListener.java
@@ -22,44 +22,51 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import io.socket.client.Socket;
 import io.socket.emitter.Emitter;
 import java.net.ConnectException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
+import org.apache.ignite.IgniteLogger;
 import org.apache.ignite.console.agent.rest.RestExecutor;
 import org.apache.ignite.console.agent.rest.RestResult;
 import 
org.apache.ignite.internal.processors.rest.client.message.GridClientNodeBean;
 import 
org.apache.ignite.internal.processors.rest.protocols.http.jetty.GridJettyObjectMapper;
 import org.apache.ignite.internal.util.typedef.F;
-import org.apache.ignite.internal.util.typedef.T2;
+import org.apache.ignite.internal.util.typedef.internal.LT;
 import org.apache.ignite.internal.util.typedef.internal.U;
 import org.apache.ignite.lang.IgniteClosure;
 import org.apache.ignite.lang.IgniteProductVersion;
-import org.slf4j.Logger;
+import org.apache.ignite.logger.slf4j.Slf4jLogger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.ignite.IgniteSystemProperties.IGNITE_CLUSTER_NAME;
 import static org.apache.ignite.console.agent.AgentUtils.toJSON;
 import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_BUILD_VER;
+import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_CLIENT_MODE;
+import static org.apache.ignite.internal.IgniteNodeAttributes.ATTR_IPS;
 import static 
org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_SUCCESS;
+import static 
org.apache.ignite.internal.visor.util.VisorTaskUtils.sortAddresses;
+import static 
org.apache.ignite.internal.visor.util.VisorTaskUtils.splitAddresses;
 
 /**
  * API to transfer topology from Ignite cluster available by node-uri.
  */
 public class ClusterListener {
     /** */
-    private static final Logger log = 
LoggerFactory.getLogger(ClusterListener.class);
+    private static final IgniteLogger log = new 
Slf4jLogger(LoggerFactory.getLogger(ClusterListener.class));
 
     /** */
     private static final String EVENT_CLUSTER_CONNECTED = "cluster:connected";
 
     /** */
     private static final String EVENT_CLUSTER_TOPOLOGY = "cluster:topology";
-    
+
     /** */
     private static final String EVENT_CLUSTER_DISCONNECTED = 
"cluster:disconnected";
 
@@ -79,17 +86,6 @@ public class ClusterListener {
     private final BroadcastTask broadcastTask = new BroadcastTask();
 
     /** */
-    private static final IgniteClosure<GridClientNodeBean, UUID> NODE2ID = new 
IgniteClosure<GridClientNodeBean, UUID>() {
-        @Override public UUID apply(GridClientNodeBean n) {
-            return n.getNodeId();
-        }
-
-        @Override public String toString() {
-            return "Node bean to node ID transformer closure.";
-        }
-    };
-
-    /** */
     private static final IgniteClosure<UUID, String> ID2ID8 = new 
IgniteClosure<UUID, String>() {
         @Override public String apply(UUID nid) {
             return U.id8(nid).toUpperCase();
@@ -127,7 +123,7 @@ public class ClusterListener {
      * @param nids Cluster nodes IDs.
      */
     private void clusterConnect(Collection<UUID> nids) {
-        log.info("Connection successfully established to cluster with nodes: 
{}", F.viewReadOnly(nids, ID2ID8));
+        log.info("Connection successfully established to cluster with nodes: " 
+ F.viewReadOnly(nids, ID2ID8));
 
         client.emit(EVENT_CLUSTER_CONNECTED, toJSON(nids));
     }
@@ -171,7 +167,7 @@ public class ClusterListener {
             @Override public void call(Object... args) {
                 safeStopRefresh();
 
-                final long timeout = args.length > 1  && args[1] instanceof 
Long ? (long)args[1] : DFLT_TIMEOUT;
+                final long timeout = args.length > 1 && args[1] instanceof 
Long ? (long)args[1] : DFLT_TIMEOUT;
 
                 refreshTask = pool.scheduleWithFixedDelay(broadcastTask, 0L, 
timeout, TimeUnit.MILLISECONDS);
             }
@@ -194,41 +190,107 @@ public class ClusterListener {
     /** */
     private static class TopologySnapshot {
         /** */
+        private String clusterName;
+
+        /** */
         private Collection<UUID> nids;
 
         /** */
-        private String clusterVer;
+        private Map<UUID, String> addrs;
+
+        /** */
+        private Map<UUID, Boolean> clients;
+
+        /** */
+        private String clusterVerStr;
+
+        /** */
+        private IgniteProductVersion clusterVer;
+
+        /** */
+        private boolean active;
+
+        /**
+         * Helper method to get attribute.
+         *
+         * @param attrs Map with attributes.
+         * @param name Attribute name.
+         * @return Attribute value.
+         */
+        private static <T> T attribute(Map<String, Object> attrs, String name) 
{
+            return (T)attrs.get(name);
+        }
 
         /**
          * @param nodes Nodes.
          */
         TopologySnapshot(Collection<GridClientNodeBean> nodes) {
-            nids = F.viewReadOnly(nodes, NODE2ID);
+            int sz = nodes.size();
+
+            nids = new ArrayList<>(sz);
+            addrs = U.newHashMap(sz);
+            clients = U.newHashMap(sz);
+            active = false;
+
+            for (GridClientNodeBean node : nodes) {
+                UUID nid = node.getNodeId();
+
+                nids.add(nid);
 
-            Collection<T2<String, IgniteProductVersion>> vers = 
F.transform(nodes,
-                new IgniteClosure<GridClientNodeBean, T2<String, 
IgniteProductVersion>>() {
-                    @Override public T2<String, IgniteProductVersion> 
apply(GridClientNodeBean bean) {
-                        String ver = 
(String)bean.getAttributes().get(ATTR_BUILD_VER);
+                Map<String, Object> attrs = node.getAttributes();
 
-                        return new T2<>(ver, 
IgniteProductVersion.fromString(ver));
-                    }
-                });
+                if (F.isEmpty(clusterName))
+                    clusterName = attribute(attrs, IGNITE_CLUSTER_NAME);
 
-            T2<String, IgniteProductVersion> min = Collections.min(vers, new 
Comparator<T2<String, IgniteProductVersion>>() {
-                @SuppressWarnings("ConstantConditions")
-                @Override public int compare(T2<String, IgniteProductVersion> 
o1, T2<String, IgniteProductVersion> o2) {
-                    return o1.get2().compareTo(o2.get2());
+                Boolean client = attribute(attrs, ATTR_CLIENT_MODE);
+
+                clients.put(nid, client);
+
+                Collection<String> nodeAddrs = client
+                    ? splitAddresses((String)attribute(attrs, ATTR_IPS))
+                    : node.getTcpAddresses();
+
+                String firstIP = F.first(sortAddresses(nodeAddrs));
+
+                addrs.put(nid, firstIP);
+
+                String nodeVerStr = attribute(attrs, ATTR_BUILD_VER);
+
+                IgniteProductVersion nodeVer = 
IgniteProductVersion.fromString(nodeVerStr);
+
+                if (clusterVer == null || clusterVer.compareTo(nodeVer) > 0) {
+                    clusterVer = nodeVer;
+                    clusterVerStr = nodeVerStr;
                 }
-            });
+            }
+        }
 
-            clusterVer = min.get1();
+        /**
+         * @return Cluster name.
+         */
+        public String getClusterName() {
+            return clusterName;
         }
 
         /**
          * @return Cluster version.
          */
         public String getClusterVersion() {
-            return clusterVer;
+            return clusterVerStr;
+        }
+
+        /**
+         * @return Cluster active flag.
+         */
+        public boolean isActive() {
+            return active;
+        }
+
+        /**
+         * @param active New cluster active state.
+         */
+        public void setActive(boolean active) {
+            this.active = active;
         }
 
         /**
@@ -238,14 +300,40 @@ public class ClusterListener {
             return nids;
         }
 
-        /**  */
+        /**
+         * @return Cluster nodes with IPs.
+         */
+        public Map<UUID, String> getAddresses() {
+            return addrs;
+        }
+
+        /**
+         * @return Cluster nodes with client mode flag.
+         */
+        public Map<UUID, Boolean> getClients() {
+            return clients;
+        }
+
+        /**
+         * @return Cluster version.
+         */
+        public IgniteProductVersion clusterVersion() {
+            return clusterVer;
+        }
+
+        /**
+         * @return Collection of short UUIDs.
+         */
         Collection<String> nid8() {
             return F.viewReadOnly(nids, ID2ID8);
         }
 
-        /**  */
-        boolean differentCluster(TopologySnapshot old) {
-            return old == null || F.isEmpty(old.nids) || 
Collections.disjoint(nids, old.nids);
+        /**
+         * @param prev Previous topology.
+         * @return {@code true} in case if current topology is a new cluster.
+         */
+        boolean differentCluster(TopologySnapshot prev) {
+            return prev == null || F.isEmpty(prev.nids) || 
Collections.disjoint(nids, prev.nids);
         }
     }
 
@@ -264,7 +352,11 @@ public class ClusterListener {
                         TopologySnapshot newTop = new TopologySnapshot(nodes);
 
                         if (newTop.differentCluster(top))
-                            log.info("Connection successfully established to 
cluster with nodes: {}", newTop.nid8());
+                            log.info("Connection successfully established to 
cluster with nodes: " + newTop.nid8());
+
+                        boolean active = 
restExecutor.active(newTop.clusterVersion(), F.first(newTop.getNids()));
+
+                        newTop.setActive(active);
 
                         top = newTop;
 
@@ -273,7 +365,7 @@ public class ClusterListener {
                         break;
 
                     default:
-                        log.warn(res.getError());
+                        LT.warn(log, res.getError());
 
                         clusterDisconnect();
                 }
@@ -288,7 +380,7 @@ public class ClusterListener {
             }
         }
     }
-    
+
     /** */
     private class BroadcastTask implements Runnable {
         /** {@inheritDoc} */
@@ -306,7 +398,7 @@ public class ClusterListener {
                         if (top.differentCluster(newTop)) {
                             clusterDisconnect();
 
-                            log.info("Connection successfully established to 
cluster with nodes: {}", newTop.nid8());
+                            log.info("Connection successfully established to 
cluster with nodes: " + newTop.nid8());
 
                             watch();
                         }
@@ -318,7 +410,7 @@ public class ClusterListener {
                         break;
 
                     default:
-                        log.warn(res.getError());
+                        LT.warn(log, res.getError());
 
                         clusterDisconnect();
                 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/1367bc98/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
----------------------------------------------------------------------
diff --git 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
index 36f3885..7fbe6f9 100644
--- 
a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
+++ 
b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/rest/RestExecutor.java
@@ -30,6 +30,7 @@ import java.io.StringWriter;
 import java.net.ConnectException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import okhttp3.Dispatcher;
 import okhttp3.FormBody;
@@ -44,6 +45,7 @@ import org.apache.ignite.console.demo.AgentClusterDemo;
 import 
org.apache.ignite.internal.processors.rest.protocols.http.jetty.GridJettyObjectMapper;
 import org.apache.ignite.internal.util.typedef.internal.LT;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteProductVersion;
 import org.apache.ignite.logger.slf4j.Slf4jLogger;
 import org.slf4j.LoggerFactory;
 
@@ -59,6 +61,18 @@ import static 
org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS
  */
 public class RestExecutor {
     /** */
+    private static final IgniteProductVersion IGNITE_2_1 = 
IgniteProductVersion.fromString("2.1.0");
+
+    /** */
+    private static final IgniteProductVersion IGNITE_2_3 = 
IgniteProductVersion.fromString("2.3.0");
+
+    /** Unique Visor key to get events last order. */
+    private static final String EVT_LAST_ORDER_KEY = "WEB_AGENT_" + 
UUID.randomUUID().toString();
+
+    /** Unique Visor key to get events throttle counter. */
+    private static final String EVT_THROTTLE_CNTR_KEY = "WEB_AGENT_" + 
UUID.randomUUID().toString();
+
+    /** */
     private static final IgniteLogger log = new 
Slf4jLogger(LoggerFactory.getLogger(RestExecutor.class));
 
     /** JSON object mapper. */
@@ -208,7 +222,9 @@ public class RestExecutor {
     }
 
     /**
-     * @param demo Is demo node request.
+     * @param demo {@code true} in case of demo mode.
+     * @param full Flag indicating whether to collect metrics or not.
+     * @throws IOException If failed to collect topology.
      */
     public RestResult topology(boolean demo, boolean full) throws IOException {
         Map<String, Object> params = new HashMap<>(3);
@@ -221,6 +237,51 @@ public class RestExecutor {
     }
 
     /**
+     * @param ver Cluster version.
+     * @param nid Node ID.
+     * @return Cluster active state.
+     * @throws IOException If failed to collect cluster active state.
+     */
+    public boolean active(IgniteProductVersion ver, UUID nid) throws 
IOException {
+        Map<String, Object> params = new HashMap<>();
+
+        boolean v23 = ver.compareTo(IGNITE_2_3) >= 0;
+
+        if (v23)
+            params.put("cmd", "currentState");
+        else {
+            params.put("cmd", "exe");
+            params.put("name", 
"org.apache.ignite.internal.visor.compute.VisorGatewayTask");
+            params.put("p1", nid);
+            params.put("p2", 
"org.apache.ignite.internal.visor.node.VisorNodeDataCollectorTask");
+            params.put("p3", 
"org.apache.ignite.internal.visor.node.VisorNodeDataCollectorTaskArg");
+            params.put("p4", false);
+            params.put("p5", EVT_LAST_ORDER_KEY);
+            params.put("p6", EVT_THROTTLE_CNTR_KEY);
+
+            if (ver.compareTo(IGNITE_2_1) >= 0)
+                params.put("p7", false);
+            else {
+                params.put("p7", 10);
+                params.put("p8", false);
+            }
+        }
+
+        RestResult res = sendRequest(false, "ignite", params, null, null);
+
+        switch (res.getStatus()) {
+            case STATUS_SUCCESS:
+                if (v23)
+                    return Boolean.valueOf(res.getData());
+
+                return res.getData().contains("\"active\":true");
+
+            default:
+                throw new IOException(res.getError());
+        }
+    }
+
+    /**
      * REST response holder Java bean.
      */
     private static class RestResponseHolder {

Reply via email to