Resolve conflicts, add stale node/index indication
1
.gitignore
vendored
|
@ -14,3 +14,4 @@
|
|||
/node_modules
|
||||
/build
|
||||
/.aws-config.json
|
||||
html_docs
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
Contributing to elasticsearch
|
||||
=============================
|
||||
|
||||
Elasticsearch is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into Elasticsearch itself.
|
||||
|
||||
Bug reports
|
||||
-----------
|
||||
|
||||
If you think you have found a bug in Elasticsearch, first make sure that you are testing against the [latest version of Elasticsearch](http://www.elasticsearch.org/download/) - your issue may already have been fixed. If not, search our [issues list](https://github.com/elasticsearch/elasticsearch/issues) on GitHub in case a similar issue has already been opened.
|
||||
|
||||
It is very helpful if you can prepare a reproduction of the bug. In other words, provide a small test case which we can run to confirm your bug. It makes it easier to find the problem and to fix it. Test cases should be provided as `curl` commands which we can copy and paste into a terminal to run it locally, for example:
|
||||
|
||||
```sh
|
||||
# delete the index
|
||||
curl -XDELETE localhost:9200/test
|
||||
|
||||
# insert a document
|
||||
curl -XPUT localhost:9200/test/test/1 -d '{
|
||||
"title": "test document"
|
||||
}'
|
||||
|
||||
# this should return XXXX but instead returns YYY
|
||||
curl ....
|
||||
```
|
||||
|
||||
Provide as much information as you can. You may think that the problem lies with your query, when actually it depends on how your data is indexed. The easier it is for us to recreate your problem, the faster it is likely to be fixed.
|
||||
|
||||
Feature requests
|
||||
----------------
|
||||
|
||||
If you find yourself wishing for a feature that doesn't exist in Elasticsearch, you are probably not alone. There are bound to be others out there with similar needs. Many of the features that Elasticsearch has today have been added because our users saw the need.
|
||||
Open an issue on our [issues list](https://github.com/elasticsearch/elasticsearch/issues) on GitHub which describes the feature you would like to see, why you need it, and how it should work.
|
||||
|
||||
Contributing code and documentation changes
|
||||
-------------------------------------------
|
||||
|
||||
If you have a bugfix or new feature that you would like to contribute to Elasticsearch, please find or open an issue about it first. Talk about what you would like to do. It may be that somebody is already working on it, or that there are particular issues that you should know about before implementing the change.
|
||||
|
||||
We enjoy working with contributors to get their code accepted. There are many approaches to fixing a problem and it is important to find the best approach before writing too much code.
|
||||
|
||||
The process for contributing to any of the [Elasticsearch repositories](https://github.com/elasticsearch/) is similar. Details for individual projects can be found below.
|
||||
|
||||
### Fork and clone the repository
|
||||
|
||||
You will need to fork the main Elasticsearch code or documentation repository and clone it to your local machine. See
|
||||
[github help page](https://help.github.com/articles/fork-a-repo) for help.
|
||||
|
||||
Further instructions for specific projects are given below.
|
||||
|
||||
### Submitting your changes
|
||||
|
||||
Once your changes and tests are ready to submit for review:
|
||||
|
||||
1. Test your changes
|
||||
Run the test suite to make sure that nothing is broken.
|
||||
|
||||
2. Sign the Contributor License Agreement
|
||||
Please make sure you have signed our [Contributor License Agreement](http://www.elasticsearch.org/contributor-agreement/). We are not asking you to assign copyright to us, but to give us the right to distribute your code without restriction. We ask this of all contributors in order to assure our users of the origin and continuing existence of the code. You only need to sign the CLA once.
|
||||
|
||||
3. Rebase your changes
|
||||
Update your local repository with the most recent code from the main Elasticsearch repository, and rebase your branch on top of the latest master branch. We prefer your changes to be squashed into a single commit.
|
||||
|
||||
4. Submit a pull request
|
||||
Push your local changes to your forked copy of the repository and [submit a pull request](https://help.github.com/articles/using-pull-requests). In the pull request, describe what your changes do and mention the number of the issue where discussion has taken place, eg "Closes #123".
|
||||
|
||||
Then sit back and wait. There will probably be discussion about the pull request and, if any changes are needed, we would love to work with you to get your pull request merged into Elasticsearch.
|
||||
|
||||
|
||||
Contributing to the Elasticsearch plugin
|
||||
----------------------------------------
|
||||
|
||||
**Repository:** [https://github.com/elasticsearch/elasticsearch-dash](https://github.com/elasticsearch/elasticsearch-dash)
|
||||
|
||||
Make sure you have [Maven](http://maven.apache.org) installed, as Elasticsearch uses it as its build system. Integration with IntelliJ and Eclipse should work out of the box. Eclipse users can automatically configure their IDE by running `mvn eclipse:eclipse` and then importing the project into their workspace: `File > Import > Existing project into workspace`.
|
||||
|
||||
Please follow these formatting guidelines:
|
||||
|
||||
* Java indent is 4 spaces
|
||||
* Line width is 140 characters
|
||||
* The rest is left to Java coding standards
|
||||
* Disable “auto-format on save” to prevent unnecessary format changes. This makes reviews much harder as it generates unnecessary formatting changes. If your IDE supports formatting only modified chunks that is fine to do.
|
||||
|
||||
To create a distribution from the source, simply run:
|
||||
|
||||
```sh
|
||||
cd elasticsearch-dash/
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
|
||||
You will find the newly built packages under: `./target/releases/`.
|
||||
|
||||
Before submitting your changes, run the test suite to make sure that nothing is broken, with:
|
||||
|
||||
```sh
|
||||
mvn clean test
|
||||
```
|
||||
|
||||
Source: [Contributing to elasticsearch](http://www.elasticsearch.org/contributing-to-elasticsearch/)
|
|
@ -5,7 +5,7 @@ module.exports = function (grunt) {
|
|||
var config = {
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
kibanaCheckoutDir: './kibana/vendor/kibana',
|
||||
kibanaRevision: 'master',
|
||||
kibanaRevision: 'marvel',
|
||||
agentDir: 'agent',
|
||||
buildDir: 'build',
|
||||
packageDir: 'build/packages',
|
||||
|
@ -19,7 +19,7 @@ module.exports = function (grunt) {
|
|||
dist: '"https://marvel-stats.elasticsearch.com/"'
|
||||
},
|
||||
kibanaPort: grunt.option('port') || 5601,
|
||||
kibanaHost: 'localhost'
|
||||
kibanaHost: grunt.option('host') ||'localhost'
|
||||
};
|
||||
|
||||
// more detailed config
|
||||
|
|
488
LICENSE.txt
|
@ -1,207 +1,204 @@
|
|||
Software License Agreement
|
||||
Marvel Software License Agreement
|
||||
|
||||
READ THIS AGREEMENT CAREFULLY, WHICH CONSITUTES A LEGALLY BINIDNG AGREEMENT AND
|
||||
GOVERNS YOUR USE OF THE ELASTICSEARCH SOFTWARE. BY DOWNLOADING, INSTALLING
|
||||
AND/OR USING THE ELASTICSEARCH SOFTWARE, YOU ARE INDICATING THAT YOU AGREE TO
|
||||
THE TERMS AND CONDITIONS SET FORTH IN THIS AGREEMENT. IF YOU DO NOT AGREE WITH
|
||||
SUCH TERMS AND CONDITIONS, YOU MAY NOT DOWNLOAD, INSTALL OR USE THE
|
||||
ELASTICSEARCH SOFTWARE, AND YOU MAY RETURN THE ELASTICSEARCH SOFTWARE FOR A
|
||||
REFUND OF ANY LICENSE FEES YOU HAVE PAID, PROVIDED THAT YOU HAVE NOT INSTALLED
|
||||
OR USED SUCH ELASTICSEARCH SOFTWARE.
|
||||
READ THIS AGREEMENT CAREFULLY, WHICH CONSITUTES A LEGALLY BINDING AGREEMENT
|
||||
AND GOVERNS YOUR USE OF ELASTICSEARCH’S MARVEL SOFTWARE. BY INSTALLING AND/OR
|
||||
USING THE MARVEL SOFTWARE, YOU ARE INDICATING THAT YOU AGREE TO THE TERMS AND
|
||||
CONDITIONS SET FORTH IN THIS AGREEMENT. IF YOU DO NOT AGREE WITH SUCH TERMS
|
||||
AND CONDITIONS, YOU MAY NOT INSTALL OR USE THE MARVEL SOFTWARE.
|
||||
|
||||
This Software License Agreement (this “Agreement") is entered into by and
|
||||
between the applicable Elasticsearch entity referred to in Attachment 1 below
|
||||
(“Elasticsearch”) and the person or entity (“You”) that has downloaded the
|
||||
Elasticsearch software to which this Agreement is attached
|
||||
(“Elasticsearch Software”), and is effective as of the date you download the
|
||||
Elasticsearch Software (the “Effective Date”).
|
||||
This Marvel Software License Agreement (this “Agreement") is entered into by
|
||||
and between the applicable Elasticsearch entity referred to in Attachment 1
|
||||
below (“Elasticsearch”) and the person or entity (“You”) that has downloaded
|
||||
Elasticsearch’s Marvel software to which this Agreement is attached (“Marvel
|
||||
Software”), and is effective as of the date you first install the Marvel
|
||||
Software (the “Effective Date”).
|
||||
|
||||
|
||||
1. Software License and Restrictions
|
||||
|
||||
1.1. License Grants. Subject to the terms and conditions of this Agreement and
|
||||
1.1 License Grants. Subject to the terms and conditions of this Agreement and
|
||||
complete payment of any and all applicable license fees (provided that no
|
||||
license fee shall be required for use of the Elasticsearch Software for purely
|
||||
development purposes), Elasticsearch agrees to grant, and does hereby grant to
|
||||
license fee shall be required for use of the Marvel Software for other than
|
||||
production purposes, Elasticsearch agrees to grant, and does hereby grant to
|
||||
You during the term and for the restricted scope of this Agreement, solely for
|
||||
Your internal business operations, a limited, non-exclusive, non-transferable
|
||||
right and license (without the right to grant or authorize sublicenses) to: (i)
|
||||
install and use the object code version of the Elasticsearch Software, subject
|
||||
to any applicable quantitative limitations set forth in any order form executed
|
||||
right and license (without the right to grant or authorize sublicenses) to:
|
||||
(i) install and use the object code version of the Marvel Software, subject to
|
||||
any applicable quantitative limitations set forth in any order form executed
|
||||
by Elasticsearch and You (“Order Form”); (ii) use, and distribute internally a
|
||||
reasonable number of copies of the documentation, if any, provided with the
|
||||
Elasticsearch Software (“Documentation”), provided that You must include on such
|
||||
Marvel Software (“Documentation”), provided that You must include on such
|
||||
copies all Elasticsearch trademarks, trade names, logos and notices present on
|
||||
the Documentation as originally provided to You by Elasticsearch; (iii) permit
|
||||
third party contractors performing services on Your behalf to use the
|
||||
Elasticsearch Software and Documentation as set forth in (i) and (ii) above,
|
||||
provided that such use must be solely for Your benefit, and You shall be
|
||||
responsible for all acts and omissions of such contractors in connection with
|
||||
their use of the Elasticsearch Software.
|
||||
third party contractors performing services on Your behalf to use the Marvel
|
||||
Software and Documentation as set forth in (i) and (ii) above, provided that
|
||||
such use must be solely for Your benefit, and You shall be responsible for all
|
||||
acts and omissions of such contractors in connection with their use of the
|
||||
Marvel Software.
|
||||
|
||||
1.2. Reservation of Rights; Restrictions. As between Elasticsearch and You,
|
||||
Elasticsearch owns all right title and interest in and to the Elasticsearch
|
||||
Software and any derivative works thereof, and except as expressly set forth in
|
||||
Section 1.1 above, no other license to the Elasticsearch Software is granted to
|
||||
You by implication, estoppel or otherwise. You agree not to: (i) prepare
|
||||
derivative works from, modify, copy or use the Elasticsearch Software in any
|
||||
manner except as expressly permitted in this Agreement or applicable law; (ii)
|
||||
transfer, sell, rent, lease, distribute, sublicense, loan or otherwise transfer
|
||||
the Elasticsearch Software in whole or in part to any third party; (iii) use the
|
||||
Elasticsearch Software for providing time-sharing services, any software-
|
||||
as-a-service offering (“SaaS”), service bureau services or as part of an
|
||||
application services provider or other service offering; (iv) alter or remove
|
||||
any proprietary notices in the Elasticsearch Software; or (v) make available to
|
||||
any third party any analysis of the results of operation of the Elasticsearch
|
||||
Software, including benchmarking results, without the prior written consent of
|
||||
Elasticsearch.
|
||||
1.2 Reservation of Rights; Restrictions. As between Elasticsearch and You,
|
||||
Elasticsearch owns all right title and interest in and to the Marvel Software
|
||||
and any derivative works thereof, and except as expressly set forth in Section
|
||||
1.1 above, no other license to the Marvel Software is granted to You by
|
||||
implication, estoppel or otherwise. You agree not to: (i) prepare derivative
|
||||
works from, modify, copy or use the Marvel Software in any manner except as
|
||||
expressly permitted in this Agreement or applicable law; (ii) transfer, sell,
|
||||
rent, lease, distribute, sublicense, loan or otherwise transfer the Marvel
|
||||
Software in whole or in part to any third party; (iii) use the Marvel Software
|
||||
for providing time-sharing services, any software-as-a-service offering
|
||||
(“SaaS”), service bureau services or as part of an application services
|
||||
provider or other service offering; (iv) alter or remove any proprietary
|
||||
notices in the Marvel Software; or (v) make available to any third party any
|
||||
analysis of the results of operation of the Marvel Software, including
|
||||
benchmarking results, without the prior written consent of Elasticsearch.
|
||||
|
||||
2. Term and Termination
|
||||
|
||||
2.1 Term. This Agreement shall commence on the Effective Date, and shall
|
||||
2.1 Term. This Agreement shall commence on the Effective Date, and shall
|
||||
continue in force for the license term set forth in the applicable Order Form,
|
||||
unless earlier terminated under Section 2.2 below.
|
||||
|
||||
2.2 Termination. Either party may, upon written notice to the other party,
|
||||
2.2 Termination. Either party may, upon written notice to the other party,
|
||||
terminate this Agreement for material breach by the other party automatically
|
||||
and without any other formality, if such party has failed to cure such material
|
||||
breach within thirty (30) days of receiving written notice of such material
|
||||
breach from the non-breaching party. Notwithstanding the foregoing, this
|
||||
Agreement shall automatically terminate in the event that You intentionally
|
||||
breach the scope of the license granted in Section 1.1 of this Agreement.
|
||||
and without any other formality, if such party has failed to cure such
|
||||
material breach within thirty (30) days of receiving written notice of such
|
||||
material breach from the non-breaching party. Notwithstanding the foregoing,
|
||||
this Agreement shall automatically terminate in the event that You
|
||||
intentionally breach the scope of the license granted in Section 1.1 of this
|
||||
Agreement.
|
||||
|
||||
2.3 Post Termination or Expiration. Upon termination or expiration of this
|
||||
Agreement, for any reason, You shall promptly cease the use of the Elasticsearch
|
||||
Software and Documentation and destroy (and certify to Elasticsearch in writing
|
||||
the fact of such destruction), or return to Elasticsearch, all copies of the
|
||||
Elasticsearch Software and Documentation then in Your possession or under Your
|
||||
2.3 Post Termination or Expiration. Upon termination or expiration of this
|
||||
Agreement, for any reason, You shall promptly cease the use of the Marvel
|
||||
Software and Documentation and destroy (and certify to Elasticsearch in
|
||||
writing the fact of such destruction), or return to Elasticsearch, all copies
|
||||
of the Marvel Software and Documentation then in Your possession or under Your
|
||||
control.
|
||||
|
||||
2.4 Survival.
|
||||
|
||||
Sections 2.3, 3, 4 and 5, as well as any payment obligations under this
|
||||
Agreement and/or the Order Form, shall survive any termination or expiration of
|
||||
this Agreement.
|
||||
2.4 Survival. Sections 2.3, 3, 4 and 5, as well as any payment obligations
|
||||
under this Agreement and/or the Order Form, shall survive any termination or
|
||||
expiration of this Agreement.
|
||||
|
||||
3. Disclaimer of Warranties
|
||||
|
||||
TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE ELASTICSEARCH SOFTWARE
|
||||
IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS
|
||||
TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE MARVEL SOFTWARE IS
|
||||
PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, AND ELASTICSEARCH AND ITS
|
||||
LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR STATUTORY REGARDING
|
||||
OR RELATING TO THE ELASTICSEARCH SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM
|
||||
EXTENT PERMITTED UNDER APPLICABLE LAW, ELASTICSEARCH AND ITS LICENSORS
|
||||
SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NON-INFRINGMENT WITH RESPECT TO THE ELASTICSEARCH
|
||||
SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE FOREGOING.
|
||||
FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE ELASTICSEARCH
|
||||
SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE ELASTICSEARCH SOFTWARE WILL
|
||||
BE UNINTERRUPTED.
|
||||
OR RELATING TO THE MARVEL SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT
|
||||
PERMITTED UNDER APPLICABLE LAW, ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY
|
||||
DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NON-INFRINGMENT WITH RESPECT TO THE MARVEL SOFTWARE AND
|
||||
DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE FOREGOING. FURTHER,
|
||||
ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT THE MARVEL SOFTWARE WILL
|
||||
BE ERROR FREE OR THAT THE USE OF THE MARVEL SOFTWARE WILL BE UNINTERRUPTED.
|
||||
|
||||
4. Limitation of Liability
|
||||
|
||||
4.1. Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR
|
||||
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR
|
||||
ITS LICENSORS BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF USE, BUSINESS
|
||||
INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
|
||||
INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN CONNECTION
|
||||
WITH OR ARISING OUT OF THE USE OR INABILITY TO USE THE ELASTICSEARCH SOFTWARE,
|
||||
OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A
|
||||
BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF THE
|
||||
RESPONSIBLE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE
|
||||
LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A
|
||||
BREACH THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU OF THE SCOPE OF
|
||||
THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY OTHER LIABILITY THAT CANNOT BE
|
||||
EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
|
||||
INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND IN
|
||||
CONNECTION WITH OR ARISING OUT OF THE USE OR INABILITY TO USE THE MARVEL
|
||||
SOFTWARE, OR THE PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER
|
||||
ALLEGED AS A BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE,
|
||||
EVEN IF THE RESPONSIBLE PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES. THE LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT
|
||||
APPLY TO A BREACH THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU OF
|
||||
THE SCOPE OF THE LICENSE GRANTED IN SECTION 1.1 OR TO ANY OTHER LIABILITY THAT
|
||||
CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
|
||||
|
||||
4.2. Damages Cap. IN NO EVENT SHALL ELASTICSEARCH’S OR ITS LICENSORS’ AGGREGATE,
|
||||
CUMULATIVE LIABILITY UNDER THIS AGREEMENT EXCEED THE AMOUNTS YOU WERE REQUIRED
|
||||
TO PAY ELASTICSEARCH IN RELATION TO THIS AGREEMENT FOR THE ELASTICSEARCH
|
||||
SOFTWARE GIVING RISE TO SUCH LIABILITY, IN THE TWELVE (12) MONTHS IMMEDIATELY
|
||||
PRIOR TO THE EVENT GIVING RISE TO LIABILITY.
|
||||
4.2 Damages Cap. IN NO EVENT SHALL ELASTICSEARCH’S OR ITS LICENSORS’
|
||||
AGGREGATE, CUMULATIVE LIABILITY UNDER THIS AGREEMENT EXCEED THE AMOUNTS YOU
|
||||
WERE REQUIRED TO PAY ELASTICSEARCH IN RELATION TO THIS AGREEMENT FOR THE
|
||||
MARVEL SOFTWARE GIVING RISE TO SUCH LIABILITY, IN THE TWELVE (12) MONTHS
|
||||
IMMEDIATELY PRIOR TO THE EVENT GIVING RISE TO LIABILITY.
|
||||
|
||||
4.3. YOU AGREE THAT THE FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS ARE A
|
||||
4.3 YOU AGREE THAT THE FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS ARE A
|
||||
REASONABLE ALLOCATION OF THE RISK BETWEEN THE PARTIES AND WILL APPLY TO THE
|
||||
MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS
|
||||
ESSENTIAL PURPOSE.
|
||||
|
||||
5. Miscellaneous
|
||||
5. Miscellaneous
|
||||
|
||||
This Agreement, including Attachment 1 hereto, which is hereby incorporated
|
||||
herein by this reference, completely and exclusively states the entire agreement
|
||||
of the parties regarding the subject matter herein, and it supersedes, and its
|
||||
terms govern, all prior proposals, agreements, or other communications between
|
||||
the parties, oral or written, regarding such subject matter. For the avoidance
|
||||
of doubt, the parties hereby expressly acknowledge and agree that if You issue
|
||||
any purchase order or similar document in connection with its purchase of a
|
||||
license to the Elasticsearch Software, You will do so only for Your internal,
|
||||
administrative purposes and not with the intent to provide any contractual
|
||||
terms. This Agreement may not be modified except by a subsequently dated,
|
||||
written amendment that expressly amends this Agreement and which is signed on
|
||||
behalf of Elasticsearch and You, by duly authorized representatives. If any
|
||||
provision(s) hereof is held unenforceable, this Agreement will continue without
|
||||
said provision and be interpreted to reflect the original intent of the parties.
|
||||
herein by this reference, completely and exclusively states the entire
|
||||
agreement of the parties regarding the subject matter herein, and it
|
||||
supersedes, and its terms govern, all prior proposals, agreements, or other
|
||||
communications between the parties, oral or written, regarding such subject
|
||||
matter. For the avoidance of doubt, the parties hereby expressly acknowledge
|
||||
and agree that if You issue any purchase order or similar document in
|
||||
connection with its purchase of a license to the Marvel Software, You will do
|
||||
so only for Your internal, administrative purposes and not with the intent to
|
||||
provide any contractual terms. This Agreement may not be modified except by a
|
||||
subsequently dated, written amendment that expressly amends this Agreement and
|
||||
which is signed on behalf of Elasticsearch and You, by duly authorized
|
||||
representatives. If any provision(s) hereof is held unenforceable, this
|
||||
Agreement will continue without said provision and be interpreted to reflect
|
||||
the original intent of the parties.
|
||||
|
||||
|
||||
|
||||
ATTACHMENT 1
|
||||
ADDITIONAL TERMS AND CONDITIONS
|
||||
ATTACHMENT 1
|
||||
|
||||
A The following additional terms and conditions apply to all Customers with
|
||||
ADDITIONAL TERMS AND CONDITIONS
|
||||
|
||||
A The following additional terms and conditions apply to all Customers with
|
||||
principal offices in North America:
|
||||
|
||||
Applicable Elasticsearch Entity. The entity providing the license is
|
||||
Elasticsearch, Inc., a Delaware Corporation.
|
||||
|
||||
Government Rights. The Elasticsearch Software product is "Commercial Computer
|
||||
Government Rights. The Marvel Software product is "Commercial Computer
|
||||
Software," as that term is defined in 48 C.F.R. 2.101, and as the term is used
|
||||
in 48 C.F.R. Part 12, and is a Commercial Item comprised of "commercial computer
|
||||
software" and "commercial computer software documentation". If acquired by or
|
||||
on behalf of a civilian agency, the U.S. Government acquires this commercial
|
||||
computer software and/or commercial computer software documentation subject to
|
||||
the terms of this Agreement, as specified in 48 C.F.R. 12.212 (Computer
|
||||
Software) and 12.211 (Technical Data) of the Federal Acquisition Regulation
|
||||
("FAR") and its successors. If acquired by or on behalf of any agency within
|
||||
the Department of Defense ("DOD"), the U.S. Government acquires this commercial
|
||||
computer software and/or commercial computer software documentation subject to
|
||||
the terms of the Elasticsearch Software License and Services Agreement as
|
||||
specified in 48 C.F.R. 227.7202-3 and 48 C.F.R. 227.7202-4 of the DOD FAR
|
||||
Supplement ("DFARS") and its successors, and consistent with 48 C.F.R. 227.7202.
|
||||
This U.S. Government Rights clause, consistent with 48 C.F.R. 12.212 and 48
|
||||
C.F.R. 227.7202 is in lieu of, and supersedes, any other FAR, DFARS, or other
|
||||
clause or provision that addresses Government rights in computer software,
|
||||
computer software documentation or technical data related to the Elasticsearch
|
||||
Software under this Agreement and in any Subcontract under which this commercial
|
||||
computer software and commercial computer software documentation is acquired or
|
||||
licensed.
|
||||
in 48 C.F.R. Part 12, and is a Commercial Item comprised of "commercial
|
||||
computer software" and "commercial computer software documentation". If
|
||||
acquired by or on behalf of a civilian agency, the U.S. Government acquires
|
||||
this commercial computer software and/or commercial computer software
|
||||
documentation subject to the terms of this Agreement, as specified in 48
|
||||
C.F.R. 12.212 (Computer Software) and 12.211 (Technical Data) of the Federal
|
||||
Acquisition Regulation ("FAR") and its successors. If acquired by or on
|
||||
behalf of any agency within the Department of Defense ("DOD"), the U.S.
|
||||
Government acquires this commercial computer software and/or commercial
|
||||
computer software documentation subject to the terms of the Elasticsearch
|
||||
Software License Agreement as specified in 48 C.F.R. 227.7202-3 and 48 C.F.R.
|
||||
227.7202-4 of the DOD FAR Supplement ("DFARS") and its successors, and
|
||||
consistent with 48 C.F.R. 227.7202. This U.S. Government Rights clause,
|
||||
consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202 is in lieu of, and
|
||||
supersedes, any other FAR, DFARS, or other clause or provision that addresses
|
||||
Government rights in computer software, computer software documentation or
|
||||
technical data related to the Marvel Software under this Agreement and in any
|
||||
Subcontract under which this commercial computer software and commercial
|
||||
computer software documentation is acquired or licensed.
|
||||
|
||||
Export Control. You acknowledge that the goods, software and technology acquired
|
||||
from Elasticsearch are subject to U.S. export control laws and regulations,
|
||||
including but not limited to the International Traffic In Arms Regulations
|
||||
(“ITAR”) (22 C.F.R. Parts 120-130 (2010)); the Export Administration Regulations
|
||||
("EAR") (15 C.F.R. Parts 730-774 (2010)); the U.S. antiboycott regulations in
|
||||
the EAR and U.S. Department of the Treasury regulations; the economic sanctions
|
||||
regulations and guidelines of the U.S. Department of the Treasury, Office of
|
||||
Foreign Assets Control, and the USA Patriot Act (Title III of Pub. L. 107-56,
|
||||
signed into law October 26, 2001), as amended. You are now and will remain in
|
||||
the future compliant with all such export control laws and regulations, and will
|
||||
not export, re-export, otherwise transfer any Elasticsearch goods, software or
|
||||
technology or disclose any Elasticsearch software or technology to any person
|
||||
contrary to such laws or regulations. You acknowledge that remote access to
|
||||
Elasticsearch Software may in certain circumstances be considered a re-export of
|
||||
Elasticsearch Software, and accordingly, may not be granted in contravention of
|
||||
U.S. export control laws and regulations. Governing Law. This Agreement will be
|
||||
governed by the laws of the State of California, without regard to its conflict
|
||||
of laws principles. This Agreement shall not be governed by the 1980 UN
|
||||
Convention on Contracts for the International Sale of Goods. All suits hereunder
|
||||
will be brought solely in Federal Court for the Northern District of California,
|
||||
or if that court lacks subject matter jurisdiction, in any California State
|
||||
Court located in Santa Clara County. The parties hereby irrevocably waive any
|
||||
and all claims and defenses either might otherwise have in any such action or
|
||||
proceeding in any of such courts based upon any alleged lack of personal
|
||||
jurisdiction, improper venue, forum non conveniens or any similar claim or
|
||||
defense.
|
||||
Export Control. You acknowledge that the goods, software and technology
|
||||
acquired from Elasticsearch are subject to U.S. export control laws and
|
||||
regulations, including but not limited to the International Traffic In Arms
|
||||
Regulations (“ITAR”) (22 C.F.R. Parts 120-130 (2010)); the Export
|
||||
Administration Regulations ("EAR") (15 C.F.R. Parts 730-774 (2010)); the U.S.
|
||||
antiboycott regulations in the EAR and U.S. Department of the Treasury
|
||||
regulations; the economic sanctions regulations and guidelines of the U.S.
|
||||
Department of the Treasury, Office of Foreign Assets Control, and the USA
|
||||
Patriot Act (Title III of Pub. L. 107-56, signed into law October 26, 2001),
|
||||
as amended. You are now and will remain in the future compliant with all such
|
||||
export control laws and regulations, and will not export, re-export, otherwise
|
||||
transfer any Elasticsearch goods, software or technology or disclose any
|
||||
Elasticsearch software or technology to any person contrary to such laws or
|
||||
regulations. You acknowledge that remote access to the Marvel Software may in
|
||||
certain circumstances be considered a re-export of Marvel Software, and
|
||||
accordingly, may not be granted in contravention of U.S. export control laws
|
||||
and regulations.
|
||||
|
||||
Governing Law. This Agreement will be governed by the laws of the State of
|
||||
California, without regard to its conflict of laws principles. This Agreement
|
||||
shall not be governed by the 1980 UN Convention on Contracts for the
|
||||
International Sale of Goods. All suits hereunder will be brought solely in
|
||||
Federal Court for the Northern District of California, or if that court lacks
|
||||
subject matter jurisdiction, in any California State Court located in Santa
|
||||
Clara County. The parties hereby irrevocably waive any and all claims and
|
||||
defenses either might otherwise have in any such action or proceeding in any
|
||||
of such courts based upon any alleged lack of personal jurisdiction, improper
|
||||
venue, forum non conveniens or any similar claim or defense.
|
||||
|
||||
B The following additional terms and conditions apply to all Customers with
|
||||
B The following additional terms and conditions apply to all Customers with
|
||||
principal offices outside of North America:
|
||||
|
||||
(1) Applicable Elasticsearch Entity. The entity providing the license in
|
||||
|
@ -210,47 +207,49 @@ principal offices outside of North America:
|
|||
|
||||
(2) Choice of Law. This Agreement shall be governed by and construed in
|
||||
(accordance with the laws of the State of New York, without reference to or
|
||||
(application of choice of law rules or principles. Notwithstanding any choice of
|
||||
(law provision or otherwise, the Uniform Computer Information Transactions Act
|
||||
(UCITA) and the United Nations Convention on the International Sale of Goods
|
||||
(shall not apply.
|
||||
(application of choice of law rules or principles. Notwithstanding any choice
|
||||
(of law provision or otherwise, the Uniform Computer Information Transactions
|
||||
(Act UCITA) and the United Nations Convention on the International Sale of
|
||||
(Goods shall not apply.
|
||||
|
||||
(3) Arbitration. Any dispute, claim or controversy arising out of or relating
|
||||
(to this Agreement or the existence, breach, termination, enforcement,
|
||||
(interpretation or validity thereof, including the determination of the scope or
|
||||
(applicability of this agreement to arbitrate, each, a “Dispute”) shall be
|
||||
(referred to and finally resolved by arbitration under the rules and at the
|
||||
(location identified below. The arbitral panel shall consist of three 3)
|
||||
(arbitrators, selected as follows: each party shall appoint one 1) arbitrator;
|
||||
(and those two 2) arbitrators shall discuss and select a chairman. If the two
|
||||
(party-appointed arbitrators are unable to agree on the chairman, the chairman
|
||||
(shall be selected in accordance with the applicable rules of the arbitration
|
||||
(body. Each arbitrator shall be independent of each of the parties. The
|
||||
(arbitrators shall have the authority to grant specific performance and to
|
||||
(allocate between the parties the costs of arbitration including service fees,
|
||||
(arbitrator fees and all other fees related to the arbitration) in such
|
||||
(equitable manner as the arbitrators may determine. The prevailing party in any
|
||||
(arbitration shall be entitled to receive reimbursement of its reasonable
|
||||
(expenses incurred in connection therewith. Judgment upon the award so rendered
|
||||
(may be entered in a court having jurisdiction or application may be made to
|
||||
(such court for judicial acceptance of any award and an order of enforcement, as
|
||||
(the case may be. Notwithstanding the forgoing, Elasticsearch shall have the
|
||||
(right to institute an action in a court of proper jurisdiction for preliminary
|
||||
(injunctive relief pending a final decision by the arbitrator, provided that a
|
||||
(permanent injunction and damages shall only be awarded by the arbitrator. The
|
||||
(language to be used in the arbitral proceedings shall be English.
|
||||
(3) Arbitration. Any dispute, claim or controversy arising out of or
|
||||
(relating to this Agreement or the existence, breach, termination,
|
||||
(enforcement, interpretation or validity thereof, including the determination
|
||||
(of the scope or applicability of this agreement to arbitrate, each, a
|
||||
(“Dispute”) shall be referred to and finally resolved by arbitration under the
|
||||
(rules and at the location identified below. The arbitral panel shall consist
|
||||
(of three 3) arbitrators, selected as follows: each party shall appoint one 1)
|
||||
(arbitrator; and those two 2) arbitrators shall discuss and select a chairman.
|
||||
(If the two party-appointed arbitrators are unable to agree on the chairman,
|
||||
(the chairman shall be selected in accordance with the applicable rules of the
|
||||
(arbitration body. Each arbitrator shall be independent of each of the
|
||||
(parties. The arbitrators shall have the authority to grant specific
|
||||
(performance and to allocate between the parties the costs of arbitration
|
||||
(including service fees, arbitrator fees and all other fees related to the
|
||||
(arbitration) in such equitable manner as the arbitrators may determine. The
|
||||
(prevailing party in any arbitration shall be entitled to receive
|
||||
(reimbursement of its reasonable expenses incurred in connection therewith.
|
||||
(Judgment upon the award so rendered may be entered in a court having
|
||||
(jurisdiction or application may be made to such court for judicial acceptance
|
||||
(of any award and an order of enforcement, as the case may be.
|
||||
(Notwithstanding the forgoing, Elasticsearch shall have the right to institute
|
||||
(an action in a court of proper jurisdiction for preliminary injunctive relief
|
||||
(pending a final decision by the arbitrator, provided that a permanent
|
||||
(injunction and damages shall only be awarded by the arbitrator. The language
|
||||
(to be used in the arbitral proceedings shall be English.
|
||||
|
||||
(3.a) In addition, the following terms only apply to Customers with principal
|
||||
(offices within Europe, the Middle East or Africa EMEA):
|
||||
(3.a) In addition, the following terms only apply to Customers with
|
||||
(principal offices within Europe, the Middle East or Africa EMEA):
|
||||
|
||||
Arbitration Rules and Location. Any Dispute shall be referred to and finally
|
||||
Arbitration Rules and Location. Any Dispute shall be referred to and finally
|
||||
resolved by arbitration under the London Court of International Arbitration
|
||||
(“LCIA”) Rules (which Rules are deemed to be incorporated by reference into this
|
||||
clause) on the basis that the governing law is the law of the State of New York,
|
||||
USA. The seat, or legal place, of arbitration shall be London, England.
|
||||
(“LCIA”) Rules (which Rules are deemed to be incorporated by reference into
|
||||
this clause) on the basis that the governing law is the law of the State of
|
||||
New York, USA. The seat, or legal place, of arbitration shall be London,
|
||||
England.
|
||||
|
||||
(3.b) In addition, the following terms only apply to Customers with principal
|
||||
(offices within Asia Pacific, Australia & New Zealand:
|
||||
(3.b) In addition, the following terms only apply to Customers with
|
||||
(principal offices within Asia Pacific, Australia & New Zealand:
|
||||
|
||||
Arbitration Rules and Location. Any Dispute shall be referred to and finally
|
||||
resolved by arbitration under the Rules of Conciliation and Arbitration of the
|
||||
|
@ -260,74 +259,71 @@ deemed to be incorporated by reference into this clause) on the basis that the
|
|||
governing law is the law of the State of New York, USA. The seat, or legal
|
||||
place, of arbitration shall be Singapore.
|
||||
|
||||
(3.c) In addition, the following terms only apply to Customers with principal
|
||||
(offices within the Americas excluding North America):
|
||||
(3.c) In addition, the following terms only apply to Customers with
|
||||
(principal offices within the Americas excluding North America):
|
||||
|
||||
Arbitration Rules and Location. Any Dispute shall be referred to and finally
|
||||
resolved by arbitration under International Dispute Resolution Procedures of the
|
||||
American Arbitration Association (“AAA”) in force on the date when the notice of
|
||||
arbitration is submitted in accordance with such Procedures (which Procedures
|
||||
are deemed to be incorporated by reference into this clause) on the basis that
|
||||
the governing law is the law of the State of New York, USA. The seat, or legal
|
||||
place, of arbitration shall be New York, New York, USA.
|
||||
Arbitration Rules and Location. Any Dispute shall be referred to and finally
|
||||
resolved by arbitration under International Dispute Resolution Procedures of
|
||||
the American Arbitration Association (“AAA”) in force on the date when the
|
||||
notice of arbitration is submitted in accordance with such Procedures (which
|
||||
Procedures are deemed to be incorporated by reference into this clause) on the
|
||||
basis that the governing law is the law of the State of New York, USA. The
|
||||
seat, or legal place, of arbitration shall be New York, New York, USA.
|
||||
|
||||
(4) In addition, for Customers with principal offices within the UK, the
|
||||
(following new sentence is added to the end of Section 4.1:
|
||||
|
||||
Nothing in this Agreement shall have effect so as to limit or exclude a party’s
|
||||
liability for death or personal injury caused by negligence or for fraud
|
||||
including fraudulent misrepresentation and this Section 4.1 shall take effect
|
||||
subject to this provision.
|
||||
Nothing in this Agreement shall have effect so as to limit or exclude a
|
||||
party’s liability for death or personal injury caused by negligence or for
|
||||
fraud including fraudulent misrepresentation and this Section 4.1 shall take
|
||||
effect subject to this provision.
|
||||
|
||||
(5) In addition, for Customers with principal offices within France, Sections
|
||||
1.2, 3 and 4.1 of the Agreement are deleted and replaced with the following new
|
||||
Sections 1.2, 3 and 4.1: 1.2 Reservation of Rights; Restrictions. Elasticsearch
|
||||
owns all right title and interest in and to the Elasticsearch Software and any
|
||||
derivative works thereof, and except as expressly set forth in Section 1.1
|
||||
above, no other license to the Elasticsearch Software is granted to You by
|
||||
implication, or otherwise. You agree not to prepare derivative works from,
|
||||
modify, copy or use the Elasticsearch Software in any manner except as expressly
|
||||
permitted in this Agreement; provided that You may copy the Elasticsearch
|
||||
Software for archival purposes, only where such software is provided on a non-
|
||||
durable medium; and You may decompile the Elasticsearch Software, where
|
||||
necessary for interoperability purposes and where necessary for the correction
|
||||
of errors making the software unfit for its intended purpose, if such right is
|
||||
not reserved by Elasticsearch as editor of the Elasticsearch Software. Pursuant
|
||||
to article L122-6-1 of the French intellectual property code, Elasticsearch
|
||||
reserves the right to correct any bugs as necessary for the Elasticsearch
|
||||
Software to serve its intended purpose. You agree not to: (i) transfer, sell,
|
||||
rent, lease, distribute, sublicense, loan or otherwise transfer the
|
||||
Elasticsearch Software in whole or in part to any third party; (ii) use the
|
||||
Elasticsearch Software for providing time-sharing services, any software-
|
||||
(5) In addition, for Customers with principal offices within France, Sections
|
||||
(1.2, 3 and 4.1 of the Agreement are deleted and replaced with the following
|
||||
(new Sections 1.2, 3 and 4.1:
|
||||
|
||||
1.2 Reservation of Rights; Restrictions. Elasticsearch owns all right title
|
||||
and interest in and to the Marvel Software and any derivative works thereof,
|
||||
and except as expressly set forth in Section 1.1 above, no other license to
|
||||
the Marvel Software is granted to You by implication, or otherwise. You agree
|
||||
not to prepare derivative works from, modify, copy or use the Marvel Software
|
||||
in any manner except as expressly permitted in this Agreement; provided that
|
||||
You may copy the Marvel Software for archival purposes, only where such
|
||||
software is provided on a non-durable medium; and You may decompile the Marvel
|
||||
Software, where necessary for interoperability purposes and where necessary
|
||||
for the correction of errors making the software unfit for its intended
|
||||
purpose, if such right is not reserved by Elasticsearch as editor of the
|
||||
Marvel Software. Pursuant to article L122-6-1 of the French intellectual
|
||||
property code, Elasticsearch reserves the right to correct any bugs as
|
||||
necessary for the Marvel Software to serve its intended purpose. You agree not
|
||||
to: (i) transfer, sell, rent, lease, distribute, sublicense, loan or otherwise
|
||||
transfer the Marvel Software in whole or in part to any third party; (ii) use
|
||||
the Marvel Software for providing time-sharing services, any software-
|
||||
as-a-service offering (“SaaS”), service bureau services or as part of an
|
||||
application services provider or other service offering; (iii) alter or remove
|
||||
any proprietary notices in the Elasticsearch Software; or (iv) make available to
|
||||
any third party any analysis of the results of operation of the Elasticsearch
|
||||
Software, including benchmarking results, without the prior written consent of
|
||||
any proprietary notices in the Marvel Software; or (iv) make available to any
|
||||
third party any analysis of the results of operation of the Marvel Software,
|
||||
including benchmarking results, without the prior written consent of
|
||||
Elasticsearch.
|
||||
|
||||
3. Disclaimer of Warranties
|
||||
TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, THE ELASTICSEARCH
|
||||
SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY
|
||||
KIND, AND ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER
|
||||
EXPRESSED, IMPLIED OR STATUTORY REGARDING OR RELATING TO THE ELASTICSEARCH
|
||||
SOFTWARE OR DOCUMENTATION. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE
|
||||
LAW, ELASTICSEARCH AND ITS LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED
|
||||
WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE WITH RESPECT TO THE
|
||||
ELASTICSEARCH SOFTWARE AND DOCUMENTATION, AND WITH RESPECT TO THE USE OF THE
|
||||
FOREGOING. FURTHER, ELASTICSEARCH DOES NOT WARRANT RESULTS OF USE OR THAT
|
||||
THE ELASTICSEARCH SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE
|
||||
ELASTICSEARCH SOFTWARE WILL BE UNINTERRUPTED.
|
||||
3. Disclaimer of Warranties TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE
|
||||
LAW, THE MARVEL SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, AND
|
||||
ELASTICSEARCH AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED
|
||||
OR STATUTORY REGARDING OR RELATING TO THE MARVEL SOFTWARE OR DOCUMENTATION.
|
||||
TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW, ELASTICSEARCH AND ITS
|
||||
LICENSORS SPECIFICALLY DISCLAIM ALL IMPLIED WARRANTIES OF FITNESS FOR A
|
||||
PARTICULAR PURPOSE WITH RESPECT TO THE MARVEL SOFTWARE AND DOCUMENTATION, AND
|
||||
WITH RESPECT TO THE USE OF THE FOREGOING. FURTHER, ELASTICSEARCH DOES NOT
|
||||
WARRANT RESULTS OF USE OR THAT THE MARVEL SOFTWARE WILL BE ERROR FREE OR THAT
|
||||
THE USE OF THE MARVEL SOFTWARE WILL BE UNINTERRUPTED.
|
||||
|
||||
4.1 Disclaimer of Certain Damages.
|
||||
IN NO EVENT SHALL YOU OR ELASTICSEARCH OR ITS LICENSORS BE LIABLE FOR ANY
|
||||
LOSS OF PROFITS, LOSS OF USE, BUSINESS
|
||||
INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
|
||||
INDIRECT OR UNFORESEEABLE DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE ELASTICSEARCH SOFTWARE, OR THE
|
||||
PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A
|
||||
BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE. THE
|
||||
LIMITATIONS OF LIABILITY SET FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A
|
||||
BREACH, THROUGH GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU, OF THE
|
||||
SCOPE OF THE LICENSE GRANTED IN SECTION 1.1, OR IN CASE OF DEATH OR PERSONAL
|
||||
INJURY.
|
||||
4.1 Disclaimer of Certain Damages. IN NO EVENT SHALL YOU OR ELASTICSEARCH OR
|
||||
ITS LICENSORS BE LIABLE FOR ANY LOSS OF PROFITS, LOSS OF USE, BUSINESS
|
||||
INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY
|
||||
INDIRECT OR UNFORESEEABLE DAMAGES OF ANY KIND IN CONNECTION WITH OR ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE MARVEL SOFTWARE, OR THE PERFORMANCE OF
|
||||
OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A BREACH OF CONTRACT
|
||||
OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE. THE LIMITATIONS OF LIABILITY SET
|
||||
FORTH IN THIS SECTION 4.1 SHALL NOT APPLY TO A BREACH, THROUGH GROSS
|
||||
NEGLIGENCE OR INTENTIONAL MISCONDUCT BY YOU, OF THE SCOPE OF THE LICENSE
|
||||
GRANTED IN SECTION 1.1, OR IN CASE OF DEATH OR PERSONAL INJURY.
|
||||
|
|
|
@ -6,16 +6,12 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>marvel</artifactId>
|
||||
<version>1.0.0.RC2-SNAPSHOT</version>
|
||||
<version>1.0.3-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<description>Elasticsearch Marvel</description>
|
||||
<inceptionYear>2013</inceptionYear>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
|
||||
</licenses>
|
||||
|
||||
<parent>
|
||||
|
|
|
@ -195,7 +195,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
|
|||
}
|
||||
|
||||
private void exportIndicesStats() {
|
||||
logger.debug("local node is master, exporting indices stats");
|
||||
logger.trace("local node is master, exporting indices stats");
|
||||
IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats().all().get();
|
||||
for (Exporter e : exporters) {
|
||||
try {
|
||||
|
@ -207,7 +207,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
|
|||
}
|
||||
|
||||
private void exportClusterStats() {
|
||||
logger.debug("local node is master, exporting cluster stats");
|
||||
logger.trace("local node is master, exporting cluster stats");
|
||||
ClusterStatsResponse stats = client.admin().cluster().prepareClusterStats().get();
|
||||
for (Exporter e : exporters) {
|
||||
try {
|
||||
|
@ -219,7 +219,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
|
|||
}
|
||||
|
||||
private void exportEvents() {
|
||||
logger.debug("exporting events");
|
||||
logger.trace("exporting events");
|
||||
ArrayList<Event> eventList = new ArrayList<Event>(pendingEventsQueue.size());
|
||||
pendingEventsQueue.drainTo(eventList);
|
||||
Event[] events = new Event[eventList.size()];
|
||||
|
@ -235,7 +235,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
|
|||
}
|
||||
|
||||
private void exportShardStats() {
|
||||
logger.debug("Collecting shard stats");
|
||||
logger.trace("Collecting shard stats");
|
||||
String[] indices = clusterService.state().metaData().concreteIndices(indicesToExport);
|
||||
|
||||
List<ShardStats> shardStats = newArrayList();
|
||||
|
@ -254,7 +254,7 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
|
|||
}
|
||||
ShardStats[] shardStatsArray = shardStats.toArray(new ShardStats[shardStats.size()]);
|
||||
|
||||
logger.debug("Exporting shards stats");
|
||||
logger.trace("Exporting shards stats");
|
||||
for (Exporter e : exporters) {
|
||||
try {
|
||||
e.exportShardStats(shardStatsArray);
|
||||
|
@ -265,10 +265,10 @@ public class AgentService extends AbstractLifecycleComponent<AgentService> {
|
|||
}
|
||||
|
||||
private void exportNodeStats() {
|
||||
logger.debug("Collecting node stats");
|
||||
logger.trace("Collecting node stats");
|
||||
NodeStats nodeStats = nodeService.stats();
|
||||
|
||||
logger.debug("Exporting node stats");
|
||||
logger.trace("Exporting node stats");
|
||||
for (Exporter e : exporters) {
|
||||
try {
|
||||
e.exportNodeStats(nodeStats);
|
||||
|
|
|
@ -37,13 +37,19 @@ public class Plugin extends AbstractPlugin {
|
|||
final ESLogger logger = Loggers.getLogger(getClass());
|
||||
|
||||
// copied here because we can't depend on it available
|
||||
public static final int V_0_90_8_ID = /*00*/900899;
|
||||
public static final int V_0_90_9_ID = /*00*/900999;
|
||||
public static final int V_1_0_0_Beta1_ID = 1000001;
|
||||
public static final int V_1_0_0_Beta2_ID = 1000002;
|
||||
|
||||
|
||||
public final boolean enabled;
|
||||
|
||||
public Plugin() {
|
||||
if (Version.CURRENT.id < V_0_90_8_ID) {
|
||||
logger.warn("Elasticsearch version [{}] is too old. Marvel is disabled (requires version 0.90.8 or higher)", Version.CURRENT);
|
||||
if (Version.CURRENT.id == V_1_0_0_Beta1_ID || Version.CURRENT.id == V_1_0_0_Beta2_ID) {
|
||||
logger.warn("Elasticsearch version [{}] is incompatible with Marvel. Please upgrade to version 1.0.0.RC1 or higher.", Version.CURRENT);
|
||||
enabled = false;
|
||||
} else if (Version.CURRENT.id < V_0_90_9_ID) {
|
||||
logger.warn("Elasticsearch version [{}] is too old. Marvel is disabled (requires version 0.90.9 or higher).", Version.CURRENT);
|
||||
enabled = false;
|
||||
} else {
|
||||
enabled = true;
|
||||
|
|
|
@ -44,6 +44,8 @@ public class Utils {
|
|||
builder.field("host", inetAddress.getHostName());
|
||||
builder.field("ip_port", inetAddress.getHostAddress() + ":" + inetSocketAddress.getPort());
|
||||
}
|
||||
} else if (node.address().uniqueAddressTypeId() == 2) { // local transport
|
||||
builder.field("ip_port", "_" + node.address()); // will end up being "_local[ID]"
|
||||
}
|
||||
|
||||
if (!node.attributes().isEmpty()) {
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
|
|||
import org.elasticsearch.action.admin.indices.stats.ShardStats;
|
||||
import org.elasticsearch.cluster.ClusterName;
|
||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||
import org.elasticsearch.common.Base64;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
|
@ -119,7 +120,7 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
|
|||
clusterStatsRenderer = new ClusterStatsRenderer();
|
||||
eventsRenderer = new EventsRenderer();
|
||||
|
||||
logger.debug("Initialized with targets: {}, index prefix [{}], index time format [{}]", hosts, indexPrefix, indexTimeFormat);
|
||||
logger.debug("initialized with targets: {}, index prefix [{}], index time format [{}]", hosts, indexPrefix, indexTimeFormat);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -145,7 +146,6 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
|
|||
Map<String, IndexStats> perIndexStats = indicesStats.getIndices();
|
||||
indexStatsRenderer.reset(perIndexStats.values().toArray(new IndexStats[perIndexStats.size()]));
|
||||
indicesStatsRenderer.reset(indicesStats.getTotal(), indicesStats.getPrimaries());
|
||||
logger.debug("exporting index_stats + indices_stats");
|
||||
HttpURLConnection conn = openExportingConnection();
|
||||
if (conn == null) {
|
||||
return;
|
||||
|
@ -289,23 +289,27 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
|
|||
}
|
||||
|
||||
|
||||
private HttpURLConnection openConnection(String method, String uri) {
|
||||
return openConnection(method, uri, null);
|
||||
private HttpURLConnection openConnection(String method, String path) {
|
||||
return openConnection(method, path, null);
|
||||
}
|
||||
|
||||
private HttpURLConnection openConnection(String method, String uri, String contentType) {
|
||||
private HttpURLConnection openConnection(String method, String path, String contentType) {
|
||||
int hostIndex = 0;
|
||||
try {
|
||||
for (; hostIndex < hosts.length; hostIndex++) {
|
||||
String host = hosts[hostIndex];
|
||||
try {
|
||||
URL templateUrl = new URL("http://" + host + "/" + uri);
|
||||
HttpURLConnection conn = (HttpURLConnection) templateUrl.openConnection();
|
||||
URL url = new URL("http://" + host + "/" + path);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod(method);
|
||||
conn.setConnectTimeout(timeout);
|
||||
if (contentType != null) {
|
||||
conn.setRequestProperty("Content-Type", XContentType.SMILE.restContentType());
|
||||
}
|
||||
if (url.getUserInfo() != null) {
|
||||
String basicAuth = "Basic " + Base64.encodeBytes(url.getUserInfo().getBytes("ISO-8859-1"));
|
||||
conn.setRequestProperty("Authorization", basicAuth);
|
||||
}
|
||||
conn.setUseCaches(false);
|
||||
if (method.equalsIgnoreCase("POST") || method.equalsIgnoreCase("PUT")) {
|
||||
conn.setDoOutput(true);
|
||||
|
@ -427,7 +431,7 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
|
|||
|
||||
HttpURLConnection conn = openConnection("HEAD", path);
|
||||
if (conn == null) {
|
||||
logger.error("Could not connect to any configured elasticsearch instances: [{}]", hosts);
|
||||
logger.error("could not connect to any configured elasticsearch instances: [{}]", hosts);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -727,7 +731,6 @@ public class ESExporter extends AbstractLifecycleComponent<ESExporter> implement
|
|||
if (closed) {
|
||||
return;
|
||||
}
|
||||
logger.trace("pinging target es");
|
||||
HttpURLConnection conn = openConnection("GET", "");
|
||||
if (conn != null) {
|
||||
conn.getInputStream().close(); // close and release to connection pool.
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"mapper.dynamic": true,
|
||||
"marvel.index_format": 1
|
||||
},
|
||||
"mappings": {
|
||||
|
@ -41,57 +42,156 @@
|
|||
},
|
||||
"node_stats": {
|
||||
"properties": {
|
||||
"indices.percolate.size_in_bytes": {
|
||||
"type": "long"
|
||||
"fs": {
|
||||
"properties": {
|
||||
"total": {
|
||||
"properties": {
|
||||
"disk_io_op": {
|
||||
"type": "long"
|
||||
},
|
||||
"disk_reads": {
|
||||
"type": "long"
|
||||
},
|
||||
"disk_writes": {
|
||||
"type": "long"
|
||||
},
|
||||
"disk_io_size_in_bytes": {
|
||||
"type": "long"
|
||||
},
|
||||
"disk_read_size_in_bytes": {
|
||||
"type": "long"
|
||||
},
|
||||
"disk_write_size_in_bytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jvm.buffer_pools.direct.used_in_bytes": {
|
||||
"type": "long"
|
||||
"indices": {
|
||||
"properties": {
|
||||
"percolate": {
|
||||
"properties": {
|
||||
"size_in_bytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jvm.buffer_pools.mapped.used_in_bytes": {
|
||||
"type": "long"
|
||||
"jvm": {
|
||||
"properties": {
|
||||
"buffer_pools": {
|
||||
"properties": {
|
||||
"direct": {
|
||||
"properties": {
|
||||
"used_in_bytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapped": {
|
||||
"properties": {
|
||||
"used_in_bytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gc": {
|
||||
"properties": {
|
||||
"collectors": {
|
||||
"properties": {
|
||||
"young": {
|
||||
"properties": {
|
||||
"collection_count": {
|
||||
"type": "long"
|
||||
},
|
||||
"collection_time_in_millis": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"old": {
|
||||
"properties": {
|
||||
"collection_count": {
|
||||
"type": "long"
|
||||
},
|
||||
"collection_time_in_millis": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jvm.gc.collectors.young.collection_count": {
|
||||
"type": "long"
|
||||
"indices": {
|
||||
"properties": {
|
||||
"percolate": {
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "long"
|
||||
},
|
||||
"time_in_millis": {
|
||||
"type": "long"
|
||||
},
|
||||
"queries": {
|
||||
"type": "long"
|
||||
},
|
||||
"memory_size_in_bytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jvm.gc.collectors.young.collection_time_in_millis": {
|
||||
"type": "long"
|
||||
},
|
||||
"jvm.gc.collectors.old.collection_count": {
|
||||
"type": "long"
|
||||
},
|
||||
"jvm.gc.collectors.old.collection_time_in_millis": {
|
||||
"type": "long"
|
||||
},
|
||||
"indices.percolate.total": {
|
||||
"type": "long"
|
||||
},
|
||||
"indices.percolate.time_in_millis": {
|
||||
"type": "long"
|
||||
},
|
||||
"indices.percolate.queries": {
|
||||
"type": "long"
|
||||
},
|
||||
"indices.percolate.memory_size_in_bytes": {
|
||||
"type": "long"
|
||||
"os": {
|
||||
"properties": {
|
||||
"load_average": {
|
||||
"properties": {
|
||||
"1m": {
|
||||
"type": "float"
|
||||
},
|
||||
"5m": {
|
||||
"type": "float"
|
||||
},
|
||||
"15m": {
|
||||
"type": "float"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"index_stats": {
|
||||
"properties": {
|
||||
"total.percolate.memory_size_in_bytes": {
|
||||
"type": "long"
|
||||
},
|
||||
"total.percolate.time_in_millis": {
|
||||
"type": "long"
|
||||
},
|
||||
"total.percolate.queries": {
|
||||
"type": "long"
|
||||
},
|
||||
"total.percolate.total": {
|
||||
"type": "long"
|
||||
"total": {
|
||||
"properties": {
|
||||
|
||||
"percolate": {
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "long"
|
||||
},
|
||||
"time_in_millis": {
|
||||
"type": "long"
|
||||
},
|
||||
"queries": {
|
||||
"type": "long"
|
||||
},
|
||||
"memory_size_in_bytes": {
|
||||
"type": "long"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
11
docs/changes.asciidoc
Normal file
|
@ -0,0 +1,11 @@
|
|||
[[change_list]]
|
||||
== Change list
|
||||
|
||||
=== 1.0.2
|
||||
- Kibana uses `window.location.protocol` (http or https) to make ES calls.
|
||||
- Added support for basic authentication when sending data from agent. See <<configuration>>.
|
||||
- Reduced DEBUG logging verbosity.
|
||||
|
||||
=== 1.0.1
|
||||
- fixed an issue with usage statistics report.
|
||||
- improve logging message when running on old Elasticsearch versions.
|
112
docs/configuration.asciidoc
Normal file
|
@ -0,0 +1,112 @@
|
|||
[[configuration]]
|
||||
== Configuration Options
|
||||
|
||||
[[stats-export]]
|
||||
=== Statistics exporting
|
||||
|
||||
`marvel.agent.exporter.es.hosts`::
|
||||
|
||||
A list of hosts in `hostname:port` format to which statistics and events will
|
||||
be sent. Data will be sent to the first host, but will failover to the next
|
||||
host(s) if the first is not reachable. Defaults to `["localhost:9200"]`.
|
||||
+
|
||||
added[1.0.2] - HTTP Basic authentication credentials can be specified as part of the host name,
|
||||
i.e., ["user:pwd@host:9200"]
|
||||
|
||||
|
||||
`marvel.agent.enabled`::
|
||||
|
||||
Set this to `false` to disable all exporting of data. Set this on the
|
||||
monitoring cluster, which is used to receive and analyze data from production,
|
||||
to avoid collecting data from the monitoring cluster itself.
|
||||
|
||||
|
||||
`marvel.agent.indices`::
|
||||
|
||||
Controls which indices to export data for. Uses simple `test1,test2,test3`
|
||||
notation (or `_all` for all indices). It also supports wildcards, for
|
||||
example: `test*`, and the ability to "add" (`+`) and "remove" (`-`), for
|
||||
example: `+test*,-test3`. Defaults to `*`
|
||||
|
||||
`marvel.agent.exporter.es.index.timeformat`::
|
||||
|
||||
Controls the time component in the index name to which data is exported.
|
||||
Defaults to `"YYYY.MM.dd"`, which produces index names like:
|
||||
`.marvel-2014.01.28`. Supports date formats as explained
|
||||
http://joda-time.sourceforge.net/api-release/org/joda/time/format/DateTimeFormat.html[here].
|
||||
|
||||
[[marvel-indices]]
|
||||
=== Marvel indices
|
||||
|
||||
Marvel stores it's data using time based indices. By default, Marvel generates
|
||||
an index per day, with one shard and one replica. We expect this to be a good
|
||||
default which will fit most use cases. You may need to change it for very big
|
||||
installations.
|
||||
|
||||
[[config-marvel-indices]]
|
||||
==== Configuring Marvel's indices
|
||||
|
||||
Marvel uses an {ref}/indices-templates.html[index template] to preconfigure newly created indices. You can retrieve it with:
|
||||
|
||||
[source,sh]
|
||||
----------------------------------
|
||||
GET /_template/marvel
|
||||
----------------------------------
|
||||
|
||||
To change the default settings, you may add your own template to override it. Make sure your template uses
|
||||
the `.marvel-*` matching pattern and has an order of 1 or higher. For example, this template will increase the
|
||||
number of shards to 5:
|
||||
|
||||
[source,json]
|
||||
----------------------------------
|
||||
PUT /_template/custom_marvel
|
||||
{
|
||||
"template": ".marvel*",
|
||||
"order": 1,
|
||||
"settings": {
|
||||
"number_of_shards": 5
|
||||
}
|
||||
}
|
||||
----------------------------------
|
||||
|
||||
IMPORTANT: We recommend only changing the `settings` section. Other sections are
|
||||
important for the correct operation of the dashboards.
|
||||
|
||||
For reference, here is the `settings` section of the default template:
|
||||
|
||||
[source,json]
|
||||
----------------------------------
|
||||
{
|
||||
"template": ".marvel*",
|
||||
"settings": {
|
||||
"number_of_shards": 1,
|
||||
"number_of_replicas": 1,
|
||||
"analysis": {
|
||||
"analyzer": {
|
||||
"default": {
|
||||
"type": "standard",
|
||||
"stopwords": "_none_"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mapper.dynamic": true,
|
||||
"marvel.index_format": 1
|
||||
}
|
||||
.....
|
||||
}
|
||||
----------------------------------
|
||||
|
||||
[[relevant-settings]]
|
||||
=== Other relevant Elasticsearch settings
|
||||
|
||||
Marvel relies on Elasticsearch's capability to automatically create new indices
|
||||
when indexing documents. Some people like to disable this feature to better
|
||||
protect against mistakes in production clusters. To do so but still allow the
|
||||
creation of Marvel indices you need to set the following:
|
||||
|
||||
[source,yaml]
|
||||
----------------------
|
||||
action.auto_create_index: .marvel-*
|
||||
----------------------
|
||||
|
||||
More information is available {ref}/docs-index_.html#index-creation[here]
|
52
docs/dashboards.asciidoc
Normal file
|
@ -0,0 +1,52 @@
|
|||
== Marvel's Dashboards
|
||||
|
||||
|
||||
=== Overview Dashboard
|
||||
|
||||
The _Overview_ dashboard is Marvel's main page. The dashboard displays the
|
||||
essentials metrics you need to know that your cluster is healthy. It also
|
||||
provides an overview of your nodes and indices, displayed in two clean tables
|
||||
with the relevant key metrics. If some value needs your attention, they will
|
||||
be highlighted in yellow or red. The nodes and indices tables also serve as an
|
||||
entry point to the more detailed _Node Statistics_ and _Index Statistics_
|
||||
dashboards.
|
||||
|
||||
image:images/overview_thumb.png["Overview Dashboard",link="images/overview.png"]
|
||||
|
||||
=== Node & Index Statistics
|
||||
|
||||
The _Node Statistics_ dashboard displays metric charts from the perspective of
|
||||
one or more nodes. Metrics include hardware level metrics (like load and CPU
|
||||
usage), process and JVM metrics (memory usage, GC), and node level
|
||||
Elasticsearch metrics such as field data usage, search requests rate and
|
||||
thread pool rejection.
|
||||
|
||||
image:images/node_stats_thumb.png["Node Statistics Dashboard",link="images/node_stats.png"]
|
||||
|
||||
The _Index Statistics_ dashboard is very similar to the _Node Statistics_
|
||||
dashboard, but it shows you all the metrics from the perspective of one or
|
||||
more indices. The metrics are per index, with data aggregated from all of the
|
||||
nodes in the cluster. For example, the ''store size'' chart shows the total
|
||||
size of the index data across the whole cluster.
|
||||
|
||||
image:images/index_stats_thumb.png["Index Statistics Dashboard",link="images/index_stats.png"]
|
||||
|
||||
=== Cluster Pulse
|
||||
|
||||
The Cluster Pulse Dashboard allows you to see any event of interest in the cluster. Typical
|
||||
events include nodes joining or leaving, master election, index creation, shard (re)allocation
|
||||
and more.
|
||||
|
||||
image:images/cluster_pulse_thumb.png["Index Statistics Dashboard",link="images/cluster_pulse.png"]
|
||||
|
||||
=== Sense
|
||||
|
||||
_Sense_ is a lightweight developer console. The console is handy when you want
|
||||
to make an extra API call to check something or perhaps tweak a setting. The
|
||||
developer console understands both JSON and the Elasticsearch API, offering
|
||||
suggestions and autocompletion. It is very useful for prototyping queries,
|
||||
researching your data or any other administrative work with the API.
|
||||
|
||||
image::images/sense_thumb.png["Developer Console",link="sense.png"]
|
||||
|
||||
|
BIN
docs/images/cluster_pulse.png
Normal file
After Width: | Height: | Size: 157 KiB |
BIN
docs/images/cluster_pulse_thumb.png
Normal file
After Width: | Height: | Size: 101 KiB |
BIN
docs/images/index_stats.png
Normal file
After Width: | Height: | Size: 98 KiB |
BIN
docs/images/index_stats_thumb.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
docs/images/node_stats.png
Normal file
After Width: | Height: | Size: 121 KiB |
BIN
docs/images/node_stats_thumb.png
Normal file
After Width: | Height: | Size: 82 KiB |
BIN
docs/images/overview.png
Normal file
After Width: | Height: | Size: 172 KiB |
BIN
docs/images/overview_thumb.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
docs/images/sense.png
Normal file
After Width: | Height: | Size: 147 KiB |
BIN
docs/images/sense_thumb.png
Normal file
After Width: | Height: | Size: 91 KiB |
26
docs/index.asciidoc
Normal file
|
@ -0,0 +1,26 @@
|
|||
= Marvel Documentation
|
||||
|
||||
:ref: http://www.elasticsearch.org/guide/en/elasticsearch/reference/current
|
||||
|
||||
Marvel is a management and monitoring product for Elasticsearch. Marvel
|
||||
aggregates cluster wide statistics and events and offers a single interface to
|
||||
view and analyze them. Marvel is free for development use but does require a
|
||||
license to run in production. For more details, please see the
|
||||
http://www.elasticsearch.com/marvel[Marvel product page].
|
||||
|
||||
|
||||
include::dashboards.asciidoc[]
|
||||
|
||||
== System Requirements
|
||||
|
||||
[horizontal]
|
||||
Elasticsearch:: 0.90.9 or above
|
||||
Browser:: The latest version of Chrome, Firefox or Safari is
|
||||
recommended. Internet Explorer 9 and above is also supported.
|
||||
|
||||
|
||||
include::install.asciidoc[]
|
||||
|
||||
include::configuration.asciidoc[]
|
||||
|
||||
include::changes.asciidoc[]
|
127
docs/install.asciidoc
Normal file
|
@ -0,0 +1,127 @@
|
|||
== Installation
|
||||
|
||||
Marvel is installed as an Elasticsearch plugin. The plugin must be installed
|
||||
on *every node in the cluster*. By default, the plugin will store data in the
|
||||
same Elasticsearch cluster that it is monitoring.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If you are monitoring a production cluster we recommend that you send this
|
||||
data to another cluster that will serve as a _monitoring cluster_.
|
||||
|
||||
This will help to prevent a problem in one cluster from impacting the other
|
||||
cluster. For example, when your production cluster is having trouble you would
|
||||
like to make sure you can view the Marvel data easily.
|
||||
====
|
||||
|
||||
=== Simple install
|
||||
|
||||
This is simplest way to install and use Marvel. Marvel data will be stored on
|
||||
the same cluster as the one being monitored and you will be able to access the
|
||||
Marvel UI from every node. Follow these steps on every node in the cluster:
|
||||
|
||||
1. From the Elasticsearch home directory, run:
|
||||
+
|
||||
[source,sh]
|
||||
----------------
|
||||
bin/plugin -i elasticsearch/marvel/latest
|
||||
----------------
|
||||
|
||||
2. Restart your Elasticsearch node
|
||||
|
||||
Once the plugin is installed on all nodes, you can access the Marvel UI by
|
||||
viewing http://any-server-in-cluster:9200/_plugin/marvel/ with any modern
|
||||
browser. It may take a minute for the initial data to appear.
|
||||
|
||||
A couple of points to consider:
|
||||
|
||||
* Since this requires to restart the nodes of your cluster, you might want
|
||||
to temporarily {ref}/modules-cluster.html[disable allocations].
|
||||
|
||||
* If you run your cluster using a non-standard port, you need to change the
|
||||
`marvel.agent.exporter.es.hosts` settings. See <<configuration>>.
|
||||
|
||||
* If your server doesn't have direct internet access see <<manual_download>>
|
||||
for an alternative way to get the Marvel binaries.
|
||||
|
||||
|
||||
=== Installing a secondary Monitoring Cluster
|
||||
|
||||
First, prepare the _monitoring cluster_ -- the cluster you will use to store
|
||||
and analyze Marvel's data. For this example, we will assume two hosts,
|
||||
`es-mon-1` and `es-mon-2`, are in the monitoring cluster.
|
||||
|
||||
==== The monitoring cluster
|
||||
|
||||
For each node in the monitoring cluster:
|
||||
|
||||
|
||||
1. Prevent the nodes in the monitoring cluster from generating their own stats,
|
||||
by adding the following line to `config/elasticsearch.yml`:
|
||||
+
|
||||
[source,yaml]
|
||||
------------------------
|
||||
marvel.agent.enabled: false
|
||||
------------------------
|
||||
|
||||
2. Install the plugin. From the Elasticsearch home directory run
|
||||
+
|
||||
[source,sh]
|
||||
----------------
|
||||
bin/plugin -i elasticsearch/marvel/latest
|
||||
----------------
|
||||
|
||||
3. Restart Elasticsearch
|
||||
|
||||
|
||||
==== The production cluster
|
||||
|
||||
1. Tell each node where to send its stats by adding this line to
|
||||
`config/elasticsearch.yml:
|
||||
+
|
||||
[source,yaml]
|
||||
------------------------
|
||||
marvel.agent.exporter.es.hosts: ["es-mon-1:9200","es-mon-2:9200"]
|
||||
------------------------
|
||||
+
|
||||
This line adds two hosts of your monitoring cluster to receive the data, a
|
||||
primary and a backup.
|
||||
|
||||
2. Install the plugin. From the Elasticsearch home directory run
|
||||
+
|
||||
[source,sh]
|
||||
------------------------
|
||||
bin/plugin -i elasticsearch/marvel/latest
|
||||
------------------------
|
||||
|
||||
3. Restart Elasticsearch
|
||||
|
||||
|
||||
Once the plugin is installed on all nodes, you can access the Marvel UI by
|
||||
opening this link with any modern browser.
|
||||
|
||||
http://any-server-in-monitoring-cluster:9200/_plugin/marvel/
|
||||
|
||||
It may take a minute or two for data to appear.
|
||||
|
||||
TIP: You may want to temporarily {ref}/modules-cluster.html[disable shard
|
||||
allocation] before you restart your nodes to avoid unnecessary shard
|
||||
reallocation during the install process.
|
||||
|
||||
|
||||
[[manual_download]]
|
||||
==== Manual download
|
||||
|
||||
Elasticsearch's `bin/plugin` script requires direct internet access for downloading
|
||||
and installing Marvel. If your server doesn't have internet access, you can download
|
||||
the Marvel binaries from the following link: https://download.elasticsearch.org/elasticsearch/marvel/marvel-latest.zip
|
||||
|
||||
Once you have transferred the zip file to your server, you can use the `bin/plugin`
|
||||
script to install it:
|
||||
|
||||
[source,sh]
|
||||
----------------
|
||||
bin/plugin -i marvel -u file://PATH_TO_MARVEL_ZIP_FILE
|
||||
----------------
|
||||
|
||||
After the script has finish, continue according to the standard installation procedure.
|
|
@ -21,7 +21,7 @@ define(['settings'],
|
|||
* kibana installed on. You probably want to set it to the FQDN of your
|
||||
* elasticsearch host
|
||||
*/
|
||||
elasticsearch: "http://"+window.location.hostname+@@port,
|
||||
elasticsearch: window.location.protocol+"//"+window.location.hostname+@@port,
|
||||
|
||||
/** @scratch /configuration/config.js/5
|
||||
* ==== default_route
|
||||
|
|
|
@ -56,7 +56,8 @@
|
|||
}
|
||||
},
|
||||
"rows": [
|
||||
{ "title": "Cluster Summary",
|
||||
{
|
||||
"title": "Cluster Summary",
|
||||
"height": "75px",
|
||||
"editable": true,
|
||||
"panels": [
|
||||
|
@ -173,6 +174,8 @@
|
|||
"trimFactor": 300,
|
||||
"normTimes": true,
|
||||
"spyable": true,
|
||||
"localTime": true,
|
||||
"timeField": "@timestamp",
|
||||
"title": "Cluster events"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -66,13 +66,8 @@ if (!_.isUndefined(ARGS.queries)) {
|
|||
}];
|
||||
}));
|
||||
} else {
|
||||
// No queries passed? Initialize a single query to match everything
|
||||
queries = {
|
||||
0: {
|
||||
query: '*',
|
||||
id: 0
|
||||
}
|
||||
};
|
||||
// No queries passed
|
||||
queries = {};
|
||||
}
|
||||
|
||||
var show = (ARGS.show || "").split(',');
|
||||
|
@ -129,7 +124,7 @@ panel_defaults_by_type["histogram"] = {
|
|||
legend_counts: false,
|
||||
options: false,
|
||||
legend: false,
|
||||
resolution: 20,
|
||||
resolution: 22,
|
||||
y_format: "short"
|
||||
};
|
||||
|
||||
|
@ -395,11 +390,11 @@ dashboard.rows = _.map(rows, function (r) {
|
|||
return r;
|
||||
});
|
||||
|
||||
if (!showedSomething && dashboard.rows.length > 0) {
|
||||
// open the first row if nothing was opened and we have queries (o.w. it's meaningless)
|
||||
if (!showedSomething && dashboard.rows.length > 0 && _.size(queries) > 0) {
|
||||
dashboard.rows[0].collapse = false;
|
||||
}
|
||||
|
||||
|
||||
dashboard.pulldowns = [
|
||||
{
|
||||
"type": "query",
|
||||
|
|
|
@ -68,13 +68,8 @@ if (!_.isUndefined(ARGS.queries)) {
|
|||
}));
|
||||
marker_query = "(" + _.pluck(queries, "query").join(") OR (") + ")";
|
||||
} else {
|
||||
// No queries passed? Initialize a single query to match everything
|
||||
queries = {
|
||||
0: {
|
||||
query: '*',
|
||||
id: 0
|
||||
}
|
||||
};
|
||||
// No queries passed
|
||||
queries = {};
|
||||
}
|
||||
|
||||
var annotate_config;
|
||||
|
@ -148,7 +143,7 @@ panel_defaults_by_type["histogram"] = {
|
|||
zoomlinks: false,
|
||||
options: false,
|
||||
legend: false,
|
||||
resolution: 20,
|
||||
resolution: 22,
|
||||
annotate: annotate_config,
|
||||
y_format: "short"
|
||||
};
|
||||
|
@ -690,7 +685,8 @@ dashboard.rows = _.map(rows, function (r) {
|
|||
return r;
|
||||
});
|
||||
|
||||
if (!showedSomething && dashboard.rows.length > 0) {
|
||||
// open the first row if nothing was opened and we have queries (o.w. it's meaningless)
|
||||
if (!showedSomething && dashboard.rows.length > 0 && _.size(queries) > 0) {
|
||||
dashboard.rows[0].collapse = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
"pointradius": 5,
|
||||
"annotate": {
|
||||
"enable": true,
|
||||
"query": "_type:cluster_event",
|
||||
"query": "_type:cluster_event OR _type:node_event",
|
||||
"size": 50,
|
||||
"field": "message",
|
||||
"sort": [
|
||||
|
@ -218,7 +218,7 @@
|
|||
"pointradius": 5,
|
||||
"annotate": {
|
||||
"enable": true,
|
||||
"query": "_type:cluster_event",
|
||||
"query": "_type:cluster_event OR _type:node_event",
|
||||
"size": 50,
|
||||
"field": "message",
|
||||
"sort": [
|
||||
|
@ -291,7 +291,7 @@
|
|||
"pointradius": 5,
|
||||
"annotate": {
|
||||
"enable": true,
|
||||
"query": "_type:cluster_event",
|
||||
"query": "_type:cluster_event OR _type:node_event",
|
||||
"size": 50,
|
||||
"field": "message",
|
||||
"sort": [
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
<div class="section">
|
||||
<div class="editor-option">
|
||||
<label class="small">Send statistics<tip>Help make Elasticsearch better by sharing anonymous usage data.</tip></label>
|
||||
<input type="checkbox" ng-model="cookies.marvelOptIn" ng-true-value="IN" ng-false-value="OUT" />
|
||||
<input type="checkbox" ng-model="marvelOpts.report"/>
|
||||
</div>
|
||||
<div ng-show="kbnVersion == '@REV@'" class="editor-option">
|
||||
<label class="small">Clear Marvel Cookies <tip>Only shown in unbuilt versions, clear optin and version cookies</tip></label>
|
||||
<button class="btn btn-danger" ng-click="clearMarvelCookies()">Clear</button>
|
||||
<label class="small">Clear marvelOpts <tip>Only shown in unbuilt versions, clear stored optin and version data</tip></label>
|
||||
<button class="btn btn-danger" ng-click="clearMarvelStorage()">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,17 +12,18 @@
|
|||
define([
|
||||
'angular',
|
||||
'app',
|
||||
'kbn',
|
||||
'lodash',
|
||||
'kbn'
|
||||
'factories/store'
|
||||
],
|
||||
function (angular, app, _, kbn) {
|
||||
function (angular, app, kbn, _) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('kibana.panels.marvel.cluster', []);
|
||||
app.useModule(module);
|
||||
|
||||
module.controller('marvel.cluster', function($scope, $modal, $q, $cookies, $http,
|
||||
querySrv, dashboard, filterSrv, kbnVersion) {
|
||||
module.controller('marvel.cluster', function($scope, $modal, $q, $http,
|
||||
querySrv, dashboard, filterSrv, kbnVersion, storeFactory) {
|
||||
$scope.panelMeta = {
|
||||
modals : [],
|
||||
editorTabs : [],
|
||||
|
@ -37,15 +38,19 @@ function (angular, app, _, kbn) {
|
|||
_.defaults($scope.panel,_d);
|
||||
|
||||
var reportInterval = 86400000;
|
||||
//var reportInterval = 30000;
|
||||
|
||||
// setup the optIn and version values
|
||||
var marvelOpts = storeFactory($scope, 'marvelOpts', {
|
||||
report: void 0,
|
||||
version: void 0,
|
||||
lastReport: void 0
|
||||
});
|
||||
|
||||
$scope.init = function () {
|
||||
// So we can access the cookies object from the view
|
||||
$scope.cookies = $cookies;
|
||||
$scope.kbnVersion = kbnVersion;
|
||||
|
||||
// If the user hasn't opted in or out, ask them to.
|
||||
if(_.isUndefined($cookies.marvelOptIn) || $cookies.marvelVersion !== kbnVersion) {
|
||||
if(marvelOpts.version == null || marvelOpts.version !== kbnVersion) {
|
||||
$scope.optInModal();
|
||||
}
|
||||
|
||||
|
@ -129,15 +134,15 @@ function (angular, app, _, kbn) {
|
|||
$scope.inspector = angular.toJson(JSON.parse(request.toString()),true);
|
||||
};
|
||||
|
||||
$scope.setOptIn = function(c) {
|
||||
$cookies.marvelVersion = kbnVersion;
|
||||
$cookies.marvelOptIn = c;
|
||||
$scope.setOptIn = function(val) {
|
||||
marvelOpts.version = kbnVersion;
|
||||
marvelOpts.report = val;
|
||||
};
|
||||
|
||||
$scope.clearMarvelCookies = function() {
|
||||
delete $cookies.marvelOptIn;
|
||||
delete $cookies.marvelVersion;
|
||||
delete $cookies.marvelLastReport;
|
||||
$scope.clearMarvelStorage = function() {
|
||||
marvelOpts.report = void 0;
|
||||
marvelOpts.version = void 0;
|
||||
marvelOpts.lastReport = void 0;
|
||||
};
|
||||
|
||||
$scope.optInModal = function() {
|
||||
|
@ -157,10 +162,10 @@ function (angular, app, _, kbn) {
|
|||
|
||||
// Checks if we should send a report
|
||||
var checkReport = function() {
|
||||
if($cookies.marvelOptIn === 'IN') {
|
||||
if(_.isUndefined($cookies.marvelLastReport)) {
|
||||
if(marvelOpts.report) {
|
||||
if(marvelOpts.lastReport == null) {
|
||||
return true;
|
||||
} else if (new Date().getTime() - parseInt($cookies.marvelLastReport,10) > reportInterval) {
|
||||
} else if (new Date().getTime() - parseInt(marvelOpts.lastReport,10) > reportInterval) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -182,7 +187,7 @@ function (angular, app, _, kbn) {
|
|||
$scope.config.stats_report_url,
|
||||
data
|
||||
).success(function() {
|
||||
$cookies.marvelLastReport = thisReport;
|
||||
marvelOpts.lastReport = thisReport;
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -26,8 +26,7 @@
|
|||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<!-- these set strings since cookies don't deal with boolean -->
|
||||
<button ng-click="setOptIn('IN');dismiss();" class="btn btn-success">Count me in!</button>
|
||||
<span ng-click="setOptIn('OUT');dismiss();" class="pointer small">No Thanks</span>
|
||||
<button ng-click="setOptIn(true);dismiss();" class="btn btn-success">Count me in!</button>
|
||||
<span ng-click="setOptIn(false);dismiss();" class="pointer small">No Thanks</span>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
<div ng-controller='marvel.navigation' ng-init="init()">
|
||||
<style>
|
||||
.marvel-navigation-dropdown {
|
||||
margin: 0px !important;
|
||||
}
|
||||
</style>
|
||||
<!-- This is a complete hack. The form actually exists in the modal, but due to transclusion
|
||||
$scope.input isn't available on the controller unless the form element is in this file -->
|
||||
<form name="input" style="margin:3px 0 0 0">
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
define([
|
||||
'angular',
|
||||
'app',
|
||||
'lodash',
|
||||
'jquery'
|
||||
'jquery',
|
||||
'lodash'
|
||||
],
|
||||
function (angular, app, _, $) {
|
||||
function (angular, app, $, _) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('kibana.panels.marvel.navigation', []);
|
||||
|
@ -75,7 +75,10 @@ function (angular, app, _, $) {
|
|||
|
||||
$scope.links = _.filter(response.data.links, function (link) {
|
||||
a.attr("href", link.url);
|
||||
return a[0].href !== window.location.href;
|
||||
var current = window.location.href;
|
||||
// remove parameters
|
||||
current = current.replace(/\?.*$/,'');
|
||||
return a[0].href !== current;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -32,10 +32,11 @@
|
|||
height: 10px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="pull-left marvel-header marvel-table" ng-show="rows.length > 0">
|
||||
<input type="text" class="input-medium" placeholder="Filter {{panel.mode}}..." ng-model="panel.rowFilter"> <span class="count">{{(rows|filter:panel.rowFilter|limitTo:rowLimit).length}} of {{rows.length}} {{panel.mode}}</span> / {{(rows|filter:{'selected':true}).length}} selected / Last 10m </span>
|
||||
<div class="pull-left marvel-header marvel-table" ng-show="rows.length > 0 || panel.rowFilter">
|
||||
<input type="text" class="input-medium" placeholder="Filter {{panel.mode}}..." ng-model="panel.rowFilter" ng-change="onRowFilterChange()"> <span class="count">{{rows.length}} of {{_.size(data)}} {{panel.mode}}</span> / {{selectedData().length}} selected / Last 10m </span>
|
||||
<br>
|
||||
<span class="small muted pull-right" ng-show="!viewSelect"> <i class="icon-warning"></i> Compact view. Filter {{panel.mode}} to 5 or less, or set the page refresh rate to 2m or greater for more options.</span>
|
||||
</div>
|
||||
|
@ -50,34 +51,34 @@
|
|||
<th class="pointer" ng-click="set_sort('__name__')">
|
||||
{{panel.mode}}
|
||||
<i ng-show="'__name__' == panel.sort[0]" class="pointer link" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
|
||||
<a ng-disabled="!hasSelected(rows)" id="detail_view_link" ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info pull-right" bs-tooltip="detailViewTip()" data-placement="right">Dashboard</a>
|
||||
<a ng-disabled="!hasSelected()" id="detail_view_link" ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info pull-right" bs-tooltip="detailViewTip()" data-placement="right">Dashboard</a>
|
||||
</th>
|
||||
|
||||
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels['_global_'][metric.field])" class="pointer" ng-click="set_sort(metric.field)">
|
||||
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[metric.field])" class="pointer" ng-click="set_sort(metric.field)">
|
||||
{{metric.name}}
|
||||
<i ng-show='metric.field == panel.sort[0]' class="pointer link" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
|
||||
</th>
|
||||
</thead>
|
||||
<tr ng-repeat="row in rows | filter:panel.rowFilter | orderBy:get_sort_value:panel.sort[1]=='desc'|limitTo:rowLimit">
|
||||
<tr ng-repeat="row in rows" >
|
||||
<td>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<div>
|
||||
<label class="checkbox" ng-class="{'text-warning':!row.alive}">
|
||||
<input type="checkbox" ng-model="row.selected" ng-checked="row.selected">
|
||||
<span class="pointer" ng-click="rowClick(row)">{{row.display_name}}</span>
|
||||
<div class="marvel-persistent-name pointer" ng-hide="row.id == row.display_name" ng-click="rowClick(row)">{{row.id}} <i bs-tooltip="'No report has been received for more than '+staleSeconds+' seconds'" ng-show="!isCurrent(data[row.id+'_@timestamp'].max,staleSeconds*1000)" class="icon-exclamation-sign"></i></div>
|
||||
<span class="pointer" ng-click="rowClick(row)">{{row.display_name}} <i bs-tooltip="'No report has been received for more than '+staleSeconds+' seconds'" ng-show="!row.alive" class="icon-exclamation-sign"></i></span>
|
||||
<div class="marvel-persistent-name pointer" ng-hide="row.id == row.display_name" ng-click="rowClick(row)">{{row.id}}</div>
|
||||
</label>
|
||||
</div>
|
||||
</td>
|
||||
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[row.id][metric.field])">
|
||||
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(row[metric.field].alert_level)">
|
||||
<div class="marvel-mean pointer" ng-click="rowClick(row,metric)">
|
||||
<span bo-text="(_.isNull(data[row.id+'_'+metric.field].mean)?0:data[row.id+'_'+metric.field].mean) | metric_format:metric"></span>
|
||||
<span bo-text="(_.isNull(row[metric.field].mean)?'n/a':row[metric.field].mean) | metric_format:metric"></span>
|
||||
<br>
|
||||
|
||||
<div class="marvel-stats-sparkline" panel='panel' field="metric.field" series="data[row.id+'_'+metric.field+'_history'].series"></div>
|
||||
<div class="marvel-stats-sparkline" panel='panel' field="metric.field" series="row[metric.field].series"></div>
|
||||
</div>
|
||||
<div class="marvel-extended pointer" ng-click="rowClick(row,metric)">
|
||||
<span>min: <span bo-text="(_.isNull(data[row.id+'_'+metric.field].min)?0:data[row.id+'_'+metric.field].min) | metric_format:metric"></span></span><br>
|
||||
<span>max: <span bo-text="(_.isNull(data[row.id+'_'+metric.field].max)?0:data[row.id+'_'+metric.field].max) | metric_format:metric"></span></span>
|
||||
<span>min: <span bo-text="(_.isNull(row[metric.field].min)?'n/a':row[metric.field].min) | metric_format:metric"></span></span><br>
|
||||
<span>max: <span bo-text="(_.isNull(row[metric.field].max)?'n/a':row[metric.field].max) | metric_format:metric"></span></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -89,15 +90,15 @@
|
|||
<th class="pointer" ng-click="set_sort('__name__')">
|
||||
{{panel.mode}}
|
||||
<i ng-show="'__name__' == panel.sort[0]" class="pointer link" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
|
||||
<a ng-disabled="!hasSelected(rows)" id="detail_view_link" ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info pull-right" bs-tooltip="detailViewTip()" data-placement="right">Dashboard</a>
|
||||
<a ng-disabled="!hasSelected()" id="detail_view_link" ng-href="{{detailViewLink()}}" class="btn btn-mini btn-info pull-right" bs-tooltip="detailViewTip()" data-placement="right">Dashboard</a>
|
||||
</th>
|
||||
|
||||
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels['_global_'][metric.field])" class="pointer" ng-click="set_sort(metric.field)">
|
||||
<th ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[metric.field])" class="pointer" ng-click="set_sort(metric.field)">
|
||||
{{metric.name}}
|
||||
<i ng-show='metric.field == panel.sort[0]' class="pointer link" ng-class="{'icon-chevron-up': panel.sort[1] == 'asc','icon-chevron-down': panel.sort[1] == 'desc'}"></i>
|
||||
</th>
|
||||
</thead>
|
||||
<tr ng-repeat="row in rows | filter:panel.rowFilter | orderBy:get_sort_value:panel.sort[1]=='desc'|limitTo:rowLimit">
|
||||
<tr ng-repeat="row in rows">
|
||||
<td>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
|
@ -106,11 +107,11 @@
|
|||
<span class="pointer" ng-click="rowClick(row)">{{ row.id }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(warnLevels[row.id][metric.field])">
|
||||
<td ng-repeat="metric in panel.metrics" ng-class="alertClass(row[metric.field].alert_level)">
|
||||
<div class="pointer" ng-click="rowClick(row,metric)">
|
||||
<span bo-text="(_.isNull(data[row.id+'_'+metric.field].mean)?0:data[row.id+'_'+metric.field].mean) | metric_format:metric"></span>
|
||||
<span bo-text="(_.isNull(row[metric.field].mean)?'n/a':row[metric.field].mean) | metric_format:metric"></span>
|
||||
|
||||
<div ng-if="sparkLines" class="marvel-stats-sparkline pointer" ng-click="rowClick(row,metric)" panel='panel' field="metric.field" series="data[row.id+'_'+metric.field+'_history'].series"></div>
|
||||
<div ng-if="sparkLines" class="marvel-stats-sparkline pointer" ng-click="rowClick(row,metric)" panel='panel' field="metric.field" series="row[metric.field].series"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -18,14 +18,15 @@ define([
|
|||
log_debug = function (msg) {
|
||||
console.log(msg);
|
||||
};
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
log_debug = function () {
|
||||
};
|
||||
}
|
||||
|
||||
function y_format_metric_value(value, metric) {
|
||||
// If this isn't a number, change nothing
|
||||
if (_.isNaN(value) || !_.isFinite(value)) {
|
||||
if (_.isNaN(value) || !_.isFinite(value) || !_.isNumber(value)) {
|
||||
return value;
|
||||
}
|
||||
if (metric.y_format === 'bytes') {
|
||||
|
@ -53,7 +54,14 @@ define([
|
|||
var _d = {
|
||||
compact: false,
|
||||
mode: 'nodes',
|
||||
sort: ['__name__', 'asc']
|
||||
sort: null,
|
||||
// if you have more rows than this number, full view will be disabled
|
||||
full_view_row_limit_on_high_refresh: 5,
|
||||
// if you have more nodes/indices than this number, refresh rate will be capped at 2,
|
||||
data_limit_for_high_refresh: 50,
|
||||
// disable display names if more than this number of nodes/indices.
|
||||
data_limit_for_display_names: 50
|
||||
//
|
||||
};
|
||||
_.defaults($scope.panel, _d);
|
||||
|
||||
|
@ -64,7 +72,7 @@ define([
|
|||
add: undefined
|
||||
};
|
||||
|
||||
$scope.staleSeconds = 60;
|
||||
$scope.staleSeconds = 20;
|
||||
|
||||
$scope.modeInfo = {
|
||||
nodes: {
|
||||
|
@ -92,8 +100,8 @@ define([
|
|||
{
|
||||
name: 'JVM Mem (%)',
|
||||
field: 'jvm.mem.heap_used_percent',
|
||||
warning: 95,
|
||||
error: 98
|
||||
warning: 90,
|
||||
error: 95
|
||||
},
|
||||
{
|
||||
name: 'Disk Free Space',
|
||||
|
@ -150,7 +158,7 @@ define([
|
|||
y_format: "bytes"
|
||||
},
|
||||
{
|
||||
name: 'Field data',
|
||||
name: 'Field Data',
|
||||
field: 'total.fielddata.memory_size_in_bytes',
|
||||
y_format: "bytes"
|
||||
},
|
||||
|
@ -193,8 +201,8 @@ define([
|
|||
return metricDefaults(m);
|
||||
});
|
||||
|
||||
$scope.$watch('panel.mode', function (m) {
|
||||
if (_.isUndefined(m)) {
|
||||
$scope.$watch('panel.mode', function (m, prev) {
|
||||
if (m === prev || _.isUndefined(m)) {
|
||||
return;
|
||||
}
|
||||
$scope.panel.display_field = $scope.modeInfo[m].defaults.display_field;
|
||||
|
@ -204,32 +212,31 @@ define([
|
|||
});
|
||||
});
|
||||
|
||||
$scope.$watch('(rows|filter:panel.rowFilter).length', function (l) {
|
||||
//Compute view based on number of rows
|
||||
rowsVsRefresh(l);
|
||||
});
|
||||
|
||||
$scope.$watch('dashboard.current.refresh', function () {
|
||||
var l = $filter('filter')($scope.rows, $scope.panel.rowFilter).length;
|
||||
rowsVsRefresh(l);
|
||||
$scope.updateUIFeaturesBasedOnData();
|
||||
});
|
||||
|
||||
|
||||
var rowsVsRefresh = function (l) {
|
||||
if (l > 5 && kbn.interval_to_seconds(dashboard.current.refresh || '1y') < 120) {
|
||||
$scope.updateUIFeaturesBasedOnData = function () {
|
||||
var l = $scope.rows.length;
|
||||
if (l > $scope.panel.full_view_row_limit_on_high_refresh
|
||||
&& kbn.interval_to_seconds(dashboard.current.refresh || '1y') < 120) {
|
||||
$scope.panel.compact = true;
|
||||
$scope.sparkLines = true;
|
||||
$scope.viewSelect = false;
|
||||
if(l > 50 && kbn.interval_to_seconds(dashboard.current.refresh || '1y') < 120) {
|
||||
dashboard.set_interval('2m');
|
||||
alertSrv.set('Refresh rate',
|
||||
'Due to the large size of your cluster, the refresh rate has been adjusted to 2m',
|
||||
'info',30000);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$scope.viewSelect = true;
|
||||
$scope.sparkLines = true;
|
||||
}
|
||||
if (_.size($scope.data) > $scope.panel.data_limit_for_high_refresh
|
||||
&& kbn.interval_to_seconds(dashboard.current.refresh || '1y') < 120) {
|
||||
dashboard.set_interval('2m');
|
||||
alertSrv.set('Refresh rate',
|
||||
'Due to the large size of your cluster, the refresh rate has been adjusted to 2m',
|
||||
'info', 30000);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.init = function () {
|
||||
|
@ -241,20 +248,28 @@ define([
|
|||
|
||||
$scope.warnLevels = {};
|
||||
$scope.rows = [];
|
||||
$scope.data = {};
|
||||
$scope.$on('refresh', function () {
|
||||
$scope.get_rows();
|
||||
$scope.get_data();
|
||||
});
|
||||
$scope.get_rows();
|
||||
$scope.get_data();
|
||||
};
|
||||
|
||||
$scope.get_mode_filter = function () {
|
||||
return $scope.ejs.TermFilter("_type", $scope.panel.mode === "nodes" ? "node_stats" : "index_stats");
|
||||
};
|
||||
|
||||
$scope.get_summary_key = function (id, m) {
|
||||
return id + "_" + m.field;
|
||||
};
|
||||
|
||||
$scope.get_history_key = function (id, m) {
|
||||
return id + "_" + m.field + "_history";
|
||||
};
|
||||
|
||||
/*
|
||||
marks the start of data retrieval. returns true if retrieval should continue
|
||||
or false if not. May schedule future retrival if needed.
|
||||
or false if not. May schedule future retrieval if needed.
|
||||
*/
|
||||
|
||||
$scope._register_data_start = function () {
|
||||
|
@ -281,7 +296,7 @@ define([
|
|||
// somehow this was not picked up... retry
|
||||
console.log("Retrying call from " + now);
|
||||
$scope._pending_data_retrieval = null;
|
||||
$scope.get_rows();
|
||||
$scope.get_data();
|
||||
}
|
||||
}, 20000);
|
||||
}
|
||||
|
@ -301,16 +316,16 @@ define([
|
|||
}
|
||||
log_debug("firing pending retrieval " + $scope._pending_data_retrieval);
|
||||
$scope._pending_data_retrieval = null;
|
||||
$scope.get_rows();
|
||||
$scope.get_data();
|
||||
}, 5000); // leave 5 second of some breathing air
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
$scope._ongoing_data_retrieval = null;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.get_rows = function () {
|
||||
$scope.get_data = function () {
|
||||
if (dashboard.indices.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -322,8 +337,7 @@ define([
|
|||
var
|
||||
request,
|
||||
filter,
|
||||
results,
|
||||
facet;
|
||||
results;
|
||||
|
||||
filter = filterSrv.getBoolFilter(filterSrv.ids);
|
||||
|
||||
|
@ -335,54 +349,122 @@ define([
|
|||
filter.must($scope.get_mode_filter()).must($scope.ejs.RangeFilter('@timestamp').from(to + "-10m/m").to(to + "/m"));
|
||||
|
||||
request = $scope.ejs.Request().indices(dashboard.indices).size(0).searchType("count");
|
||||
facet = $scope.ejs.TermsFacet('terms')
|
||||
.field($scope.panel.persistent_field)
|
||||
.size(9999999)
|
||||
request.query($scope.ejs.FilteredQuery($scope.ejs.MatchAllQuery(), filter));
|
||||
|
||||
|
||||
// timestamp facet to give us the proper time ranges for each node
|
||||
request.facet($scope.ejs.TermStatsFacet("timestamp")
|
||||
.keyField($scope.panel.persistent_field).valueField("@timestamp")
|
||||
.order('term')
|
||||
.facetFilter(filter);
|
||||
.size(2000));
|
||||
|
||||
if (!$scope.panel.show_hidden) {
|
||||
facet.regex("[^.].*");
|
||||
}
|
||||
|
||||
request.facet(facet);
|
||||
_.each($scope.panel.metrics, function (m) {
|
||||
request.facet($scope.ejs.TermStatsFacet(m.field)
|
||||
.keyField($scope.panel.persistent_field).valueField(m.field)
|
||||
.order('term')
|
||||
.size(2000));
|
||||
});
|
||||
|
||||
results = request.doSearch();
|
||||
|
||||
results.then(function (r) {
|
||||
|
||||
var newPersistentIds = _.pluck(r.facets.terms.terms, 'term'),
|
||||
mrequest;
|
||||
var mrequest, newData;
|
||||
|
||||
if (newPersistentIds.length === 0) {
|
||||
// populate the summary data based on the other facets
|
||||
newData = {};
|
||||
|
||||
_.each(r.facets['timestamp'].terms, function (f) {
|
||||
if (!$scope.panel.show_hidden && f.term[0] === ".") {
|
||||
return;
|
||||
}
|
||||
newData[f.term] = {
|
||||
id: f.term,
|
||||
time_span: (f.max - f.min) / 1000,
|
||||
alive: (new Date().getTime() - f.max > $scope.staleSeconds*1000) ? false : true,
|
||||
selected: ($scope.data[f.term] || {}).selected,
|
||||
alert_level: 0
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
_.each($scope.panel.metrics, function (m) {
|
||||
_.each(r.facets[m.field].terms, function (f) {
|
||||
var summary = newData[f.term];
|
||||
if (!summary) {
|
||||
return; // filtered
|
||||
}
|
||||
var m_summary = {
|
||||
mean: null,
|
||||
max: null,
|
||||
min: null
|
||||
};
|
||||
|
||||
if (m.derivative) {
|
||||
// no min max, but we can do avg, if we have a timestamp
|
||||
if (!summary.time_span) {
|
||||
summary[m.field] = m_summary;
|
||||
return;
|
||||
}
|
||||
m_summary.mean = (f.max - f.min) / summary.time_span;
|
||||
if (m.scale && m.scale !== 1) {
|
||||
m_summary.mean /= m.scale;
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_summary.min = f.min;
|
||||
m_summary.max = f.max;
|
||||
m_summary.mean = f.mean;
|
||||
if (m.scale && m.scale !== 1) {
|
||||
m_summary.mean /= m.scale;
|
||||
m_summary.max /= m.scale;
|
||||
m_summary.min /= m.scale;
|
||||
}
|
||||
}
|
||||
summary[m.field] = m_summary;
|
||||
m_summary.alert_level = $scope.alertLevel(m, m_summary.mean);
|
||||
// if (f.term === "index_t_200" && m.field === "primaries.docs.count") {
|
||||
// m_summary.alert_level = 1;
|
||||
// }
|
||||
// if (f.term === "index_t_300" && m.field === "primaries.indexing.index_total") {
|
||||
// m_summary.alert_level = 2;
|
||||
// }
|
||||
if (m_summary.alert_level > summary.alert_level) {
|
||||
summary.alert_level = m_summary.alert_level;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
if (_.isEmpty(newData)) {
|
||||
// call the get data function so it will clear out all other data related objects.
|
||||
$scope.get_data([]);
|
||||
$scope._register_data_end();
|
||||
$scope.select_display_data_and_enrich(newData);
|
||||
return;
|
||||
}
|
||||
|
||||
// in all this cases we don't need the display name, short cut it.
|
||||
if (!$scope.panel.display_field || $scope.panel.display_field === $scope.panel.persistent_field ||
|
||||
$scope.panel.compact
|
||||
_.size(newData) > $scope.panel.data_limit_for_display_names
|
||||
) {
|
||||
$scope.get_data(_.map(newPersistentIds, function (id) {
|
||||
return {
|
||||
display_name: id,
|
||||
id: id,
|
||||
// using findWhere here, though its not very efficient
|
||||
selected: (_.findWhere($scope.rows, {id: id}) || {}).selected
|
||||
};
|
||||
}));
|
||||
_.each(newData, function (s) {
|
||||
s.display_name = s.id;
|
||||
});
|
||||
$scope._register_data_end();
|
||||
|
||||
$scope.select_display_data_and_enrich(newData);
|
||||
return;
|
||||
}
|
||||
|
||||
// go get display names.
|
||||
mrequest = $scope.ejs.MultiSearchRequest().indices(dashboard.indices);
|
||||
|
||||
_.each(newPersistentIds, function (persistentId) {
|
||||
_.each(newData, function (s) {
|
||||
var rowRequest = $scope.ejs.Request().filter(filter);
|
||||
rowRequest.query(
|
||||
$scope.ejs.ConstantScoreQuery().query(
|
||||
$scope.ejs.TermQuery($scope.panel.persistent_field, persistentId)
|
||||
$scope.ejs.TermQuery($scope.panel.persistent_field, s.id)
|
||||
)
|
||||
);
|
||||
rowRequest.size(1).fields(_.unique([ stripRaw($scope.panel.display_field), stripRaw($scope.panel.persistent_field)]));
|
||||
|
@ -391,10 +473,8 @@ define([
|
|||
});
|
||||
|
||||
mrequest.doSearch(function (r) {
|
||||
|
||||
esVersion.is(">=1.0.0.RC1").then(function(version) {
|
||||
|
||||
var newRows = [],
|
||||
esVersion.is('>=1.0.0.RC1').then(function(version) {
|
||||
var
|
||||
hit,
|
||||
display_name,
|
||||
persistent_name;
|
||||
|
@ -408,90 +488,192 @@ define([
|
|||
if (version) {
|
||||
display_name = (hit.fields[stripRaw($scope.panel.display_field)] || [ undefined ])[0];
|
||||
persistent_name = (hit.fields[stripRaw($scope.panel.persistent_field)] || [ undefined] )[0];
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
display_name = hit.fields[stripRaw($scope.panel.display_field)];
|
||||
persistent_name = hit.fields[stripRaw($scope.panel.persistent_field)];
|
||||
}
|
||||
|
||||
newRows.push({
|
||||
display_name: display_name || persistent_name,
|
||||
id: persistent_name,
|
||||
// using findWhere here, though its not very efficient
|
||||
selected: (_.findWhere($scope.rows, {id: persistent_name}) || {}).selected
|
||||
});
|
||||
(newData[persistent_name] || {}).display_name = display_name;
|
||||
});
|
||||
$scope.get_data(newRows);
|
||||
|
||||
$scope._register_data_end();
|
||||
$scope.select_display_data_and_enrich(newData);
|
||||
});
|
||||
}, $scope._register_data_end);
|
||||
}, $scope._register_data_end);
|
||||
|
||||
};
|
||||
|
||||
$scope.get_data = function (newRows) {
|
||||
function applyNewData(rows, data) {
|
||||
$scope.rows = rows;
|
||||
$scope.data = data;
|
||||
$scope.updateUIFeaturesBasedOnData();
|
||||
$scope.panelMeta.loading = false;
|
||||
$scope.calculateWarnings();
|
||||
}
|
||||
|
||||
$scope.select_display_data_and_enrich = function (newData) {
|
||||
// Make sure we have everything for the request to complete
|
||||
|
||||
if (_.isUndefined(newRows)) {
|
||||
newRows = $scope.rows;
|
||||
if (_.isUndefined(newData)) {
|
||||
newData = $scope.data;
|
||||
}
|
||||
|
||||
if (dashboard.indices.length === 0 || newRows.length === 0) {
|
||||
$scope.rows = newRows;
|
||||
$scope.data = {};
|
||||
$scope.panelMeta.loading = false;
|
||||
$scope.calculateWarnings();
|
||||
$scope._register_data_end();
|
||||
if (dashboard.indices.length === 0 || _.isEmpty(newData)) {
|
||||
applyNewData([], {});
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.panelMeta.loading = true;
|
||||
var
|
||||
request,
|
||||
results;
|
||||
results,
|
||||
newRows, newRowsIds;
|
||||
|
||||
// decide what rows we're showing...
|
||||
newRowsIds = [];
|
||||
if ($scope.panel.rowFilter) {
|
||||
var lcFilter = $scope.panel.rowFilter.toLowerCase();
|
||||
_.each(newData, function (s) {
|
||||
var data = s.id.toLowerCase();
|
||||
if (data.indexOf(lcFilter) >= 0) {
|
||||
newRowsIds.push(s.id);
|
||||
return;
|
||||
}
|
||||
data = s.display_name.toLowerCase();
|
||||
if (data.indexOf(lcFilter) >= 0) {
|
||||
newRowsIds.push(s.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
_.each(newData, function (s) {
|
||||
newRowsIds.push(s.id);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function compareIdBySelection(id1, id2) {
|
||||
var s1 = newData[id1], s2 = newData[id2];
|
||||
if (s1.selected && !s2.selected) {
|
||||
return -1;
|
||||
}
|
||||
if (!s1.selected && s2.selected) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareIdByAlert(id1, id2) {
|
||||
var s1 = newData[id1], s2 = newData[id2];
|
||||
if (s1.alert_level > s2.alert_level) {
|
||||
return -1;
|
||||
}
|
||||
if (s1.alert_level < s2.alert_level) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareIdByName(id1, id2) {
|
||||
var s1 = newData[id1], s2 = newData[id2];
|
||||
if (s1.display_name < s2.display_name) {
|
||||
return -1;
|
||||
}
|
||||
if (s1.display_name > s2.display_name) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function compareIdByPanelSort(id1, id2) {
|
||||
var v1 = $scope.get_sort_value(id1, newData),
|
||||
v2 = $scope.get_sort_value(id2, newData),
|
||||
r = 0;
|
||||
if (v1 < v2) {
|
||||
r = -1;
|
||||
}
|
||||
else if (v1 > v2) {
|
||||
r = 1;
|
||||
}
|
||||
|
||||
if ($scope.panel.sort[1] === "desc") {
|
||||
r *= -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function concatSorting() {
|
||||
var funcs = arguments;
|
||||
return function (d1, d2) {
|
||||
for (var i = 0; i < funcs.length; i++) {
|
||||
var r = funcs[i].call(this, d1, d2);
|
||||
if (r !== 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
if ($scope.panel.sort) {
|
||||
newRowsIds.sort(concatSorting(compareIdByPanelSort, compareIdByAlert, compareIdBySelection));
|
||||
newRowsIds = newRowsIds.slice(0, $scope.rowLimit);
|
||||
|
||||
}
|
||||
else {
|
||||
newRowsIds.sort(concatSorting(compareIdBySelection, compareIdByAlert, compareIdByName));
|
||||
newRowsIds = newRowsIds.slice(0, $scope.rowLimit);
|
||||
// sort again for visual effect
|
||||
// sort again for visual placement
|
||||
newRowsIds.sort(concatSorting(compareIdByAlert, compareIdByName));
|
||||
}
|
||||
|
||||
|
||||
newRows = _.map(newRowsIds, function (id) {
|
||||
return newData[id];
|
||||
});
|
||||
|
||||
// now that we have selections, sort by name (if
|
||||
|
||||
|
||||
request = $scope.ejs.Request().indices(dashboard.indices);
|
||||
|
||||
var to = filterSrv.timeRange(false).to;
|
||||
if (to !== "now") {
|
||||
to = kbn.parseDate(to).valueOf() + "||";
|
||||
}
|
||||
var filter = $scope.ejs.BoolFilter()
|
||||
.must($scope.ejs.RangeFilter('@timestamp').from(to + "-10m/m").to(to + "/m"))
|
||||
.must($scope.get_mode_filter());
|
||||
request.query($scope.ejs.FilteredQuery($scope.ejs.MatchAllQuery(), filter)).size(0);
|
||||
|
||||
//to = kbn.parseDate(to).valueOf();
|
||||
_.each(_.pluck(newRows, 'id'), function (id) {
|
||||
var filter = $scope.ejs.BoolFilter()
|
||||
.must($scope.ejs.RangeFilter('@timestamp').from(to + "-10m/m").to(to + "/m"))
|
||||
.must($scope.ejs.TermsFilter($scope.panel.persistent_field, id))
|
||||
.must($scope.get_mode_filter());
|
||||
|
||||
_.each(newRows, function (row) {
|
||||
_.each($scope.panel.metrics, function (m) {
|
||||
request = request
|
||||
.facet($scope.ejs.StatisticalFacet(id + "_" + m.field)
|
||||
.field(m.field)
|
||||
.facetFilter(filter));
|
||||
request = request.facet($scope.ejs.DateHistogramFacet(id + "_" + m.field + "_history")
|
||||
if (!row[m.field] || row[m.field].series) {
|
||||
// already have it or the field was not present in the first iteration. Ignore for now.
|
||||
return;
|
||||
}
|
||||
request.facet($scope.ejs.DateHistogramFacet($scope.get_history_key(row.id, m))
|
||||
.keyField('@timestamp').valueField(m.field).interval('1m')
|
||||
.facetFilter(filter)).size(0);
|
||||
.facetFilter($scope.ejs.TermFilter($scope.panel.persistent_field, row.id))
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
// Get the max of timestamp to figure out the last time the node reported
|
||||
request = request
|
||||
.facet($scope.ejs.StatisticalFacet(id + "_@timestamp")
|
||||
.field("@timestamp")
|
||||
.facetFilter(filter));
|
||||
|
||||
});
|
||||
|
||||
if (!request.facet() || request.facet().length === 0) {
|
||||
applyNewData(newRows, newData);
|
||||
return;
|
||||
}
|
||||
|
||||
results = request.doSearch();
|
||||
|
||||
// Populate scope when we have results
|
||||
results.then(function (results) {
|
||||
$scope.rows = newRows;
|
||||
$scope.data = normalizeFacetResults(results.facets, newRows, $scope.panel.metrics);
|
||||
$scope.panelMeta.loading = false;
|
||||
$scope.calculateWarnings();
|
||||
$scope._register_data_end();
|
||||
},
|
||||
$scope._register_data_end
|
||||
addHistoryFacetResults(results.facets, newRows, newData, $scope.panel.metrics);
|
||||
applyNewData(newRows, newData);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -499,31 +681,30 @@ define([
|
|||
return (new Date().getTime() - time > allowed) ? false : true;
|
||||
};
|
||||
|
||||
var normalizeFacetResults = function (facets, rows, metrics) {
|
||||
facets = facets || {}; // deal better with no data.
|
||||
var addHistoryFacetResults = function (facets, rows, data, metrics) {
|
||||
_.each(metrics, function (m) {
|
||||
_.each(_.pluck(rows, 'id'), function (id) {
|
||||
var summary_key = id + "_" + m.field;
|
||||
var history_key = id + "_" + m.field + "_history";
|
||||
var summary = facets[summary_key];
|
||||
if (!summary) {
|
||||
_.each(rows, function (row) {
|
||||
var history_key = $scope.get_history_key(row.id, m);
|
||||
var history_facet = facets[history_key];
|
||||
if (!history_facet) {
|
||||
// no data for this chart.
|
||||
return;
|
||||
}
|
||||
var series_data = _.pluck(facets[history_key].entries, m.derivative ? 'min' : 'mean');
|
||||
var series_time = _.pluck(facets[history_key].entries, 'time');
|
||||
var series_data = _.pluck(history_facet.entries, m.derivative ? 'min' : 'mean');
|
||||
var series_time = _.pluck(history_facet.entries, 'time');
|
||||
var summary = row[m.field];
|
||||
|
||||
if (m.scale && m.scale !== 1) {
|
||||
series_data = _.map(series_data, function (v) {
|
||||
return v / m.scale;
|
||||
});
|
||||
summary.mean /= m.scale;
|
||||
summary.max /= m.scale;
|
||||
summary.min /= m.scale;
|
||||
}
|
||||
|
||||
if (m.derivative) {
|
||||
|
||||
// update mean to match min & max. Mean is calculated using the entire period's min/max
|
||||
// this can be different than the calculation here that is based of the min of every small bucket
|
||||
|
||||
var _l = series_data.length - 1;
|
||||
if (_l <= 0) {
|
||||
summary.mean = null;
|
||||
|
@ -533,12 +714,14 @@ define([
|
|||
summary.mean = (series_data[_l] - series_data[0]) / avg_time;
|
||||
}
|
||||
|
||||
|
||||
series_data = _.map(series_data, function (p, i) {
|
||||
|
||||
var _v;
|
||||
if (i === 0) {
|
||||
_v = null;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
var _t = ((series_time[i] - series_time[i - 1]) / 1000); // milliseconds -> seconds.
|
||||
_v = (p - series_data[i - 1]) / _t;
|
||||
}
|
||||
|
@ -551,13 +734,16 @@ define([
|
|||
summary.min = _.reduce(series_data, function (m, v) {
|
||||
return m > v && v != null ? v : m;
|
||||
}, Number.POSITIVE_INFINITY);
|
||||
if (summary.max === Number.NEGATIVE_INFINITY) {
|
||||
summary.max = null;
|
||||
}
|
||||
if (summary.min === Number.POSITIVE_INFINITY) {
|
||||
summary.min = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var series = _.zip(series_time, series_data);
|
||||
|
||||
facets[summary_key] = summary;
|
||||
facets[history_key].series = series;
|
||||
summary.series = _.zip(series_time, series_data);
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -565,32 +751,53 @@ define([
|
|||
return facets;
|
||||
};
|
||||
|
||||
$scope.hasSelected = function (nodes) {
|
||||
return _.some(nodes, function (n) {
|
||||
$scope.hasSelected = function (rows) {
|
||||
return _.some(rows || $scope.data, function (n) {
|
||||
return n.selected;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.get_sort_value = function (row) {
|
||||
if ($scope.panel.sort[0] === '__name__') {
|
||||
return row.display_name;
|
||||
$scope.selectedData = function (data) {
|
||||
return _.filter(data || $scope.data, function (d) {
|
||||
return d.selected;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.get_sort_value = function (id, data) {
|
||||
if (!data) {
|
||||
data = $scope.data;
|
||||
}
|
||||
return $scope.data[row.id + '_' + $scope.panel.sort[0]].mean;
|
||||
id = data[id];
|
||||
if ($scope.panel.sort[0] === '__name__') {
|
||||
return id.display_name;
|
||||
}
|
||||
return id[$scope.panel.sort[0]].mean;
|
||||
};
|
||||
|
||||
$scope.set_sort = function (field) {
|
||||
if ($scope.panel.sort && $scope.panel.sort[0] === field) {
|
||||
$scope.panel.sort[1] = $scope.panel.sort[1] === "asc" ? "desc" : "asc";
|
||||
if ($scope.panel.sort[1] === "asc") {
|
||||
$scope.panel.sort[1] = "desc";
|
||||
}
|
||||
else if ($scope.panel.sort[1] === "desc") {
|
||||
$scope.panel.sort = null;
|
||||
}
|
||||
else {
|
||||
// shouldn't happen, but whatever
|
||||
$scope.panel.sort[1] = "asc";
|
||||
}
|
||||
}
|
||||
else {
|
||||
$scope.panel.sort = [field, 'asc'];
|
||||
}
|
||||
$scope.select_display_data_and_enrich();
|
||||
};
|
||||
|
||||
$scope.showFullTable = function () {
|
||||
if ($scope.panel.compact) {
|
||||
return false;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -609,17 +816,16 @@ define([
|
|||
return !a ? "" : (a.type === "upper_bound" ? ">" : "<") + y_format_metric_value(a.threshold, metric);
|
||||
};
|
||||
|
||||
|
||||
$scope.detailViewLink = function (rows, fields) {
|
||||
var
|
||||
query,
|
||||
time,
|
||||
show,
|
||||
from,
|
||||
to;
|
||||
query,
|
||||
time,
|
||||
show,
|
||||
from,
|
||||
to;
|
||||
|
||||
if (_.isUndefined(rows)) {
|
||||
rows = _.where($scope.rows, {selected: true});
|
||||
rows = $scope.selectedData();
|
||||
}
|
||||
rows = _.map(rows, function (row) {
|
||||
query = $scope.panel.persistent_field + ':"' + row.id + '"';
|
||||
|
@ -629,15 +835,15 @@ define([
|
|||
};
|
||||
});
|
||||
if (rows.length === 0) {
|
||||
/*jshint -W107 */
|
||||
return "javascript:;";
|
||||
return null;
|
||||
}
|
||||
rows = JSON.stringify(rows);
|
||||
time = filterSrv.timeRange(false);
|
||||
show;
|
||||
|
||||
if (!_.isUndefined(fields)) {
|
||||
show = "&show=" + fields.join(",");
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
show = "";
|
||||
}
|
||||
|
||||
|
@ -663,23 +869,13 @@ define([
|
|||
};
|
||||
|
||||
$scope.calculateWarnings = function () {
|
||||
$scope.warnLevels = {_global_: {}};
|
||||
$scope.warnLevels = {};
|
||||
_.each($scope.panel.metrics, function (metric) {
|
||||
$scope.warnLevels._global_[metric.field] = 0;
|
||||
_.each(_.pluck($scope.rows, 'id'), function (id) {
|
||||
var num, level, summary;
|
||||
|
||||
$scope.warnLevels[id] = $scope.warnLevels[id] || {};
|
||||
|
||||
summary = $scope.data[id + '_' + metric.field];
|
||||
if (!summary) {
|
||||
return; // no data
|
||||
}
|
||||
num = summary.mean;
|
||||
level = $scope.alertLevel(metric, num);
|
||||
$scope.warnLevels[id][metric.field] = level;
|
||||
if (level > $scope.warnLevels._global_[metric.field]) {
|
||||
$scope.warnLevels._global_[metric.field] = level;
|
||||
$scope.warnLevels[metric.field] = 0;
|
||||
_.each($scope.data, function (s) {
|
||||
var level = (s[metric.field] || {}).alert_level;
|
||||
if (!_.isUndefined(level) && level > $scope.warnLevels[metric.field]) {
|
||||
$scope.warnLevels[metric.field] = level;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -700,7 +896,8 @@ define([
|
|||
}
|
||||
if (testAlert(metric.error, num)) {
|
||||
level = 2;
|
||||
} else if (testAlert(metric.warning, num)) {
|
||||
}
|
||||
else if (testAlert(metric.warning, num)) {
|
||||
level = 1;
|
||||
}
|
||||
|
||||
|
@ -708,7 +905,8 @@ define([
|
|||
var r = Math.random();
|
||||
if (r > 0.9) {
|
||||
level = 2;
|
||||
} else if (r > 0.8) {
|
||||
}
|
||||
else if (r > 0.8) {
|
||||
level = 1;
|
||||
}
|
||||
|
||||
|
@ -735,7 +933,8 @@ define([
|
|||
if (s[0] === '<') {
|
||||
ret.type = "lower_bound";
|
||||
s = s.substr(1);
|
||||
} else if (s[0] === '>') {
|
||||
}
|
||||
else if (s[0] === '>') {
|
||||
s = s.substr(1);
|
||||
}
|
||||
|
||||
|
@ -747,7 +946,7 @@ define([
|
|||
|
||||
};
|
||||
|
||||
$scope.addMetric = function (panel,metric) {
|
||||
$scope.addMetric = function (panel, metric) {
|
||||
metric = metric || {};
|
||||
metric = metricDefaults(metric);
|
||||
panel.metrics.push(metric);
|
||||
|
@ -780,16 +979,21 @@ define([
|
|||
index: -1
|
||||
};
|
||||
if ($scope._needs_refresh) {
|
||||
$scope.get_rows();
|
||||
$scope.get_data();
|
||||
}
|
||||
$scope._needs_refresh = false;
|
||||
$scope.$emit('render');
|
||||
};
|
||||
|
||||
$scope.deleteMetric = function (panel,index) {
|
||||
$scope.deleteMetric = function (panel, index) {
|
||||
panel.metrics = _.without(panel.metrics, panel.metrics[index]);
|
||||
};
|
||||
|
||||
$scope.onRowFilterChange = _.debounce(function () {
|
||||
$scope.$apply(function (scope) {
|
||||
$scope.select_display_data_and_enrich(scope.data);
|
||||
});
|
||||
}, 500);
|
||||
|
||||
});
|
||||
|
||||
|
@ -807,7 +1011,8 @@ define([
|
|||
// it is valid
|
||||
ctrl.$setValidity('alertValue', true);
|
||||
return scope.parseAlert(viewValue);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// it is invalid, return undefined (no model update)
|
||||
ctrl.$setValidity('alertValue', false);
|
||||
return undefined;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"company": "Elasticsearch BV"
|
||||
},
|
||||
"name": "marvel",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.3-SNAPSHOT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/elasticsearch/elasticsearch-marvel.git"
|
||||
|
|
|
@ -162,25 +162,50 @@ define([
|
|||
input.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Make the editor resizeable
|
||||
*/
|
||||
input.$el.resizable({
|
||||
autoHide: false,
|
||||
handles: 'e',
|
||||
start: function (e, ui) {
|
||||
$(".ui-resizable-e").addClass("active");
|
||||
},
|
||||
stop: function (e, ui) {
|
||||
$(".ui-resizable-e").removeClass("active");
|
||||
var parent = ui.element.parent();
|
||||
var editorSize = ui.element.outerWidth();
|
||||
output.$el.css("left", editorSize + 20);
|
||||
input.$actions.css("margin-right", -editorSize + 3);
|
||||
input.resize(true);
|
||||
output.resize(true);
|
||||
(function stuffThatsTooHardWithCSS() {
|
||||
var $editors = input.$el.parent().add(output.$el.parent());
|
||||
var $resizer = miscInputs.$resizer;
|
||||
var $header = miscInputs.$header;
|
||||
|
||||
var delay;
|
||||
var headerHeight;
|
||||
var resizerHeight;
|
||||
|
||||
$resizer
|
||||
.html('︙') // vertical elipses
|
||||
.css('vertical-align', 'middle');
|
||||
|
||||
function update() {
|
||||
var newHeight;
|
||||
|
||||
delay = clearTimeout(delay);
|
||||
|
||||
newHeight = $header.outerHeight();
|
||||
if (headerHeight != newHeight) {
|
||||
headerHeight = newHeight;
|
||||
$editors.css('top', newHeight + 10);
|
||||
}
|
||||
|
||||
newHeight = $resizer.height();
|
||||
if (resizerHeight != newHeight) {
|
||||
resizerHeight = newHeight;
|
||||
$resizer.css('line-height', newHeight + 'px');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// update at key moments in the loading process
|
||||
$(update);
|
||||
$(window).load(update);
|
||||
|
||||
// and when the window resizes (once every 30 ms)
|
||||
$(window)
|
||||
.resize(function (event) {
|
||||
if (!delay && event.target === window) {
|
||||
delay = setTimeout(update, 30);
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
||||
|
||||
/**
|
||||
* Setup the "send" shortcut
|
||||
|
@ -191,14 +216,14 @@ define([
|
|||
exec: function () {
|
||||
output.update('');
|
||||
submitCurrentRequestToES(function (resp) {
|
||||
output.update(resp);
|
||||
output.update(resp, 'ace/mode/json');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$send.click(function () {
|
||||
submitCurrentRequestToES(function (resp) {
|
||||
output.update(resp);
|
||||
output.update(resp, 'ace/mode/json');
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
@ -206,7 +231,6 @@ define([
|
|||
/*
|
||||
* initialize navigation menu
|
||||
*/
|
||||
|
||||
$.get('../common/marvelLinks.json', function (marvelLinks) {
|
||||
var linkMenu = $("#nav_btn ul");
|
||||
_.map(marvelLinks.links, function (link) {
|
||||
|
|
|
@ -25,7 +25,7 @@ define([
|
|||
});
|
||||
|
||||
$helpPopup.on('hidden', function () {
|
||||
$('#example_editor').remove();
|
||||
$('#help_example_editor').remove();
|
||||
});
|
||||
|
||||
return $helpPopup;
|
||||
|
|
|
@ -9,7 +9,8 @@ define([
|
|||
'require',
|
||||
'utils',
|
||||
'zeroclip',
|
||||
'ace_ext_language_tools'
|
||||
'ace_ext_language_tools',
|
||||
'ace_ext_searchbox'
|
||||
], function (ace, Autocomplete, $, mappings, output, SenseEditor, settings, require, utils, ZeroClipboard) {
|
||||
'use strict';
|
||||
|
||||
|
@ -29,6 +30,9 @@ define([
|
|||
|
||||
input.autocomplete = new Autocomplete(input);
|
||||
|
||||
input.$actions = $("#editor_actions");
|
||||
|
||||
|
||||
ace.require('ace/ext/language_tools').addCompleter(input.autocomplete.completer);
|
||||
|
||||
input.commands.addCommand({
|
||||
|
@ -74,7 +78,7 @@ define([
|
|||
zc.on('wrongflash noflash', function () {
|
||||
if (!localStorage.getItem('flash_warning_shown')) {
|
||||
alert('Sense needs flash version 10.0 or greater in order to provide "Copy as cURL" functionality');
|
||||
localStorage.setItem('flash_warning_shown');
|
||||
localStorage.setItem('flash_warning_shown', 'true');
|
||||
}
|
||||
$copyAsCURL.hide();
|
||||
});
|
||||
|
|
|
@ -5,11 +5,15 @@ define(function () {
|
|||
|
||||
filters.and = {
|
||||
__template: {
|
||||
filters: [{}]
|
||||
filters: [
|
||||
{}
|
||||
]
|
||||
},
|
||||
filters: [{
|
||||
__scope_link: '.filter'
|
||||
}],
|
||||
filters: [
|
||||
{
|
||||
__scope_link: '.filter'
|
||||
}
|
||||
],
|
||||
_cache: {
|
||||
__one_of: [false, true]
|
||||
}
|
||||
|
@ -17,15 +21,21 @@ define(function () {
|
|||
|
||||
|
||||
filters.bool = {
|
||||
must: [{
|
||||
__scope_link: '.filter'
|
||||
}],
|
||||
must_not: [{
|
||||
__scope_link: '.filter'
|
||||
}],
|
||||
should: [{
|
||||
__scope_link: '.filter'
|
||||
}],
|
||||
must: [
|
||||
{
|
||||
__scope_link: '.filter'
|
||||
}
|
||||
],
|
||||
must_not: [
|
||||
{
|
||||
__scope_link: '.filter'
|
||||
}
|
||||
],
|
||||
should: [
|
||||
{
|
||||
__scope_link: '.filter'
|
||||
}
|
||||
],
|
||||
_cache: {
|
||||
__one_of: [false, true]
|
||||
}
|
||||
|
@ -166,20 +176,25 @@ define(function () {
|
|||
filters.geo_polygon = {
|
||||
__template: {
|
||||
'FIELD': {
|
||||
'points': [{
|
||||
lat: 40.73,
|
||||
lon: -74.1
|
||||
}, {
|
||||
lat: 40.83,
|
||||
lon: -75.1
|
||||
}]
|
||||
'points': [
|
||||
{
|
||||
lat: 40.73,
|
||||
lon: -74.1
|
||||
},
|
||||
{
|
||||
lat: 40.83,
|
||||
lon: -75.1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
'$FIELD$': {
|
||||
points: [{
|
||||
lat: 40.73,
|
||||
lon: -74.1
|
||||
}]
|
||||
points: [
|
||||
{
|
||||
lat: 40.73,
|
||||
lon: -74.1
|
||||
}
|
||||
]
|
||||
},
|
||||
_cache: {
|
||||
__one_of: [false, true]
|
||||
|
@ -240,7 +255,7 @@ define(function () {
|
|||
};
|
||||
|
||||
|
||||
filters.m = filters.missing = {
|
||||
filters.m = filters.missing = {
|
||||
__template: {
|
||||
field: 'FIELD'
|
||||
},
|
||||
|
@ -274,24 +289,30 @@ define(function () {
|
|||
to: 20
|
||||
}
|
||||
},
|
||||
from: 1,
|
||||
to: 20,
|
||||
include_lower: {
|
||||
__one_of: [true, false]
|
||||
},
|
||||
include_upper: {
|
||||
__one_of: [true, false]
|
||||
"$FIELD$": {
|
||||
from: 1,
|
||||
to: 20,
|
||||
include_lower: {
|
||||
__one_of: [true, false]
|
||||
},
|
||||
include_upper: {
|
||||
__one_of: [true, false]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
filters.or = {
|
||||
__template: {
|
||||
filters: [{}]
|
||||
filters: [
|
||||
{}
|
||||
]
|
||||
},
|
||||
filters: [{
|
||||
__scope_link: '.filter'
|
||||
}],
|
||||
filters: [
|
||||
{
|
||||
__scope_link: '.filter'
|
||||
}
|
||||
],
|
||||
_cache: {
|
||||
__one_of: [false, true]
|
||||
}
|
||||
|
@ -335,16 +356,18 @@ define(function () {
|
|||
to: 20
|
||||
}
|
||||
},
|
||||
from: 1,
|
||||
to: 20,
|
||||
include_lower: {
|
||||
__one_of: [true, false]
|
||||
},
|
||||
include_upper: {
|
||||
__one_of: [true, false]
|
||||
},
|
||||
_cache: {
|
||||
__one_of: [false, true]
|
||||
"$FIELD$": {
|
||||
from: 1,
|
||||
to: 20,
|
||||
include_lower: {
|
||||
__one_of: [true, false]
|
||||
},
|
||||
include_upper: {
|
||||
__one_of: [true, false]
|
||||
},
|
||||
_cache: {
|
||||
__one_of: [false, true]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -162,6 +162,10 @@ define([
|
|||
per_index_types = {};
|
||||
$.each(mappings, function (index, index_mapping) {
|
||||
var normalized_index_mappings = {};
|
||||
// 1.0.0 mapping format has changed, extract underlying mapping
|
||||
if (index_mapping.mappings && _.keys(index_mapping).length === 1) {
|
||||
index_mapping = index_mapping.mappings;
|
||||
}
|
||||
$.each(index_mapping, function (type_name, type_mapping) {
|
||||
var field_list = getFieldNamesFromTypeMapping(type_mapping);
|
||||
normalized_index_mappings[type_name] = field_list;
|
||||
|
|
|
@ -3,10 +3,11 @@ define([
|
|||
'history',
|
||||
'input',
|
||||
'mappings',
|
||||
|
||||
'output',
|
||||
|
||||
'bootstrap',
|
||||
'jquery-ui'
|
||||
], function ($, history, input, mappings) {
|
||||
], function ($, history, input, mappings, output) {
|
||||
'use strict';
|
||||
|
||||
var $esServer = $("#es_server");
|
||||
|
@ -32,9 +33,36 @@ define([
|
|||
e.preventDefault();
|
||||
});
|
||||
|
||||
var $header = $('.navbar.navbar-static-top');
|
||||
|
||||
// containers for the two editors
|
||||
var $left = input.$el.parent();
|
||||
var $right = output.$el.parent();
|
||||
|
||||
$left.resizable({
|
||||
autoHide: false,
|
||||
handles: 'e',
|
||||
start: function () {
|
||||
$resizer.addClass('active');
|
||||
},
|
||||
resize: function () {
|
||||
$right.css('left', $left.outerWidth() + 20);
|
||||
},
|
||||
stop: function () {
|
||||
$resizer.removeClass('active');
|
||||
$left.css('height', 'auto'); // $.resizeable sets the height which prevents it from reshaping later
|
||||
input.resize(true);
|
||||
output.resize(true);
|
||||
}
|
||||
});
|
||||
|
||||
var $resizer = input.$el.siblings('.ui-resizable-e');
|
||||
|
||||
return {
|
||||
$esServer: $esServer,
|
||||
$send: $send,
|
||||
$autoIndent: $autoIndent
|
||||
$autoIndent: $autoIndent,
|
||||
$header: $header,
|
||||
$resizer: $resizer
|
||||
};
|
||||
})
|
|
@ -7,16 +7,24 @@ define([
|
|||
|
||||
var $el = $("#output");
|
||||
var output = ace.require('ace/ace').edit($el[0]);
|
||||
|
||||
output.update = function (val, cb) {
|
||||
output.getSession().setValue(val);
|
||||
|
||||
output.update = function (val, mode, cb) {
|
||||
if (typeof mode === 'function') {
|
||||
cb = mode;
|
||||
mode = void 0;
|
||||
}
|
||||
|
||||
var session = output.getSession();
|
||||
|
||||
session.setMode(val ? (mode || 'ace/mode/json') : 'ace/mode/text');
|
||||
session.setValue(val);
|
||||
if (typeof cb === 'function') {
|
||||
setTimeout(cb);
|
||||
}
|
||||
};
|
||||
|
||||
output.$el = $el;
|
||||
output.getSession().setMode("ace/mode/json");
|
||||
output.getSession().setMode("ace/mode/text");
|
||||
output.getSession().setFoldStyle('markbeginend');
|
||||
output.getSession().setUseWrapMode(true);
|
||||
output.setShowPrintMargin(false);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
'ace': '../vendor/ace/ace',
|
||||
'ace_mode_json': '../vendor/ace/mode-json',
|
||||
'ace_ext_language_tools': '../vendor/ace/ext-language_tools',
|
||||
'ace_ext_searchbox': '../vendor/ace/ext-searchbox'
|
||||
},
|
||||
map: {
|
||||
'*': {
|
||||
|
|
|
@ -55,7 +55,8 @@ define([
|
|||
var CURRENT_REQ_RANGE = null;
|
||||
|
||||
editor.$el = $el;
|
||||
editor.$actions = $("#editor_actions");
|
||||
// place holder for an action bar, needs to be set externally.
|
||||
editor.$actions = null;
|
||||
|
||||
// mixin the RowParser
|
||||
editor.parser = new RowParser(editor);
|
||||
|
@ -191,6 +192,10 @@ define([
|
|||
var pos = currentReqRange.start;
|
||||
var tokenIter = editor.iterForPosition(pos.row, pos.column, editor);
|
||||
var t = tokenIter.getCurrentToken();
|
||||
if (editor.parser.isEmptyToken(t)) {
|
||||
// if the row starts with some spaces, skip them.
|
||||
t = editor.parser.nextNonEmptyToken(tokenIter);
|
||||
}
|
||||
request.method = t.value;
|
||||
t = editor.parser.nextNonEmptyToken(tokenIter);
|
||||
if (!t || t.type == "method") return null;
|
||||
|
@ -354,39 +359,54 @@ define([
|
|||
editor.highlightCurrentRequestAndUpdateActionBar();
|
||||
});
|
||||
|
||||
editor.updateActionsBar = function () {
|
||||
var editor_actions = $("#editor_actions");
|
||||
editor.updateActionsBar = (function () {
|
||||
var set = function (top) {
|
||||
if (top == null) {
|
||||
editor.$actions.css('visibility', 'hidden');
|
||||
} else {
|
||||
editor.$actions.css({
|
||||
top: top,
|
||||
visibility: 'visible'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (CURRENT_REQ_RANGE) {
|
||||
var row = CURRENT_REQ_RANGE.start.row;
|
||||
var column = CURRENT_REQ_RANGE.start.column;
|
||||
var session = editor.session;
|
||||
var firstLine = session.getLine(row);
|
||||
var offset = 0;
|
||||
if (firstLine.length > session.getScreenWidth() - 5) {
|
||||
// overlap first row
|
||||
if (row > 0) row--; else row++;
|
||||
}
|
||||
var screen_pos = editor.renderer.textToScreenCoordinates(row, column);
|
||||
offset += screen_pos.pageY;
|
||||
var end_offset = editor.renderer.textToScreenCoordinates(CURRENT_REQ_RANGE.end.row,
|
||||
CURRENT_REQ_RANGE.end.column).pageY;
|
||||
var hide = function () {
|
||||
set();
|
||||
};
|
||||
|
||||
offset = Math.min(end_offset, Math.max(offset, 47));
|
||||
if (offset >= 47) {
|
||||
editor_actions.css("top", Math.max(offset, 47));
|
||||
editor_actions.css('visibility', 'visible');
|
||||
return function () {
|
||||
if (!editor.$actions) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
editor_actions.css("top", 0);
|
||||
editor_actions.css('visibility', 'hidden');
|
||||
if (CURRENT_REQ_RANGE) {
|
||||
// elements are positioned relative to the editor's container
|
||||
// pageY is relative to page, so subtract the offset
|
||||
// from pageY to get the new top value
|
||||
var offsetFromPage = editor.$el.offset().top;
|
||||
|
||||
var topOfReq = editor.renderer.textToScreenCoordinates(
|
||||
CURRENT_REQ_RANGE.start.row,
|
||||
CURRENT_REQ_RANGE.start.column
|
||||
).pageY - offsetFromPage;
|
||||
|
||||
if (topOfReq >= 0) {
|
||||
return set(topOfReq);
|
||||
}
|
||||
|
||||
var bottomOfReq = editor.renderer.textToScreenCoordinates(
|
||||
CURRENT_REQ_RANGE.end.row,
|
||||
CURRENT_REQ_RANGE.end.column
|
||||
).pageY - offsetFromPage;
|
||||
|
||||
if (bottomOfReq >= 0) {
|
||||
return set(0);
|
||||
}
|
||||
}
|
||||
|
||||
hide();
|
||||
}
|
||||
else {
|
||||
editor_actions.css("top", 0);
|
||||
editor_actions.css('visibility', 'hidden');
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
editor.getSession().on("changeScrollTop", editor.updateActionsBar);
|
||||
|
||||
|
|
|
@ -1628,9 +1628,6 @@ define("sense_editor/mode/worker_parser", ['require', 'exports', 'module' ], fun
|
|||
newLine();
|
||||
strictWhite();
|
||||
if (ch == '{') {
|
||||
if (meth == "GET") {
|
||||
annotate("warning", "Browsers do not support " + meth + " requests with a body. This will be executed as a POST.");
|
||||
}
|
||||
object();
|
||||
}
|
||||
// multi doc request
|
||||
|
@ -1658,8 +1655,15 @@ define("sense_editor/mode/worker_parser", ['require', 'exports', 'module' ], fun
|
|||
multi_request = function () {
|
||||
while (ch && ch != '') {
|
||||
white();
|
||||
if (!ch) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
comment();
|
||||
white();
|
||||
if (!ch) {
|
||||
continue;
|
||||
}
|
||||
request();
|
||||
white();
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ define([
|
|||
url: url,
|
||||
data: method == "GET" ? null : data,
|
||||
password: password,
|
||||
cache: false,
|
||||
username: uname,
|
||||
crossDomain: true,
|
||||
type: method,
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
<h4>Quick intro to the UI</h4>
|
||||
|
||||
<p>Sense is split into two panes: an editor pane (white) and a response pane (black).
|
||||
Use the editor to type requests and submit them to Elasticsearch. The results will be displayed in
|
||||
the response pane on the right side.
|
||||
</p>
|
||||
|
||||
<p>Sense understands requests in a compact format, similar to cURL:
|
||||
<div id="welcome_example_container"></div>
|
||||
|
||||
<p>While typing a request, Sense will make suggestions which you can than accept by hitting Enter/Tab.
|
||||
These suggestions are made based on the request structure <i>as well as</i> your indices and types.
|
||||
</p>
|
||||
|
||||
|
||||
<h4>A few quick tips, while I have your attention</h4>
|
||||
<ul>
|
||||
<li>Submit requests to ES using the green triangle button.</li>
|
||||
<li>Use the wrench menu for other useful things.</li>
|
||||
<li>You can paste requests in cURL format and they will be translated to the Sense syntax.</li>
|
||||
<li>You can resize the editor and output panes by dragging the separator between them.</li>
|
||||
<li>Study the keyboard shortcuts under the Help button. Good stuff in there!</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
|
@ -20,13 +20,30 @@ body.fouc {
|
|||
width: 100px;
|
||||
}
|
||||
|
||||
#editor_container {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
bottom: 5px;
|
||||
left: 5px;
|
||||
width: 468px;
|
||||
}
|
||||
|
||||
#output_container {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
bottom: 5px;
|
||||
left: 488px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
#editor_actions {
|
||||
position: absolute;
|
||||
top: 47px;
|
||||
right: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
line-height: 1;
|
||||
margin-right: -468px;
|
||||
z-index: 100;
|
||||
padding: 1px 3px 0 0;
|
||||
/* by pass any other element in ace and resize bar, but not modal popups */
|
||||
z-index: 1005;
|
||||
}
|
||||
|
||||
#editor_actions > .btn-group > a {
|
||||
|
@ -44,25 +61,9 @@ body.fouc {
|
|||
z-index: 100;
|
||||
}
|
||||
|
||||
#editor_container {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
bottom: 5px;
|
||||
left: 5px;
|
||||
width: 468px;
|
||||
}
|
||||
|
||||
#output_container {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
bottom: 5px;
|
||||
left: 488px;
|
||||
right: 5px;
|
||||
min-width: 700px;
|
||||
}
|
||||
|
||||
#output, #editor {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ace_gutter {
|
||||
|
@ -72,7 +73,8 @@ body.fouc {
|
|||
#autocomplete {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
/* by pass any other element in ace and resize bar, but not modal popups */
|
||||
z-index: 1003;
|
||||
margin-top: 22px;
|
||||
/*font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;*/
|
||||
/*font-size: 12px;*/
|
||||
|
@ -217,17 +219,16 @@ body.fouc {
|
|||
|
||||
.ui-resizable-e {
|
||||
cursor: ew-resize;
|
||||
width: 10px;
|
||||
right: -5px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 13px;
|
||||
right: -14px;
|
||||
top: -1px;
|
||||
bottom: -2px;
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
z-index: 50 !important;
|
||||
}
|
||||
|
||||
.ui-resizable-e:hover {
|
||||
background-color: rgba(194, 193, 208, 0.80);
|
||||
background-color: #DCDCDC;
|
||||
}
|
||||
|
||||
.ui-resizable-e.active {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/* sadly ace doesn't carry the theme over to the auto complete popup */
|
||||
.ace_autocomplete {
|
||||
background-color: #2e3236 !important;
|
||||
color: #bbbfc2 !important;
|
||||
color: rgb(233, 233, 233) !important;
|
||||
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,14 @@
|
|||
color: #AE81FF !important;
|
||||
}
|
||||
|
||||
.ace_selected {
|
||||
background: #222 !important;
|
||||
.ace_selection {
|
||||
background: #575858 !important;
|
||||
}
|
||||
|
||||
.ace_selected {
|
||||
background: #575858 !important;
|
||||
}
|
||||
|
||||
/* end of theming for auto complete- remove when issue in ace is fixed. */
|
||||
|
||||
.ace_url {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/html">
|
||||
<head>
|
||||
<title>Sense - a JSON aware interface to ElasticSearch</title>
|
||||
<title>Marvel - Sense</title>
|
||||
<link id="bootstrapThemeCss" href="vendor/bootstrap/css/bootstrap.light.min.css" type="text/css" rel="stylesheet"/>
|
||||
<link href="vendor/font-awesome/css/font-awesome.min.css" type="text/css" rel="stylesheet"/>
|
||||
<link id="senseThemeCss" href="css/sense.light.css" rel="stylesheet" type="text/css"/>
|
||||
|
@ -46,22 +46,21 @@
|
|||
<div id="editor">GET _search
|
||||
{
|
||||
"query": { "match_all": {} }
|
||||
}
|
||||
}</div>
|
||||
<div id="editor_actions">
|
||||
<a id="send" href="#" data-toggle="tooltip" title="click to send request"><i
|
||||
class="fa fa-play"></i></a>
|
||||
<a id="request_wrench" data-toggle="dropdown" href="#"><i
|
||||
class="fa fa-wrench"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="copy_as_curl" tabindex="-1" href="#">Copy as cURL</a></li>
|
||||
<li><a id="auto_indent" tabindex="-1" href="#">Auto indent</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="output_container">
|
||||
<div id="output">{}</div>
|
||||
</div>
|
||||
<div id="editor_actions">
|
||||
<a id="send" href="#" data-toggle="tooltip" title="click to send request"><i
|
||||
class="fa fa-play"></i></a>
|
||||
<a id="request_wrench" data-toggle="dropdown" href="#"><i
|
||||
class="fa fa-wrench"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="copy_as_curl" tabindex="-1" href="#">Copy as cURL</a></li>
|
||||
<li><a id="auto_indent" tabindex="-1" href="#">Auto indent</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul id="autocomplete" style="left: -1000px;"></ul>
|
||||
</div>
|
||||
|
||||
|
@ -177,12 +176,35 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div id="welcome_popup" class="modal hide fade" data-keyboard="true" data-remote="app/welcome.html">
|
||||
<div id="welcome_popup" class="modal hide fade" data-keyboard="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Welcome to Sense</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h4>Quick intro to the UI</h4>
|
||||
|
||||
<p>Sense is split into two panes: an editor pane (white) and a response pane (black).
|
||||
Use the editor to type requests and submit them to Elasticsearch. The results will be displayed in
|
||||
the response pane on the right side.
|
||||
</p>
|
||||
|
||||
<p>Sense understands requests in a compact format, similar to cURL:
|
||||
<div id="welcome_example_container"></div>
|
||||
|
||||
<p>While typing a request, Sense will make suggestions which you can than accept by hitting Enter/Tab.
|
||||
These suggestions are made based on the request structure <i>as well as</i> your indices and types.
|
||||
</p>
|
||||
|
||||
|
||||
<h4>A few quick tips, while I have your attention</h4>
|
||||
<ul>
|
||||
<li>Submit requests to ES using the green triangle button.</li>
|
||||
<li>Use the wrench menu for other useful things.</li>
|
||||
<li>You can paste requests in cURL format and they will be translated to the Sense syntax.</li>
|
||||
<li>You can resize the editor and output panes by dragging the separator between them.</li>
|
||||
<li>Study the keyboard shortcuts under the Help button. Good stuff in there!</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn btn-primary" data-dismiss="modal" aria-hidden="true">Get to work</a>
|
||||
|
|
|
@ -88,6 +88,31 @@ define([
|
|||
});
|
||||
});
|
||||
|
||||
utils_test("simple request range, prefixed with spaces", " " + simple_request.prefix, simple_request.data, function () {
|
||||
input.getCurrentRequestRange(function (range) {
|
||||
var expected = new aceRange.Range(
|
||||
0, 0,
|
||||
3, 1
|
||||
);
|
||||
deepEqual(range, expected);
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
utils_test("simple request data, prefixed with spaces", " " + simple_request.prefix, simple_request.data, function () {
|
||||
input.getCurrentRequest(function (request) {
|
||||
var expected = {
|
||||
method: "POST",
|
||||
url: "_search",
|
||||
data: [simple_request.data]
|
||||
};
|
||||
|
||||
deepEqual(request, expected);
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
utils_test("single line request range", single_line_request.prefix, single_line_request.data, function () {
|
||||
input.getCurrentRequestRange(function (range) {
|
||||
var expected = new aceRange.Range(
|
||||
|
|
|
@ -71,6 +71,26 @@ define([
|
|||
deepEqual(mappings.getFields("index").sort(fc), [f("number", "int"), f("str", "string") ]);
|
||||
});
|
||||
|
||||
test("Simple fields - 1.0 style", function () {
|
||||
mappings.loadMappings({
|
||||
"index": {
|
||||
"mappings": {
|
||||
"tweet": {
|
||||
"properties": {
|
||||
"str": {
|
||||
"type": "string"
|
||||
},
|
||||
"number": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
deepEqual(mappings.getFields("index").sort(fc), [f("number", "int"), f("str", "string") ]);
|
||||
});
|
||||
|
||||
test("Nested fields", function () {
|
||||
mappings.loadMappings({
|
||||
|
@ -208,13 +228,13 @@ define([
|
|||
});
|
||||
|
||||
deepEqual(mappings.getIndices().sort(),
|
||||
[ "_all", "alias1", "alias2", "test_index1", "test_index2" ]
|
||||
[ "_all", "alias1", "alias2", "test_index1", "test_index2" ]
|
||||
);
|
||||
deepEqual(mappings.getIndices(false).sort(),
|
||||
["test_index1", "test_index2" ]
|
||||
["test_index1", "test_index2" ]
|
||||
);
|
||||
deepEqual(mappings.expandAliases(["alias1", "test_index2"]).sort(),
|
||||
["test_index1", "test_index2" ]
|
||||
["test_index1", "test_index2" ]
|
||||
);
|
||||
deepEqual(mappings.expandAliases("alias2"), "test_index2");
|
||||
});
|
||||
|
|
421
sense/vendor/ace/ext-searchbox.js
vendored
Normal file
|
@ -0,0 +1,421 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
ace.define('ace/ext/searchbox', ['require', 'exports', 'module' , 'ace/lib/dom', 'ace/lib/lang', 'ace/lib/event', 'ace/keyboard/hash_handler', 'ace/lib/keys'], function(require, exports, module) {
|
||||
|
||||
|
||||
var dom = require("../lib/dom");
|
||||
var lang = require("../lib/lang");
|
||||
var event = require("../lib/event");
|
||||
var searchboxCss = "\
|
||||
/* ------------------------------------------------------------------------------------------\
|
||||
* Editor Search Form\
|
||||
* --------------------------------------------------------------------------------------- */\
|
||||
.ace_search {\
|
||||
background-color: #ddd;\
|
||||
border: 1px solid #cbcbcb;\
|
||||
border-top: 0 none;\
|
||||
max-width: 297px;\
|
||||
overflow: hidden;\
|
||||
margin: 0;\
|
||||
padding: 4px;\
|
||||
padding-right: 6px;\
|
||||
padding-bottom: 0;\
|
||||
position: absolute;\
|
||||
top: 0px;\
|
||||
z-index: 99;\
|
||||
white-space: normal;\
|
||||
}\
|
||||
.ace_search.left {\
|
||||
border-left: 0 none;\
|
||||
border-radius: 0px 0px 5px 0px;\
|
||||
left: 0;\
|
||||
}\
|
||||
.ace_search.right {\
|
||||
border-radius: 0px 0px 0px 5px;\
|
||||
border-right: 0 none;\
|
||||
right: 0;\
|
||||
}\
|
||||
.ace_search_form, .ace_replace_form {\
|
||||
border-radius: 3px;\
|
||||
border: 1px solid #cbcbcb;\
|
||||
float: left;\
|
||||
margin-bottom: 4px;\
|
||||
overflow: hidden;\
|
||||
}\
|
||||
.ace_search_form.ace_nomatch {\
|
||||
outline: 1px solid red;\
|
||||
}\
|
||||
.ace_search_field {\
|
||||
background-color: white;\
|
||||
border-right: 1px solid #cbcbcb;\
|
||||
border: 0 none;\
|
||||
-webkit-box-sizing: border-box;\
|
||||
-moz-box-sizing: border-box;\
|
||||
box-sizing: border-box;\
|
||||
display: block;\
|
||||
float: left;\
|
||||
height: 22px;\
|
||||
outline: 0;\
|
||||
padding: 0 7px;\
|
||||
width: 214px;\
|
||||
margin: 0;\
|
||||
}\
|
||||
.ace_searchbtn,\
|
||||
.ace_replacebtn {\
|
||||
background: #fff;\
|
||||
border: 0 none;\
|
||||
border-left: 1px solid #dcdcdc;\
|
||||
cursor: pointer;\
|
||||
display: block;\
|
||||
float: left;\
|
||||
height: 22px;\
|
||||
margin: 0;\
|
||||
padding: 0;\
|
||||
position: relative;\
|
||||
}\
|
||||
.ace_searchbtn:last-child,\
|
||||
.ace_replacebtn:last-child {\
|
||||
border-top-right-radius: 3px;\
|
||||
border-bottom-right-radius: 3px;\
|
||||
}\
|
||||
.ace_searchbtn:disabled {\
|
||||
background: none;\
|
||||
cursor: default;\
|
||||
}\
|
||||
.ace_searchbtn {\
|
||||
background-position: 50% 50%;\
|
||||
background-repeat: no-repeat;\
|
||||
width: 27px;\
|
||||
}\
|
||||
.ace_searchbtn.prev {\
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=); \
|
||||
}\
|
||||
.ace_searchbtn.next {\
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=); \
|
||||
}\
|
||||
.ace_searchbtn_close {\
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;\
|
||||
border-radius: 50%;\
|
||||
border: 0 none;\
|
||||
color: #656565;\
|
||||
cursor: pointer;\
|
||||
display: block;\
|
||||
float: right;\
|
||||
font-family: Arial;\
|
||||
font-size: 16px;\
|
||||
height: 14px;\
|
||||
line-height: 16px;\
|
||||
margin: 5px 1px 9px 5px;\
|
||||
padding: 0;\
|
||||
text-align: center;\
|
||||
width: 14px;\
|
||||
}\
|
||||
.ace_searchbtn_close:hover {\
|
||||
background-color: #656565;\
|
||||
background-position: 50% 100%;\
|
||||
color: white;\
|
||||
}\
|
||||
.ace_replacebtn.prev {\
|
||||
width: 54px\
|
||||
}\
|
||||
.ace_replacebtn.next {\
|
||||
width: 27px\
|
||||
}\
|
||||
.ace_button {\
|
||||
margin-left: 2px;\
|
||||
cursor: pointer;\
|
||||
-webkit-user-select: none;\
|
||||
-moz-user-select: none;\
|
||||
-o-user-select: none;\
|
||||
-ms-user-select: none;\
|
||||
user-select: none;\
|
||||
overflow: hidden;\
|
||||
opacity: 0.7;\
|
||||
border: 1px solid rgba(100,100,100,0.23);\
|
||||
padding: 1px;\
|
||||
-moz-box-sizing: border-box;\
|
||||
box-sizing: border-box;\
|
||||
color: black;\
|
||||
}\
|
||||
.ace_button:hover {\
|
||||
background-color: #eee;\
|
||||
opacity:1;\
|
||||
}\
|
||||
.ace_button:active {\
|
||||
background-color: #ddd;\
|
||||
}\
|
||||
.ace_button.checked {\
|
||||
border-color: #3399ff;\
|
||||
opacity:1;\
|
||||
}\
|
||||
.ace_search_options{\
|
||||
margin-bottom: 3px;\
|
||||
text-align: right;\
|
||||
-webkit-user-select: none;\
|
||||
-moz-user-select: none;\
|
||||
-o-user-select: none;\
|
||||
-ms-user-select: none;\
|
||||
user-select: none;\
|
||||
}";
|
||||
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
||||
var keyUtil = require("../lib/keys");
|
||||
|
||||
dom.importCssString(searchboxCss, "ace_searchbox");
|
||||
|
||||
var html = '<div class="ace_search right">\
|
||||
<button type="button" action="hide" class="ace_searchbtn_close"></button>\
|
||||
<div class="ace_search_form">\
|
||||
<input class="ace_search_field" placeholder="Search for" spellcheck="false"></input>\
|
||||
<button type="button" action="findNext" class="ace_searchbtn next"></button>\
|
||||
<button type="button" action="findPrev" class="ace_searchbtn prev"></button>\
|
||||
</div>\
|
||||
<div class="ace_replace_form">\
|
||||
<input class="ace_search_field" placeholder="Replace with" spellcheck="false"></input>\
|
||||
<button type="button" action="replaceAndFindNext" class="ace_replacebtn">Replace</button>\
|
||||
<button type="button" action="replaceAll" class="ace_replacebtn">All</button>\
|
||||
</div>\
|
||||
<div class="ace_search_options">\
|
||||
<span action="toggleRegexpMode" class="ace_button" title="RegExp Search">.*</span>\
|
||||
<span action="toggleCaseSensitive" class="ace_button" title="CaseSensitive Search">Aa</span>\
|
||||
<span action="toggleWholeWords" class="ace_button" title="Whole Word Search">\\b</span>\
|
||||
</div>\
|
||||
</div>'.replace(/>\s+/g, ">");
|
||||
|
||||
var SearchBox = function(editor, range, showReplaceForm) {
|
||||
var div = dom.createElement("div");
|
||||
div.innerHTML = html;
|
||||
this.element = div.firstChild;
|
||||
|
||||
this.$init();
|
||||
this.setEditor(editor);
|
||||
};
|
||||
|
||||
(function() {
|
||||
this.setEditor = function(editor) {
|
||||
editor.searchBox = this;
|
||||
editor.container.appendChild(this.element);
|
||||
this.editor = editor;
|
||||
};
|
||||
|
||||
this.$initElements = function(sb) {
|
||||
this.searchBox = sb.querySelector(".ace_search_form");
|
||||
this.replaceBox = sb.querySelector(".ace_replace_form");
|
||||
this.searchOptions = sb.querySelector(".ace_search_options");
|
||||
this.regExpOption = sb.querySelector("[action=toggleRegexpMode]");
|
||||
this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]");
|
||||
this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]");
|
||||
this.searchInput = this.searchBox.querySelector(".ace_search_field");
|
||||
this.replaceInput = this.replaceBox.querySelector(".ace_search_field");
|
||||
};
|
||||
|
||||
this.$init = function() {
|
||||
var sb = this.element;
|
||||
|
||||
this.$initElements(sb);
|
||||
|
||||
var _this = this;
|
||||
event.addListener(sb, "mousedown", function(e) {
|
||||
setTimeout(function(){
|
||||
_this.activeInput.focus();
|
||||
}, 0);
|
||||
event.stopPropagation(e);
|
||||
});
|
||||
event.addListener(sb, "click", function(e) {
|
||||
var t = e.target || e.srcElement;
|
||||
var action = t.getAttribute("action");
|
||||
if (action && _this[action])
|
||||
_this[action]();
|
||||
else if (_this.$searchBarKb.commands[action])
|
||||
_this.$searchBarKb.commands[action].exec(_this);
|
||||
event.stopPropagation(e);
|
||||
});
|
||||
|
||||
event.addCommandKeyListener(sb, function(e, hashId, keyCode) {
|
||||
var keyString = keyUtil.keyCodeToString(keyCode);
|
||||
var command = _this.$searchBarKb.findKeyCommand(hashId, keyString);
|
||||
if (command && command.exec) {
|
||||
command.exec(_this);
|
||||
event.stopEvent(e);
|
||||
}
|
||||
});
|
||||
|
||||
this.$onChange = lang.delayedCall(function() {
|
||||
_this.find(false, false);
|
||||
});
|
||||
|
||||
event.addListener(this.searchInput, "input", function() {
|
||||
_this.$onChange.schedule(20);
|
||||
});
|
||||
event.addListener(this.searchInput, "focus", function() {
|
||||
_this.activeInput = _this.searchInput;
|
||||
_this.searchInput.value && _this.highlight();
|
||||
});
|
||||
event.addListener(this.replaceInput, "focus", function() {
|
||||
_this.activeInput = _this.replaceInput;
|
||||
_this.searchInput.value && _this.highlight();
|
||||
});
|
||||
};
|
||||
this.$closeSearchBarKb = new HashHandler([{
|
||||
bindKey: "Esc",
|
||||
name: "closeSearchBar",
|
||||
exec: function(editor) {
|
||||
editor.searchBox.hide();
|
||||
}
|
||||
}]);
|
||||
this.$searchBarKb = new HashHandler();
|
||||
this.$searchBarKb.bindKeys({
|
||||
"Ctrl-f|Command-f|Ctrl-H|Command-Option-F": function(sb) {
|
||||
var isReplace = sb.isReplace = !sb.isReplace;
|
||||
sb.replaceBox.style.display = isReplace ? "" : "none";
|
||||
sb[isReplace ? "replaceInput" : "searchInput"].focus();
|
||||
},
|
||||
"Ctrl-G|Command-G": function(sb) {
|
||||
sb.findNext();
|
||||
},
|
||||
"Ctrl-Shift-G|Command-Shift-G": function(sb) {
|
||||
sb.findPrev();
|
||||
},
|
||||
"esc": function(sb) {
|
||||
setTimeout(function() { sb.hide();});
|
||||
},
|
||||
"Return": function(sb) {
|
||||
if (sb.activeInput == sb.replaceInput)
|
||||
sb.replace();
|
||||
sb.findNext();
|
||||
},
|
||||
"Shift-Return": function(sb) {
|
||||
if (sb.activeInput == sb.replaceInput)
|
||||
sb.replace();
|
||||
sb.findPrev();
|
||||
},
|
||||
"Tab": function(sb) {
|
||||
(sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus();
|
||||
}
|
||||
});
|
||||
|
||||
this.$searchBarKb.addCommands([{
|
||||
name: "toggleRegexpMode",
|
||||
bindKey: {win: "Alt-R|Alt-/", mac: "Ctrl-Alt-R|Ctrl-Alt-/"},
|
||||
exec: function(sb) {
|
||||
sb.regExpOption.checked = !sb.regExpOption.checked;
|
||||
sb.$syncOptions();
|
||||
}
|
||||
}, {
|
||||
name: "toggleCaseSensitive",
|
||||
bindKey: {win: "Alt-C|Alt-I", mac: "Ctrl-Alt-R|Ctrl-Alt-I"},
|
||||
exec: function(sb) {
|
||||
sb.caseSensitiveOption.checked = !sb.caseSensitiveOption.checked;
|
||||
sb.$syncOptions();
|
||||
}
|
||||
}, {
|
||||
name: "toggleWholeWords",
|
||||
bindKey: {win: "Alt-B|Alt-W", mac: "Ctrl-Alt-B|Ctrl-Alt-W"},
|
||||
exec: function(sb) {
|
||||
sb.wholeWordOption.checked = !sb.wholeWordOption.checked;
|
||||
sb.$syncOptions();
|
||||
}
|
||||
}]);
|
||||
|
||||
this.$syncOptions = function() {
|
||||
dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked);
|
||||
dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked);
|
||||
dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked);
|
||||
this.find(false, false);
|
||||
};
|
||||
|
||||
this.highlight = function(re) {
|
||||
this.editor.session.highlight(re || this.editor.$search.$options.re);
|
||||
this.editor.renderer.updateBackMarkers()
|
||||
};
|
||||
this.find = function(skipCurrent, backwards) {
|
||||
var range = this.editor.find(this.searchInput.value, {
|
||||
skipCurrent: skipCurrent,
|
||||
backwards: backwards,
|
||||
wrap: true,
|
||||
regExp: this.regExpOption.checked,
|
||||
caseSensitive: this.caseSensitiveOption.checked,
|
||||
wholeWord: this.wholeWordOption.checked
|
||||
});
|
||||
var noMatch = !range && this.searchInput.value;
|
||||
dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
|
||||
this.editor._emit("findSearchBox", { match: !noMatch });
|
||||
this.highlight();
|
||||
};
|
||||
this.findNext = function() {
|
||||
this.find(true, false);
|
||||
};
|
||||
this.findPrev = function() {
|
||||
this.find(true, true);
|
||||
};
|
||||
this.replace = function() {
|
||||
if (!this.editor.getReadOnly())
|
||||
this.editor.replace(this.replaceInput.value);
|
||||
};
|
||||
this.replaceAndFindNext = function() {
|
||||
if (!this.editor.getReadOnly()) {
|
||||
this.editor.replace(this.replaceInput.value);
|
||||
this.findNext()
|
||||
}
|
||||
};
|
||||
this.replaceAll = function() {
|
||||
if (!this.editor.getReadOnly())
|
||||
this.editor.replaceAll(this.replaceInput.value);
|
||||
};
|
||||
|
||||
this.hide = function() {
|
||||
this.element.style.display = "none";
|
||||
this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb);
|
||||
this.editor.focus();
|
||||
};
|
||||
this.show = function(value, isReplace) {
|
||||
this.element.style.display = "";
|
||||
this.replaceBox.style.display = isReplace ? "" : "none";
|
||||
|
||||
this.isReplace = isReplace;
|
||||
|
||||
if (value)
|
||||
this.searchInput.value = value;
|
||||
this.searchInput.focus();
|
||||
this.searchInput.select();
|
||||
|
||||
this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb);
|
||||
};
|
||||
|
||||
}).call(SearchBox.prototype);
|
||||
|
||||
exports.SearchBox = SearchBox;
|
||||
|
||||
exports.Search = function(editor, isReplace) {
|
||||
var sb = editor.searchBox || new SearchBox(editor);
|
||||
sb.show(editor.session.getTextRange(), isReplace);
|
||||
};
|
||||
|
||||
});
|
|
@ -16,7 +16,8 @@ module.exports = function (grunt) {
|
|||
'build_sense',
|
||||
'copy:artifacts_to_build',
|
||||
'clean:build_tmp',
|
||||
'replace:git_commits'
|
||||
'replace:git_commits',
|
||||
'replace:kibana_replace_title'
|
||||
]);
|
||||
|
||||
grunt.registerTask('build_sense', [
|
||||
|
|
|
@ -53,6 +53,21 @@ module.exports = function (config) {
|
|||
cwd: '<%= buildSiteDir %>',
|
||||
expand: true, src: ['**/*'], dest: '<%= buildSiteDir %>/'}
|
||||
]
|
||||
},
|
||||
kibana_replace_title: {
|
||||
options: {
|
||||
patterns: [
|
||||
{
|
||||
match: /<title>.*?<\/title>/,
|
||||
replacement: '<title>{{dashboard.current.title ? dashboard.current.title : "Marvel"}}</title>'
|
||||
}
|
||||
]
|
||||
},
|
||||
files: [
|
||||
{
|
||||
cwd: '<%= buildSiteDir %>',
|
||||
expand: true, src: ['kibana/index.html'], dest: '<%= buildSiteDir %>/'}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|