Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id A9813200D5D for ; Wed, 6 Dec 2017 04:37:42 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id A7DF5160C20; Wed, 6 Dec 2017 03:37:42 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 01439160C1B for ; Wed, 6 Dec 2017 04:37:40 +0100 (CET) Received: (qmail 47155 invoked by uid 500); 6 Dec 2017 03:37:40 -0000 Mailing-List: contact commits-help@ignite.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ignite.apache.org Delivered-To: mailing list commits@ignite.apache.org Received: (qmail 47146 invoked by uid 99); 6 Dec 2017 03:37:39 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 06 Dec 2017 03:37:39 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id C3022E00BD; Wed, 6 Dec 2017 03:37:39 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: akuznetsov@apache.org To: commits@ignite.apache.org Date: Wed, 06 Dec 2017 03:37:39 -0000 Message-Id: <7a4664780bbd4b94a2dd72f85c89b69d@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/5] ignite git commit: IGNITE-6390 Web Console: Added component for cluster selection. archived-at: Wed, 06 Dec 2017 03:37:42 -0000 Repository: ignite Updated Branches: refs/heads/master cbd69d6b3 -> 1367bc98e 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
By default first column - X values, second column - Y values
In case of one column it will be treated as Y values') - +btn-toolbar-data('fa-pie-chart', 'pie', 'Show pie chart
By default first column - pie labels, second column - pie values
In case of one column it will be treated as pie values') - +btn-toolbar-data('fa-line-chart', 'line', 'Show line chart
By default first column - X values, second column - Y values
In case of one column it will be treated as Y values') - +btn-toolbar-data('fa-area-chart', 'area', 'Show area chart
By default first column - X values, second column - Y values
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='') - 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.
\ - Nested joins are not supported for now.
\ - NOTE: 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.
\ - If set, then query optimizer will not reorder tables within join.
\ - NOTE: 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.
\ - For small and medium result sets this provides optimal performance and minimize duration of internal database locks, thus increasing concurrency.
\ - If result set is too big to fit in available memory this could lead to excessive GC pauses and even OutOfMemoryError.
\ - 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: 'Copy to clipboard', 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: 'Copy to clipboard', 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')   with filter: #[b {{ paragraph.queryArgs.filter }}] - span(ng-if='paragraph.queryArgs.localNid')   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.
\ - This will allow to execute query on specified cache without specify schema name.
\ - NOTE: 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 NODE2ID = new IgniteClosure() { - @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 ID2ID8 = new IgniteClosure() { @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 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 nids; /** */ - private String clusterVer; + private Map addrs; + + /** */ + private Map 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 attribute(Map attrs, String name) { + return (T)attrs.get(name); + } /** * @param nodes Nodes. */ TopologySnapshot(Collection 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> vers = F.transform(nodes, - new IgniteClosure>() { - @Override public T2 apply(GridClientNodeBean bean) { - String ver = (String)bean.getAttributes().get(ATTR_BUILD_VER); + Map attrs = node.getAttributes(); - return new T2<>(ver, IgniteProductVersion.fromString(ver)); - } - }); + if (F.isEmpty(clusterName)) + clusterName = attribute(attrs, IGNITE_CLUSTER_NAME); - T2 min = Collections.min(vers, new Comparator>() { - @SuppressWarnings("ConstantConditions") - @Override public int compare(T2 o1, T2 o2) { - return o1.get2().compareTo(o2.get2()); + Boolean client = attribute(attrs, ATTR_CLIENT_MODE); + + clients.put(nid, client); + + Collection 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 getAddresses() { + return addrs; + } + + /** + * @return Cluster nodes with client mode flag. + */ + public Map getClients() { + return clients; + } + + /** + * @return Cluster version. + */ + public IgniteProductVersion clusterVersion() { + return clusterVer; + } + + /** + * @return Collection of short UUIDs. + */ Collection 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 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 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 {