mirror of
https://github.com/NationalSecurityAgency/timely.git
synced 2026-11-10 03:41:25 -10:00
Compare commits
478
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
577688aa54 | ||
|
|
1f0fe4198d | ||
|
|
385b142f71 | ||
|
|
9065dcdce2 | ||
|
|
3877695949 | ||
|
|
d4e504b31c | ||
|
|
32ae6ee399 | ||
|
|
9688514a0f | ||
|
|
9aa089a585 | ||
|
|
8d75089525 | ||
|
|
7857d862b3 | ||
|
|
e8e2e5e144 | ||
|
|
33515061b7 | ||
|
|
7d2ab1be7e | ||
|
|
521969b9ee | ||
|
|
62fb143490 | ||
|
|
08cf3dc5c2 | ||
|
|
6831223f50 | ||
|
|
20ed753723 | ||
|
|
a8e3737200 | ||
|
|
d1c3989a8d | ||
|
|
5712ebe2ae | ||
|
|
e47cbe5e3b | ||
|
|
f5b3a205b2 | ||
|
|
a2b61191c7 | ||
|
|
adfd4080bd | ||
|
|
c5da744883 | ||
|
|
ebc35e5c63 | ||
|
|
3e49716525 | ||
|
|
b47668f277 | ||
|
|
483f5f8f1a | ||
|
|
eb45592f90 | ||
|
|
7ad726a551 | ||
|
|
87a3b43b13 | ||
|
|
b13bd4c65d | ||
|
|
5a5c539688 | ||
|
|
465223cdb6 | ||
|
|
99e4e93603 | ||
|
|
193c9f2c5b | ||
|
|
8490e9cbc6 | ||
|
|
aa08e5df03 | ||
|
|
053017ba39 | ||
|
|
6f188b3473 | ||
|
|
b27b3c3e52 | ||
|
|
b2330262bd | ||
|
|
0702fa3d47 | ||
|
|
813d8e5d28 | ||
|
|
e63f41a20c | ||
|
|
9d7ca484e3 | ||
|
|
bb787fc8ca | ||
|
|
6dd96e1c15 | ||
|
|
a68507a00e | ||
|
|
8d3eb0a433 | ||
|
|
e9384ed058 | ||
|
|
31e021a01f | ||
|
|
ad752cd538 | ||
|
|
ed6578ca73 | ||
|
|
1e54d1dd41 | ||
|
|
cec5d662ad | ||
|
|
f380e4c203 | ||
|
|
19d651a65a | ||
|
|
13b113d1a5 | ||
|
|
979aaa91d5 | ||
|
|
b84d76cd83 | ||
|
|
6a8c406f56 | ||
|
|
30c270c029 | ||
|
|
28066ed281 | ||
|
|
6e37ca292f | ||
|
|
db48d22661 | ||
|
|
365a0b58ea | ||
|
|
d4bd994f03 | ||
|
|
1c5bd80d4b | ||
|
|
d6a7b5ff97 | ||
|
|
ac9cb0aef9 | ||
|
|
d88d714d90 | ||
|
|
ffa2829fe5 | ||
|
|
b5c6495662 | ||
|
|
471c19a7c0 | ||
|
|
1d108d0f40 | ||
|
|
7f1d6da6dc | ||
|
|
a4b9208af0 | ||
|
|
9f83bbd158 | ||
|
|
5c87f10383 | ||
|
|
ccd158df4f | ||
|
|
20d94974fa | ||
|
|
4f5443ce06 | ||
|
|
79bb28c2f8 | ||
|
|
8cba352315 | ||
|
|
884cda666a | ||
|
|
c0dddf7560 | ||
|
|
44cf741f01 | ||
|
|
63e3e30bc5 | ||
|
|
b6084cfd0a | ||
|
|
02428eb687 | ||
|
|
7f88aafcba | ||
|
|
f4dea9008d | ||
|
|
e1026d921c | ||
|
|
35690907be | ||
|
|
3ad0542fc7 | ||
|
|
65c3dbd544 | ||
|
|
bc8ac808b4 | ||
|
|
75f61557a2 | ||
|
|
f90e6b076d | ||
|
|
47cca3d8e7 | ||
|
|
837a7e7873 | ||
|
|
b871b44b72 | ||
|
|
97a4c89d0c | ||
|
|
8e14f805dc | ||
|
|
82717e043d | ||
|
|
018a6939db | ||
|
|
2618c77db0 | ||
|
|
c9d8d34e79 | ||
|
|
40443b32fd | ||
|
|
1151d00926 | ||
|
|
c76359868a | ||
|
|
964c25a36b | ||
|
|
1e86f824af | ||
|
|
01fd02005a | ||
|
|
fb4c2a9ceb | ||
|
|
ecdce169bc | ||
|
|
f0a8dec91c | ||
|
|
e9cfc6bf3c | ||
|
|
150ff8a800 | ||
|
|
727ac193ab | ||
|
|
3c1c9c49d5 | ||
|
|
4a3fdfaea2 | ||
|
|
ce2f2ae8de | ||
|
|
4136f7b0f7 | ||
|
|
dc45e382ce | ||
|
|
57aa27c0eb | ||
|
|
236ca52c93 | ||
|
|
787eb795b6 | ||
|
|
e0cdc7339e | ||
|
|
fd27069a2d | ||
|
|
fcea9014d2 | ||
|
|
9e42fbb04c | ||
|
|
1c508a738f | ||
|
|
7078f7f6e8 | ||
|
|
92d732f8a4 | ||
|
|
c90eab79f9 | ||
|
|
977f2568d7 | ||
|
|
ed9e958df7 | ||
|
|
22ffeba825 | ||
|
|
5d27eaf9cf | ||
|
|
07c5ccefc0 | ||
|
|
47e18611f2 | ||
|
|
83d655b55e | ||
|
|
d4e94080d4 | ||
|
|
befb8d6fac | ||
|
|
b0e0192374 | ||
|
|
87a2950ade | ||
|
|
eb1c0db712 | ||
|
|
5e605079eb | ||
|
|
ebc8810d65 | ||
|
|
5cfcbc7f7b | ||
|
|
adeb496a88 | ||
|
|
b9d45b2185 | ||
|
|
0c37f0dfc4 | ||
|
|
ad3b2769fe | ||
|
|
2e22c87c1b | ||
|
|
7639ce60bc | ||
|
|
ae3d50277e | ||
|
|
dcc563b406 | ||
|
|
f3591651f1 | ||
|
|
7f873ca9e0 | ||
|
|
415dcf2c90 | ||
|
|
fd5cba00f0 | ||
|
|
d14fd0f70e | ||
|
|
bb24528f85 | ||
|
|
872550d0b7 | ||
|
|
a15a6ba1b1 | ||
|
|
be57777a3d | ||
|
|
1fe3ce6396 | ||
|
|
203bc79211 | ||
|
|
685b1554e9 | ||
|
|
1eac62fd59 | ||
|
|
48c514abc7 | ||
|
|
8ab0ed7b24 | ||
|
|
a38011b4a5 | ||
|
|
1f4e6b915d | ||
|
|
402f931fc9 | ||
|
|
6d6c3e6070 | ||
|
|
2e8be87224 | ||
|
|
b7e65975b0 | ||
|
|
d27f5c4b7d | ||
|
|
384ffac2fd | ||
|
|
ab816e432a | ||
|
|
99ee16e359 | ||
|
|
3a53a3c029 | ||
|
|
67263d8db4 | ||
|
|
06c1641ad1 | ||
|
|
234d1e197d | ||
|
|
22e8e480e5 | ||
|
|
22fc8af88c | ||
|
|
bdebff8250 | ||
|
|
d0e46a3894 | ||
|
|
d7769c653d | ||
|
|
094c38223e | ||
|
|
fddf304087 | ||
|
|
452876af0d | ||
|
|
16dca4d000 | ||
|
|
f989143692 | ||
|
|
4fe37604b7 | ||
|
|
5da237e7fe | ||
|
|
23eb3f433e | ||
|
|
fd684122dd | ||
|
|
6ea72a13fa | ||
|
|
a91ac15fab | ||
|
|
e14878ff56 | ||
|
|
8276ba8ff4 | ||
|
|
ec2415e319 | ||
|
|
f5b34ba69a | ||
|
|
c39c82c917 | ||
|
|
e3f36f9057 | ||
|
|
a7596ff0a5 | ||
|
|
a9d8b9b3b1 | ||
|
|
c8e7c646dc | ||
|
|
0dac148f95 | ||
|
|
f2ebab9325 | ||
|
|
1d13e198d0 | ||
|
|
b550f04dae | ||
|
|
b1e0ff5d94 | ||
|
|
3a236aa81e | ||
|
|
a60fd38cda | ||
|
|
6062141cce | ||
|
|
506095a1ed | ||
|
|
7452f6b172 | ||
|
|
321a994cdc | ||
|
|
d82f9707b3 | ||
|
|
923add84f6 | ||
|
|
58bcd3922d | ||
|
|
0bb523732e | ||
|
|
e644e3a5d3 | ||
|
|
8e0396d7d3 | ||
|
|
ebfe330a2b | ||
|
|
f5dcc4efb8 | ||
|
|
2011c772a1 | ||
|
|
fd2f54b3de | ||
|
|
384f8d7b19 | ||
|
|
97f87c9c4a | ||
|
|
13b4487a2b | ||
|
|
e619c48a94 | ||
|
|
b744ba51bb | ||
|
|
44a0dafe03 | ||
|
|
dfa162fed5 | ||
|
|
fd1937a3f7 | ||
|
|
10179a6613 | ||
|
|
8eada057da | ||
|
|
872c00a894 | ||
|
|
bb5c721450 | ||
|
|
7c110ede15 | ||
|
|
eb5235ff8c | ||
|
|
5ad521614d | ||
|
|
73d2997a24 | ||
|
|
5e89f01652 | ||
|
|
757cf8ef79 | ||
|
|
ddf0346f04 | ||
|
|
115d04f19e | ||
|
|
f3eaecc7a9 | ||
|
|
04944c9e8f | ||
|
|
868ea757f1 | ||
|
|
be28b785a1 | ||
|
|
902ddd7fdc | ||
|
|
c665bb4e8f | ||
|
|
14a9aac0fc | ||
|
|
c74bf7b50e | ||
|
|
2828342821 | ||
|
|
482f0b9979 | ||
|
|
058429a9fd | ||
|
|
77cdc3fc66 | ||
|
|
5e649aa91a | ||
|
|
a01287460a | ||
|
|
3530073f2e | ||
|
|
39dc7095f7 | ||
|
|
28b58aed86 | ||
|
|
ae07d2f6d2 | ||
|
|
ef4da2a0c0 | ||
|
|
f00be01caf | ||
|
|
ed5fa1b75c | ||
|
|
b1256f4800 | ||
|
|
b40b3b47ed | ||
|
|
9c7ea33305 | ||
|
|
ff7f3c6708 | ||
|
|
bd6865585c | ||
|
|
c59addd90e | ||
|
|
1f438aaea6 | ||
|
|
8e7b03b64e | ||
|
|
82a99d5537 | ||
|
|
3c36233e6b | ||
|
|
54e4408d8c | ||
|
|
965156d54b | ||
|
|
f1d92f01dd | ||
|
|
052036b0c2 | ||
|
|
c5381c62af | ||
|
|
60d1dbd37d | ||
|
|
512b57d799 | ||
|
|
395502b152 | ||
|
|
18cd104883 | ||
|
|
51e537aca2 | ||
|
|
1b1427c3ee | ||
|
|
324da17ba3 | ||
|
|
719061a638 | ||
|
|
e8a491c9ea | ||
|
|
0f89c96ba0 | ||
|
|
d552a1b5cb | ||
|
|
1bb564f84a | ||
|
|
f868ff0bcf | ||
|
|
d1fdd685a6 | ||
|
|
0f93ffcc2b | ||
|
|
bdbd7a38a8 | ||
|
|
b147e6a2d7 | ||
|
|
200a871f85 | ||
|
|
0a8c73adab | ||
|
|
c1359e81a7 | ||
|
|
3e4f52dfc9 | ||
|
|
f4f2a1abfa | ||
|
|
b5419b1e9c | ||
|
|
3a91faa8e6 | ||
|
|
062e35a3b3 | ||
|
|
00a93ec76e | ||
|
|
65496915b8 | ||
|
|
3125ad8f72 | ||
|
|
1e144aaa66 | ||
|
|
1478579c59 | ||
|
|
d77f1e7b9f | ||
|
|
e559ad1097 | ||
|
|
c2c35cfa25 | ||
|
|
ec9a688846 | ||
|
|
4d4f6efeb8 | ||
|
|
5a69760f54 | ||
|
|
0aff03f8da | ||
|
|
b363be873b | ||
|
|
7dead4c392 | ||
|
|
d71ae97f7a | ||
|
|
cfa9f48dd0 | ||
|
|
1e341563fb | ||
|
|
34c6c69d61 | ||
|
|
a35e1341b6 | ||
|
|
078c42a082 | ||
|
|
e0cee862d9 | ||
|
|
e5cf1f2589 | ||
|
|
6bf197da74 | ||
|
|
0eb4caa425 | ||
|
|
2f617a2160 | ||
|
|
938a056fcc | ||
|
|
f9c4195f23 | ||
|
|
2384d9af3f | ||
|
|
adcf6c7099 | ||
|
|
126ea199cc | ||
|
|
edd22fde15 | ||
|
|
84b3e5e3d6 | ||
|
|
0316c0c5e9 | ||
|
|
259bf0f560 | ||
|
|
6d73ffb052 | ||
|
|
75869a561f | ||
|
|
215d773fcf | ||
|
|
03b28598cc | ||
|
|
df2d2ec5e9 | ||
|
|
0feb513407 | ||
|
|
7386fd2e21 | ||
|
|
fc0c995cb6 | ||
|
|
06680512a8 | ||
|
|
e3e72aec83 | ||
|
|
26eef2f000 | ||
|
|
9bbb62877e | ||
|
|
cabfe33e58 | ||
|
|
a4e1fb3c24 | ||
|
|
0ebbae94fe | ||
|
|
440c799c4c | ||
|
|
3de499063c | ||
|
|
3c4e0d0bae | ||
|
|
0a0e84b8e8 | ||
|
|
477146e5bf | ||
|
|
4e37eeb524 | ||
|
|
4d568401a3 | ||
|
|
535a3b0486 | ||
|
|
5892700b5c | ||
|
|
59ee05c606 | ||
|
|
f2d1387971 | ||
|
|
02e48810ba | ||
|
|
0ddd142279 | ||
|
|
e669a0d4ac | ||
|
|
d1b98be182 | ||
|
|
b171d3c48a | ||
|
|
adf94ffbf6 | ||
|
|
68807cd485 | ||
|
|
ebbd2d89b4 | ||
|
|
53db9d4a64 | ||
|
|
32e0470613 | ||
|
|
ca23e5ccdc | ||
|
|
0c41f3c213 | ||
|
|
c091fae675 | ||
|
|
e307f1dbbb | ||
|
|
c364a2329b | ||
|
|
88fcba6798 | ||
|
|
b90436e48e | ||
|
|
dc7a7dce41 | ||
|
|
6e7c428315 | ||
|
|
fa0115eb57 | ||
|
|
8eb3ce6237 | ||
|
|
bb83688fc7 | ||
|
|
5b3dd91fff | ||
|
|
3c5231fbd0 | ||
|
|
8ea97c7f77 | ||
|
|
5bc89ce207 | ||
|
|
b7a4296978 | ||
|
|
3321fd1616 | ||
|
|
7defe86bf6 | ||
|
|
35598a4e16 | ||
|
|
d84692b7b9 | ||
|
|
1c9d74a75b | ||
|
|
d744b8a22f | ||
|
|
730ee46de9 | ||
|
|
a406646361 | ||
|
|
c1502d0476 | ||
|
|
446f9341b9 | ||
|
|
6d7ba45f2c | ||
|
|
24729b9308 | ||
|
|
bc9b1263be | ||
|
|
9331e3b9db | ||
|
|
27e0db7d14 | ||
|
|
42cb37cc9e | ||
|
|
5b306d8134 | ||
|
|
b39deb560f | ||
|
|
198484ef2b | ||
|
|
d3ab3cc4b7 | ||
|
|
ca40fed573 | ||
|
|
bdd05b3c0c | ||
|
|
7c6594c595 | ||
|
|
c404bc02df | ||
|
|
5acbe01c3d | ||
|
|
e2b70aff42 | ||
|
|
834119ecd8 | ||
|
|
26d47b06a0 | ||
|
|
dc33dfe0a7 | ||
|
|
81b9110cad | ||
|
|
052ac6db37 | ||
|
|
f10e324f5f | ||
|
|
6a22d7c11d | ||
|
|
35a5d35512 | ||
|
|
04c0abbcfa | ||
|
|
e5658f498a | ||
|
|
2e9e2b65f8 | ||
|
|
91ff8a738a | ||
|
|
20f303f3d1 | ||
|
|
a250c6dbc2 | ||
|
|
489fb16b94 | ||
|
|
d72f08d936 | ||
|
|
e7a0552edc | ||
|
|
916412b51b | ||
|
|
6155c542a3 | ||
|
|
8aa611f77a | ||
|
|
84b7705fd1 | ||
|
|
2d75b7a04d | ||
|
|
dac9cff8fc | ||
|
|
b15573155e | ||
|
|
e641bd7d4a | ||
|
|
d562c8f71e | ||
|
|
0a78bb259f | ||
|
|
a4e05ed506 | ||
|
|
82b8ca4616 | ||
|
|
63429f1e9a | ||
|
|
1030376e37 | ||
|
|
f7e6117e85 | ||
|
|
38ad55851a | ||
|
|
7ae21ae972 | ||
|
|
fd80b240bd | ||
|
|
28f442e529 | ||
|
|
7ba798c51d | ||
|
|
5cc9188e3c | ||
|
|
9e84ebf28c | ||
|
|
541998da39 | ||
|
|
bc79a655e5 | ||
|
|
6c077ba873 | ||
|
|
7edf6c803d | ||
|
|
a1e673e599 | ||
|
|
f656a9aa6d | ||
|
|
9a707a0172 |
No files matched your search
@@ -0,0 +1,9 @@
|
||||
If this is a question or feature request, make sure to:
|
||||
|
||||
- [ ] The title starts with `Question:` or `Feature:`.
|
||||
|
||||
If this is an bug report, not a question, make sure to:
|
||||
|
||||
- [ ] I'm not running Windows (which is unsupported), or if I am, I can confirm this issue appears on another platform, or Vagrant.
|
||||
- [ ] This issue appears in the latest `dev` branch.
|
||||
- [ ] I've included my browser and Ruby version in this issue.
|
||||
@@ -0,0 +1,5 @@
|
||||
!!!!! STOP AND READ !!!!!
|
||||
|
||||
If the dropdown above says "base fork: lord/master", you are submitting your change to ALL USERS OF SLATE, not just your company. This is probably not what you want. Click "base fork" to change it to the right place.
|
||||
|
||||
If you're actually trying to submit a change to upstream Slate, please submit to our dev branch, PRs sent to the master branch are generally rejected.
|
||||
+23
-7
@@ -1,9 +1,25 @@
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
target/
|
||||
*.gem
|
||||
*.rbc
|
||||
.bundle
|
||||
.config
|
||||
coverage
|
||||
InstalledFiles
|
||||
lib/bundler/man
|
||||
pkg
|
||||
rdoc
|
||||
spec/reports
|
||||
test/tmp
|
||||
test/version_tmp
|
||||
tmp
|
||||
*.DS_STORE
|
||||
build/
|
||||
.cache
|
||||
.vagrant
|
||||
.sass-cache
|
||||
|
||||
# YARD artifacts
|
||||
.yardoc
|
||||
_yardoc
|
||||
doc/
|
||||
.idea/
|
||||
*.iml
|
||||
logs/
|
||||
server/pki/
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
sudo: false
|
||||
|
||||
language: ruby
|
||||
|
||||
rvm:
|
||||
- 2.2.5
|
||||
- 2.3.1
|
||||
|
||||
cache: bundler
|
||||
script: bundle exec middleman build
|
||||
@@ -0,0 +1,88 @@
|
||||
# Changelog
|
||||
|
||||
## Version 1.4.0
|
||||
|
||||
*November 24, 2016*
|
||||
|
||||
- Upgrade Middleman and Rouge gems, should hopefully solve a number of bugs
|
||||
- Update some links in README
|
||||
- Fix broken Vagrant startup script
|
||||
- Fix some problems with deploy.sh help message
|
||||
- Fix bug with language tabs not hiding properly if no error
|
||||
- Add `!default` to SASS variables
|
||||
- Fix bug with logo margin
|
||||
- Bump tested Ruby versions in .travis.yml
|
||||
|
||||
## Version 1.3.3
|
||||
|
||||
*June 11, 2016*
|
||||
|
||||
Documentation and example changes.
|
||||
|
||||
## Version 1.3.2
|
||||
|
||||
*February 3, 2016*
|
||||
|
||||
A small bugfix for slightly incorrect background colors on code samples in some cases.
|
||||
|
||||
## Version 1.3.1
|
||||
|
||||
*January 31, 2016*
|
||||
|
||||
A small bugfix for incorrect whitespace in code blocks.
|
||||
|
||||
## Version 1.3
|
||||
|
||||
*January 27, 2016*
|
||||
|
||||
We've upgraded Middleman and a number of other dependencies, which should fix quite a few bugs.
|
||||
|
||||
Instead of `rake build` and `rake deploy`, you should now run `bundle exec middleman build --clean` to build your server, and `./deploy.sh` to deploy it to Github Pages.
|
||||
|
||||
## Version 1.2
|
||||
|
||||
*June 20, 2015*
|
||||
|
||||
**Fixes:**
|
||||
|
||||
- Remove crash on invalid languages
|
||||
- Update Tocify to scroll to the highlighted header in the Table of Contents
|
||||
- Fix variable leak and update search algorithms
|
||||
- Update Python examples to be valid Python
|
||||
- Update gems
|
||||
- More misc. bugfixes of Javascript errors
|
||||
- Add Dockerfile
|
||||
- Remove unused gems
|
||||
- Optimize images, fonts, and generated asset files
|
||||
- Add chinese font support
|
||||
- Remove RedCarpet header ID patch
|
||||
- Update language tabs to not disturb existing query strings
|
||||
|
||||
## Version 1.1
|
||||
|
||||
*July 27, 2014*
|
||||
|
||||
**Fixes:**
|
||||
|
||||
- Finally, a fix for the redcarpet upgrade bug
|
||||
|
||||
## Version 1.0
|
||||
|
||||
*July 2, 2014*
|
||||
|
||||
[View Issues](https://github.com/tripit/slate/issues?milestone=1&state=closed)
|
||||
|
||||
**Features:**
|
||||
|
||||
- Responsive designs for phones and tablets
|
||||
- Started tagging versions
|
||||
|
||||
**Fixes:**
|
||||
|
||||
- Fixed 'unrecognized expression' error
|
||||
- Fixed #undefined hash bug
|
||||
- Fixed bug where the current language tab would be unselected
|
||||
- Fixed bug where tocify wouldn't highlight the current section while searching
|
||||
- Fixed bug where ids of header tags would have special characters that caused problems
|
||||
- Updated layout so that pages with disabled search wouldn't load search.js
|
||||
- Cleaned up Javascript
|
||||
@@ -1,6 +0,0 @@
|
||||
Dave Marion ([email protected])
|
||||
Eric Newton ([email protected])
|
||||
Jim Klucar ([email protected])
|
||||
Charlie Williams ([email protected])
|
||||
Drew Farris ([email protected])
|
||||
Brian Loss ([email protected])
|
||||
@@ -0,0 +1,9 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Middleman
|
||||
gem 'middleman', '~>4.1.0'
|
||||
gem 'middleman-syntax', '~> 3.0.0'
|
||||
gem 'middleman-autoprefixer', '~> 2.7.0'
|
||||
gem "middleman-sprockets", "~> 4.0.0"
|
||||
gem 'rouge', '~> 2.0.5'
|
||||
gem 'redcarpet', '~> 3.3.2'
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (4.2.7.1)
|
||||
i18n (~> 0.7)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.4.0)
|
||||
autoprefixer-rails (6.4.1)
|
||||
execjs
|
||||
backports (3.6.8)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.10.0)
|
||||
compass-import-once (1.0.5)
|
||||
sass (>= 3.2, < 3.5)
|
||||
concurrent-ruby (1.0.2)
|
||||
contracts (0.13.0)
|
||||
dotenv (2.1.1)
|
||||
erubis (2.7.0)
|
||||
execjs (2.7.0)
|
||||
fast_blank (1.0.0)
|
||||
fastimage (2.0.0)
|
||||
addressable (~> 2)
|
||||
ffi (1.9.14)
|
||||
haml (4.0.7)
|
||||
tilt
|
||||
hamster (3.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
hashie (3.4.4)
|
||||
i18n (0.7.0)
|
||||
json (1.8.3)
|
||||
kramdown (1.12.0)
|
||||
listen (3.0.8)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
memoist (0.15.0)
|
||||
middleman (4.1.10)
|
||||
coffee-script (~> 2.2)
|
||||
compass-import-once (= 1.0.5)
|
||||
haml (>= 4.0.5)
|
||||
kramdown (~> 1.2)
|
||||
middleman-cli (= 4.1.10)
|
||||
middleman-core (= 4.1.10)
|
||||
sass (>= 3.4.0, < 4.0)
|
||||
middleman-autoprefixer (2.7.0)
|
||||
autoprefixer-rails (>= 6.3.1, < 7.0.0)
|
||||
middleman-core (>= 3.3.3)
|
||||
middleman-cli (4.1.10)
|
||||
thor (>= 0.17.0, < 2.0)
|
||||
middleman-core (4.1.10)
|
||||
activesupport (~> 4.2)
|
||||
addressable (~> 2.3)
|
||||
backports (~> 3.6)
|
||||
bundler (~> 1.1)
|
||||
contracts (~> 0.13.0)
|
||||
dotenv
|
||||
erubis
|
||||
execjs (~> 2.0)
|
||||
fast_blank
|
||||
fastimage (~> 2.0)
|
||||
hamster (~> 3.0)
|
||||
hashie (~> 3.4)
|
||||
i18n (~> 0.7.0)
|
||||
listen (~> 3.0.0)
|
||||
memoist (~> 0.14)
|
||||
padrino-helpers (~> 0.13.0)
|
||||
parallel
|
||||
rack (>= 1.4.5, < 2.0)
|
||||
sass (>= 3.4)
|
||||
servolux
|
||||
tilt (~> 1.4.1)
|
||||
uglifier (~> 3.0)
|
||||
middleman-sprockets (4.0.0)
|
||||
middleman-core (~> 4.0)
|
||||
sprockets (>= 3.0)
|
||||
middleman-syntax (3.0.0)
|
||||
middleman-core (>= 3.2)
|
||||
rouge (~> 2.0)
|
||||
minitest (5.9.0)
|
||||
padrino-helpers (0.13.3.1)
|
||||
i18n (~> 0.6, >= 0.6.7)
|
||||
padrino-support (= 0.13.3.1)
|
||||
tilt (>= 1.4.1, < 3)
|
||||
padrino-support (0.13.3.1)
|
||||
activesupport (>= 3.1)
|
||||
parallel (1.9.0)
|
||||
rack (1.6.4)
|
||||
rb-fsevent (0.9.7)
|
||||
rb-inotify (0.9.7)
|
||||
ffi (>= 0.5.0)
|
||||
redcarpet (3.3.4)
|
||||
rouge (2.0.5)
|
||||
sass (3.4.22)
|
||||
servolux (0.12.0)
|
||||
sprockets (3.7.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.5)
|
||||
tilt (1.4.1)
|
||||
tzinfo (1.2.2)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (3.0.2)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
middleman (~> 4.1.0)
|
||||
middleman-autoprefixer (~> 2.7.0)
|
||||
middleman-sprockets (~> 4.0.0)
|
||||
middleman-syntax (~> 3.0.0)
|
||||
redcarpet (~> 3.3.2)
|
||||
rouge (~> 2.0.5)
|
||||
|
||||
BUNDLED WITH
|
||||
1.12.5
|
||||
@@ -1,203 +1,13 @@
|
||||
Copyright 2008-2013 Concur Technologies, Inc.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
@@ -1,2 +0,0 @@
|
||||
This product includes Google Flatbuffers (https://google.github.io/flatbuffers/)
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
@@ -1,8 +1,110 @@
|
||||

|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/lord/img/master/logo-slate.png" alt="Slate: API Documentation Generator" width="226">
|
||||
<br>
|
||||
<a href="https://travis-ci.org/lord/slate"><img src="https://travis-ci.org/lord/slate.svg?branch=master" alt="Build Status"></a>
|
||||
</p>
|
||||
|
||||
[![Apache License][li]][ll]
|
||||
<p align="center">Slate helps you create beautiful, intelligent, responsive API documentation.</p>
|
||||
|
||||
Timely is a time series database application that provides secure access to time series data. Timely is written in Java and designed to work with [Apache Accumulo](http://accumulo.apache.org/) and [Grafana](http://www.grafana.org). Documentation is located [here](https://nationalsecurityagency.github.io/timely/).
|
||||
<p align="center"><img src="https://dl.dropboxusercontent.com/u/95847291/github%20images/slate/slate_screenshot_new.png" width=700 alt="Screenshot of Example Documentation created with Slate"></p>
|
||||
|
||||
[li]: http://img.shields.io/badge/license-ASL-blue.svg
|
||||
[ll]: https://www.apache.org/licenses/LICENSE-2.0
|
||||
<p align="center"><em>The example above was created with Slate. Check it out at <a href="https://lord.github.io/slate">lord.github.io/slate</a>.</em></p>
|
||||
|
||||
Features
|
||||
------------
|
||||
|
||||
* **Clean, intuitive design** — With Slate, the description of your API is on the left side of your documentation, and all the code examples are on the right side. Inspired by [Stripe's](https://stripe.com/docs/api) and [Paypal's](https://developer.paypal.com/webapps/developer/docs/api/) API docs. Slate is responsive, so it looks great on tablets, phones, and even in print.
|
||||
|
||||
* **Everything on a single page** — Gone are the days when your users had to search through a million pages to find what they wanted. Slate puts the entire documentation on a single page. We haven't sacrificed linkability, though. As you scroll, your browser's hash will update to the nearest header, so linking to a particular point in the documentation is still natural and easy.
|
||||
|
||||
* **Slate is just Markdown** — When you write docs with Slate, you're just writing Markdown, which makes it simple to edit and understand. Everything is written in Markdown — even the code samples are just Markdown code blocks.
|
||||
|
||||
* **Write code samples in multiple languages** — If your API has bindings in multiple programming languages, you can easily put in tabs to switch between them. In your document, you'll distinguish different languages by specifying the language name at the top of each code block, just like with Github Flavored Markdown.
|
||||
|
||||
* **Out-of-the-box syntax highlighting** for [over 100 languages](https://github.com/jneen/rouge/wiki/List-of-supported-languages-and-lexers), no configuration required.
|
||||
|
||||
* **Automatic, smoothly scrolling table of contents** on the far left of the page. As you scroll, it displays your current position in the document. It's fast, too. We're using Slate at TripIt to build documentation for our new API, where our table of contents has over 180 entries. We've made sure that the performance remains excellent, even for larger documents.
|
||||
|
||||
* **Let your users update your documentation for you** — By default, your Slate-generated documentation is hosted in a public Github repository. Not only does this mean you get free hosting for your docs with Github Pages, but it also makes it simple for other developers to make pull requests to your docs if they find typos or other problems. Of course, if you don't want to use GitHub, you're also welcome to host your docs elsewhere.
|
||||
|
||||
Getting started with Slate is super easy! Simply fork this repository and follow the instructions below. Or, if you'd like to check out what Slate is capable of, take a look at the [sample docs](http://lord.github.io/slate).
|
||||
|
||||
Getting Started with Slate
|
||||
------------------------------
|
||||
|
||||
### Prerequisites
|
||||
|
||||
You're going to need:
|
||||
|
||||
- **Linux or OS X** — Windows may work, but is unsupported.
|
||||
- **Ruby, version 2.2.5 or newer**
|
||||
- **Bundler** — If Ruby is already installed, but the `bundle` command doesn't work, just run `gem install bundler` in a terminal.
|
||||
|
||||
### Getting Set Up
|
||||
|
||||
1. Fork this repository on Github.
|
||||
2. Clone *your forked repository* (not our original one) to your hard drive with `git clone https://github.com/YOURUSERNAME/slate.git`
|
||||
3. `cd slate`
|
||||
4. Initialize and start Slate. You can either do this locally, or with Vagrant:
|
||||
|
||||
```shell
|
||||
# either run this to run locally
|
||||
bundle install
|
||||
bundle exec middleman server
|
||||
|
||||
# OR run this to run with vagrant
|
||||
vagrant up
|
||||
```
|
||||
|
||||
You can now see the docs at http://localhost:4567. Whoa! That was fast!
|
||||
|
||||
Now that Slate is all set up on your machine, you'll probably want to learn more about [editing Slate markdown](https://github.com/lord/slate/wiki/Markdown-Syntax), or [how to publish your docs](https://github.com/lord/slate/wiki/Deploying-Slate).
|
||||
|
||||
If you'd prefer to use Docker, instructions are available [in the wiki](https://github.com/lord/slate/wiki/Docker).
|
||||
|
||||
Companies Using Slate
|
||||
---------------------------------
|
||||
|
||||
* [NASA](https://api.nasa.gov)
|
||||
* [IBM](https://docs.cloudant.com/api.html)
|
||||
* [Sony](http://developers.cimediacloud.com)
|
||||
* [Mozilla](http://mozilla.github.io/localForage/)
|
||||
* [Best Buy](https://bestbuyapis.github.io/api-documentation/)
|
||||
* [Travis-CI](https://docs.travis-ci.com/api/)
|
||||
* [Greenhouse](https://developers.greenhouse.io/harvest.html)
|
||||
* [Woocommerce](http://woothemes.github.io/woocommerce-rest-api-docs/)
|
||||
* [Appium](http://appium.io/slate/en/master)
|
||||
* [Dwolla](https://docs.dwolla.com/)
|
||||
* [Clearbit](https://clearbit.com/docs)
|
||||
* [Coinbase](https://developers.coinbase.com/api)
|
||||
* [Parrot Drones](http://developer.parrot.com/docs/bebop/)
|
||||
* [Fidor Bank](http://docs.fidor.de/)
|
||||
|
||||
You can view more in [the list on the wiki](https://github.com/lord/slate/wiki/Slate-in-the-Wild).
|
||||
|
||||
Need Help? Found a bug?
|
||||
--------------------
|
||||
|
||||
[Submit an issue](https://github.com/lord/slate/issues) to the Slate Github if you need any help. And, of course, feel free to submit pull requests with bug fixes or changes.
|
||||
|
||||
Contributors
|
||||
--------------------
|
||||
|
||||
Slate was built by [Robert Lord](https://lord.io) while interning at [TripIt](https://www.tripit.com/).
|
||||
|
||||
Thanks to the following people who have submitted major pull requests:
|
||||
|
||||
- [@chrissrogers](https://github.com/chrissrogers)
|
||||
- [@bootstraponline](https://github.com/bootstraponline)
|
||||
- [@realityking](https://github.com/realityking)
|
||||
- [@cvkef](https://github.com/cvkef)
|
||||
|
||||
Also, thanks to [Sauce Labs](http://saucelabs.com) for helping sponsor the project.
|
||||
|
||||
Special Thanks
|
||||
--------------------
|
||||
- [Middleman](https://github.com/middleman/middleman)
|
||||
- [jquery.tocify.js](https://github.com/gfranko/jquery.tocify.js)
|
||||
- [middleman-syntax](https://github.com/middleman/middleman-syntax)
|
||||
- [middleman-gh-pages](https://github.com/edgecase/middleman-gh-pages)
|
||||
- [Font Awesome](http://fortawesome.github.io/Font-Awesome/)
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
Vagrant.configure(2) do |config|
|
||||
config.vm.box = "ubuntu/trusty64"
|
||||
config.vm.network :forwarded_port, guest: 4567, host: 4567
|
||||
|
||||
config.vm.provision "bootstrap",
|
||||
type: "shell",
|
||||
inline: <<-SHELL
|
||||
sudo apt-get update
|
||||
sudo apt-get install -yq ruby2.0 ruby2.0-dev pkg-config build-essential nodejs git libxml2-dev libxslt-dev
|
||||
sudo apt-get autoremove -yq
|
||||
gem2.0 install --no-ri --no-rdoc bundler
|
||||
SHELL
|
||||
|
||||
# add the local user git config to the vm
|
||||
config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig"
|
||||
|
||||
config.vm.provision "install",
|
||||
type: "shell",
|
||||
privileged: false,
|
||||
inline: <<-SHELL
|
||||
echo "=============================================="
|
||||
echo "Installing app dependencies"
|
||||
cd /vagrant
|
||||
bundle config build.nokogiri --use-system-libraries
|
||||
bundle install
|
||||
SHELL
|
||||
|
||||
config.vm.provision "run",
|
||||
type: "shell",
|
||||
privileged: false,
|
||||
run: "always",
|
||||
inline: <<-SHELL
|
||||
echo "=============================================="
|
||||
echo "Starting up middleman at http://localhost:4567"
|
||||
echo "If it does not come up, check the ~/middleman.log file for any error messages"
|
||||
cd /vagrant
|
||||
bundle exec middleman server --watcher-force-polling --watcher_latency=1 &> ~/middleman.log &
|
||||
SHELL
|
||||
end
|
||||
@@ -1,2 +0,0 @@
|
||||
/bin/
|
||||
/target
|
||||
@@ -1,20 +0,0 @@
|
||||
timelyHostname=
|
||||
timelyTcpPort=
|
||||
timelyHttpsPort=
|
||||
timelyWssPort=
|
||||
doLogin=
|
||||
#timelyUsername=
|
||||
#timelyPassword=
|
||||
keyStoreFile=
|
||||
keyStoreType=
|
||||
keyStorePass=
|
||||
trustStoreFile=
|
||||
trustStoreType=
|
||||
trustStorePass=
|
||||
hostVerificationEnabled=false
|
||||
bufferSize=10485760
|
||||
metrics=
|
||||
startTime=0
|
||||
endTime=0
|
||||
interval=1
|
||||
intervalUnits=HOURS
|
||||
@@ -1,336 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>timely-analytics</artifactId>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
<properties>
|
||||
<flink.version>1.1.4</flink.version>
|
||||
</properties>
|
||||
<!--
|
||||
|
||||
Execute "mvn clean package -Pbuild-jar"
|
||||
to build a jar file out of this project!
|
||||
|
||||
How to use the Flink Quickstart pom:
|
||||
|
||||
a) Adding new dependencies:
|
||||
You can add dependencies to the list below.
|
||||
Please check if the maven-shade-plugin below is filtering out your dependency
|
||||
and remove the exclude from there.
|
||||
|
||||
b) Build a jar for running on the cluster:
|
||||
There are two options for creating a jar from this project
|
||||
|
||||
b.1) "mvn clean package" -> this will create a fat jar which contains all
|
||||
dependencies necessary for running the jar created by this pom in a cluster.
|
||||
The "maven-shade-plugin" excludes everything that is provided on a running Flink cluster.
|
||||
|
||||
b.2) "mvn clean package -Pbuild-jar" -> This will also create a fat-jar, but with much
|
||||
nicer dependency exclusion handling. This approach is preferred and leads to
|
||||
much cleaner jar files.
|
||||
-->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.flink</groupId>
|
||||
<artifactId>flink-java</artifactId>
|
||||
<version>${flink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.flink</groupId>
|
||||
<artifactId>flink-streaming-java_2.11</artifactId>
|
||||
<version>${flink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.flink</groupId>
|
||||
<artifactId>flink-clients_2.11</artifactId>
|
||||
<version>${flink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely-client</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely-common</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<!-- Profile for packaging correct JAR files -->
|
||||
<id>build-jar</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.flink</groupId>
|
||||
<artifactId>flink-java</artifactId>
|
||||
<version>${flink.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.flink</groupId>
|
||||
<artifactId>flink-streaming-java_2.11</artifactId>
|
||||
<version>${flink.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.flink</groupId>
|
||||
<artifactId>flink-clients_2.11</artifactId>
|
||||
<version>${flink.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- disable the exclusion rules -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<excludes combine.self="override"/>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- We use the maven-shade plugin to create a fat jar that contains all dependencies
|
||||
except flink and it's transitive dependencies. The resulting fat-jar can be executed
|
||||
on a cluster. Change the value of Program-Class if your program entry point changes. -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<!-- Run shade goal on package phase -->
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<!-- This list contains all dependencies of flink-dist
|
||||
Everything else will be packaged into the fat-jar
|
||||
-->
|
||||
<exclude>org.apache.flink:flink-annotations</exclude>
|
||||
<exclude>org.apache.flink:flink-shaded-hadoop1_2.11</exclude>
|
||||
<exclude>org.apache.flink:flink-shaded-hadoop2</exclude>
|
||||
<exclude>org.apache.flink:flink-shaded-curator-recipes</exclude>
|
||||
<exclude>org.apache.flink:flink-core</exclude>
|
||||
<exclude>org.apache.flink:flink-java</exclude>
|
||||
<exclude>org.apache.flink:flink-scala_2.11</exclude>
|
||||
<exclude>org.apache.flink:flink-runtime_2.11</exclude>
|
||||
<exclude>org.apache.flink:flink-optimizer_2.11</exclude>
|
||||
<exclude>org.apache.flink:flink-clients_2.11</exclude>
|
||||
<exclude>org.apache.flink:flink-avro_2.11</exclude>
|
||||
<exclude>org.apache.flink:flink-examples-batch_2.11</exclude>
|
||||
<exclude>org.apache.flink:flink-examples-streaming_2.11</exclude>
|
||||
<exclude>org.apache.flink:flink-streaming-java_2.11</exclude>
|
||||
<!-- Also exclude very big transitive dependencies of Flink
|
||||
WARNING: You have to remove these excludes if your code relies on other
|
||||
versions of these dependencies.
|
||||
-->
|
||||
<exclude>org.scala-lang:scala-library</exclude>
|
||||
<exclude>org.scala-lang:scala-compiler</exclude>
|
||||
<exclude>org.scala-lang:scala-reflect</exclude>
|
||||
<exclude>com.typesafe.akka:akka-actor_*</exclude>
|
||||
<exclude>com.typesafe.akka:akka-remote_*</exclude>
|
||||
<exclude>com.typesafe.akka:akka-slf4j_*</exclude>
|
||||
<exclude>io.netty:netty-all</exclude>
|
||||
<exclude>io.netty:netty</exclude>
|
||||
<exclude>commons-fileupload:commons-fileupload</exclude>
|
||||
<exclude>org.apache.avro:avro</exclude>
|
||||
<exclude>commons-collections:commons-collections</exclude>
|
||||
<exclude>org.codehaus.jackson:jackson-core-asl</exclude>
|
||||
<exclude>org.codehaus.jackson:jackson-mapper-asl</exclude>
|
||||
<exclude>com.thoughtworks.paranamer:paranamer</exclude>
|
||||
<exclude>org.xerial.snappy:snappy-java</exclude>
|
||||
<exclude>org.apache.commons:commons-compress</exclude>
|
||||
<exclude>org.tukaani:xz</exclude>
|
||||
<exclude>com.esotericsoftware.kryo:kryo</exclude>
|
||||
<exclude>com.esotericsoftware.minlog:minlog</exclude>
|
||||
<exclude>org.objenesis:objenesis</exclude>
|
||||
<exclude>com.twitter:chill_*</exclude>
|
||||
<exclude>com.twitter:chill-java</exclude>
|
||||
<exclude>commons-lang:commons-lang</exclude>
|
||||
<exclude>junit:junit</exclude>
|
||||
<exclude>org.apache.commons:commons-lang3</exclude>
|
||||
<exclude>org.slf4j:slf4j-api</exclude>
|
||||
<exclude>org.slf4j:slf4j-log4j12</exclude>
|
||||
<exclude>log4j:log4j</exclude>
|
||||
<exclude>org.apache.commons:commons-math</exclude>
|
||||
<exclude>org.apache.sling:org.apache.sling.commons.json</exclude>
|
||||
<exclude>commons-logging:commons-logging</exclude>
|
||||
<exclude>commons-codec:commons-codec</exclude>
|
||||
<exclude>com.fasterxml.jackson.core:jackson-core</exclude>
|
||||
<exclude>com.fasterxml.jackson.core:jackson-databind</exclude>
|
||||
<exclude>com.fasterxml.jackson.core:jackson-annotations</exclude>
|
||||
<exclude>stax:stax-api</exclude>
|
||||
<exclude>com.typesafe:config</exclude>
|
||||
<exclude>org.uncommons.maths:uncommons-maths</exclude>
|
||||
<exclude>com.github.scopt:scopt_*</exclude>
|
||||
<exclude>commons-io:commons-io</exclude>
|
||||
<exclude>commons-cli:commons-cli</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>org.apache.flink:*</artifact>
|
||||
<excludes>
|
||||
<!-- exclude shaded google but include shaded curator -->
|
||||
<exclude>org/apache/flink/shaded/com/**</exclude>
|
||||
<exclude>web-docs/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<!-- Do not copy the signatures in the META-INF folder.
|
||||
Otherwise, this might cause SecurityExceptions when using the JAR.
|
||||
-->
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<!-- If you want to use ./bin/flink run <quickstart jar> uncomment the following lines.
|
||||
This will add a Main-Class entry to the manifest file -->
|
||||
<!--
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>jar.StreamingJob</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
-->
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<compilerId>jdt</compilerId>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.tycho</groupId>
|
||||
<artifactId>tycho-compiler-jdt</artifactId>
|
||||
<version>0.21.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.eclipse.m2e</groupId>
|
||||
<artifactId>lifecycle-mapping</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<configuration>
|
||||
<lifecycleMappingMetadata>
|
||||
<pluginExecutions>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<versionRange>[2.4,)</versionRange>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
<pluginExecution>
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<versionRange>[3.1,)</versionRange>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</pluginExecutionFilter>
|
||||
<action>
|
||||
<ignore/>
|
||||
</action>
|
||||
</pluginExecution>
|
||||
</pluginExecutions>
|
||||
</lifecycleMappingMetadata>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,169 +0,0 @@
|
||||
package timely.analytics.flink;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.flink.streaming.util.serialization.SerializationSchema;
|
||||
|
||||
import timely.model.Tag;
|
||||
|
||||
public class MetricHistogram implements Serializable, SerializationSchema<MetricHistogram> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String SPACE = " ";
|
||||
private static final String AVG = " sample=avg\n";
|
||||
private static final String COUNT = " sample=count\n";
|
||||
private static final String MAX = " sample=max\n";
|
||||
private static final String MIN = " sample=min\n";
|
||||
private static final String SUM = " sample=sum\n";
|
||||
private static final String p50 = " sample=50p\n";
|
||||
private static final String p75 = " sample=75p\n";
|
||||
private static final String p90 = " sample=90p\n";
|
||||
private static final String p99 = " sample=99p\n";
|
||||
|
||||
private String metric = null;
|
||||
private List<Tag> tags = null;
|
||||
private long timestamp = 0L;
|
||||
private boolean initialized = false;
|
||||
private List<Double> values = new ArrayList<>();
|
||||
|
||||
public void initialize(String metric, List<Tag> tags) {
|
||||
this.metric = metric;
|
||||
this.tags = tags;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void update(Double d, long timestamp) {
|
||||
values.add(d);
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void done() {
|
||||
Collections.sort(values);
|
||||
}
|
||||
|
||||
public Double avg() {
|
||||
if (values.size() == 0) {
|
||||
return 0.0D;
|
||||
}
|
||||
return sum() / count();
|
||||
}
|
||||
|
||||
public Double min() {
|
||||
if (values.size() == 0) {
|
||||
return 0.0D;
|
||||
}
|
||||
return values.get(0);
|
||||
}
|
||||
|
||||
public Double max() {
|
||||
if (values.size() == 0) {
|
||||
return 0.0D;
|
||||
}
|
||||
return values.get(values.size() - 1);
|
||||
}
|
||||
|
||||
public Double getPercentile(int p) {
|
||||
if (values.size() == 0) {
|
||||
return 0.0D;
|
||||
}
|
||||
int position = Math.round((p * 1.0f / 100) * values.size());
|
||||
position = (0 == position) ? 0 : position - 1;
|
||||
return values.get(position);
|
||||
}
|
||||
|
||||
public Double count() {
|
||||
return values.size() * 1.0D;
|
||||
}
|
||||
|
||||
public Double sum() {
|
||||
double sum = 0.0D;
|
||||
for (Double d : values) {
|
||||
sum += d;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
done();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("metric: ").append(metric);
|
||||
buf.append(" tags: ").append(tags);
|
||||
buf.append(" timestamp: ").append(timestamp);
|
||||
buf.append(" count: ").append(count());
|
||||
buf.append(" min: ").append(min());
|
||||
buf.append(" max: ").append(max());
|
||||
buf.append(" sum: ").append(sum());
|
||||
buf.append(" 50p: ").append(getPercentile(50));
|
||||
buf.append(" 75p: ").append(getPercentile(75));
|
||||
buf.append(" 99p: ").append(getPercentile(99));
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public byte[] serialize(MetricHistogram histo) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
String tags = histo.getTags().stream().map(t -> t.join()).collect(Collectors.joining(" "));
|
||||
|
||||
// @formatter:off
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.min())
|
||||
.append(SPACE).append(tags).append(MIN);
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.max())
|
||||
.append(SPACE).append(tags).append(MAX);
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.count())
|
||||
.append(SPACE).append(tags).append(COUNT);
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.sum())
|
||||
.append(SPACE).append(tags).append(SUM);
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.avg())
|
||||
.append(SPACE).append(tags).append(AVG);
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.getPercentile(50))
|
||||
.append(SPACE).append(tags).append(p50);
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.getPercentile(75))
|
||||
.append(SPACE).append(tags).append(p75);
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.getPercentile(90))
|
||||
.append(SPACE).append(tags).append(p90);
|
||||
buf.append("put ").append(histo.getMetric() + "_summarized")
|
||||
.append(SPACE).append(histo.getTimestamp())
|
||||
.append(SPACE).append(histo.getPercentile(99))
|
||||
.append(SPACE).append(tags).append(p99);
|
||||
// @formatter:off
|
||||
return buf.toString().getBytes();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package timely.analytics.flink;
|
||||
|
||||
import org.apache.flink.api.java.functions.KeySelector;
|
||||
|
||||
import timely.api.response.MetricResponse;
|
||||
|
||||
/**
|
||||
* Uniquely defines a time series in a Flink stream. A unique time series is the
|
||||
* metric name and all of its tags, to include visibility if it exists.
|
||||
*/
|
||||
public class MetricKeySelector implements KeySelector<MetricResponse, String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String getKey(MetricResponse m) throws Exception {
|
||||
return m.getMetric() + m.getTags().toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package timely.analytics.flink;
|
||||
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableLong;
|
||||
import org.apache.flink.api.common.accumulators.Accumulator;
|
||||
|
||||
/**
|
||||
* Custom accumulator that retains a sorted map of strings and their counts
|
||||
*
|
||||
*/
|
||||
public class SortedStringAccumulator implements Accumulator<String, ConcurrentSkipListMap<String, MutableLong>> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private ConcurrentSkipListMap<String, MutableLong> values = new ConcurrentSkipListMap<>();
|
||||
|
||||
@Override
|
||||
public void add(String value) {
|
||||
if (values.containsKey(value)) {
|
||||
values.get(value).increment();
|
||||
} else {
|
||||
values.put(value, new MutableLong(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConcurrentSkipListMap<String, MutableLong> getLocalValue() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLocal() {
|
||||
values.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void merge(Accumulator<String, ConcurrentSkipListMap<String, MutableLong>> other) {
|
||||
other.getLocalValue().forEach((k, v) -> {
|
||||
if (values.containsKey(k)) {
|
||||
values.get(k).add(v.longValue());
|
||||
} else {
|
||||
values.put(k, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Accumulator<String, ConcurrentSkipListMap<String, MutableLong>> clone() {
|
||||
SortedStringAccumulator a = new SortedStringAccumulator();
|
||||
a.getLocalValue().putAll(this.values);
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
package timely.analytics.flink;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.apache.flink.api.common.accumulators.LongCounter;
|
||||
import org.apache.flink.api.common.functions.StoppableFunction;
|
||||
import org.apache.flink.configuration.Configuration;
|
||||
import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
|
||||
import org.apache.flink.streaming.api.watermark.Watermark;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.response.MetricResponse;
|
||||
import timely.api.response.MetricResponses;
|
||||
import timely.client.websocket.ClientHandler;
|
||||
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
|
||||
import timely.serialize.JsonSerializer;
|
||||
|
||||
public class SubscriptionSource extends RichSourceFunction<MetricResponse> implements Serializable, StoppableFunction {
|
||||
|
||||
private static class SubscriptionThreadFactory implements ThreadFactory {
|
||||
|
||||
private static final String NAME = "Subscription Source";
|
||||
private AtomicInteger threadNum = new AtomicInteger(1);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, NAME + " " + threadNum.getAndIncrement());
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SubscriptionSource.class);
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private transient WebSocketSubscriptionClient client = null;
|
||||
private long start = 0L;
|
||||
private long end = 0L;
|
||||
private final String[] metrics;
|
||||
private final SummarizationJobParameters jp;
|
||||
private final long window;
|
||||
private final AtomicInteger subscriptionsRemaining;
|
||||
|
||||
public SubscriptionSource(SummarizationJobParameters jp) {
|
||||
this.jp = jp;
|
||||
start = jp.getStartTime();
|
||||
end = jp.getEndTime();
|
||||
metrics = jp.getMetrics();
|
||||
window = jp.getSummarizationInterval().toMilliseconds();
|
||||
subscriptionsRemaining = new AtomicInteger(metrics.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(Configuration parameters) throws Exception {
|
||||
LOG.info("Opening summarization job.");
|
||||
super.open(parameters);
|
||||
client = new WebSocketSubscriptionClient(jp.getTimelyHostname(), jp.getTimelyHttpsPort(),
|
||||
jp.getTimelyWssPort(), jp.isDoLogin(), jp.getTimelyUsername(), jp.getTimelyPassword(),
|
||||
jp.getKeyStoreFile(), jp.getKeyStoreType(), jp.getKeyStorePass(), jp.getTrustStoreFile(),
|
||||
jp.getTrustStoreType(), jp.getTrustStorePass(), jp.isHostVerificationEnabled(), jp.getBufferSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws Exception {
|
||||
super.close();
|
||||
if (null != client) {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(SourceContext<MetricResponse> ctx) throws Exception {
|
||||
LOG.info("Running summarization job");
|
||||
final SortedStringAccumulator dateTimeAccumulator = new SortedStringAccumulator();
|
||||
this.getRuntimeContext().addAccumulator("source hourly count", dateTimeAccumulator);
|
||||
final LongCounter sourceInputs = new LongCounter();
|
||||
this.getRuntimeContext().addAccumulator("source input counter", sourceInputs);
|
||||
final LongCounter inputProcessedInMainThread = new LongCounter();
|
||||
this.getRuntimeContext().addAccumulator("input processed in main thread", inputProcessedInMainThread);
|
||||
|
||||
// Use only one thread to ensure that time is still ordered
|
||||
final ExecutorService svc = Executors.newFixedThreadPool(1, new SubscriptionThreadFactory());
|
||||
|
||||
ClientHandler handler = new ClientHandler() {
|
||||
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
session.addMessageHandler(new MessageHandler.Whole<String>() {
|
||||
|
||||
private final SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd-HH");
|
||||
private long lastWatermarkTime = 0L;
|
||||
|
||||
@Override
|
||||
public void onMessage(final String message) {
|
||||
LOG.info("Message received on Websocket session {}, length: {}", session.getId(),
|
||||
message.length());
|
||||
try {
|
||||
// Deserialize in this thread
|
||||
final MetricResponses responses = JsonSerializer.getObjectMapper().readValue(message,
|
||||
MetricResponses.class);
|
||||
// Process in the other
|
||||
final Runnable r = () -> {
|
||||
responses.getResponses().forEach(response -> {
|
||||
if (response.isComplete()) {
|
||||
LOG.info("Received last message.");
|
||||
ctx.emitWatermark(new Watermark(Long.MAX_VALUE));
|
||||
subscriptionsRemaining.getAndDecrement();
|
||||
return;
|
||||
}
|
||||
LOG.trace("Received metric: {}", response);
|
||||
long time = response.getTimestamp();
|
||||
ctx.collectWithTimestamp(response, time);
|
||||
dateTimeAccumulator.add(formatter.format(new Date(response.getTimestamp())));
|
||||
sourceInputs.add(1);
|
||||
// Emit a watermark every second of event
|
||||
// time
|
||||
if (lastWatermarkTime == 0) {
|
||||
lastWatermarkTime = time;
|
||||
} else if ((time - lastWatermarkTime) > window) {
|
||||
lastWatermarkTime = time;
|
||||
ctx.emitWatermark(new Watermark(time - 1));
|
||||
}
|
||||
});
|
||||
};
|
||||
try {
|
||||
svc.execute(r);
|
||||
} catch (RejectedExecutionException e) {
|
||||
// Run it in this thread
|
||||
r.run();
|
||||
inputProcessedInMainThread.add(1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error deserializing metric response, closing", e);
|
||||
try {
|
||||
close();
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Error closing client", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, CloseReason reason) {
|
||||
super.onClose(session, reason);
|
||||
try {
|
||||
close();
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Error closing client", e1);
|
||||
}
|
||||
// Signal done sending data
|
||||
ctx.emitWatermark(new Watermark(Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Session session, Throwable error) {
|
||||
super.onError(session, error);
|
||||
try {
|
||||
close();
|
||||
} catch (Exception e1) {
|
||||
LOG.error("Error closing client", e1);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
try {
|
||||
client.open(handler);
|
||||
for (String m : metrics) {
|
||||
LOG.info("Adding subscription for {}", m);
|
||||
client.addSubscription(m, null, start, end, 5000);
|
||||
}
|
||||
while (!client.isClosed() && subscriptionsRemaining.get() > 0) {
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.error("Sleep interrupted.");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
svc.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
LOG.info("Cancelling subscription source.");
|
||||
try {
|
||||
close();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error closing web socket client");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
LOG.info("Stopping subscription source.");
|
||||
try {
|
||||
close();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error closing web socket client");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package timely.analytics.flink;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.flink.api.common.accumulators.LongCounter;
|
||||
import org.apache.flink.api.common.functions.FoldFunction;
|
||||
import org.apache.flink.api.java.utils.ParameterTool;
|
||||
import org.apache.flink.configuration.Configuration;
|
||||
import org.apache.flink.streaming.api.TimeCharacteristic;
|
||||
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
|
||||
import org.apache.flink.streaming.api.functions.sink.SocketClientSink;
|
||||
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.response.MetricResponse;
|
||||
|
||||
public class SummarizationJob {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SummarizationJob.class);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
if (args.length != 1) {
|
||||
System.err.println("Usage: timely.analytics.flink.SummarizationJob <path_to_properties_file>");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
LOG.info("Loading properties from: {}", args[0]);
|
||||
ParameterTool params = ParameterTool.fromPropertiesFile(args[0]);
|
||||
|
||||
final SummarizationJobParameters jp = new SummarizationJobParameters(params);
|
||||
LOG.info("Properties: {}", jp);
|
||||
|
||||
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
|
||||
// @formatter:off
|
||||
env.getConfig().setGlobalJobParameters(jp);
|
||||
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
|
||||
env.addSource(new SubscriptionSource(jp), "Timely Metrics Subscription")
|
||||
// partition by unique metric
|
||||
.keyBy(new MetricKeySelector())
|
||||
.window(TumblingEventTimeWindows.of(jp.getSummarizationInterval()))
|
||||
.fold(new MetricHistogram(), new FoldFunction<MetricResponse, MetricHistogram>() {
|
||||
|
||||
private final Logger LOG = LoggerFactory
|
||||
.getLogger("timely.analytics.SummarizationJob.FoldFunction@");
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public MetricHistogram fold(MetricHistogram histo, MetricResponse metric) throws Exception {
|
||||
if (!histo.isInitialized()) {
|
||||
histo.initialize(metric.getMetric(), metric.getTags());
|
||||
} else {
|
||||
if (!histo.getMetric().equals(metric.getMetric())) {
|
||||
throw new RuntimeException("Metric name does not match - partitioning problem");
|
||||
}
|
||||
if (!histo.getTags().equals(metric.getTags())) {
|
||||
throw new RuntimeException("Tags don't not match - partitioning problem");
|
||||
}
|
||||
}
|
||||
LOG.trace("Updating histogram for {} with value {} at {} for metric {}", histo.getMetric()
|
||||
+ histo.getTags(), metric.getValue(), metric.getTimestamp(), metric.getMetric()
|
||||
+ metric.getTags());
|
||||
histo.update(metric.getValue(), metric.getTimestamp());
|
||||
return histo;
|
||||
}
|
||||
})
|
||||
.name("Metric Windows")
|
||||
.addSink(
|
||||
new SocketClientSink<MetricHistogram>(jp.getTimelyHostname(), jp.getTimelyTcpPort(),
|
||||
new MetricHistogram(), 2, true) {
|
||||
|
||||
private final Logger LOG = LoggerFactory
|
||||
.getLogger("timely.analytics.SummarizationJob.SocketClientSink");
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final LongCounter sinkCounter = new LongCounter();
|
||||
private final SortedStringAccumulator dateTimeAccumulator = new SortedStringAccumulator();
|
||||
private final SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd-HH");
|
||||
|
||||
@Override
|
||||
public void open(Configuration parameters) throws Exception {
|
||||
super.open(parameters);
|
||||
this.getRuntimeContext().addAccumulator("sink counter", this.sinkCounter);
|
||||
this.getRuntimeContext().addAccumulator("sink hourly count", dateTimeAccumulator);
|
||||
LOG.info("Sink opened");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(MetricHistogram histo) throws Exception {
|
||||
histo.done();
|
||||
super.invoke(histo);
|
||||
this.sinkCounter.add(1);
|
||||
this.dateTimeAccumulator.add(formatter.format(new Date(histo.getTimestamp())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
super.close();
|
||||
LOG.info("Sink closed");
|
||||
}
|
||||
|
||||
}).name("Timely Metric Sink");
|
||||
// @formatter:on
|
||||
env.execute("Metric Summarization");
|
||||
// Looks like there is no way to stop Flink job if underlying web socket
|
||||
// is closed.
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
package timely.analytics.flink;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.flink.api.common.ExecutionConfig.GlobalJobParameters;
|
||||
import org.apache.flink.api.java.utils.ParameterTool;
|
||||
import org.apache.flink.configuration.Configuration;
|
||||
import org.apache.flink.streaming.api.windowing.time.Time;
|
||||
|
||||
public class SummarizationJobParameters extends GlobalJobParameters implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String timelyHostname;
|
||||
private final int timelyTcpPort;
|
||||
private final int timelyHttpsPort;
|
||||
private final int timelyWssPort;
|
||||
private final boolean doLogin;
|
||||
private final String timelyUsername;
|
||||
private final String timelyPassword;
|
||||
private final String keyStoreFile;
|
||||
private final String keyStoreType;
|
||||
private final String keyStorePass;
|
||||
private final String trustStoreFile;
|
||||
private final String trustStoreType;
|
||||
private final String trustStorePass;
|
||||
private final boolean hostVerificationEnabled;
|
||||
private final int bufferSize;
|
||||
private final String[] metrics;
|
||||
private final long startTime;
|
||||
private final long endTime;
|
||||
private final String interval;
|
||||
private final String intervalUnits;
|
||||
|
||||
public SummarizationJobParameters(ParameterTool params) {
|
||||
timelyHostname = params.getRequired("timelyHostname");
|
||||
timelyTcpPort = params.getInt("timelyTcpPort", 4241);
|
||||
timelyHttpsPort = params.getInt("timelyHttpsPort", 4242);
|
||||
timelyWssPort = params.getInt("timelyWssPort", 4243);
|
||||
doLogin = params.getBoolean("doLogin", false);
|
||||
timelyUsername = params.get("timelyUsername", null);
|
||||
timelyPassword = params.get("timelyPassword", null);
|
||||
keyStoreFile = params.getRequired("keyStoreFile");
|
||||
keyStoreType = params.get("keyStoreType", "JKS");
|
||||
keyStorePass = params.getRequired("keyStorePass");
|
||||
trustStoreFile = params.getRequired("trustStoreFile");
|
||||
trustStoreType = params.get("trustStoreType", "JKS");
|
||||
trustStorePass = params.getRequired("trustStorePass");
|
||||
hostVerificationEnabled = params.getBoolean("hostVerificationEnabled", true);
|
||||
bufferSize = params.getInt("bufferSize", 10485760);
|
||||
String metricNames = params.getRequired("metrics");
|
||||
if (null != metricNames) {
|
||||
metrics = metricNames.split(",");
|
||||
} else {
|
||||
metrics = null;
|
||||
}
|
||||
startTime = params.getLong("startTime", 0L);
|
||||
endTime = params.getLong("endTime", 0L);
|
||||
interval = params.getRequired("interval");
|
||||
intervalUnits = params.getRequired("intervalUnits");
|
||||
}
|
||||
|
||||
public SummarizationJobParameters(Configuration conf) {
|
||||
System.out.println("Creating job parameters from configuration: " + conf);
|
||||
timelyHostname = conf.getString("timelyHostname", null);
|
||||
timelyTcpPort = conf.getInteger("timelyTcpPort", 4241);
|
||||
timelyHttpsPort = conf.getInteger("timelyHttpsPort", 4242);
|
||||
timelyWssPort = conf.getInteger("timelyWssPort", 4243);
|
||||
doLogin = conf.getBoolean("doLogin", false);
|
||||
timelyUsername = conf.getString("timelyUsername", null);
|
||||
timelyPassword = conf.getString("timelyPassword", null);
|
||||
keyStoreFile = conf.getString("keyStoreFile", null);
|
||||
keyStoreType = conf.getString("keyStoreType", "JKS");
|
||||
keyStorePass = conf.getString("keyStorePass", null);
|
||||
trustStoreFile = conf.getString("trustStoreFile", null);
|
||||
trustStoreType = conf.getString("trustStoreType", "JKS");
|
||||
trustStorePass = conf.getString("trustStorePass", null);
|
||||
hostVerificationEnabled = conf.getBoolean("hostVerificationEnabled", true);
|
||||
bufferSize = conf.getInteger("bufferSize", 10485760);
|
||||
String metricNames = conf.getString("metrics", null);
|
||||
if (null != metricNames) {
|
||||
metrics = metricNames.split(",");
|
||||
} else {
|
||||
metrics = null;
|
||||
}
|
||||
startTime = conf.getLong("startTime", 0L);
|
||||
endTime = conf.getLong("endTime", 0L);
|
||||
interval = conf.getString("interval", null);
|
||||
intervalUnits = conf.getString("intervalUnits", null);
|
||||
}
|
||||
|
||||
public Time getSummarizationInterval() {
|
||||
return Time.of(Long.parseLong(interval), TimeUnit.valueOf(intervalUnits));
|
||||
}
|
||||
|
||||
public String getTimelyHostname() {
|
||||
return timelyHostname;
|
||||
}
|
||||
|
||||
public int getTimelyTcpPort() {
|
||||
return timelyTcpPort;
|
||||
}
|
||||
|
||||
public int getTimelyHttpsPort() {
|
||||
return timelyHttpsPort;
|
||||
}
|
||||
|
||||
public int getTimelyWssPort() {
|
||||
return timelyWssPort;
|
||||
}
|
||||
|
||||
public boolean isDoLogin() {
|
||||
return doLogin;
|
||||
}
|
||||
|
||||
public String getTimelyUsername() {
|
||||
return timelyUsername;
|
||||
}
|
||||
|
||||
public String getTimelyPassword() {
|
||||
return timelyPassword;
|
||||
}
|
||||
|
||||
public String getKeyStoreFile() {
|
||||
return keyStoreFile;
|
||||
}
|
||||
|
||||
public String getKeyStoreType() {
|
||||
return keyStoreType;
|
||||
}
|
||||
|
||||
public String getKeyStorePass() {
|
||||
return keyStorePass;
|
||||
}
|
||||
|
||||
public String getTrustStoreFile() {
|
||||
return trustStoreFile;
|
||||
}
|
||||
|
||||
public String getTrustStoreType() {
|
||||
return trustStoreType;
|
||||
}
|
||||
|
||||
public String getTrustStorePass() {
|
||||
return trustStorePass;
|
||||
}
|
||||
|
||||
public boolean isHostVerificationEnabled() {
|
||||
return hostVerificationEnabled;
|
||||
}
|
||||
|
||||
public int getBufferSize() {
|
||||
return bufferSize;
|
||||
}
|
||||
|
||||
public String[] getMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public long getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public String getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
public String getIntervalUnits() {
|
||||
return intervalUnits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> toMap() {
|
||||
Map<String, String> m = new HashMap<>();
|
||||
m.put("timelyHostname", this.timelyHostname);
|
||||
m.put("timelyHttpsPort", Integer.toString(this.timelyHttpsPort));
|
||||
m.put("timelyWssPort", Integer.toString(this.timelyWssPort));
|
||||
m.put("doLogin", Boolean.toString(this.doLogin));
|
||||
m.put("timelyUsername", this.timelyUsername);
|
||||
m.put("timelyPassword", this.timelyPassword);
|
||||
m.put("keyStoreFile", this.keyStoreFile);
|
||||
m.put("keyStoreType", this.keyStoreType);
|
||||
m.put("keyStorePass", this.keyStorePass);
|
||||
m.put("trustStoreFile", this.trustStoreFile);
|
||||
m.put("trustStoreType", this.trustStoreType);
|
||||
m.put("trustStorePass", this.trustStorePass);
|
||||
m.put("hostVerificationEnabled", Boolean.toString(this.hostVerificationEnabled));
|
||||
m.put("bufferSize", Integer.toString(this.bufferSize));
|
||||
m.put("metrics", String.join(",", this.metrics));
|
||||
m.put("startTime", Long.toString(this.startTime));
|
||||
m.put("endTime", Long.toString(this.endTime));
|
||||
m.put("interval", this.interval);
|
||||
m.put("intervalUnits", this.intervalUnits);
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package timely.analytics.flink;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import timely.model.Metric;
|
||||
import timely.model.Tag;
|
||||
import timely.model.parse.MetricParser;
|
||||
|
||||
public class MetricHistogramTest {
|
||||
|
||||
private MetricHistogram m = null;
|
||||
private long timestamp = System.currentTimeMillis();
|
||||
|
||||
@Before
|
||||
public void createMetric() throws Exception {
|
||||
m = new MetricHistogram();
|
||||
for (double d = 1.0D; d <= 100.0; d += 1.0) {
|
||||
timestamp += 1000;
|
||||
m.update(d, timestamp);
|
||||
}
|
||||
m.toString();
|
||||
Assert.assertEquals(timestamp, m.getTimestamp());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMin() throws Exception {
|
||||
Assert.assertEquals(1.0D, m.min(), 0.0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMax() throws Exception {
|
||||
Assert.assertEquals(100.0D, m.max(), 0.0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvg() throws Exception {
|
||||
int sum = 0;
|
||||
for (int i = 1; i <= 100; i++) {
|
||||
sum += i;
|
||||
}
|
||||
Assert.assertEquals((sum / 100.0D), m.avg(), 0.0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCount() throws Exception {
|
||||
Assert.assertEquals(100.0D, m.count(), 0.0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test50thPercentile() throws Exception {
|
||||
Assert.assertEquals(50.0D, m.getPercentile(50), 0.0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test75thPercentile() throws Exception {
|
||||
Assert.assertEquals(75.0D, m.getPercentile(75), 0.0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test90thPercentile() throws Exception {
|
||||
Assert.assertEquals(90.0D, m.getPercentile(90), 0.0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test99thPercentile() throws Exception {
|
||||
Assert.assertEquals(99.0D, m.getPercentile(99), 0.0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialization() throws Exception {
|
||||
MetricParser metricParser = new MetricParser();
|
||||
Tag t1 = new Tag("tag1=value1");
|
||||
Tag t2 = new Tag("tag2=value2");
|
||||
Tag avg = new Tag("sample=avg");
|
||||
Tag min = new Tag("sample=min");
|
||||
Tag max = new Tag("sample=max");
|
||||
Tag sum = new Tag("sample=sum");
|
||||
Tag count = new Tag("sample=count");
|
||||
Tag p50 = new Tag("sample=50p");
|
||||
Tag p75 = new Tag("sample=75p");
|
||||
Tag p90 = new Tag("sample=90p");
|
||||
Tag p99 = new Tag("sample=99p");
|
||||
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
tags.add(t1);
|
||||
tags.add(t2);
|
||||
m.initialize("sys.cpu.user", tags);
|
||||
|
||||
byte[] bytes = m.serialize(m);
|
||||
String puts = new String(bytes);
|
||||
for (String put : puts.split("\n")) {
|
||||
Metric metric = metricParser.parse(put);
|
||||
Assert.assertEquals("sys.cpu.user_summarized", metric.getName());
|
||||
metric.getTags().forEach(
|
||||
t -> {
|
||||
Assert.assertTrue(t.equals(t1) || t.equals(t2) || t.equals(avg) || t.equals(min)
|
||||
|| t.equals(max) || t.equals(sum) || t.equals(count) || t.equals(p50) || t.equals(p75)
|
||||
|| t.equals(p90) || t.equals(p99));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
/target/
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>timely-client</artifactId>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
<properties>
|
||||
<!-- PEP 440 compliant version gets supstituted into src/main/python/template/setup.py -->
|
||||
<python.client.version>0.0.6.dev0</python.client.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely-common</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.tyrus</groupId>
|
||||
<artifactId>tyrus-client</artifactId>
|
||||
<version>1.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.tyrus</groupId>
|
||||
<artifactId>tyrus-container-grizzly-client</artifactId>
|
||||
<version>1.13</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>filter-setup</id>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<phase>process-resources</phase>
|
||||
<configuration>
|
||||
<outputDirectory>${project.basedir}/src/main/python</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.basedir}/src/main/python/template</directory>
|
||||
<includes>
|
||||
<include>setup.py</include>
|
||||
</includes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>python-dist</id>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<executable>python</executable>
|
||||
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
|
||||
<commandlineArgs>setup.py sdist - -dist-dir ${project.build.directory}</commandlineArgs>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>python-clean</id>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<executable>rm</executable>
|
||||
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
|
||||
<commandlineArgs>-rf timely.egg-info setup.py</commandlineArgs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
-->
|
||||
</plugins>
|
||||
</build>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>${version.findbugs-maven}</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${version.jacoco}</version>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<id>JaCoCo Unit Test Coverage</id>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/coverage/jacoco-ut.exec</dataFile>
|
||||
</configuration>
|
||||
<reports>
|
||||
<report>report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
<reportSet>
|
||||
<id>JaCoCo Integration Test Coverage</id>
|
||||
<configuration>
|
||||
<dataFile>${project.build.directory}/coverage/jacoco-it.exec</dataFile>
|
||||
</configuration>
|
||||
<reports>
|
||||
<report>report-integration</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</project>
|
||||
@@ -1,82 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AddSubscription extends SubscriptionRequest {
|
||||
|
||||
private String operation = "add";
|
||||
private String metric = null;
|
||||
private Optional<Map<String, String>> tags = Optional.empty();
|
||||
private Optional<Long> startTime = Optional.empty();
|
||||
private Optional<Long> endTime = Optional.empty();
|
||||
private Optional<Long> delayTime = Optional.empty();
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public Optional<Map<String, String>> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public Optional<Long> getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public Optional<Long> getDelayTime() {
|
||||
return delayTime;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public void setTags(Optional<Map<String, String>> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public void setStartTime(Optional<Long> startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public void setDelayTime(Optional<Long> delayTime) {
|
||||
this.delayTime = delayTime;
|
||||
}
|
||||
|
||||
public Optional<Long> getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setEndTime(Optional<Long> endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (operation + subscriptionId + metric + tags + startTime + endTime + delayTime).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof AddSubscription) {
|
||||
AddSubscription other = (AddSubscription) obj;
|
||||
return (this.subscriptionId.equals(other.subscriptionId) && this.metric.equals(other.metric)
|
||||
&& this.tags.equals(other.tags) && this.startTime.equals(other.startTime)
|
||||
&& this.endTime.equals(other.endTime) && this.delayTime.equals(other.delayTime));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
public class BasicAuthLogin {
|
||||
|
||||
protected String username;
|
||||
protected String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String user) {
|
||||
this.username = user;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (username + password).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof BasicAuthLogin) {
|
||||
BasicAuthLogin other = (BasicAuthLogin) obj;
|
||||
return (this.username.equals(other.username) && this.password.equals(other.password));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
public class CloseSubscription extends SubscriptionRequest {
|
||||
|
||||
private String operation = "close";
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (operation + subscriptionId).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof CloseSubscription) {
|
||||
CloseSubscription other = (CloseSubscription) obj;
|
||||
return (this.subscriptionId.equals(other.subscriptionId));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
public class CreateSubscription extends SubscriptionRequest {
|
||||
|
||||
private String operation = "create";
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (operation + subscriptionId).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof CreateSubscription) {
|
||||
CreateSubscription other = (CreateSubscription) obj;
|
||||
return (this.subscriptionId.equals(other.subscriptionId));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
public class RemoveSubscription extends SubscriptionRequest {
|
||||
|
||||
private String operation = "remove";
|
||||
private String metric = null;
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (operation + subscriptionId + metric).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof RemoveSubscription) {
|
||||
RemoveSubscription other = (RemoveSubscription) obj;
|
||||
return (this.subscriptionId.equals(other.subscriptionId) && this.metric.equals(other.metric));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
public abstract class SubscriptionRequest {
|
||||
|
||||
protected String subscriptionId = null;
|
||||
|
||||
public final String getSubscriptionId() {
|
||||
return subscriptionId;
|
||||
}
|
||||
|
||||
public final void setSubscriptionId(String subscriptionId) {
|
||||
this.subscriptionId = subscriptionId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package timely.api.response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
||||
import timely.model.Metric;
|
||||
import timely.model.Tag;
|
||||
|
||||
public class MetricResponse {
|
||||
|
||||
private String metric;
|
||||
private long timestamp;
|
||||
private double value;
|
||||
private List<Tag> tags;
|
||||
private String subscriptionId;
|
||||
private boolean complete = false;
|
||||
|
||||
public String getMetric() {
|
||||
return metric;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return complete;
|
||||
}
|
||||
|
||||
public void setMetric(String metric) {
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
public void setTimestamp(long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void setTags(List<Tag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public String getSubscriptionId() {
|
||||
return subscriptionId;
|
||||
}
|
||||
|
||||
public void setSubscriptionId(String subscriptionId) {
|
||||
this.subscriptionId = subscriptionId;
|
||||
}
|
||||
|
||||
public void setComplete(boolean closed) {
|
||||
this.complete = closed;
|
||||
}
|
||||
|
||||
public static MetricResponse fromMetric(Metric metric, String subscriptionId) {
|
||||
MetricResponse response = new MetricResponse();
|
||||
response.setMetric(metric.getName());
|
||||
response.setTimestamp(metric.getValue().getTimestamp());
|
||||
response.setTags(metric.getTags());
|
||||
response.setValue(metric.getValue().getMeasure());
|
||||
response.setSubscriptionId(subscriptionId);
|
||||
response.setComplete(false);
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder hash = new HashCodeBuilder();
|
||||
hash.append(this.metric);
|
||||
hash.append(this.timestamp);
|
||||
hash.append(this.value);
|
||||
hash.append(this.tags);
|
||||
hash.append(this.subscriptionId);
|
||||
hash.append(this.complete);
|
||||
return hash.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof MetricResponse)) {
|
||||
return false;
|
||||
}
|
||||
MetricResponse other = (MetricResponse) obj;
|
||||
EqualsBuilder equals = new EqualsBuilder();
|
||||
equals.append(this.metric, other.metric);
|
||||
equals.append(this.timestamp, other.timestamp);
|
||||
equals.append(this.value, other.value);
|
||||
equals.append(this.tags, other.tags);
|
||||
equals.append(this.subscriptionId, other.subscriptionId);
|
||||
equals.append(this.complete, other.complete);
|
||||
return equals.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder tsb = new ToStringBuilder(this);
|
||||
tsb.append("metric", this.metric);
|
||||
tsb.append("timestamp", this.timestamp);
|
||||
tsb.append("value", this.value);
|
||||
tsb.append("tags", this.tags);
|
||||
tsb.append("subscriptionId", this.subscriptionId);
|
||||
tsb.append("complete", this.complete);
|
||||
return tsb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package timely.api.response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MetricResponses {
|
||||
|
||||
private List<MetricResponse> responses = new ArrayList<>();
|
||||
|
||||
public synchronized List<MetricResponse> getResponses() {
|
||||
return this.responses;
|
||||
}
|
||||
|
||||
public synchronized void setResponses(List<MetricResponse> responses) {
|
||||
this.responses = responses;
|
||||
}
|
||||
|
||||
public synchronized void addResponse(MetricResponse r) {
|
||||
this.responses.add(r);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.responses.size();
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
this.responses.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package timely.client.http;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.http.client.CookieStore;
|
||||
import org.apache.http.client.config.CookieSpecs;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.glassfish.tyrus.client.SslContextConfigurator;
|
||||
|
||||
public class HttpClient {
|
||||
|
||||
public static SSLContext getSSLContext(String trustStoreFile, String trustStoreType, String trustStorePass,
|
||||
String keyStoreFile, String keyStoreType, String keyStorePass) {
|
||||
SslContextConfigurator scc = new SslContextConfigurator();
|
||||
scc.setTrustStoreFile(trustStoreFile);
|
||||
scc.setTrustStoreType(trustStoreType);
|
||||
if (null != trustStorePass) {
|
||||
scc.setTrustStorePassword(trustStorePass);
|
||||
}
|
||||
scc.setKeyStoreFile(keyStoreFile);
|
||||
scc.setKeyStoreType(keyStoreType);
|
||||
if (null != keyStorePass) {
|
||||
scc.setKeyStorePassword(keyStorePass);
|
||||
}
|
||||
return scc.createSSLContext();
|
||||
}
|
||||
|
||||
public static CloseableHttpClient get(String trustStoreFile, String trustStoreType, String trustStorePass,
|
||||
String keyStoreFile, String keyStoreType, String keyStorePass, CookieStore cookieStore,
|
||||
boolean hostVerificationEnabled) {
|
||||
|
||||
SSLContext ssl = getSSLContext(trustStoreFile, trustStoreType, trustStorePass, keyStoreFile, keyStoreType,
|
||||
keyStorePass);
|
||||
return get(ssl, cookieStore, hostVerificationEnabled);
|
||||
}
|
||||
|
||||
public static CloseableHttpClient get(SSLContext ssl, CookieStore cookieStore, boolean hostVerificationEnabled) {
|
||||
RequestConfig defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
|
||||
|
||||
HttpClientBuilder builder = HttpClients.custom().setSSLContext(ssl).setDefaultCookieStore(cookieStore)
|
||||
.setDefaultRequestConfig(defaultRequestConfig);
|
||||
if (hostVerificationEnabled) {
|
||||
builder.setSSLHostnameVerifier(new DefaultHostnameVerifier());
|
||||
} else {
|
||||
builder.setSSLHostnameVerifier(new NoopHostnameVerifier());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package timely.client.tcp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TcpClient implements AutoCloseable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TcpClient.class);
|
||||
|
||||
private final String host;
|
||||
private final int port;
|
||||
private Socket sock = null;
|
||||
private PrintWriter out = null;
|
||||
private long connectTime = 0L;
|
||||
private long backoff = 2000;
|
||||
|
||||
public TcpClient(String hostname, int port) {
|
||||
this.host = hostname;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a TCP connection to the specified host and port
|
||||
*
|
||||
* @throws IOException
|
||||
* if an error occurs
|
||||
*/
|
||||
public void open() throws IOException {
|
||||
if (connect() != 0) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a metric to Timely
|
||||
*
|
||||
* @param metric
|
||||
* newline terminated string representation of Timely metric
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized void write(String metric) throws IOException {
|
||||
if (connect() != 0) {
|
||||
throw new IOException();
|
||||
}
|
||||
out.write(metric);
|
||||
}
|
||||
|
||||
public synchronized void flush() {
|
||||
if (null != out) {
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the tcp connection to Timely
|
||||
*
|
||||
* @throws IOException
|
||||
* if an error occurs
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
LOG.info("Shutting down connection to Timely at {}:{}", host, port);
|
||||
if (null != sock) {
|
||||
try {
|
||||
if (null != out) {
|
||||
out.close();
|
||||
}
|
||||
sock.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error closing connection to Timely at " + host + ":" + port + ". Error: " + e.getMessage());
|
||||
} finally {
|
||||
sock = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized int connect() {
|
||||
if (null == sock || !sock.isConnected() || out.checkError()) {
|
||||
if (System.currentTimeMillis() > (connectTime + backoff)) {
|
||||
try {
|
||||
connectTime = System.currentTimeMillis();
|
||||
sock = new Socket(host, port);
|
||||
out = new PrintWriter(sock.getOutputStream(), false);
|
||||
backoff = 2000;
|
||||
LOG.info("Connected to Timely at {}:{}", host, port);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error connecting to Timely at {}:" + host + ":" + port + ". Error: " + e.getMessage());
|
||||
backoff = backoff * 2;
|
||||
sock = null;
|
||||
out = null;
|
||||
LOG.warn("Will retry connection in {} ms.", backoff);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Not writing to Timely, waiting to reconnect");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package timely.client.udp;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class UdpClient implements AutoCloseable {
|
||||
|
||||
private final InetSocketAddress address;
|
||||
private final DatagramPacket packet;
|
||||
private DatagramSocket sock;
|
||||
|
||||
public UdpClient(String hostname, int port) {
|
||||
this.address = new InetSocketAddress(hostname, port);
|
||||
this.packet = new DatagramPacket("".getBytes(UTF_8), 0, 0, address.getAddress(), port);
|
||||
}
|
||||
|
||||
public void open() throws IOException {
|
||||
if (null == sock) {
|
||||
this.sock = new DatagramSocket();
|
||||
}
|
||||
}
|
||||
|
||||
public void write(String metric) throws IOException {
|
||||
if (null == this.sock) {
|
||||
throw new IllegalStateException("Must call open first");
|
||||
}
|
||||
this.packet.setData(metric.getBytes(UTF_8));
|
||||
this.sock.send(packet);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (null != sock) {
|
||||
sock.close();
|
||||
sock = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package timely.client.websocket;
|
||||
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class ClientHandler extends Endpoint {
|
||||
|
||||
private final static Logger LOG = LoggerFactory.getLogger(ClientHandler.class);
|
||||
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
LOG.info("Websocket session {} opened.", session.getId());
|
||||
session.addMessageHandler(new MessageHandler.Whole<String>() {
|
||||
|
||||
@Override
|
||||
public void onMessage(String message) {
|
||||
LOG.info("Message received on Websocket session {}: {}", session.getId(), message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, CloseReason reason) {
|
||||
LOG.info("Websocket session {} closed.", session.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Session session, Throwable error) {
|
||||
LOG.error("Error occurred on Websocket session" + session.getId(), error);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package timely.client.websocket;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.ClientEndpointConfig;
|
||||
import javax.websocket.Decoder;
|
||||
import javax.websocket.Encoder;
|
||||
import javax.websocket.Extension;
|
||||
|
||||
import org.apache.http.cookie.Cookie;
|
||||
|
||||
public class TimelyEndpointConfig implements ClientEndpointConfig {
|
||||
|
||||
private String sessionCookie = null;
|
||||
|
||||
private static final String FORMAT = "%s=%s;";
|
||||
|
||||
public TimelyEndpointConfig(Cookie sessionCookie) {
|
||||
if (null != sessionCookie) {
|
||||
this.sessionCookie = String.format(FORMAT, sessionCookie.getName(), sessionCookie.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<? extends Encoder>> getEncoders() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Class<? extends Decoder>> getDecoders() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getUserProperties() {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPreferredSubprotocols() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Extension> getExtensions() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configurator getConfigurator() {
|
||||
return new Configurator() {
|
||||
|
||||
@Override
|
||||
public void beforeRequest(Map<String, List<String>> headers) {
|
||||
super.beforeRequest(headers);
|
||||
if (null != sessionCookie) {
|
||||
headers.put("Cookie", Collections.singletonList(sessionCookie));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
package timely.client.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.Session;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.HttpResponseException;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.utils.HttpClientUtils;
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.glassfish.tyrus.client.ClientManager;
|
||||
import org.glassfish.tyrus.client.ClientProperties;
|
||||
import org.glassfish.tyrus.client.SslEngineConfigurator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.BasicAuthLogin;
|
||||
import timely.client.http.HttpClient;
|
||||
import timely.serialize.JsonSerializer;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
public class WebSocketClient implements AutoCloseable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WebSocketClient.class);
|
||||
|
||||
private final String timelyHostname;
|
||||
private final int timelyHttpsPort;
|
||||
private final int timelyWssPort;
|
||||
private final boolean doLogin;
|
||||
private final String timelyUsername;
|
||||
private final String timelyPassword;
|
||||
private final boolean hostVerificationEnabled;
|
||||
private final int bufferSize;
|
||||
private final SSLContext ssl;
|
||||
|
||||
private ClientManager webSocketClient = null;
|
||||
protected Session session = null;
|
||||
protected volatile boolean closed = true;
|
||||
|
||||
protected WebSocketClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort,
|
||||
boolean doLogin, String timelyUsername, String timelyPassword, boolean hostVerificationEnabled,
|
||||
int bufferSize) {
|
||||
this.ssl = ssl;
|
||||
this.timelyHostname = timelyHostname;
|
||||
this.timelyHttpsPort = timelyHttpsPort;
|
||||
this.timelyWssPort = timelyWssPort;
|
||||
this.doLogin = doLogin;
|
||||
this.timelyUsername = timelyUsername;
|
||||
this.timelyPassword = timelyPassword;
|
||||
this.hostVerificationEnabled = hostVerificationEnabled;
|
||||
this.bufferSize = bufferSize;
|
||||
|
||||
Preconditions.checkNotNull(timelyHostname, "%s must be supplied", "Timely host name");
|
||||
Preconditions.checkNotNull(timelyHttpsPort, "%s must be supplied", "Timely HTTPS port");
|
||||
Preconditions.checkNotNull(timelyWssPort, "%s must be supplied", "Timely WSS port");
|
||||
|
||||
if (doLogin
|
||||
&& ((StringUtils.isEmpty(timelyUsername) && !StringUtils.isEmpty(timelyPassword) || (!StringUtils
|
||||
.isEmpty(timelyUsername) && StringUtils.isEmpty(timelyPassword))))) {
|
||||
throw new IllegalArgumentException("Both Timely username and password must be empty or non-empty");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected WebSocketClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean doLogin,
|
||||
String timelyUsername, String timelyPassword, String keyStoreFile, String keyStoreType,
|
||||
String keyStorePass, String trustStoreFile, String trustStoreType, String trustStorePass,
|
||||
boolean hostVerificationEnabled, int bufferSize) {
|
||||
this(HttpClient.getSSLContext(trustStoreFile, trustStoreType, trustStorePass, keyStoreFile, keyStoreType,
|
||||
keyStorePass), timelyHostname, timelyHttpsPort, timelyWssPort, doLogin, timelyUsername, timelyPassword,
|
||||
hostVerificationEnabled, bufferSize);
|
||||
}
|
||||
|
||||
public void open(ClientHandler clientEndpoint) throws IOException, DeploymentException, URISyntaxException {
|
||||
|
||||
Cookie sessionCookie = null;
|
||||
if (doLogin) {
|
||||
BasicCookieStore cookieJar = new BasicCookieStore();
|
||||
try (CloseableHttpClient client = HttpClient.get(ssl, cookieJar, hostVerificationEnabled)) {
|
||||
|
||||
String target = "https://" + timelyHostname + ":" + timelyHttpsPort + "/login";
|
||||
|
||||
HttpRequestBase request = null;
|
||||
if (StringUtils.isEmpty(timelyUsername)) {
|
||||
// HTTP GET to /login to use certificate based login
|
||||
request = new HttpGet(target);
|
||||
LOG.trace("Performing client certificate login");
|
||||
} else {
|
||||
// HTTP POST to /login to use username/password
|
||||
BasicAuthLogin login = new BasicAuthLogin();
|
||||
login.setUsername(timelyUsername);
|
||||
login.setPassword(timelyPassword);
|
||||
String payload = JsonSerializer.getObjectMapper().writeValueAsString(login);
|
||||
HttpPost post = new HttpPost(target);
|
||||
post.setEntity(new StringEntity(payload));
|
||||
request = post;
|
||||
LOG.trace("Performing BasicAuth login");
|
||||
}
|
||||
|
||||
HttpResponse response = null;
|
||||
try {
|
||||
response = client.execute(request);
|
||||
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
|
||||
throw new HttpResponseException(response.getStatusLine().getStatusCode(), response
|
||||
.getStatusLine().getReasonPhrase());
|
||||
}
|
||||
for (Cookie c : cookieJar.getCookies()) {
|
||||
if (c.getName().equals("TSESSIONID")) {
|
||||
sessionCookie = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null == sessionCookie) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to find TSESSIONID cookie header in Timely login response");
|
||||
}
|
||||
} finally {
|
||||
HttpClientUtils.closeQuietly(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SslEngineConfigurator sslEngine = new SslEngineConfigurator(ssl);
|
||||
sslEngine.setClientMode(true);
|
||||
sslEngine.setHostVerificationEnabled(hostVerificationEnabled);
|
||||
|
||||
webSocketClient = ClientManager.createClient();
|
||||
webSocketClient.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngine);
|
||||
webSocketClient.getProperties().put(ClientProperties.INCOMING_BUFFER_SIZE, bufferSize);
|
||||
String wssPath = "wss://" + timelyHostname + ":" + timelyWssPort + "/websocket";
|
||||
session = webSocketClient.connectToServer(clientEndpoint, new TimelyEndpointConfig(sessionCookie), new URI(
|
||||
wssPath));
|
||||
|
||||
final ByteBuffer pingData = ByteBuffer.allocate(0);
|
||||
webSocketClient.getScheduledExecutorService().scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
session.getBasicRemote().sendPing(pingData);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error sending ping", e);
|
||||
}
|
||||
}, 30, 60, TimeUnit.SECONDS);
|
||||
closed = false;
|
||||
}
|
||||
|
||||
public synchronized void close() throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (null != webSocketClient) {
|
||||
webSocketClient.shutdown();
|
||||
}
|
||||
} finally {
|
||||
session = null;
|
||||
webSocketClient = null;
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
package timely.client.websocket.subscription;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.DeploymentException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import timely.api.AddSubscription;
|
||||
import timely.api.CloseSubscription;
|
||||
import timely.api.CreateSubscription;
|
||||
import timely.api.RemoveSubscription;
|
||||
import timely.client.websocket.ClientHandler;
|
||||
import timely.client.websocket.WebSocketClient;
|
||||
import timely.serialize.JsonSerializer;
|
||||
|
||||
public class WebSocketSubscriptionClient extends WebSocketClient {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WebSocketSubscriptionClient.class);
|
||||
private final String subscriptionId;
|
||||
|
||||
public WebSocketSubscriptionClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort,
|
||||
boolean doLogin, String timelyUsername, String timelyPassword, boolean hostVerificationEnabled,
|
||||
int bufferSize) {
|
||||
super(ssl, timelyHostname, timelyHttpsPort, timelyWssPort, doLogin, timelyUsername, timelyPassword,
|
||||
hostVerificationEnabled, bufferSize);
|
||||
subscriptionId = UUID.randomUUID().toString();
|
||||
LOG.trace("Created WebSocketClient with subscriptionId {}", this.subscriptionId);
|
||||
}
|
||||
|
||||
public WebSocketSubscriptionClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean doLogin,
|
||||
String timelyUsername, String timelyPassword, String keyStoreFile, String keyStoreType,
|
||||
String keyStorePass, String trustStoreFile, String trustStoreType, String trustStorePass,
|
||||
boolean hostVerificationEnabled, int bufferSize) {
|
||||
super(timelyHostname, timelyHttpsPort, timelyWssPort, doLogin, timelyUsername, timelyPassword, keyStoreFile,
|
||||
keyStoreType, keyStorePass, trustStoreFile, trustStoreType, trustStorePass, hostVerificationEnabled,
|
||||
bufferSize);
|
||||
subscriptionId = UUID.randomUUID().toString();
|
||||
LOG.trace("Created WebSocketClient with subscriptionId {}", this.subscriptionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(ClientHandler clientEndpoint) throws IOException, DeploymentException, URISyntaxException {
|
||||
super.open(clientEndpoint);
|
||||
CreateSubscription create = new CreateSubscription();
|
||||
create.setSubscriptionId(subscriptionId);
|
||||
session.getBasicRemote().sendText(JsonSerializer.getObjectMapper().writeValueAsString(create));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (null != session) {
|
||||
CloseSubscription close = new CloseSubscription();
|
||||
close.setSubscriptionId(subscriptionId);
|
||||
try {
|
||||
session.getBasicRemote().sendText(JsonSerializer.getObjectMapper().writeValueAsString(close));
|
||||
session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Client called close."));
|
||||
} catch (Exception e) {
|
||||
LOG.info("Unable to send close message to server: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
|
||||
public Future<Void> addSubscription(String metric, Map<String, String> tags, long startTime, long endTime,
|
||||
long delayTime) throws IOException {
|
||||
AddSubscription add = new AddSubscription();
|
||||
add.setSubscriptionId(subscriptionId);
|
||||
add.setMetric(metric);
|
||||
add.setTags(Optional.ofNullable(tags));
|
||||
add.setStartTime(Optional.ofNullable(startTime));
|
||||
add.setEndTime(Optional.ofNullable(endTime));
|
||||
add.setDelayTime(Optional.ofNullable(delayTime));
|
||||
return session.getAsyncRemote().sendText(JsonSerializer.getObjectMapper().writeValueAsString(add));
|
||||
}
|
||||
|
||||
public Future<Void> removeSubscription(String metric) throws Exception {
|
||||
RemoveSubscription remove = new RemoveSubscription();
|
||||
remove.setSubscriptionId(subscriptionId);
|
||||
remove.setMetric(metric);
|
||||
return session.getAsyncRemote().sendText(JsonSerializer.getObjectMapper().writeValueAsString(remove));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
package timely.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import timely.model.parse.TagParser;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||
import com.fasterxml.jackson.annotation.JsonUnwrapped;
|
||||
|
||||
/**
|
||||
* A Metric consists of a metric name, tags, and a Value
|
||||
*
|
||||
* Json has been flattened to reduce overhead. Example:
|
||||
* {"name":"m1","timestamp":1,"measure":1.0,"tags":[{"k1":"v1"}]}
|
||||
*
|
||||
*/
|
||||
@JsonRootName("metric")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonPropertyOrder({ "name" })
|
||||
public class Metric implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String name;
|
||||
private List<Tag> tags;
|
||||
private Value value;
|
||||
|
||||
public Metric() {
|
||||
tags = new ArrayList<>();
|
||||
value = new Value();
|
||||
}
|
||||
|
||||
public Metric(Metric other) {
|
||||
this();
|
||||
this.name = other.getName();
|
||||
this.tags.addAll(other.getTags());
|
||||
this.value = new Value(other.getValue());
|
||||
}
|
||||
|
||||
public Metric(String name, long timestamp, double measure) {
|
||||
this();
|
||||
this.name = name;
|
||||
this.value.setTimestamp(timestamp);
|
||||
this.value.setMeasure(measure);
|
||||
}
|
||||
|
||||
public Metric(String name, long timestamp, double measure, List<Tag> tags) {
|
||||
this(name, timestamp, measure);
|
||||
this.setTags(tags);
|
||||
}
|
||||
|
||||
public Metric(String name, List<Tag> tags) {
|
||||
this();
|
||||
this.name = name;
|
||||
this.setTags(tags);
|
||||
}
|
||||
|
||||
public static Builder newBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
@JsonGetter("name")
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@JsonGetter("tags")
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(List<Tag> tags) {
|
||||
this.tags.addAll(tags);
|
||||
}
|
||||
|
||||
public void addTag(Tag tag) {
|
||||
tags.add(new Tag(tag));
|
||||
}
|
||||
|
||||
@JsonUnwrapped
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(Value value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Collections.sort(tags);
|
||||
return "Metric{" + "name='" + name + '\'' + ", tags=" + tags + ", value=" + value + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
Metric metric = (Metric) o;
|
||||
|
||||
if (name != null ? !name.equals(metric.name) : metric.name != null)
|
||||
return false;
|
||||
|
||||
if (tags != null) {
|
||||
if (tags.size() != metric.tags.size()) {
|
||||
return false;
|
||||
} else if (!tags.containsAll(metric.tags)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return value != null ? value.equals(metric.value) : metric.value == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name != null ? name.hashCode() : 0;
|
||||
result = 31 * result + (tags != null ? tags.hashCode() : 0);
|
||||
result = 31 * result + (value != null ? value.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Builder for Metric. Note: cannot re-use a builder after build is
|
||||
* called.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
Metric metric;
|
||||
|
||||
private Builder() {
|
||||
metric = new Metric();
|
||||
}
|
||||
|
||||
private static TagParser parser = new TagParser();
|
||||
|
||||
public Builder tag(Tag tag) {
|
||||
metric.addTag(tag);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder tags(List<Tag> tags) {
|
||||
metric.setTags(tags);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder tag(String key, String value) {
|
||||
metric.addTag(new Tag(key, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder tag(String rawTag) {
|
||||
metric.addTag(parser.parse(rawTag));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder name(String name) {
|
||||
metric.setName(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder value(Value value) {
|
||||
metric.setValue(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder value(long timestamp, double measure) {
|
||||
metric.setValue(new Value(timestamp, measure));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Metric build() {
|
||||
return this.metric;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
package timely.model;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A simple interface that objects can implement to return the object size.
|
||||
*/
|
||||
public interface ObjectSizeOf {
|
||||
|
||||
/**
|
||||
* The (approximate) size of the object
|
||||
*/
|
||||
long sizeInBytes();
|
||||
|
||||
public static class ObjectInstance {
|
||||
|
||||
private Object o;
|
||||
|
||||
public ObjectInstance(Object _o) {
|
||||
this.o = _o;
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
return o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof ObjectInstance)) {
|
||||
return false;
|
||||
}
|
||||
return ((ObjectInstance) obj).o == this.o;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sizer {
|
||||
|
||||
private static final Logger log = Logger.getLogger(Sizer.class.getCanonicalName());
|
||||
public static final short OBJECT_OVERHEAD = 8;
|
||||
public static final short ARRAY_OVERHEAD = 12;
|
||||
public static final short REFERENCE = 4;
|
||||
// The size of the basic Number constructs (and Boolean and Character)
|
||||
// is 16: roundUp(8 + primitiveSize)
|
||||
public static final short NUMBER_SIZE = 16;
|
||||
|
||||
/**
|
||||
* Get the size of an object. Note that we want something relatively
|
||||
* fast that gives us an order of magnitude here. The java
|
||||
* Instrumentation agent mechanism is a little too costly for general
|
||||
* use here. This will look for the ObjectSizeOf interface and if
|
||||
* implemented on the object will use that. Otherwise it will do a
|
||||
* simple navigation of the fields using reflection.
|
||||
*
|
||||
* @param o
|
||||
* @return an approximation of the object size
|
||||
*/
|
||||
public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) {
|
||||
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(),
|
||||
useSizeInBytesMethod, debug);
|
||||
return estSize;
|
||||
}
|
||||
|
||||
public static long getObjectSize(Object o) {
|
||||
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(), true, false);
|
||||
return estSize;
|
||||
}
|
||||
|
||||
public static long getObjectSize(Object o, Set<ObjectInstance> visited, Stack<ObjectInstance> stack,
|
||||
boolean useSizeInBytesMethod, boolean debug) {
|
||||
long totalSize = 0;
|
||||
stack.add(new ObjectInstance(o));
|
||||
while (!stack.isEmpty()) {
|
||||
long size = 0;
|
||||
ObjectInstance oi = stack.pop();
|
||||
o = oi.getObject();
|
||||
if (o != null && !visited.contains(oi)) {
|
||||
visited.add(oi);
|
||||
try {
|
||||
if (useSizeInBytesMethod) {
|
||||
try {
|
||||
if (o instanceof ObjectSizeOf) {
|
||||
size = ((ObjectSizeOf) o).sizeInBytes();
|
||||
} else {
|
||||
Method sizeInBytes = o.getClass().getMethod("sizeInBytes", (Class<?>[]) null);
|
||||
size = (Long) sizeInBytes.invoke(o);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// ok, lets do this the hard way...
|
||||
}
|
||||
}
|
||||
if (size == 0) {
|
||||
// the hard way...
|
||||
// do not include Class related objects or
|
||||
// reflection objects
|
||||
if ((o instanceof Class)
|
||||
|| (o instanceof ClassLoader)
|
||||
|| (o.getClass().getPackage() != null && o.getClass().getPackage().getName()
|
||||
.startsWith("java.lang.reflect"))) {
|
||||
size = 0;
|
||||
} else if (o instanceof Number || o instanceof Boolean || o instanceof Character) {
|
||||
size = NUMBER_SIZE;
|
||||
} else {
|
||||
// lets do a simple sizing
|
||||
Class<?> c = o.getClass();
|
||||
if (c.isArray()) {
|
||||
size = ARRAY_OVERHEAD;
|
||||
int length = Array.getLength(o);
|
||||
if (c.getComponentType().isPrimitive()) {
|
||||
size += length * getPrimitiveObjectSize(c.getComponentType());
|
||||
} else {
|
||||
size += length * REFERENCE;
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object element = Array.get(o, i);
|
||||
if (element != null) {
|
||||
stack.add(new ObjectInstance(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size += OBJECT_OVERHEAD;
|
||||
while (c != null) {
|
||||
for (Field field : c.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
if (field.getType().isPrimitive()) {
|
||||
size += getPrimitiveObjectSize(field.getType());
|
||||
} else {
|
||||
size += REFERENCE;
|
||||
boolean accessible = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
Object fieldObject = field.get(o);
|
||||
if (fieldObject != null) {
|
||||
stack.push(new ObjectInstance(fieldObject));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// cannot get to field, so
|
||||
// ignore it in this size
|
||||
// calculation
|
||||
e.printStackTrace();
|
||||
}
|
||||
field.setAccessible(accessible);
|
||||
}
|
||||
}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
}
|
||||
size = roundUp(size);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.warning("Unable to determine object size for " + o);
|
||||
}
|
||||
if (debug) {
|
||||
System.out.println(o.getClass().getCanonicalName() + "=" + size);
|
||||
}
|
||||
}
|
||||
totalSize += size;
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
public static long roundUp(long size) {
|
||||
long extra = size % 8;
|
||||
if (extra > 0) {
|
||||
size = size + 8 - extra;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public static short getPrimitiveObjectSize(Class<?> primitiveType) {
|
||||
if (primitiveType.equals(int.class) || primitiveType.equals(float.class)) {
|
||||
return 4;
|
||||
} else if (primitiveType.equals(boolean.class) || primitiveType.equals(byte.class)) {
|
||||
return 1;
|
||||
} else if (primitiveType.equals(char.class) || primitiveType.equals(short.class)) {
|
||||
return 2;
|
||||
} else if (primitiveType.equals(long.class) || primitiveType.equals(double.class)) {
|
||||
return 8;
|
||||
} else { // if (primitiveType.equals(void.class)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package timely.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import timely.model.parse.TagParser;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Tag consists of key value pair
|
||||
*/
|
||||
@JsonRootName("tag")
|
||||
public class Tag implements Comparable<Tag>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final TagParser tagParser = new TagParser();
|
||||
|
||||
private String key;
|
||||
private String value;
|
||||
|
||||
public Tag() {
|
||||
}
|
||||
|
||||
public Tag(Tag other) {
|
||||
this.setKey(other.getKey());
|
||||
this.setValue(other.getValue());
|
||||
}
|
||||
|
||||
public Tag(String tag) {
|
||||
this(tagParser.parse(tag));
|
||||
}
|
||||
|
||||
public Tag(String key, String value) {
|
||||
Preconditions.checkNotNull(key);
|
||||
Preconditions.checkNotNull(value);
|
||||
this.setKey(key);
|
||||
this.setValue(value);
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, String> get() {
|
||||
return Stream.of(this).collect(Collectors.toMap(Tag::getKey, Tag::getValue));
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
public void set(String key, String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins key and value into key=value
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String join() {
|
||||
return tagParser.combine(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Tag{" + join() + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.toString().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (null == o || getClass() != o.getClass())
|
||||
return false;
|
||||
Tag other = (Tag) o;
|
||||
return new EqualsBuilder().append(this.key, other.key).append(this.value, other.value).isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Tag other) {
|
||||
return new CompareToBuilder().append(this.key, other.key).append(this.value, other.value).toComparison();
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package timely.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Value consists of a long timestamp and a double measurement
|
||||
*/
|
||||
public class Value implements Comparator<Value>, Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long timestamp;
|
||||
private Double measure;
|
||||
|
||||
public Value() {
|
||||
}
|
||||
|
||||
public Value(Value other) {
|
||||
this.setTimestamp(other.getTimestamp());
|
||||
this.setMeasure(other.getMeasure());
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public Value(@JsonProperty("timestamp") Long timestamp, @JsonProperty("measure") Double measure) {
|
||||
this.setTimestamp(timestamp);
|
||||
this.setMeasure(measure);
|
||||
}
|
||||
|
||||
@JsonProperty("timestamp")
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(final Long timestamp) {
|
||||
this.setTimestamp(timestamp, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void setTimestamp(final Long timestamp, TimeUnit timeUnit) {
|
||||
this.timestamp = timeUnit.toMillis(timestamp);
|
||||
}
|
||||
|
||||
@JsonProperty("measure")
|
||||
public Double getMeasure() {
|
||||
return measure;
|
||||
}
|
||||
|
||||
public void setMeasure(final Double measure) {
|
||||
this.measure = measure;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Compares timestamps for sorting.
|
||||
*
|
||||
* Note: this comparator imposes orderings that are inconsistent with equals
|
||||
*
|
||||
*/
|
||||
public int compare(Value v1, Value v2) {
|
||||
if (v1.getTimestamp().equals(v2.getTimestamp()))
|
||||
return 0;
|
||||
return v1.getTimestamp() > v2.getTimestamp() ? 1 : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (null == o || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
if (getTimestamp() != null ? !getTimestamp().equals(((Value) o).getTimestamp())
|
||||
: ((Value) o).getTimestamp() != null)
|
||||
return false;
|
||||
return !(getMeasure() != null ? !getMeasure().equals(((Value) o).getMeasure())
|
||||
: ((Value) o).getMeasure() != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(timestamp, measure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Value{" + "timestamp=" + timestamp + ", measure=" + measure + "}";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package timely.model.parse;
|
||||
|
||||
public interface Combiner<T> {
|
||||
|
||||
String combine(T o);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package timely.model.parse;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Splitter;
|
||||
import timely.model.Metric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MetricParser implements Parser<Metric> {
|
||||
|
||||
private static final Splitter spaceSplitter = Splitter.on(" ").omitEmptyStrings().trimResults();
|
||||
|
||||
@Override
|
||||
public Metric parse(String line) {
|
||||
|
||||
// put specification
|
||||
//
|
||||
// put <metricName> <timestamp> <value> <tagK=tagV> <tagK=tagV> ...
|
||||
|
||||
List<String> parts = spaceSplitter.splitToList(line);
|
||||
Preconditions.checkPositionIndex(3, parts.size());
|
||||
|
||||
Metric.Builder builder = Metric.newBuilder();
|
||||
// index 0 is put
|
||||
builder.name(parts.get(1)); // metric name
|
||||
builder.value(Long.parseLong(parts.get(2)), Double.parseDouble(parts.get(3)));
|
||||
parts.stream().skip(4).forEach(builder::tag);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package timely.model.parse;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Parser<T> {
|
||||
|
||||
T parse(String t);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package timely.model.parse;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import timely.model.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Parses tag1=val1,tag2=val2,tag3=val3 into a {@code List} of {@code Tag}
|
||||
* Combines {@code List} of {@code Tag} into {@code String} of
|
||||
* tag1=val1,tag2=val2,tag3=val3.
|
||||
*/
|
||||
public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag>> {
|
||||
|
||||
private static final Splitter commaSplitter = Splitter.on(",").omitEmptyStrings();
|
||||
private static final TagParser tagParser = new TagParser();
|
||||
|
||||
@Override
|
||||
public List<Tag> parse(String t) {
|
||||
ArrayList<Tag> tags = new ArrayList<>();
|
||||
commaSplitter.splitToList(t).forEach(p -> tags.add(tagParser.parse(p)));
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Collection<Tag> tags) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (!tags.isEmpty()) {
|
||||
tags.forEach(p -> builder.append(tagParser.combine(p)).append(','));
|
||||
builder.setLength(builder.length() - 1);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternative combiner for a map of string to string instead of a list
|
||||
* of tags
|
||||
*
|
||||
* @param tags
|
||||
* @return string representation
|
||||
*/
|
||||
public String combine(Map<String, String> tags) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (!tags.isEmpty()) {
|
||||
tags.forEach((k, v) -> builder.append(tagParser.combine(new Tag(k, v))).append(','));
|
||||
builder.setLength(builder.length() - 1);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package timely.model.parse;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import timely.model.Tag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parses tag1=val1 into a {@code Tag} Combines {@code Tag} into {@code String}
|
||||
* of tag1=val1
|
||||
*/
|
||||
public class TagParser implements Parser<Tag>, Combiner<Tag> {
|
||||
|
||||
private static final Splitter equalSplitter = Splitter.on("=").trimResults();
|
||||
private static final Joiner equalsJoiner = Joiner.on("=");
|
||||
|
||||
@Override
|
||||
public Tag parse(String t) {
|
||||
List<String> parts = equalSplitter.splitToList(t);
|
||||
if (parts.size() != 2) {
|
||||
throw new IllegalArgumentException("Invalid tag format: " + t);
|
||||
}
|
||||
return new Tag(parts.get(0), parts.get(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Tag t) {
|
||||
return equalsJoiner.join(t.getKey(), t.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package timely.serialize;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
|
||||
public class JsonSerializer {
|
||||
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
mapper.registerModule(new Jdk8Module());
|
||||
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
|
||||
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
|
||||
mapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
|
||||
}
|
||||
|
||||
public static ObjectMapper getObjectMapper() {
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='timely',
|
||||
version='${python.client.version}',
|
||||
packages=['timely'],
|
||||
install_requires=[
|
||||
'seaborn',
|
||||
'plotly'
|
||||
],
|
||||
license='Apache License, Version 2.0'
|
||||
)
|
||||
@@ -1,328 +0,0 @@
|
||||
import time
|
||||
from datetime import timedelta
|
||||
from datetime import datetime
|
||||
from TimeDateRange import UTC
|
||||
import pandas
|
||||
import matplotlib.dates as mdates
|
||||
import plotly.graph_objs as go
|
||||
import plotly.offline as py
|
||||
import seaborn
|
||||
import matplotlib.ticker as tkr
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
utc = UTC()
|
||||
|
||||
def pivot(df, metric, groupByColumn=None):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if groupByColumn is not None:
|
||||
dataFrame["date"] = dataFrame.index.to_pydatetime()
|
||||
dataFrame = dataFrame.pivot_table(index="date", columns=groupByColumn, values=metric)
|
||||
return dataFrame
|
||||
|
||||
|
||||
def resample(df, sample, how='mean'):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if sample is not None:
|
||||
dataFrame = dataFrame.resample(sample, how=how).interpolate()
|
||||
return dataFrame
|
||||
|
||||
|
||||
def unpivot(df, metric, groupByColumn=None):
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
dataFrame['date'] = dataFrame.index.values
|
||||
dataFrame = pandas.melt(dataFrame, id_vars=['date'], value_name=metric, var_name=groupByColumn)
|
||||
dataFrame = dataFrame.set_index('date')
|
||||
return dataFrame
|
||||
|
||||
|
||||
def rolling_average(df, metric, rolling_average=None):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if rolling_average is not None:
|
||||
dataFrame[metric] = pandas.rolling_mean(dataFrame[metric], rolling_average)
|
||||
return dataFrame
|
||||
|
||||
|
||||
def graph(analyticConfig, df, timelyMetric, seriesConfig={}, graphConfig={}, notebook=False, type='png'):
|
||||
if type == 'png':
|
||||
return graphSeaborn(analyticConfig, df, timelyMetric, seriesConfig=seriesConfig, graphConfig=graphConfig, notebook=notebook)
|
||||
else:
|
||||
return graphPlotly(analyticConfig, df, timelyMetric, seriesConfig=seriesConfig, graphConfig=graphConfig, notebook=notebook)
|
||||
|
||||
|
||||
def graphSeaborn(analyticConfig, df, timelyMetric, seriesConfig={}, graphConfig={}, notebook=False):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
dataFrame = ensureMinSeriesLength(dataFrame, analyticConfig.groupByColumn)
|
||||
dataFrame['date'] = dataFrame.index.values
|
||||
dataFrame['dateFloat'] = dataFrame['date'].apply(lambda x: datetime2graphdays(x))
|
||||
dataFrame['index'] = dataFrame['dateFloat']
|
||||
groupByColumn = analyticConfig.groupByColumn
|
||||
|
||||
if groupByColumn is not None:
|
||||
# sort on date and the groupByColumn so that the series legend is sorted
|
||||
dataFrame = dataFrame.sort_values(['index', analyticConfig.groupByColumn])
|
||||
else:
|
||||
dataFrame = dataFrame.sort_values(['index'])
|
||||
|
||||
#dataFrame = dataFrame.set_index('index', drop=True)
|
||||
dataFrame = dataFrame.dropna()
|
||||
seaborn.set(style="darkgrid", palette="Set2")
|
||||
fig, ax = seaborn.plt.subplots()
|
||||
|
||||
colors = np.random.random((len(dataFrame), 3))
|
||||
i = 0
|
||||
for col in df[analyticConfig.groupByColumn].unique():
|
||||
tempDataFrame = dataFrame.loc[dataFrame[analyticConfig.groupByColumn] == col]
|
||||
addlColConfig = seriesConfig.get(col, seriesConfig.get('default', dict()))
|
||||
markerDict = addlColConfig.get("marker", dict())
|
||||
color = markerDict.get("color", "")
|
||||
if color == "red":
|
||||
seaborn.tsplot(tempDataFrame, time="dateFloat", unit=None, interpolate=False, color="red", marker="d",
|
||||
estimator=np.nanmean, condition=groupByColumn, value=timelyMetric.metric, ax=ax)
|
||||
else:
|
||||
seaborn.tsplot(tempDataFrame, time="dateFloat", unit=None, interpolate=True, color=colors[i],
|
||||
estimator=np.nanmean, condition=groupByColumn, value=timelyMetric.metric, ax=ax)
|
||||
i = i + 1
|
||||
|
||||
ax.xaxis_date()
|
||||
ax.xaxis.set_major_locator(mdates.AutoDateLocator())
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y/%m/%d %H:%M:%S'))
|
||||
ax.set_xlim(xmin=dataFrame['dateFloat'].min(), xmax=dataFrame['dateFloat'].max())
|
||||
|
||||
formatter = tkr.ScalarFormatter(useMathText=False)
|
||||
formatter.set_scientific(False)
|
||||
ax.yaxis.set_major_formatter(formatter)
|
||||
|
||||
graphTitle = getTitle(timelyMetric, analyticConfig)
|
||||
seaborn.plt.title(graphTitle, fontsize='large')
|
||||
seaborn.plt.xlabel('date/time')
|
||||
seaborn.plt.ylabel(timelyMetric.metric)
|
||||
|
||||
legendTitle = "Legend"
|
||||
if groupByColumn is not None:
|
||||
legendTitle = groupByColumn
|
||||
|
||||
seaborn.plt.legend(loc='upper center', prop={'size':8}, ncol=10, title=legendTitle, bbox_to_anchor=(0.5,-0.4))
|
||||
fig.autofmt_xdate(rotation=45)
|
||||
|
||||
seaborn.plt.autoscale(tight=False)
|
||||
|
||||
if notebook == True:
|
||||
seaborn.plt.show()
|
||||
return None
|
||||
else:
|
||||
directory = analyticConfig.output_dir
|
||||
try:
|
||||
os.makedirs(directory, mode=0755)
|
||||
except OSError, e:
|
||||
# be happy if someone already created the path
|
||||
pass
|
||||
baseFilename = getFilename(timelyMetric.metric, analyticConfig)
|
||||
filename = directory+'/'+baseFilename
|
||||
fig.savefig(filename + '.png')
|
||||
return baseFilename + '.png'
|
||||
|
||||
|
||||
def graphPlotly(analyticConfig, df, timelyMetric, seriesConfig={}, graphConfig={}, notebook=False):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
dataFrame['colFromIndex'] = dataFrame.index
|
||||
if analyticConfig.groupByColumn is not None:
|
||||
# sort on date and the groupByColumn so that the series legend is sorted
|
||||
dataFrame = dataFrame.sort_values(['colFromIndex', analyticConfig.groupByColumn])
|
||||
else:
|
||||
dataFrame = dataFrame.sort_values(['colFromIndex'])
|
||||
|
||||
title = getTitle(timelyMetric, analyticConfig)
|
||||
|
||||
dataFrame['date'] = dataFrame.index.values
|
||||
|
||||
layoutConfig = dict(
|
||||
title=title,
|
||||
autosize=False,
|
||||
width=1000,
|
||||
height=700,
|
||||
showlegend=True,
|
||||
xaxis=dict(
|
||||
autorange=True,
|
||||
tickangle=-45
|
||||
),
|
||||
yaxis=dict(
|
||||
autorange=True,
|
||||
title=timelyMetric.metric
|
||||
)
|
||||
)
|
||||
|
||||
layoutConfig.update(graphConfig)
|
||||
|
||||
layout = go.Layout(layoutConfig)
|
||||
|
||||
data = None
|
||||
groupByValues = sorted(dataFrame[analyticConfig.groupByColumn].unique())
|
||||
for col in groupByValues:
|
||||
tempDataFrame = dataFrame.loc[dataFrame[analyticConfig.groupByColumn] == col]
|
||||
|
||||
addlColConfig = seriesConfig.get(col, seriesConfig.get('default', dict()))
|
||||
|
||||
config = dict(
|
||||
name=col,
|
||||
x=tempDataFrame.index,
|
||||
y=tempDataFrame[timelyMetric.metric],
|
||||
hoverinfo='x+y+text',
|
||||
text=col
|
||||
)
|
||||
|
||||
config.update(addlColConfig)
|
||||
|
||||
s = go.Scatter(config)
|
||||
|
||||
if data is None:
|
||||
data = [s]
|
||||
else:
|
||||
data.append(s)
|
||||
|
||||
directory = analyticConfig.output_dir
|
||||
try:
|
||||
os.makedirs(directory, mode=0755)
|
||||
except OSError, e:
|
||||
# be happy if someone already created the path
|
||||
pass
|
||||
baseFilename = getFilename(timelyMetric.metric, analyticConfig)
|
||||
filename = directory+'/'+baseFilename
|
||||
|
||||
fig = go.Figure(data=data, layout=layout)
|
||||
if notebook:
|
||||
py.iplot(fig, filename=filename, show_link=False)
|
||||
return None
|
||||
else:
|
||||
py.plot(fig, output_type='file', filename=filename+'.html', image_filename=filename, image=None, auto_open=False, show_link=False)
|
||||
return baseFilename+'.html'
|
||||
|
||||
def ensureMinSeriesLength(df, groupByColumn):
|
||||
|
||||
dataFrame = df
|
||||
if groupByColumn is not None:
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
dataFrame = dataFrame.dropna()
|
||||
|
||||
extra = pandas.DataFrame()
|
||||
for c, (cond, df_c) in enumerate(dataFrame.groupby(groupByColumn, sort=False)):
|
||||
if len(df_c) <= 1:
|
||||
extra = extra.append(df_c)
|
||||
|
||||
extra.index = extra.index + pandas.DateOffset(seconds=1)
|
||||
dataFrame = dataFrame.append(extra)
|
||||
|
||||
return dataFrame
|
||||
|
||||
def datetime2graphdays(dt):
|
||||
|
||||
return mdates.epoch2num((time.mktime(dt.timetuple()) / 86400) * mdates.SEC_PER_DAY)
|
||||
|
||||
def graphdays2datetime(x):
|
||||
epoch = mdates.num2epoch(x)
|
||||
return datetime.fromtimestamp(int(epoch), utc)
|
||||
|
||||
|
||||
def getFilename(metric, analyticConfig):
|
||||
|
||||
now = datetime.now(tz=utc)
|
||||
format = "%Y%m%d%H%M%S"
|
||||
nowStr = time.strftime(format, now.timetuple())
|
||||
fileNameBase = metric
|
||||
if analyticConfig.sample is not None:
|
||||
fileNameBase += "-" + analyticConfig.sample
|
||||
if analyticConfig.how is not None:
|
||||
fileNameBase += "-" + analyticConfig.how
|
||||
|
||||
return fileNameBase + '-' + nowStr
|
||||
|
||||
def getDirectoryPathForDate(format='%Y/%m/%d'):
|
||||
now = datetime.now(tz=utc)
|
||||
return time.strftime(format, now.timetuple())
|
||||
|
||||
def getTitle(timelyMetric, config, separator='\n'):
|
||||
if config.orCondition:
|
||||
condition = "OR"
|
||||
else:
|
||||
condition = "AND"
|
||||
|
||||
metricTitle = timelyMetric.metric
|
||||
if timelyMetric.tags is not None and len(timelyMetric.tags) > 0:
|
||||
metricTitle += ' (' + timelyMetric.tags + ')'
|
||||
|
||||
sampleTitle = ""
|
||||
alertTitle = ""
|
||||
if config.sample_period is not None:
|
||||
sampleTitle = config.sample_period + " " + config.how + " samples"
|
||||
|
||||
if config.rolling_average_period is not None:
|
||||
sampleTitle += ', ' + config.rolling_average_period + ' rolling average'
|
||||
|
||||
if config.min_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y < " + str(config.min_threshold) + ")"
|
||||
|
||||
if config.average_min_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y-avg < " + str(config.average_min_threshold) + ")"
|
||||
|
||||
if config.max_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y > " + str(config.max_threshold) + ")"
|
||||
|
||||
if config.average_max_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y-avg > " + str(config.average_max_threshold) + ")"
|
||||
|
||||
if config.min_threshold_percentage is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
if config.min_threshold_percentage >= 0:
|
||||
descriptivePercentage = 100 + config.min_threshold_percentage
|
||||
else:
|
||||
descriptivePercentage = abs(config.min_threshold_percentage)
|
||||
alertTitle += "(y < " + str(descriptivePercentage) + "% of " + str(
|
||||
config.rolling_average_period) + " sample average)"
|
||||
|
||||
if config.max_threshold_percentage is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
if config.max_threshold_percentage >= 0:
|
||||
descriptivePercentage = 100 + config.max_threshold_percentage
|
||||
else:
|
||||
descriptivePercentage = abs(config.max_threshold_percentage)
|
||||
alertTitle += "(y > " + str(descriptivePercentage) + "% of " + str(
|
||||
config.rolling_average_period) + " sample average)"
|
||||
|
||||
if config.system_name is None:
|
||||
title = metricTitle
|
||||
else:
|
||||
title = config.system_name + ' - ' + metricTitle
|
||||
|
||||
if sampleTitle != "":
|
||||
title += separator + sampleTitle
|
||||
|
||||
if alertTitle != "":
|
||||
if config.min_alert_period is not None:
|
||||
alertTitle = '(' + alertTitle + ') for at least ' + config.min_alert_period
|
||||
if config.last_alert is not None:
|
||||
alertTitle = alertTitle + ' in last ' + config.last_alert
|
||||
title += separator + alertTitle
|
||||
|
||||
|
||||
return title
|
||||
@@ -1,128 +0,0 @@
|
||||
import time
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from datetime import tzinfo
|
||||
from pandas.tslib import Timedelta
|
||||
import pandas
|
||||
|
||||
class TimeDateError(ValueError):
|
||||
|
||||
def __init__(self, message, *args):
|
||||
self.message = message
|
||||
super(TimeDateError, self).__init__(message, *args)
|
||||
|
||||
|
||||
ZERO = timedelta(0)
|
||||
|
||||
# A UTC class.
|
||||
class UTC(tzinfo):
|
||||
"""UTC"""
|
||||
|
||||
def __str__(self):
|
||||
return "UTC"
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
return "UTC"
|
||||
|
||||
def dst(self, dt):
|
||||
return ZERO
|
||||
|
||||
utc = UTC()
|
||||
|
||||
class TimeDateRange:
|
||||
|
||||
beginDt = None
|
||||
endDt = None
|
||||
format = "%Y%m%d %H%M%S"
|
||||
|
||||
def getBeginMs(self):
|
||||
if self.beginDt == None:
|
||||
return None
|
||||
return int(time.mktime(self.beginDt.timetuple()) * 1000)
|
||||
|
||||
def getBeginStr(self):
|
||||
if self.beginDt == None:
|
||||
return None
|
||||
return time.strftime(self.format, self.beginDt.timetuple())
|
||||
|
||||
def getEndMs(self):
|
||||
if self.endDt == None:
|
||||
return None
|
||||
return int(time.mktime(self.endDt.timetuple()) * 1000)
|
||||
|
||||
def getEndStr(self):
|
||||
if self.endDt == None:
|
||||
return None
|
||||
return time.strftime(self.format, self.endDt.timetuple())
|
||||
|
||||
def __init__(self, beginStr, endStr, period, format="%Y%m%d-%H%M%S"):
|
||||
|
||||
now = datetime.now(tz=utc)
|
||||
nowMs = TimeDateRange.unix_time_millis(now)
|
||||
|
||||
if beginStr != None and endStr != None and period != None:
|
||||
raise TimeDateError("You can not provide a begin, end, and period")
|
||||
elif (beginStr == None and endStr == None and period == None) or (period == None and beginStr == None):
|
||||
raise TimeDateError("You must provide a time range")
|
||||
|
||||
if period == None:
|
||||
# calculate begin time
|
||||
beginTuple = time.strptime(beginStr, self.format)
|
||||
self.beginDt = datetime(*beginTuple[0:6], tzinfo=utc)
|
||||
|
||||
# calculate end time
|
||||
# end is either specified or now
|
||||
if endStr == None or endStr == 'now':
|
||||
self.endDt = now
|
||||
else:
|
||||
|
||||
self.endDt = datetime(*time.strptime(endStr, self.format)[0:6], tzinfo=utc)
|
||||
if self.endDt > now:
|
||||
self.endDt = now
|
||||
else:
|
||||
# period must be specified
|
||||
td = pandas.to_timedelta(period)
|
||||
rangeInSec = td.total_seconds()
|
||||
|
||||
if beginStr == None:
|
||||
# calculate begin time from end tme and period
|
||||
|
||||
# calculate end time
|
||||
# end is either specified or now
|
||||
if endStr == None or endStr == 'now':
|
||||
self.endDt = now
|
||||
else:
|
||||
self.endDt = datetime(*time.strptime(endStr, self.format)[0:6], tzinfo=utc)
|
||||
if self.endDt > now:
|
||||
self.endDt = now
|
||||
|
||||
# calculate begin time
|
||||
self.beginDt = self.endDt - timedelta(milliseconds=rangeInSec * 1000)
|
||||
|
||||
else:
|
||||
# calculate end time from begin tme and period
|
||||
# calculate begin time
|
||||
self.beginDt = datetime(*time.strptime(beginStr, self.format)[0:6], tzinfo=utc)
|
||||
|
||||
# calculate end time
|
||||
self.endDt = self.beginDt + timedelta(milliseconds=rangeInSec * 1000)
|
||||
|
||||
if self.endDt > now:
|
||||
self.endDt = now
|
||||
|
||||
if self.beginDt > now:
|
||||
raise TimeDateError("begin date/time can not be in the future")
|
||||
|
||||
if self.beginDt > self.endDt:
|
||||
raise TimeDateError("begin date/time can not be past end date/time")
|
||||
|
||||
epoch = datetime.utcfromtimestamp(0).replace(tzinfo=utc)
|
||||
|
||||
@classmethod
|
||||
def unix_time_millis(cls, dt):
|
||||
return (dt - TimeDateRange.epoch).total_seconds() * 1000.0
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import smtplib
|
||||
from os.path import basename
|
||||
from email.mime.application import MIMEApplication
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import COMMASPACE, formatdate
|
||||
import TimelyMetric
|
||||
import syslog
|
||||
import DataOperations
|
||||
|
||||
syslog.openlog("TimelyNotifications")
|
||||
|
||||
|
||||
class TimelyAlert():
|
||||
|
||||
def __init__(self, timelyMetric, dataFrame, message, seriesConfig, analyticConfig, notebook):
|
||||
self.timelyMetric = timelyMetric
|
||||
self.dataFrame = dataFrame
|
||||
self.seriesConfig = seriesConfig
|
||||
self.analyticConfig = analyticConfig
|
||||
self.notebook = notebook
|
||||
self.message = message
|
||||
|
||||
def email(self, send_from, send_to, subject, text, files=None, server="127.0.0.1"):
|
||||
assert isinstance(send_to, list)
|
||||
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = send_from
|
||||
msg['To'] = COMMASPACE.join(send_to)
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
msg['Subject'] = subject
|
||||
|
||||
msg.attach(MIMEText(text))
|
||||
|
||||
for f in files or []:
|
||||
with open(f, "rb") as fil:
|
||||
part = MIMEApplication(
|
||||
fil.read(),
|
||||
Name=basename(f)
|
||||
)
|
||||
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
|
||||
msg.attach(part)
|
||||
|
||||
|
||||
smtp = smtplib.SMTP(server)
|
||||
smtp.sendmail(send_from, send_to, msg.as_string())
|
||||
smtp.close()
|
||||
|
||||
def log(self, message):
|
||||
|
||||
syslog.systlog(syslog.LOG_ALERT, message)
|
||||
|
||||
def graph(self, type="png"):
|
||||
|
||||
graphConfig = {}
|
||||
graphConfig["title"] = DataOperations.getTitle(self.timelyMetric, self.analyticConfig)
|
||||
return TimelyMetric.graph(self.analyticConfig, self.dataFrame, self.timelyMetric, seriesConfig=self.seriesConfig, graphConfig=graphConfig, notebook=self.notebook, type=type)
|
||||
|
||||
def getDataFrame(self):
|
||||
return self.dataFrame
|
||||
|
||||
def getAnalyticConfig(self):
|
||||
return self.analyticConfig
|
||||
|
||||
def getSeriesConfig(self):
|
||||
return self.seriesConfig
|
||||
|
||||
def getTimelyMetric(self):
|
||||
return self.timelyMetric
|
||||
|
||||
def getMessage(self):
|
||||
return self.message
|
||||
@@ -1,224 +0,0 @@
|
||||
import TimelyMetric
|
||||
from TimeDateRange import TimeDateRange
|
||||
from TimeDateRange import UTC
|
||||
from TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
|
||||
import DataOperations
|
||||
import pandas
|
||||
import re
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
|
||||
from TimelyAlert import TimelyAlert
|
||||
import numpy as np
|
||||
|
||||
utc = UTC()
|
||||
|
||||
def logTiming(timelyMetric, startTime, stopTime):
|
||||
df = timelyMetric.getDataFrame()
|
||||
print("start: " + str(startTime))
|
||||
print("stop: " + str(stopTime))
|
||||
print("num records: " + str(len(df)))
|
||||
elapsedSec = (TimeDateRange.unix_time_millis(stopTime) - TimeDateRange.unix_time_millis(startTime)) / 1000
|
||||
print("elapsed seconds: " + str(elapsedSec))
|
||||
recPerSec = len(df) / elapsedSec
|
||||
print("records per second: " + recPerSec)
|
||||
|
||||
def addCondition(orCondition, currResult, condition):
|
||||
if orCondition:
|
||||
currResult = currResult | condition
|
||||
else:
|
||||
currResult = currResult & condition
|
||||
return currResult
|
||||
|
||||
def keepConsecutiveAlerts(dataFrame, exceptions, minimumSpan):
|
||||
|
||||
if exceptions.size > 0 and minimumSpan is not None:
|
||||
|
||||
currentFirst = None
|
||||
currentLast = None
|
||||
result_span_values = pandas.DataFrame()
|
||||
result_span_values['bool'] = np.zeros(exceptions.shape, bool)
|
||||
result_span_values = result_span_values.set_index(exceptions.index)
|
||||
current_result_span_values = pandas.DataFrame()
|
||||
current_result_span_values['bool'] = np.zeros(exceptions.shape, bool)
|
||||
current_result_span_values = current_result_span_values.set_index(exceptions.index)
|
||||
|
||||
for index, row in dataFrame.iterrows():
|
||||
if index in exceptions.index:
|
||||
current_result_span_values.loc[index]['bool'] = True
|
||||
currentLast = index
|
||||
if currentFirst is None:
|
||||
currentFirst = index
|
||||
if int((currentLast - currentFirst).total_seconds() / 60) >= int(minimumSpan):
|
||||
result_span_values['bool'] = result_span_values['bool'] | current_result_span_values['bool']
|
||||
else:
|
||||
current_result_span_values['bool'] = np.zeros(exceptions.shape, bool)
|
||||
currentFirst = None
|
||||
currentLast = None
|
||||
|
||||
return exceptions.loc[result_span_values['bool']]
|
||||
else:
|
||||
return exceptions
|
||||
|
||||
def convertCounterToRate(timelyMetric, analyticConfig):
|
||||
# Adjust counter metric into a rate metric
|
||||
df = timelyMetric.getDataFrame()
|
||||
columnNames = df[analyticConfig.groupByColumn].unique()
|
||||
df = DataOperations.pivot(df, timelyMetric.metric, analyticConfig.groupByColumn)
|
||||
df = DataOperations.resample(df, analyticConfig.sample, how=analyticConfig.how)
|
||||
for c in columnNames:
|
||||
df[c] = df[c].diff(1) / (analyticConfig.sample_minutes * 60 * 1000)
|
||||
df = DataOperations.unpivot(df, timelyMetric.metric, analyticConfig.groupByColumn)
|
||||
df = df.dropna()
|
||||
timelyMetric.setDataFrame(df)
|
||||
|
||||
def find_alerts(timelyMetric, analyticConfig, notebook=False):
|
||||
|
||||
if (analyticConfig.counter == True):
|
||||
convertCounterToRate(timelyMetric, analyticConfig)
|
||||
|
||||
df = timelyMetric.getDataFrame()
|
||||
|
||||
graphDF = TimelyMetric.pivot(df, timelyMetric.metric, groupByColumn=analyticConfig.groupByColumn)
|
||||
|
||||
if analyticConfig.excludeColRegex is not None:
|
||||
graphDF = graphDF.select(lambda x : not (re.search(analyticConfig.excludeColRegex, x)), axis=1)
|
||||
if analyticConfig.includeColRegex is not None:
|
||||
graphDF = graphDF.select(lambda x : re.search(analyticConfig.includeColRegex, x), axis=1)
|
||||
|
||||
if analyticConfig.sample is not None:
|
||||
graphDF = TimelyMetric.resample(graphDF, analyticConfig.sample, how=analyticConfig.how)
|
||||
|
||||
graphDF_avg = pandas.DataFrame(graphDF, copy=True)
|
||||
|
||||
combined = pandas.DataFrame()
|
||||
|
||||
seriesConfig = {}
|
||||
for i in graphDF_avg.columns:
|
||||
col = str(i)
|
||||
|
||||
any_conditions_values = False
|
||||
result_values = np.ones(graphDF[col].shape, bool)
|
||||
if analyticConfig.orCondition:
|
||||
result_values = np.zeros(graphDF[col].shape, bool)
|
||||
|
||||
any_conditions_average = False
|
||||
result_average = np.ones(graphDF_avg[col].shape, bool)
|
||||
if analyticConfig.orCondition:
|
||||
result_average = np.zeros(graphDF_avg[col].shape, bool)
|
||||
|
||||
if analyticConfig.min_threshold is not None:
|
||||
currCondition = graphDF[col].astype(float) < analyticConfig.min_threshold
|
||||
result_values = addCondition(analyticConfig.orCondition, result_values, currCondition)
|
||||
any_conditions_values = True
|
||||
|
||||
if analyticConfig.max_threshold is not None:
|
||||
currCondition = graphDF[col].astype(float) > analyticConfig.max_threshold
|
||||
result_values = addCondition(analyticConfig.orCondition, result_values, currCondition)
|
||||
any_conditions_values = True
|
||||
|
||||
if analyticConfig.rolling_average_samples is not None:
|
||||
graphDF_avg = TimelyMetric.rolling_average(graphDF_avg, col, rolling_average=analyticConfig.rolling_average_samples)
|
||||
if analyticConfig.min_threshold_percentage is not None:
|
||||
if analyticConfig.min_threshold_percentage >= 0:
|
||||
multiple = 1.0 + (float(abs(analyticConfig.min_threshold_percentage)) / float(100))
|
||||
else:
|
||||
multiple = 1.0 - (float(abs(analyticConfig.min_threshold_percentage)) / float(100))
|
||||
currCondition = graphDF[col].astype(float) < (graphDF_avg[col].astype(float) * multiple)
|
||||
result_values = addCondition(analyticConfig.orCondition, result_values, currCondition)
|
||||
any_conditions_values = True
|
||||
|
||||
if analyticConfig.max_threshold_percentage is not None:
|
||||
if analyticConfig.max_threshold_percentage >= 0:
|
||||
multiple = 1.0 + (float(abs(analyticConfig.max_threshold_percentage)) / float(100))
|
||||
else:
|
||||
multiple = 1.0 - (float(abs(analyticConfig.max_threshold_percentage)) / float(100))
|
||||
currCondition = graphDF[col].astype(float) > (graphDF_avg[col].astype(float) * multiple)
|
||||
result_values = addCondition(analyticConfig.orCondition, result_values, currCondition)
|
||||
any_conditions_values = True
|
||||
|
||||
if analyticConfig.average_min_threshold is not None:
|
||||
currCondition = graphDF_avg[col].astype(float) < analyticConfig.average_min_threshold
|
||||
result_average = addCondition(analyticConfig.orCondition, result_average, currCondition)
|
||||
any_conditions_average = True
|
||||
if analyticConfig.average_max_threshold is not None:
|
||||
currCondition = graphDF_avg[col].astype(float) > analyticConfig.average_max_threshold
|
||||
result_average = addCondition(analyticConfig.orCondition, result_average, currCondition)
|
||||
any_conditions_average = True
|
||||
|
||||
# if orCondition is AND and no exceptional conditions have been found, then result_values will be all True
|
||||
if any_conditions_values == False:
|
||||
result_values = np.zeros(graphDF[col].shape, bool)
|
||||
exceptional_values = graphDF.loc[result_values, col]
|
||||
|
||||
# if orCondition is AND and no exceptional conditions have been found, then result_average will be all True
|
||||
if any_conditions_average == False:
|
||||
result_average = np.zeros(graphDF_avg[col].shape, bool)
|
||||
exceptional_average = graphDF_avg.loc[result_average, col]
|
||||
|
||||
# only keep alerts that are in consecutive periods of length analyticConfig.min_alert_minutes
|
||||
exceptional_values = keepConsecutiveAlerts(graphDF, exceptional_values, analyticConfig.min_alert_minutes)
|
||||
exceptional_average = keepConsecutiveAlerts(graphDF_avg, exceptional_average, analyticConfig.min_alert_minutes)
|
||||
|
||||
# only evaluate the last analyticConfig.last_alert_minutes if set
|
||||
if analyticConfig.last_alert_minutes is not None:
|
||||
end = datetime.fromtimestamp(timelyMetric.timeDateRange.getEndMs() / 1000.00, DataOperations.utc)
|
||||
recentEnoughBegin = end - timedelta(minutes=analyticConfig.last_alert_minutes)
|
||||
exceptional_values = exceptional_values.ix[recentEnoughBegin:end]
|
||||
exceptional_average = exceptional_average.ix[recentEnoughBegin:end]
|
||||
|
||||
anyValueExceptions = exceptional_values.size > 0
|
||||
anyAverageExceptions = exceptional_average.size > 0
|
||||
|
||||
if (analyticConfig.display.lower() == "all") or (analyticConfig.display.lower() == "alerts" and anyValueExceptions):
|
||||
combined[col] = graphDF[col]
|
||||
|
||||
if analyticConfig.rolling_average_samples is not None:
|
||||
if (analyticConfig.display.lower() == "all"):
|
||||
combined[col + '_avg'] = graphDF_avg[col]
|
||||
else:
|
||||
if (anyAverageExceptions):
|
||||
combined[col + '_avg'] = graphDF_avg[col]
|
||||
if (anyValueExceptions and (analyticConfig.min_threshold_percentage is not None or analyticConfig.max_threshold_percentage is not None)):
|
||||
combined[col + '_avg'] = graphDF_avg[col]
|
||||
|
||||
if ((analyticConfig.display.lower() == "all") or (analyticConfig.display.lower() == "alerts" and anyValueExceptions)):
|
||||
combined[col + '_warn'] = exceptional_values.dropna()
|
||||
|
||||
seriesConfig[col + '_warn'] = {
|
||||
"mode" : "markers",
|
||||
"marker" : {
|
||||
"symbol" : "hash-open",
|
||||
"color" : "red"
|
||||
}
|
||||
}
|
||||
|
||||
if ((analyticConfig.display.lower() == "all") or (analyticConfig.display.lower() == "alerts" and anyAverageExceptions)):
|
||||
combined[col + '_avg_warn'] = exceptional_average.dropna()
|
||||
|
||||
seriesConfig[col + '_avg_warn'] = {
|
||||
"mode" : "markers",
|
||||
"marker" : {
|
||||
"symbol" : "hash-open",
|
||||
"color" : "red"
|
||||
}
|
||||
}
|
||||
|
||||
timelyAlert = None
|
||||
if not combined.empty:
|
||||
alertAnalyticConfig = TimelyAnalyticConfiguration(analyticConfig)
|
||||
if alertAnalyticConfig.groupByColumn is None:
|
||||
alertAnalyticConfig.groupByColumn = timelyMetric.metric + "_obs"
|
||||
combined = TimelyMetric.unpivot(combined, timelyMetric.metric, groupByColumn=alertAnalyticConfig.groupByColumn)
|
||||
combined = combined.sort_index()
|
||||
combined['date'] = combined.index.values
|
||||
combined = combined.sort_values(['date', alertAnalyticConfig.groupByColumn])
|
||||
combined = combined.drop(['date'], 1)
|
||||
combined = combined.dropna()
|
||||
combined = DataOperations.ensureMinSeriesLength(combined, alertAnalyticConfig.groupByColumn)
|
||||
|
||||
message = DataOperations.getTitle(timelyMetric, analyticConfig, separator=', ')
|
||||
|
||||
timelyAlert = TimelyAlert(timelyMetric, combined, message, seriesConfig, alertAnalyticConfig, notebook)
|
||||
|
||||
return timelyAlert
|
||||
@@ -1,84 +0,0 @@
|
||||
import pandas
|
||||
|
||||
|
||||
class TimelyAnalyticConfiguration():
|
||||
|
||||
|
||||
def __init__(self, analyticConfig):
|
||||
|
||||
if isinstance(analyticConfig, dict):
|
||||
self.groupByColumn = analyticConfig.get('groupByColumn', None)
|
||||
self.includeColRegex = analyticConfig.get('includeColRegex', None)
|
||||
self.excludeColRegex = analyticConfig.get('excludeColRegex', None)
|
||||
self.counter = analyticConfig.get('counter', False)
|
||||
self.sample_period = analyticConfig.get('sample', None)
|
||||
self.how = analyticConfig.get('how', 'mean')
|
||||
self.rolling_average_period = analyticConfig.get('rolling_average_period', None)
|
||||
self.min_threshold = analyticConfig.get('min_threshold', None)
|
||||
self.average_min_threshold = analyticConfig.get('average_min_threshold', None)
|
||||
self.max_threshold = analyticConfig.get('max_threshold', None)
|
||||
self.average_max_threshold = analyticConfig.get('average_max_threshold', None)
|
||||
self.min_threshold_percentage = analyticConfig.get('min_threshold_percentage', None)
|
||||
self.max_threshold_percentage = analyticConfig.get('max_threshold_percentage', None)
|
||||
self.min_alert_period = analyticConfig.get('min_alert_period', None)
|
||||
boolean = analyticConfig.get('boolean', 'or')
|
||||
self.orCondition = boolean == 'or' or boolean == 'OR'
|
||||
# alerts or all
|
||||
self.display = analyticConfig.get('display', 'alerts')
|
||||
self.send_alerts_to = analyticConfig.get('send_alerts_to', [])
|
||||
self.output_dir = analyticConfig.get('output_dir', '/tmp')
|
||||
self.last_alert = analyticConfig.get('last_alert', None)
|
||||
self.system_name = analyticConfig.get('system_name', None)
|
||||
|
||||
self.sample = None
|
||||
self.sample_minutes = None
|
||||
if self.sample_period is not None:
|
||||
td = pandas.to_timedelta(self.sample_period)
|
||||
self.sample_minutes = int(td.total_seconds() / 60)
|
||||
self.sample = str(self.sample_minutes) + 'min'
|
||||
|
||||
self.rolling_average_samples = None
|
||||
self.rolling_average_minutes = None
|
||||
if (self.rolling_average_period is not None) and (self.sample_minutes is not None):
|
||||
td = pandas.to_timedelta(self.rolling_average_period)
|
||||
self.rolling_average_minutes = int(td.total_seconds() / 60)
|
||||
self.rolling_average_samples = int(self.rolling_average_minutes / self.sample_minutes)
|
||||
|
||||
self.min_alert_minutes = None
|
||||
if self.min_alert_period is not None:
|
||||
td = pandas.to_timedelta(self.min_alert_period)
|
||||
self.min_alert_minutes = int(td.total_seconds() / 60)
|
||||
|
||||
self.last_alert_minutes = None
|
||||
if self.last_alert is not None:
|
||||
td = pandas.to_timedelta(self.last_alert)
|
||||
self.last_alert_minutes = int(td.total_seconds() / 60)
|
||||
|
||||
elif isinstance(analyticConfig, TimelyAnalyticConfiguration):
|
||||
self.groupByColumn = analyticConfig.groupByColumn
|
||||
self.includeColRegex = analyticConfig.includeColRegex
|
||||
self.excludeColRegex = analyticConfig.excludeColRegex
|
||||
self.counter = analyticConfig.counter
|
||||
self.sample_period = analyticConfig.sample_period
|
||||
self.sample_minutes = analyticConfig.sample_minutes
|
||||
self.sample = analyticConfig.sample
|
||||
self.how = analyticConfig.how
|
||||
self.rolling_average_period = analyticConfig.rolling_average_period
|
||||
self.rolling_average_samples = analyticConfig.rolling_average_samples
|
||||
self.rolling_average_minutes = analyticConfig.rolling_average_minutes
|
||||
self.min_threshold = analyticConfig.min_threshold
|
||||
self.average_min_threshold = analyticConfig.average_min_threshold
|
||||
self.max_threshold = analyticConfig.max_threshold
|
||||
self.average_max_threshold = analyticConfig.average_max_threshold
|
||||
self.min_threshold_percentage = analyticConfig.min_threshold_percentage
|
||||
self.max_threshold_percentage = analyticConfig.max_threshold_percentage
|
||||
self.min_alert_period = analyticConfig.min_alert_period
|
||||
self.min_alert_minutes = analyticConfig.min_alert_minutes
|
||||
self.orCondition = analyticConfig.orCondition
|
||||
# alerts or all
|
||||
self.display = analyticConfig.display
|
||||
self.send_alerts_to = analyticConfig.send_alerts_to
|
||||
self.output_dir = analyticConfig.output_dir
|
||||
self.last_alert = analyticConfig.last_alert
|
||||
self.last_alert_minutes = analyticConfig.last_alert_minutes
|
||||
self.system_name = analyticConfig.system_name
|
||||
@@ -1,216 +0,0 @@
|
||||
from TimeDateRange import TimeDateRange
|
||||
from TimeDateRange import TimeDateError
|
||||
from TimeDateRange import UTC
|
||||
from DataOperations import *
|
||||
import json
|
||||
import pandas
|
||||
import getopt
|
||||
import sys
|
||||
import random
|
||||
|
||||
from WebSocketClient import WebSocketClient
|
||||
from tornado import ioloop
|
||||
|
||||
utc = UTC()
|
||||
|
||||
class TimelyWebSocketClient(WebSocketClient):
|
||||
|
||||
def __init__(self, hostport, metric, tags, startTime, endTime, connect_timeout=WebSocketClient.DEFAULT_CONNECT_TIMEOUT,
|
||||
request_timeout=WebSocketClient.DEFAULT_REQUEST_TIMEOUT):
|
||||
|
||||
self.metric = metric
|
||||
self.tags = tags
|
||||
self.startTime = startTime
|
||||
self.endTime = endTime
|
||||
self.subscriptionId = random.randint(1000000000, 9999999999)
|
||||
WebSocketClient.__init__(self, hostport, connect_timeout, request_timeout)
|
||||
|
||||
def _on_message(self, msg):
|
||||
# implement in subclass
|
||||
pass
|
||||
|
||||
def _on_connection_success(self):
|
||||
print('Connected.')
|
||||
|
||||
create = {
|
||||
"operation": "create",
|
||||
"subscriptionId": self.subscriptionId,
|
||||
}
|
||||
self.send(create)
|
||||
self.send(create)
|
||||
|
||||
m1 = {
|
||||
"operation": "add",
|
||||
"subscriptionId": self.subscriptionId,
|
||||
"metric": self.metric,
|
||||
"startTime": self.startTime,
|
||||
"endTime" : self.endTime
|
||||
}
|
||||
|
||||
if self.tags is not None:
|
||||
t = self.tags.strip().split(',')
|
||||
tagDict = {}
|
||||
for pair in t:
|
||||
k = pair.split('=')[0].strip()
|
||||
v = pair.split('=')[1].strip()
|
||||
tagDict = dict(tagDict.items() + dict({k : v}).items())
|
||||
m1["tags"] = tagDict
|
||||
|
||||
self.send(m1)
|
||||
|
||||
def _on_connection_close(self):
|
||||
print('Connection closed!')
|
||||
exit()
|
||||
|
||||
def _on_connection_error(self, exception):
|
||||
print('Connection error: %s', exception)
|
||||
|
||||
|
||||
|
||||
|
||||
class TimelyMetric(TimelyWebSocketClient):
|
||||
|
||||
client = None
|
||||
endtime = None
|
||||
|
||||
def __init__(self, hostport, metric, tags, beginStr, endStr, period, sample):
|
||||
self.series = pandas.Series()
|
||||
self.metric = metric
|
||||
self.tags = tags
|
||||
self.sample = sample
|
||||
self.debug = False
|
||||
self.dataFrame = None
|
||||
self.data = []
|
||||
self.timeDateRange = TimeDateRange(beginStr, endStr, period)
|
||||
|
||||
TimelyWebSocketClient.__init__(self, hostport, metric, tags, self.timeDateRange.getBeginMs(), self.timeDateRange.getEndMs())
|
||||
|
||||
def debugOn(self):
|
||||
self.debug = True
|
||||
|
||||
def debugOff(self):
|
||||
self.debug = False
|
||||
|
||||
def fetch(self):
|
||||
|
||||
ioloop.IOLoop().make_current()
|
||||
self.connect(ioloop.IOLoop.current(instance=False))
|
||||
ioloop.IOLoop.current(instance=False).start()
|
||||
return self
|
||||
|
||||
|
||||
def _on_message(self, msg):
|
||||
|
||||
global df
|
||||
global endtime
|
||||
|
||||
responsesObj = json.loads(msg)
|
||||
responses = responsesObj.get("responses")
|
||||
|
||||
for obj in responses:
|
||||
complete = bool(obj.get("complete"))
|
||||
if complete:
|
||||
self._on_connection_close()
|
||||
return
|
||||
else:
|
||||
date = int(obj.get("timestamp")/1000)
|
||||
dt = pandas.datetime.utcfromtimestamp(date)
|
||||
metricName = str(obj.get("metric"))
|
||||
metricValue = obj.get("value")
|
||||
|
||||
newData = {}
|
||||
newData["date"] = dt
|
||||
newData[metricName] = metricValue
|
||||
|
||||
tags = obj.get("tags")
|
||||
for d in tags:
|
||||
for k,v in d.items():
|
||||
newData[k] = v
|
||||
|
||||
self.data.append(newData)
|
||||
|
||||
|
||||
def _on_connection_close(self):
|
||||
global client
|
||||
|
||||
self.dataFrame = pandas.DataFrame(self.data)
|
||||
if len(self.dataFrame) > 0:
|
||||
self.dataFrame['date'] = pandas.DatetimeIndex(self.dataFrame['date'])
|
||||
self.dataFrame = self.dataFrame.set_index('date')
|
||||
|
||||
self.close()
|
||||
ioloop.IOLoop.current(instance=False).stop()
|
||||
print("Exiting.")
|
||||
|
||||
def getDataFrame(self):
|
||||
|
||||
return self.dataFrame;
|
||||
|
||||
def setDataFrame(self, dataFrame):
|
||||
|
||||
self.dataFrame = dataFrame
|
||||
|
||||
def print_debug(self):
|
||||
if self.debug:
|
||||
print(self.dataFrame)
|
||||
|
||||
def graph(self, groupByColumn=None):
|
||||
return graph(self.dataFrame, self, sample=self.sample, how=self.how, groupByColumn=groupByColumn)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
period = None
|
||||
beginStr = None
|
||||
endStr = None
|
||||
|
||||
metric = "timely.metrics.received"
|
||||
sample = None
|
||||
how = None
|
||||
tags = None
|
||||
groupByColumn = None
|
||||
try:
|
||||
argv = sys.argv
|
||||
options, remainder = getopt.getopt(argv[1:], 'h', ['metric=', 'tags=', 'period=', 'groupByColumn=', 'begin=', 'end=', 'sample=', 'how=', 'hostport='])
|
||||
except getopt.GetoptError:
|
||||
print('TimelyMetric.py ---hostport=<host:port> --metric=<metric> --tags=<tag1=val1,tag2=val2> --period=<period>, --groupByColumn=<groupByColumn>, --sample=<sample_period> --how=<mean|min|max>')
|
||||
sys.exit(2)
|
||||
for opt, arg in options:
|
||||
if opt == '-h':
|
||||
print('TimelyMetric.py --hostport=<host:port> --metric=<metric> --tags=<tag1=val1,tag2=val2> --period=<period>, --groupByColumn=<groupByColumn>, --sample=<sample_period> --how=<mean|min|max>')
|
||||
sys.exit()
|
||||
elif opt in ("--metric"):
|
||||
metric = arg
|
||||
elif opt in ("--period"):
|
||||
period = arg
|
||||
elif opt in ("--begin"):
|
||||
beginStr = arg
|
||||
elif opt in ("--end"):
|
||||
endStr = arg
|
||||
elif opt in ("--groupByColumn"):
|
||||
groupByColumn = arg
|
||||
elif opt in ("--sample"):
|
||||
sample = arg
|
||||
elif opt in ("--how"):
|
||||
how = arg
|
||||
elif opt in ("--hostport"):
|
||||
hostport = arg
|
||||
try:
|
||||
|
||||
timelyMetric = TimelyMetric(hostport, metric, tags, beginStr, endStr, period, sample).fetch()
|
||||
dataFrame = timelyMetric.getDataFrame()
|
||||
|
||||
if dataFrame is not None:
|
||||
dataFrame = resample(dataFrame, metric, sample, how)
|
||||
dataFrame = pivot(dataFrame, metric, groupByColumn=groupByColumn)
|
||||
graph(dataFrame, timelyMetric, sample=sample, groupByColumn=groupByColumn, graphConfig={}, notebook=False)
|
||||
|
||||
except TimeDateError as e:
|
||||
print(e.message)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
from tornado import escape
|
||||
from tornado import gen
|
||||
from tornado import ioloop
|
||||
from tornado import websocket
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class WebSocketClient():
|
||||
"""Base for web socket clients.
|
||||
"""
|
||||
|
||||
DEFAULT_CONNECT_TIMEOUT = 60
|
||||
DEFAULT_REQUEST_TIMEOUT = 60
|
||||
APPLICATION_JSON = 'application/json'
|
||||
|
||||
def __init__(self, hostport, connect_timeout=DEFAULT_CONNECT_TIMEOUT,
|
||||
request_timeout=DEFAULT_REQUEST_TIMEOUT):
|
||||
|
||||
self.connect_timeout = connect_timeout
|
||||
self.request_timeout = request_timeout
|
||||
self.url = 'wss://' + hostport + '/websocket'
|
||||
|
||||
def connect(self, ioloop=ioloop.IOLoop.current(instance=False)):
|
||||
"""Connect to the server.
|
||||
:param str url: server URL.
|
||||
"""
|
||||
request = websocket.httpclient.HTTPRequest(self.url, connect_timeout=self.connect_timeout, request_timeout=self.request_timeout, validate_cert=False)
|
||||
websocket.websocket_connect(request, io_loop=ioloop, callback=self._connect_callback)
|
||||
|
||||
def send(self, data):
|
||||
"""Send message to the server
|
||||
:param str data: message.
|
||||
"""
|
||||
if not self._ws_connection:
|
||||
raise RuntimeError('Web socket connection is closed.')
|
||||
|
||||
self._ws_connection.write_message(escape.utf8(json.dumps(data)))
|
||||
|
||||
def close(self):
|
||||
"""Close connection.
|
||||
"""
|
||||
if not self._ws_connection:
|
||||
raise RuntimeError('Web socket connection is already closed.')
|
||||
|
||||
self._ws_connection.close()
|
||||
self._ws_connection = None
|
||||
|
||||
def _connect_callback(self, future):
|
||||
if future.exception() is None:
|
||||
self._ws_connection = future.result()
|
||||
self._on_connection_success()
|
||||
self._read_messages()
|
||||
else:
|
||||
self._on_connection_error(future.exception())
|
||||
|
||||
@gen.coroutine
|
||||
def _read_messages(self):
|
||||
while True and not self._ws_connection is None:
|
||||
msg = yield self._ws_connection.read_message()
|
||||
if msg is None:
|
||||
self._on_connection_close()
|
||||
break
|
||||
|
||||
self._on_message(msg)
|
||||
|
||||
def _on_message(self, msg):
|
||||
"""This is called when new message is available from the server.
|
||||
:param str msg: server message.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def _on_connection_success(self):
|
||||
"""This is called on successful connection ot the server.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
def _on_connection_close(self):
|
||||
"""This is called when server closed the connection.
|
||||
"""
|
||||
pass
|
||||
|
||||
def _on_connection_error(self, exception):
|
||||
"""This is called in case if connection to the server could
|
||||
not established.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
Whitespace-only changes.
@@ -1,47 +0,0 @@
|
||||
import TimelyMetric
|
||||
import TimelyAnalytic
|
||||
import DataOperations
|
||||
import os
|
||||
from TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
|
||||
|
||||
hostport = "127.0.0.1:4244"
|
||||
timelyMetric = TimelyMetric.TimelyMetric(hostport, '', None, None, None, 48, None).fetch()
|
||||
|
||||
analyticConfig = TimelyAnalyticConfiguration({
|
||||
'system_name' : 'TestSystem',
|
||||
'counter' : False,
|
||||
'includeColRegex' : None,
|
||||
'excludeColRegex' : None,
|
||||
'groupByColumn' : 'instance',
|
||||
'sample' : '10 minutes',
|
||||
'how' : 'mean',
|
||||
'rolling_average_period' : '12 hours',
|
||||
'min_threshold' : None,
|
||||
'average_min_threshold' : None,
|
||||
'max_threshold' : None,
|
||||
'average_max_threshold' : None,
|
||||
'min_threshold_percentage' : -50,
|
||||
'max_threshold_percentage' : 50,
|
||||
'boolean' : 'and',
|
||||
'min_alert_period' : '5 minutes',
|
||||
'last_alert' : '1 hour',
|
||||
'display' : 'all',
|
||||
'output_dir' : '/path/to/output'
|
||||
})
|
||||
|
||||
alert = TimelyAnalytic.find_alerts(timelyMetric, analyticConfig)
|
||||
|
||||
if alert is not None:
|
||||
# write graph to file
|
||||
oldmask = os.umask(022)
|
||||
file = alert.graph(type='html')
|
||||
os.umask(oldmask)
|
||||
|
||||
text = DataOperations.getTitle(timelyMetric, analyticConfig)
|
||||
|
||||
# send email with graph attached
|
||||
alert.email("", "", text, text, [file])
|
||||
|
||||
# log to syslog
|
||||
alert.log(text)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
<Or>
|
||||
<Bug pattern="DM_DEFAULT_ENCODING"/>
|
||||
<Method name="connect" />
|
||||
<Class name="timely.client.tcp.TcpClient"/>
|
||||
</Or>
|
||||
</FindBugsFilter>
|
||||
@@ -1,58 +0,0 @@
|
||||
package timely.api;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import timely.serialize.JsonSerializer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public class SerializationTest {
|
||||
|
||||
private ObjectMapper mapper = JsonSerializer.getObjectMapper();
|
||||
|
||||
@Test
|
||||
public void testBasicAuth() throws Exception {
|
||||
BasicAuthLogin login = new BasicAuthLogin();
|
||||
login.setUsername("test");
|
||||
login.setPassword("pass");
|
||||
testSerialization(login);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSubscription() throws Exception {
|
||||
CreateSubscription create = new CreateSubscription();
|
||||
create.setSubscriptionId("1234");
|
||||
testSerialization(create);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseSubscription() throws Exception {
|
||||
CloseSubscription close = new CloseSubscription();
|
||||
close.setSubscriptionId("1234");
|
||||
testSerialization(close);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSubscription() throws Exception {
|
||||
AddSubscription add = new AddSubscription();
|
||||
add.setSubscriptionId("1234");
|
||||
add.setMetric("sys.cpu.user");
|
||||
testSerialization(add);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveSubscription() throws Exception {
|
||||
RemoveSubscription remove = new RemoveSubscription();
|
||||
remove.setSubscriptionId("1234");
|
||||
remove.setMetric("sys.cpu.user");
|
||||
testSerialization(remove);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> void testSerialization(T o) throws Exception {
|
||||
String ser = mapper.writeValueAsString(o);
|
||||
T des = mapper.readValue(ser, (Class<T>) o.getClass());
|
||||
Assert.assertEquals(o, des);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package timely.model;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class MetricTest {
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
Metric m1 = Metric.newBuilder().name("m1").tag("t1", "v1").tag("t2", "v2").value(1, 0.0).build();
|
||||
Metric m2 = Metric.newBuilder().name("m1").tag("t2", "v2").tag("t1", "v1").value(1, 0.0).build();
|
||||
|
||||
assertTrue(m1.equals(m2));
|
||||
assertTrue(m2.equals(m1));
|
||||
|
||||
Metric m3 = Metric.newBuilder().name("m1").tag("t1", "v1").value(1, 0.0).build();
|
||||
assertFalse(m1.equals(m3));
|
||||
|
||||
Metric m4 = Metric.newBuilder().name("m4").tag("t2", "v2").tag("t1", "v1").value(1, 0.0).build();
|
||||
assertFalse(m1.equals(m4));
|
||||
|
||||
Metric m5 = Metric.newBuilder().name("m1").tag("t3", "v3").tag("t2", "v2").tag("t1", "v1").value(1, 0.0)
|
||||
.build();
|
||||
assertFalse(m1.equals(m5));
|
||||
|
||||
Metric m6 = Metric.newBuilder().name("m1").tag("t2", "v2").tag("t1", "v1").value(2, 0.0).build();
|
||||
assertFalse(m1.equals(m6));
|
||||
|
||||
Metric m7 = Metric.newBuilder().name("m1").tag("t2", "v2").tag("t1", "v1").value(1, 1.0).build();
|
||||
assertFalse(m1.equals(m7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJson() throws IOException {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
String expectedJson = "{\"name\":\"m1\",\"timestamp\":1,\"measure\":1.0,\"tags\":[{\"k1\":\"v1\"}]}";
|
||||
|
||||
Metric m1 = Metric.newBuilder().name("m1").tag("k1", "v1").value(1, 1.0).build();
|
||||
|
||||
Metric expectedMetric = mapper.readValue(expectedJson, Metric.class);
|
||||
|
||||
assertTrue(m1.equals(expectedMetric));
|
||||
|
||||
expectedJson = "{\"name\":\"m1\",\"tags\":[{\"k1\":\"v1\"}],\"timestamp\":5,\"measure\":5.0}";
|
||||
expectedMetric = mapper.readValue(expectedJson, Metric.class);
|
||||
|
||||
assertEquals((long) expectedMetric.getValue().getTimestamp(), 5L);
|
||||
assertEquals(expectedMetric.getValue().getMeasure(), 5.0D, 0.0);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package timely.model;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import timely.model.parse.TagListParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class TagListParserTest {
|
||||
|
||||
@Test
|
||||
public void testListParse() {
|
||||
String value = "tag1=value1,tag2=value2";
|
||||
List<Tag> tags = new TagListParser().parse(value);
|
||||
Assert.assertEquals(2, tags.size());
|
||||
Assert.assertEquals(new Tag("tag1", "value1"), tags.get(0));
|
||||
Assert.assertEquals(new Tag("tag2", "value2"), tags.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListCombine() {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
tags.add(new Tag("tag1", "value1"));
|
||||
tags.add(new Tag("tag2", "value2"));
|
||||
String combined = new TagListParser().combine(tags);
|
||||
Assert.assertEquals("tag1=value1,tag2=value2", combined);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListCombineMap() {
|
||||
Map<String, String> map = new TreeMap<>();
|
||||
map.put("tag1", "value1");
|
||||
map.put("tag2", "value2");
|
||||
String combined = new TagListParser().combine(map);
|
||||
Assert.assertEquals("tag1=value1,tag2=value2", combined);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>collectd-nsq-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade</id>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.PooledObjectFactory;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.collectd.api.OConfigItem;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PooledCloseableHttpClientFactory implements PooledObjectFactory {
|
||||
|
||||
private int connectionRequestTimeout = 10000; // 10 seconds in milliseconds
|
||||
private int connectTimeout = 30000; // 30 seconds in milliseconds
|
||||
private int socketTimeout = 60000; // 60 seconds in milliseconds
|
||||
private long connectionTimeToLive = 300000; // 300 seconds in milliseconds
|
||||
|
||||
public int config(OConfigItem config) {
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "connectionRequestTimeout":
|
||||
case "ConnectionRequestTimeout":
|
||||
connectionRequestTimeout = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "connectTimeout":
|
||||
case "ConnectTimeout":
|
||||
connectTimeout = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "socketTimeout":
|
||||
case "SocketTimeout":
|
||||
socketTimeout = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "connectionTimeToLive":
|
||||
case "ConnectionTimeToLive":
|
||||
connectionTimeToLive = Long.parseLong(child.getValues().get(0).getString());
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject makeObject() throws Exception {
|
||||
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout)
|
||||
.setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setDefaultRequestConfig(requestConfig);
|
||||
builder.setConnectionTimeToLive(connectionTimeToLive, TimeUnit.MILLISECONDS);
|
||||
return new DefaultPooledObject(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(PooledObject pooledObject) throws Exception {
|
||||
try {
|
||||
((CloseableHttpClient) pooledObject.getObject()).close();
|
||||
} catch (Exception e) {
|
||||
// do nothing
|
||||
} finally {
|
||||
pooledObject.invalidate();
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(PooledObject pooledObject) {
|
||||
return pooledObject.getObject() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(PooledObject pooledObject) throws Exception {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(PooledObject pooledObject) throws Exception {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void setConnectionRequestTimeout(int connectionRequestTimeout) {
|
||||
this.connectionRequestTimeout = connectionRequestTimeout;
|
||||
}
|
||||
|
||||
public void setConnectTimeout(int connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
public void setSocketTimeout(int socketTimeout) {
|
||||
this.socketTimeout = socketTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeToLive(long connectionTimeToLive) {
|
||||
this.connectionTimeToLive = connectionTimeToLive;
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.entity.EntityBuilder;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.CollectdConfigInterface;
|
||||
import org.collectd.api.CollectdShutdownInterface;
|
||||
import org.collectd.api.CollectdWriteInterface;
|
||||
import org.collectd.api.OConfigItem;
|
||||
import org.collectd.api.ValueList;
|
||||
|
||||
/**
|
||||
*
|
||||
* CollectD plugin that writes metrics collected by the StatsD plugin to NSQ.
|
||||
* NSQ can be configured to write statsd metrics to CollectD. If you do this,
|
||||
* set the statsd prefix in NSQ to 'nsq'.
|
||||
*
|
||||
*/
|
||||
public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConfigInterface, CollectdShutdownInterface,
|
||||
CollectdWriteInterface {
|
||||
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private String topic = "metrics#ephemeral";
|
||||
private String endpoint = null;
|
||||
private GenericObjectPool<CloseableHttpClient> clientPool = null;
|
||||
|
||||
// pool should be limited by the number of WriteThreads configured in
|
||||
// collectd
|
||||
final private int POOL_MAX_SIZE = Integer.MAX_VALUE;
|
||||
|
||||
public WriteNSQPlugin() {
|
||||
Collectd.registerConfig(WriteNSQPlugin.class.getName(), this);
|
||||
Collectd.registerShutdown(WriteNSQPlugin.class.getName(), this);
|
||||
Collectd.registerWrite(WriteNSQPlugin.class.getName(), this);
|
||||
}
|
||||
|
||||
public int write(ValueList vl) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
super.process(vl, out);
|
||||
CloseableHttpClient client = null;
|
||||
try {
|
||||
out.flush();
|
||||
HttpPost post = new HttpPost(endpoint);
|
||||
EntityBuilder request = EntityBuilder.create();
|
||||
request.setBinary(baos.toByteArray());
|
||||
post.setEntity(request.build());
|
||||
client = clientPool.borrowObject();
|
||||
CloseableHttpResponse response = client.execute(post);
|
||||
try {
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
String msg = response.getStatusLine().getReasonPhrase();
|
||||
// Consume the entire response so the connection will be reused
|
||||
EntityUtils.consume(response.getEntity());
|
||||
if (code != HttpStatus.SC_OK) {
|
||||
Collectd.logInfo("Code: " + code + ", msg: " + msg);
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Collectd.logError("Error sending metrics to NSQ: " + e.getMessage());
|
||||
if (client != null) {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
try {
|
||||
clientPool.invalidateObject(client);
|
||||
} catch (Exception e2) {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (client != null) {
|
||||
clientPool.returnObject(client);
|
||||
}
|
||||
if (null != out) {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String metric, OutputStream out) {
|
||||
try {
|
||||
((DataOutputStream) out).writeBytes(metric);
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error writing metric: " + e.getMessage());
|
||||
try {
|
||||
out.close(); // Should cause IOException in write.
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int shutdown() {
|
||||
Collectd.logInfo("Shutting down connection to NSQ at " + host + ":" + port);
|
||||
clientPool.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int config(OConfigItem config) {
|
||||
super.config(config);
|
||||
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
|
||||
// use max size for maxTotal and maxIdle
|
||||
// no need to activate or passivate
|
||||
poolConfig.setMaxTotal(POOL_MAX_SIZE);
|
||||
poolConfig.setMaxIdle(POOL_MAX_SIZE);
|
||||
poolConfig.setTestOnReturn(true);
|
||||
PooledCloseableHttpClientFactory pooledCloseableHttpClientFactory = new PooledCloseableHttpClientFactory();
|
||||
pooledCloseableHttpClientFactory.config(config);
|
||||
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "host":
|
||||
case "hostname":
|
||||
case "Host":
|
||||
case "HostName":
|
||||
String hostString = child.getValues().get(0).getString();
|
||||
String[] hosts = hostString.split(",");
|
||||
if (hosts.length == 1) {
|
||||
host = hosts[0];
|
||||
} else {
|
||||
host = hosts[new Random().nextInt(hosts.length)];
|
||||
}
|
||||
break;
|
||||
case "Port":
|
||||
case "port":
|
||||
port = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "topic":
|
||||
case "Topic":
|
||||
topic = child.getValues().get(0).getString();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
if (host == null || port == 0 || topic == null) {
|
||||
Collectd.logError("NSQ host, port, and topic must be configured");
|
||||
return -1;
|
||||
}
|
||||
this.clientPool = new GenericObjectPool(pooledCloseableHttpClientFactory, poolConfig);
|
||||
this.endpoint = "http://" + host + ":" + port + "/mpub?topic=" + topic;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
</FindBugsFilter>
|
||||
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.jcip</groupId>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-easymock-release-full</artifactId>
|
||||
<classifier>full</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.DataSource;
|
||||
import org.collectd.api.OConfigItem;
|
||||
import org.collectd.api.ValueList;
|
||||
|
||||
public abstract class CollectDPluginParent {
|
||||
|
||||
private static final String PUT = "put {0} {1} {2}{3}\n";
|
||||
private static final Pattern HADOOP_STATSD_PATTERN = Pattern
|
||||
.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
|
||||
private static final String INSTANCE = " instance=";
|
||||
private static final String NAME = " name=";
|
||||
private static final String SAMPLE = " sample=";
|
||||
private static final String CODE = " code=";
|
||||
private static final String PERIOD = ".";
|
||||
private static final String SAMPLE_TYPE = " sampleType=";
|
||||
private static final String COUNTER = "COUNTER";
|
||||
private static final String GAUGE = "GAUGE";
|
||||
private static final String DERIVE = "DERIVE";
|
||||
private static final String ABSOLUTE = "ABSOLUTE";
|
||||
private static final Pattern NSQ_PATTERN1 = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
|
||||
private static final Pattern NSQ_PATTERN2 = Pattern.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
|
||||
private static final Pattern NSQ_PATTERN3 = Pattern
|
||||
.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_#]+)\\.([\\w-_]+)");
|
||||
private static final Pattern ETHSTAT_PATTERN = Pattern.compile("([\\w-_]+)_queue_([\\w-_]+)_([\\w-_]+)");
|
||||
private static final String STATSD_PREFIX = "statsd";
|
||||
private static final String NSQ_PREFIX = STATSD_PREFIX + ".nsq.";
|
||||
private static final Pattern HAPROXY_PATTERN = Pattern.compile("\\[([\\w-_=]+),([\\w-_=]+)\\]");
|
||||
|
||||
private final SMARTCodeMapping smart = new SMARTCodeMapping();
|
||||
|
||||
protected Set<String> addlTags = new HashSet<>();
|
||||
|
||||
public int config(OConfigItem config) {
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "Tags":
|
||||
case "tags":
|
||||
String[] additionalTags = child.getValues().get(0).getString().split(",");
|
||||
for (String t : additionalTags) {
|
||||
addlTags.add(" " + t.trim());
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public abstract void write(String metric, OutputStream out);
|
||||
|
||||
public void process(ValueList vl, OutputStream out) {
|
||||
debugValueList(vl);
|
||||
|
||||
StringBuilder metric = new StringBuilder();
|
||||
Long timestamp = vl.getTime();
|
||||
StringBuilder tags = new StringBuilder();
|
||||
String host = vl.getHost();
|
||||
int idx = host.indexOf(PERIOD);
|
||||
if (-1 != idx) {
|
||||
tags.append(" host=").append(host.substring(0, idx));
|
||||
} else {
|
||||
tags.append(" host=").append(host);
|
||||
}
|
||||
int nIdx = host.indexOf('n');
|
||||
if (-1 != nIdx) {
|
||||
tags.append(" rack=").append(host.substring(0, nIdx));
|
||||
}
|
||||
for (String tag : addlTags) {
|
||||
tags.append(tag);
|
||||
}
|
||||
if (vl.getPlugin().equals(STATSD_PREFIX)) {
|
||||
Matcher m = HADOOP_STATSD_PATTERN.matcher(vl.getTypeInstance());
|
||||
Matcher n1 = NSQ_PATTERN1.matcher(vl.getTypeInstance());
|
||||
Matcher n2 = NSQ_PATTERN2.matcher(vl.getTypeInstance());
|
||||
Matcher n3 = NSQ_PATTERN3.matcher(vl.getTypeInstance());
|
||||
String instance = null;
|
||||
if (m.matches() && !vl.getTypeInstance().startsWith("nsq")) {
|
||||
// Here we are processing the statsd metrics coming from the
|
||||
// Hadoop Metrics2 StatsDSink without the host name.
|
||||
// The format of metric is:
|
||||
// serviceName.contextName.recordName.metricName. The recordName
|
||||
// is typically duplicative and is dropped here. The serviceName
|
||||
// is used as the instance.
|
||||
metric.append(STATSD_PREFIX).append(PERIOD).append(m.group(2)).append(PERIOD).append(m.group(4));
|
||||
instance = m.group(1);
|
||||
} else if (n1.matches() && vl.getTypeInstance().startsWith("nsq")) {
|
||||
metric.append(NSQ_PREFIX).append(n1.group(2)).append(PERIOD).append(n1.group(3));
|
||||
} else if (n2.matches() && vl.getTypeInstance().startsWith("nsq")) {
|
||||
metric.append(NSQ_PREFIX).append(n2.group(2)).append(PERIOD).append(n2.group(4));
|
||||
instance = n2.group(3);
|
||||
} else if (n3.matches() && vl.getTypeInstance().startsWith("nsq")) {
|
||||
metric.append(NSQ_PREFIX).append(n3.group(4)).append(PERIOD).append(n3.group(6));
|
||||
instance = n3.group(5);
|
||||
} else {
|
||||
// Handle StatsD metrics of unknown formats. If there is a
|
||||
// period in the metric name, use everything up to that as
|
||||
// the instance.
|
||||
int period = vl.getTypeInstance().indexOf('.');
|
||||
if (-1 == period) {
|
||||
metric.append(STATSD_PREFIX).append(PERIOD).append(vl.getTypeInstance());
|
||||
} else {
|
||||
instance = vl.getTypeInstance().substring(0, period);
|
||||
metric.append(STATSD_PREFIX).append(PERIOD).append(vl.getTypeInstance().substring(period + 1));
|
||||
}
|
||||
}
|
||||
timestamp = vl.getTime();
|
||||
if (null != instance) {
|
||||
tags.append(INSTANCE).append(instance);
|
||||
}
|
||||
} else if (vl.getPlugin().equals("ethstat")) {
|
||||
metric.append("sys.ethstat.");
|
||||
if (vl.getTypeInstance().contains("queue")) {
|
||||
Matcher m = ETHSTAT_PATTERN.matcher(vl.getTypeInstance());
|
||||
if (m.matches()) {
|
||||
metric.append(m.group(1)).append("_").append(m.group(3));
|
||||
tags.append(" queue=").append(m.group(2));
|
||||
} else {
|
||||
metric.append(vl.getTypeInstance());
|
||||
}
|
||||
} else {
|
||||
metric.append(vl.getTypeInstance());
|
||||
}
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance());
|
||||
} else if (vl.getPlugin().equals("hddtemp")) {
|
||||
metric.append("sys.hddtemp.").append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getTypeInstance());
|
||||
} else if (vl.getPlugin().equals("smart")) {
|
||||
int code = -1;
|
||||
String name = null;
|
||||
if (vl.getTypeInstance().startsWith("attribute-")) {
|
||||
int hyphen = vl.getTypeInstance().indexOf('-');
|
||||
code = Integer.parseInt(vl.getTypeInstance().substring(hyphen + 1));
|
||||
name = smart.get(code);
|
||||
}
|
||||
if (code == -1) {
|
||||
if (notEmpty(vl.getTypeInstance())) {
|
||||
metric.append("sys.smart.").append(vl.getTypeInstance());
|
||||
} else {
|
||||
metric.append("sys.smart.").append(vl.getType());
|
||||
}
|
||||
} else {
|
||||
metric.append("sys.smart.").append(name);
|
||||
tags.append(CODE).append(code);
|
||||
}
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance());
|
||||
} else if (vl.getPlugin().equals("sensors")) {
|
||||
String instance = "";
|
||||
if (vl.getTypeInstance().startsWith("temp")) {
|
||||
instance = vl.getTypeInstance().substring(4);
|
||||
}
|
||||
metric.append("sys.sensors.").append(vl.getType()).append(PERIOD).append(vl.getPluginInstance());
|
||||
tags.append(INSTANCE).append(instance);
|
||||
} else if (vl.getPlugin().equals("haproxy")) {
|
||||
metric.append("sys.haproxy.").append(vl.getTypeInstance());
|
||||
Matcher m = HAPROXY_PATTERN.matcher(vl.getPluginInstance());
|
||||
if (m.matches()) {
|
||||
tags.append(" ").append(m.group(1));
|
||||
tags.append(" ").append(m.group(2));
|
||||
}
|
||||
} else if (vl.getPlugin().equals("ipmi")) {
|
||||
metric.append("sys.ipmi.").append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getTypeInstance().replaceAll(" ", "_"));
|
||||
} else if (vl.getPlugin().equals("snmp")) {
|
||||
metric.append("sys.snmp.").append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getTypeInstance().replaceAll(" ", "_"));
|
||||
} else if (vl.getPlugin().equals("GenericJMX")) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
.append(vl.getTypeInstance());
|
||||
String[] pluginInstanceSplit = vl.getPluginInstance().split("-");
|
||||
if (pluginInstanceSplit.length > 0) {
|
||||
tags.append(INSTANCE).append(pluginInstanceSplit[0].replaceAll(" ", "_"));
|
||||
}
|
||||
if (pluginInstanceSplit.length > 1) {
|
||||
tags.append(NAME).append(pluginInstanceSplit[1].replaceAll(" ", "_"));
|
||||
}
|
||||
} else if (notEmpty(vl.getTypeInstance()) && notEmpty(vl.getType()) && notEmpty(vl.getPluginInstance())
|
||||
&& notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
.append(vl.getTypeInstance());
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance().replaceAll(" ", "_"));
|
||||
} else if (notEmpty(vl.getTypeInstance()) && notEmpty(vl.getType()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
.append(vl.getTypeInstance());
|
||||
} else if (notEmpty(vl.getType()) && notEmpty(vl.getPluginInstance()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance().replaceAll(" ", "_"));
|
||||
} else if (notEmpty(vl.getType()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType());
|
||||
} else {
|
||||
Collectd.logWarning("Unhandled metric: " + vl.toString());
|
||||
return;
|
||||
}
|
||||
final String metricName = metric.toString().replaceAll(" ", "_");
|
||||
for (int i = 0; i < vl.getValues().size(); i++) {
|
||||
StringBuilder tagsWithSample = new StringBuilder(tags.toString());
|
||||
String sampleName = vl.getDataSet().getDataSources().get(i).getName();
|
||||
int type = vl.getDataSet().getDataSources().get(i).getType();
|
||||
String sampleType = convertType(type);
|
||||
if (null != sampleName) {
|
||||
tagsWithSample.append(SAMPLE).append(sampleName);
|
||||
}
|
||||
if (null != sampleType) {
|
||||
tagsWithSample.append(SAMPLE_TYPE).append(sampleType);
|
||||
}
|
||||
Double value = vl.getValues().get(i).doubleValue();
|
||||
String datapoint = MessageFormat.format(PUT, metricName, timestamp.toString(), value.toString(),
|
||||
tagsWithSample.toString());
|
||||
Collectd.logDebug("Writing: " + datapoint);
|
||||
write(datapoint, out);
|
||||
}
|
||||
}
|
||||
|
||||
private void debugValueList(ValueList vl) {
|
||||
Collectd.logDebug("Input: " + vl.toString());
|
||||
Collectd.logDebug("Plugin: " + vl.getPlugin());
|
||||
Collectd.logDebug("PluginInstance: " + vl.getPluginInstance());
|
||||
Collectd.logDebug("Type: " + vl.getType());
|
||||
Collectd.logDebug("TypeInstance: " + vl.getTypeInstance());
|
||||
for (int i = 0; i < vl.getValues().size(); i++) {
|
||||
Number value = vl.getValues().get(i);
|
||||
DataSource ds = vl.getDataSet().getDataSources().get(i);
|
||||
Collectd.logDebug(convertType(ds.getType()) + " " + ds.getName() + " = " + value);
|
||||
}
|
||||
}
|
||||
|
||||
private String convertType(int type) {
|
||||
String result = null;
|
||||
switch (type) {
|
||||
case 0:
|
||||
result = COUNTER;
|
||||
break;
|
||||
case 1:
|
||||
result = GAUGE;
|
||||
break;
|
||||
case 2:
|
||||
result = DERIVE;
|
||||
break;
|
||||
case 3:
|
||||
result = ABSOLUTE;
|
||||
break;
|
||||
default:
|
||||
result = GAUGE;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean notEmpty(String arg) {
|
||||
return (null != arg) && !(arg.equals(""));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SMARTCodeMapping extends HashMap<Integer, String> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Map<Integer, String> smart;
|
||||
static {
|
||||
Map<Integer, String> map = new HashMap<>();
|
||||
/**
|
||||
* mapping from https://en.wikipedia.org/wiki/S.M.A.R.T.
|
||||
*/
|
||||
map.put(1, "Read_Error_Rate");
|
||||
map.put(2, "Throughput_Performance");
|
||||
map.put(3, "Spin_Up_Time");
|
||||
map.put(4, "Start_Stop_Count");
|
||||
map.put(5, "Reallocated_Sectors_Count");
|
||||
map.put(6, "Read_Channel_Margin");
|
||||
map.put(7, "Seek_Error_Rate");
|
||||
map.put(8, "Seek_Time_Performance");
|
||||
map.put(9, "Power_On_Hours");
|
||||
map.put(10, "Spin_Retry_Count");
|
||||
map.put(11, "Calibration_Retry_Count");
|
||||
map.put(12, "Power_Cycle_Count");
|
||||
map.put(13, "Soft_Read_Error_Rate");
|
||||
map.put(22, "Current_Helium_Level");
|
||||
map.put(170, "Available_Reserved_Space");
|
||||
map.put(171, "SSD_Program_Fail_Count");
|
||||
map.put(172, "SSD_Erase_Fail_Count");
|
||||
map.put(173, "SSD_Wear_Leveling_Count");
|
||||
map.put(174, "Unexpected_power_loss_count");
|
||||
map.put(175, "Power_Loss_Protection_Failure");
|
||||
map.put(176, "Erase_Fail_Count");
|
||||
map.put(177, "Wear_Range_Delta");
|
||||
map.put(179, "Used_Reserved_Block_Count_Total");
|
||||
map.put(180, "Unused_Reserved_Block_Count_Total");
|
||||
map.put(181, "Program_Fail_Count_Total");
|
||||
map.put(182, "Erase_Fail_Count");
|
||||
map.put(183, "SATA_Downshift_Error_Count");
|
||||
map.put(184, "End_to_End_error");
|
||||
map.put(185, "Head_Stability");
|
||||
map.put(186, "Induced_Op_Vibration_Detection");
|
||||
map.put(187, "Reported_Uncorrectable_Errors");
|
||||
map.put(188, "Command_Timeout");
|
||||
map.put(189, "High_Fly_Writes");
|
||||
map.put(190, "Airflow_Temperature_Celsius");
|
||||
map.put(190, "Temperature_Difference_from_100");
|
||||
map.put(191, "G_sense_Error_Rate");
|
||||
map.put(192, "Unsafe_Shutdown_Count");
|
||||
map.put(193, "Load_Unload_Cycle_Count");
|
||||
map.put(194, "Temperature_Celsius");
|
||||
map.put(195, "Hardware_ECC_Recovered");
|
||||
map.put(196, "Reallocation_Event_Count");
|
||||
map.put(197, "Current_Pending_Sector_Count");
|
||||
map.put(198, "Uncorrectable_Sector_Count");
|
||||
map.put(199, "UltraDMA_CRC_Error_Count");
|
||||
// map.put(200,"Multi_Zone_Error_Rate_[41]");
|
||||
map.put(200, "Write_Error_Rate");
|
||||
map.put(201, "Soft_Read_Error_Rate");
|
||||
map.put(202, "Data_Address_Mark_errors");
|
||||
map.put(203, "Run_Out_Cancel");
|
||||
map.put(204, "Soft_ECC_Correction");
|
||||
map.put(205, "Thermal_Asperity_Rate");
|
||||
map.put(206, "Flying_Height");
|
||||
map.put(207, "Spin_High_Current");
|
||||
map.put(208, "Spin_Buzz");
|
||||
map.put(209, "Offline_Seek_Performance");
|
||||
map.put(210, "Vibration_During_Write");
|
||||
map.put(211, "Vibration_During_Write");
|
||||
map.put(212, "Shock_During_Write");
|
||||
map.put(220, "Disk_Shift");
|
||||
map.put(221, "G_Sense_Error_Rate");
|
||||
map.put(222, "Loaded_Hours");
|
||||
map.put(223, "Load_Unload_Retry_Count");
|
||||
map.put(224, "Load_Friction");
|
||||
map.put(225, "Load_Unload_Cycle_Count");
|
||||
map.put(226, "Load_In_time");
|
||||
map.put(227, "Torque_Amplification_Count");
|
||||
map.put(228, "Power_Off_Retract_Cycle");
|
||||
// map.put(230,"GMR_Head_Amplitude");
|
||||
map.put(230, "Drive_Life_Protection_Status");
|
||||
map.put(231, "Temperature");
|
||||
// map.put(231,"SSD_Life_Left");
|
||||
map.put(232, "Endurance_Remaining");
|
||||
// map.put(232,"Available_Reserved_Space");
|
||||
map.put(233, "Power_On_Hours");
|
||||
// map.put(233,"Media_Wearout_Indicator");
|
||||
map.put(234, "Average_erase_count");
|
||||
map.put(235, "Good_Block_Count");
|
||||
map.put(240, "Head_Flying_Hours");
|
||||
// map.put(240,"Transfer_Error_Rate_(Fujitsu)");
|
||||
map.put(241, "Total_LBAs_Written");
|
||||
map.put(242, "Total_LBAs_Read");
|
||||
map.put(243, "Total_LBAs_Written_Expanded");
|
||||
map.put(244, "Total_LBAs_Read_Expanded");
|
||||
map.put(249, "NAND_Writes_1GiB");
|
||||
map.put(250, "Read_Error_Retry_Rate");
|
||||
map.put(251, "Minimum_Spares_Remaining");
|
||||
map.put(252, "Newly_Added_Bad_Flash_Block");
|
||||
map.put(254, "Free_Fall_Protection");
|
||||
smart = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
protected SMARTCodeMapping() {
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return smart.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return smart.isEmpty();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return smart.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return smart.containsValue(value);
|
||||
}
|
||||
|
||||
public String get(Object key) {
|
||||
return smart.get(key);
|
||||
}
|
||||
|
||||
public String put(Integer key, String value) {
|
||||
return smart.put(key, value);
|
||||
}
|
||||
|
||||
public String remove(Object key) {
|
||||
return smart.remove(key);
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends Integer, ? extends String> m) {
|
||||
smart.putAll(m);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
smart.clear();
|
||||
}
|
||||
|
||||
public Set<Integer> keySet() {
|
||||
return smart.keySet();
|
||||
}
|
||||
|
||||
public Collection<String> values() {
|
||||
return smart.values();
|
||||
}
|
||||
|
||||
public Set<java.util.Map.Entry<Integer, String>> entrySet() {
|
||||
return smart.entrySet();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return smart.equals(o);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return smart.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
</FindBugsFilter>
|
||||
@@ -1,338 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.jcip.annotations.NotThreadSafe;
|
||||
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.DataSet;
|
||||
import org.collectd.api.DataSource;
|
||||
import org.collectd.api.ValueList;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.api.easymock.PowerMock;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ Collectd.class })
|
||||
@NotThreadSafe
|
||||
public class PluginParentTest {
|
||||
|
||||
public class TestPlugin extends CollectDPluginParent {
|
||||
|
||||
public TestPlugin() {
|
||||
super.addlTags.add(" addl1=foo");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String metric, OutputStream out) {
|
||||
result = metric;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final String HOST = "r01n01.test";
|
||||
private static final Long TIME = 1456156976840L;
|
||||
public String result = null;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
PowerMock.suppress(PowerMock.everythingDeclaredIn(Collectd.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatsDHadoopFormat() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("statsd");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("DataNode.dfs.datanode.BlocksRead");
|
||||
vl.setValues(Collections.singletonList((Number) 1.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put statsd.dfs.BlocksRead 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo instance=DataNode sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatsDUnknownFormat() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("statsd");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("baz");
|
||||
vl.setValues(Collections.singletonList((Number) 1.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals("put statsd.baz 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStatsDUnknownFormat2() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("statsd");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("bar.baz");
|
||||
vl.setValues(Collections.singletonList((Number) 1.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put statsd.baz 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo instance=bar sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHddTemp() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("hddtemp");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("temperature");
|
||||
vl.setTypeInstance("sda");
|
||||
vl.setValues(Collections.singletonList((Number) 35.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.hddtemp.temperature 1456156976840 35.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmart1() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("smart");
|
||||
vl.setPluginInstance("sda");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("smart_badsectors");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.smart.smart_badsectors 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmart2() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("smart");
|
||||
vl.setPluginInstance("sda");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("smart_attribute");
|
||||
vl.setTypeInstance("raw-read-error-rate");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.smart.raw-read-error-rate 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmartCode() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("smart");
|
||||
vl.setPluginInstance("sda");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("smart_attribute");
|
||||
vl.setTypeInstance("attribute-242");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.smart.Total_LBAs_Read 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo code=242 instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnmp() throws Exception {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("snmp");
|
||||
// vl.setPluginInstance("sda");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("if_octets");
|
||||
vl.setTypeInstance("Ethernet1");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.snmp.if_octets 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=Ethernet1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSensors() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("sensors");
|
||||
vl.setPluginInstance("coretemp-isa-0000");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("temperature");
|
||||
vl.setTypeInstance("temp1");
|
||||
vl.setValues(Collections.singletonList((Number) 35.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.sensors.temperature.coretemp-isa-0000 1456156976840 35.0 host=r01n01 rack=r01 addl1=foo instance=1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHAProxy1() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("haproxy");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("gauge");
|
||||
vl.setTypeInstance("run_queue");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.haproxy.run_queue 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHAProxy2() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("haproxy");
|
||||
vl.setPluginInstance("[proxy_name=proxy1,service_name=server1]");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("gauge");
|
||||
vl.setTypeInstance("queue_current");
|
||||
vl.setValues(Collections.singletonList((Number) 0.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 0.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.haproxy.queue_current 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo proxy_name=proxy1 service_name=server1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEthStatNoQueue() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("ethstat");
|
||||
vl.setPluginInstance("eth0");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("tx_comp_queue_full");
|
||||
vl.setValues(Collections.singletonList((Number) 6.0D));
|
||||
DataSet ds = new DataSet("DERIVE");
|
||||
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.ethstat.tx_comp_queue_full 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo instance=eth0 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEthStatWithQueue() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("ethstat");
|
||||
vl.setPluginInstance("eth0");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("derive");
|
||||
vl.setTypeInstance("rx_queue_1_bytes");
|
||||
vl.setValues(Collections.singletonList((Number) 6.0D));
|
||||
DataSet ds = new DataSet("DERIVE");
|
||||
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.ethstat.rx_bytes 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo queue=1 instance=eth0 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIpmi() {
|
||||
ValueList vl = new ValueList();
|
||||
vl.setHost(HOST);
|
||||
vl.setPlugin("ipmi");
|
||||
vl.setTime(TIME);
|
||||
vl.setType("temperature");
|
||||
vl.setTypeInstance("LAN NIC Temp system_board (3.2)");
|
||||
vl.setValues(Collections.singletonList((Number) 6.0D));
|
||||
DataSet ds = new DataSet("GAUGE");
|
||||
ds.addDataSource(new DataSource("value", 1, 6.0D, 100.0D));
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.ipmi.temperature 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo instance=LAN_NIC_Temp_system_board_(3.2) sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>collectd-timely-plugin</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.jcip</groupId>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-easymock-release-full</artifactId>
|
||||
<classifier>full</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<includes>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade</id>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,125 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.PooledObjectFactory;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.OConfigItem;
|
||||
|
||||
import java.net.Socket;
|
||||
|
||||
public class PooledSocketFactory implements PooledObjectFactory {
|
||||
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private static final long initialBackoff = 2000;
|
||||
private int socketTimeout = 60000; // 60 seconds in milliseconds
|
||||
private long connectionTimeToLive = 300000; // 300 seconds in milliseconds
|
||||
|
||||
public int config(OConfigItem config) {
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "host":
|
||||
case "hostname":
|
||||
case "Host":
|
||||
case "HostName":
|
||||
host = child.getValues().get(0).getString();
|
||||
break;
|
||||
case "Port":
|
||||
case "port":
|
||||
port = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "socketTimeout":
|
||||
case "SocketTimeout":
|
||||
socketTimeout = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "connectionTimeToLive":
|
||||
case "ConnectionTimeToLive":
|
||||
connectionTimeToLive = Long.parseLong(child.getValues().get(0).getString());
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
if (null != host) {
|
||||
return 0;
|
||||
}
|
||||
if (host == null) {
|
||||
Collectd.logError("Timely host must be configured");
|
||||
}
|
||||
if (port == 0) {
|
||||
Collectd.logError("Timely port must be configured");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private Socket connect() {
|
||||
|
||||
Socket socket = null;
|
||||
long currentBackoff = initialBackoff;
|
||||
while (socket == null) {
|
||||
try {
|
||||
socket = new Socket(host, port);
|
||||
if (socketTimeout > -1) {
|
||||
socket.setSoTimeout(socketTimeout);
|
||||
}
|
||||
Collectd.logInfo("Connected to Timely at " + host + ":" + port + " from local port:"
|
||||
+ socket.getLocalPort());
|
||||
} catch (Exception e) {
|
||||
Collectd.logError("Error connecting to Timely at " + host + ":" + port + ". Error: " + e.getMessage()
|
||||
+ ". Will retry connection in " + currentBackoff + " ms.");
|
||||
try {
|
||||
Thread.sleep(currentBackoff);
|
||||
} catch (InterruptedException e1) {
|
||||
|
||||
}
|
||||
// max out reconnect period at one minute
|
||||
currentBackoff = (currentBackoff * 2 > 60000) ? 60000 : currentBackoff * 2;
|
||||
}
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject makeObject() throws Exception {
|
||||
return new DefaultPooledObject(connect());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(PooledObject pooledObject) throws Exception {
|
||||
try {
|
||||
Collectd.logDebug("Shutting down connection to Timely at " + host + ":" + port);
|
||||
Socket socket = (Socket) pooledObject.getObject();
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Collectd.logError("Error closing connection to Timely at " + host + ":" + port + ". Error: "
|
||||
+ e.getMessage());
|
||||
} finally {
|
||||
pooledObject.invalidate();
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(PooledObject pooledObject) {
|
||||
if (connectionTimeToLive > -1) {
|
||||
if (pooledObject.getCreateTime() + connectionTimeToLive > System.currentTimeMillis()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Socket socket = (Socket) pooledObject.getObject();
|
||||
return socket != null && !socket.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(PooledObject pooledObject) throws Exception {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(PooledObject pooledObject) throws Exception {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.CollectdConfigInterface;
|
||||
import org.collectd.api.CollectdShutdownInterface;
|
||||
import org.collectd.api.CollectdWriteInterface;
|
||||
import org.collectd.api.OConfigItem;
|
||||
import org.collectd.api.ValueList;
|
||||
|
||||
/**
|
||||
*
|
||||
* CollectD plugin that writes metrics collected by the StatsD plugin to Timely.
|
||||
*
|
||||
*/
|
||||
public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdConfigInterface,
|
||||
CollectdShutdownInterface, CollectdWriteInterface {
|
||||
|
||||
private GenericObjectPool<Socket> socketPool = null;
|
||||
|
||||
// pool should be limited by the number of WriteThreads configured in
|
||||
// collectd
|
||||
final private int POOL_MAX_SIZE = Integer.MAX_VALUE;
|
||||
|
||||
public WriteTimelyPlugin() {
|
||||
Collectd.registerConfig(WriteTimelyPlugin.class.getName(), this);
|
||||
Collectd.registerShutdown(WriteTimelyPlugin.class.getName(), this);
|
||||
Collectd.registerWrite(WriteTimelyPlugin.class.getName(), this);
|
||||
}
|
||||
|
||||
public int write(ValueList vl) {
|
||||
|
||||
Socket socket = null;
|
||||
try {
|
||||
socket = socketPool.borrowObject();
|
||||
if (socket == null) {
|
||||
return -1;
|
||||
}
|
||||
super.process(vl, socket.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
Collectd.logWarning(e.getMessage());
|
||||
try {
|
||||
// close socket so that the object pool will discard it and
|
||||
// reconnect
|
||||
socket.close();
|
||||
} catch (IOException e1) {
|
||||
Collectd.logError(e1.getMessage());
|
||||
}
|
||||
return -1;
|
||||
} finally {
|
||||
if (socket != null) {
|
||||
socketPool.returnObject(socket);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String metric, OutputStream out) {
|
||||
PrintWriter printWriter = new PrintWriter(out, false);
|
||||
printWriter.write(metric);
|
||||
printWriter.flush();
|
||||
if (printWriter.checkError()) {
|
||||
throw new RuntimeException("Error writing to Timely");
|
||||
}
|
||||
}
|
||||
|
||||
public int shutdown() {
|
||||
socketPool.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int config(OConfigItem config) {
|
||||
super.config(config);
|
||||
int retval = 0;
|
||||
PooledSocketFactory socketFactory = new PooledSocketFactory();
|
||||
retval = socketFactory.config(config);
|
||||
if (retval == 0) {
|
||||
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
|
||||
// use max size for maxTotal and maxIdle
|
||||
// no need to activate and passivate
|
||||
poolConfig.setMaxTotal(POOL_MAX_SIZE);
|
||||
poolConfig.setMaxIdle(POOL_MAX_SIZE);
|
||||
poolConfig.setTestOnReturn(true);
|
||||
socketPool = new GenericObjectPool(socketFactory, poolConfig);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
<Or>
|
||||
<Bug pattern="DM_DEFAULT_ENCODING"/>
|
||||
<Method name="connect" />
|
||||
<Class name="timely.collectd.plugin.WriteTimelyPlugin"/>
|
||||
</Or>
|
||||
</FindBugsFilter>
|
||||
-204
@@ -1,204 +0,0 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.jcip.annotations.NotThreadSafe;
|
||||
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.DataSet;
|
||||
import org.collectd.api.DataSource;
|
||||
import org.collectd.api.OConfigItem;
|
||||
import org.collectd.api.ValueList;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.api.easymock.PowerMock;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ Collectd.class })
|
||||
@NotThreadSafe
|
||||
@PowerMockIgnore("javax.management.*")
|
||||
public class WriteTimelyPluginTest {
|
||||
|
||||
private WriteTimelyPlugin plugin = null;
|
||||
private FakeServer server = null;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
PowerMock.suppress(PowerMock.everythingDeclaredIn(Collectd.class));
|
||||
server = new FakeServer();
|
||||
}
|
||||
|
||||
private ValueList createMetric() {
|
||||
ValueList val = new ValueList();
|
||||
val.setHost("r001n01.localdomain");
|
||||
val.setTime(System.currentTimeMillis());
|
||||
val.setPlugin("hddtemp");
|
||||
val.setPluginInstance(null);
|
||||
val.setType("temperature");
|
||||
val.setTypeInstance("sda");
|
||||
val.setValues(Collections.singletonList((Number) new Double(2.0)));
|
||||
DataSet ds = new DataSet("test");
|
||||
ds.addDataSource(new DataSource("test", DataSource.TYPE_COUNTER, 0, 100));
|
||||
val.setDataSet(ds);
|
||||
return val;
|
||||
}
|
||||
|
||||
private void setupPlugin() throws Exception {
|
||||
OConfigItem host = new OConfigItem("Host");
|
||||
host.addValue(server.getHost());
|
||||
OConfigItem port = new OConfigItem("Port");
|
||||
port.addValue(Integer.toString(server.getPort()));
|
||||
OConfigItem config = new OConfigItem("");
|
||||
config.addChild(host);
|
||||
config.addChild(port);
|
||||
plugin = new WriteTimelyPlugin();
|
||||
Assert.assertEquals(0, plugin.config(config));
|
||||
// Seed with one metric to create the connection
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite() throws Exception {
|
||||
Thread t = new Thread(server);
|
||||
t.start();
|
||||
setupPlugin();
|
||||
while (!server.ready()) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
plugin.shutdown();
|
||||
server.shutdown();
|
||||
t.join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteAfterServerRestart() throws Exception {
|
||||
Thread t = new Thread(server);
|
||||
t.start();
|
||||
setupPlugin();
|
||||
while (!server.ready()) {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
server.shutdown();
|
||||
t.join();
|
||||
Thread.sleep(2000);
|
||||
|
||||
server.create();
|
||||
Thread t2 = new Thread(server);
|
||||
t2.start();
|
||||
// Need to call this again because the server is not guaranteed to be
|
||||
// listening on the same local port as the first time that it was
|
||||
// started
|
||||
setupPlugin();
|
||||
while (!server.ready()) {
|
||||
Thread.sleep(1000);
|
||||
// Keep sending metrics to plugin to force reconnect
|
||||
int result = plugin.write(createMetric());
|
||||
System.out.println("Wrote to client, result: " + result);
|
||||
Assert.assertEquals(0, result);
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
Thread.sleep(1000);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
plugin.shutdown();
|
||||
server.shutdown();
|
||||
t2.join();
|
||||
}
|
||||
|
||||
private static class FakeServer implements Runnable {
|
||||
|
||||
private ServerSocket server = null;
|
||||
private BufferedReader reader = null;
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private Socket s = null;
|
||||
private boolean received = false;
|
||||
|
||||
public FakeServer() throws Exception {
|
||||
create();
|
||||
}
|
||||
|
||||
public void create() throws Exception {
|
||||
server = new ServerSocket(port);
|
||||
server.setReceiveBufferSize(8192);
|
||||
host = server.getInetAddress().getHostAddress();
|
||||
port = server.getLocalPort();
|
||||
System.out.println("Local port " + port);
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void shutdown() throws Exception {
|
||||
System.out.println("Closing server.");
|
||||
s.close();
|
||||
s = null;
|
||||
reader.close();
|
||||
reader = null;
|
||||
server.close();
|
||||
server = null;
|
||||
}
|
||||
|
||||
public boolean ready() throws Exception {
|
||||
return (s != null && s.isConnected());
|
||||
}
|
||||
|
||||
public boolean messageReceived() {
|
||||
try {
|
||||
return Boolean.valueOf(this.received);
|
||||
} finally {
|
||||
this.received = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
s = server.accept();
|
||||
System.out.println("Client connected.");
|
||||
reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
|
||||
while (server != null && !server.isClosed() && s != null && s.isConnected()) {
|
||||
String line;
|
||||
try {
|
||||
line = reader.readLine();
|
||||
} catch (Exception e) {
|
||||
// Might have been closed
|
||||
System.out.println("Closed by Exception: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
if (null != line) {
|
||||
this.received = true;
|
||||
}
|
||||
System.out.println(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
System.out.println("Exiting run method.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<Console name="CONSOLE" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="TRACE">
|
||||
<AppenderRef ref="CONSOLE"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -1 +0,0 @@
|
||||
/target/
|
||||
@@ -1,24 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>timely-common</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,4 +0,0 @@
|
||||
package timely.test;
|
||||
|
||||
public interface IntegrationTest {
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
# Markdown
|
||||
set :markdown_engine, :redcarpet
|
||||
set :markdown,
|
||||
fenced_code_blocks: true,
|
||||
smartypants: true,
|
||||
disable_indented_code_blocks: true,
|
||||
prettify: true,
|
||||
tables: true,
|
||||
with_toc_data: true,
|
||||
no_intra_emphasis: true
|
||||
|
||||
# Assets
|
||||
set :css_dir, 'stylesheets'
|
||||
set :js_dir, 'javascripts'
|
||||
set :images_dir, 'images'
|
||||
set :fonts_dir, 'fonts'
|
||||
|
||||
# Activate the syntax highlighter
|
||||
activate :syntax
|
||||
|
||||
activate :sprockets
|
||||
|
||||
activate :autoprefixer do |config|
|
||||
config.browsers = ['last 2 version', 'Firefox ESR']
|
||||
config.cascade = false
|
||||
config.inline = true
|
||||
end
|
||||
|
||||
# Github pages require relative links
|
||||
activate :relative_assets
|
||||
set :relative_links, true
|
||||
|
||||
# Build Configuration
|
||||
configure :build do
|
||||
# If you're having trouble with Middleman hanging, commenting
|
||||
# out the following two lines has been known to help
|
||||
activate :minify_css
|
||||
activate :minify_javascript
|
||||
# activate :relative_assets
|
||||
# activate :asset_hash
|
||||
# activate :gzip
|
||||
end
|
||||
|
||||
# Deploy Configuration
|
||||
# If you want Middleman to listen on a different port, you can set that below
|
||||
set :port, 4567
|
||||
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env bash
|
||||
set -o errexit #abort if any command fails
|
||||
me=$(basename "$0")
|
||||
|
||||
help_message="\
|
||||
Usage: $me [-c FILE] [<options>]
|
||||
Deploy generated files to a git branch.
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Show this help information.
|
||||
-v, --verbose Increase verbosity. Useful for debugging.
|
||||
-e, --allow-empty Allow deployment of an empty directory.
|
||||
-m, --message MESSAGE Specify the message used when committing on the
|
||||
deploy branch.
|
||||
-n, --no-hash Don't append the source commit's hash to the deploy
|
||||
commit's message.
|
||||
"
|
||||
|
||||
bundle exec middleman build --clean
|
||||
|
||||
parse_args() {
|
||||
# Set args from a local environment file.
|
||||
if [ -e ".env" ]; then
|
||||
source .env
|
||||
fi
|
||||
|
||||
# Parse arg flags
|
||||
# If something is exposed as an environment variable, set/overwrite it
|
||||
# here. Otherwise, set/overwrite the internal variable instead.
|
||||
while : ; do
|
||||
if [[ $1 = "-h" || $1 = "--help" ]]; then
|
||||
echo "$help_message"
|
||||
return 0
|
||||
elif [[ $1 = "-v" || $1 = "--verbose" ]]; then
|
||||
verbose=true
|
||||
shift
|
||||
elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then
|
||||
allow_empty=true
|
||||
shift
|
||||
elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then
|
||||
commit_message=$2
|
||||
shift 2
|
||||
elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then
|
||||
GIT_DEPLOY_APPEND_HASH=false
|
||||
shift
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Set internal option vars from the environment and arg flags. All internal
|
||||
# vars should be declared here, with sane defaults if applicable.
|
||||
|
||||
# Source directory & target branch.
|
||||
deploy_directory=build
|
||||
deploy_branch=gh-pages
|
||||
|
||||
#if no user identity is already set in the current git environment, use this:
|
||||
default_username=${GIT_DEPLOY_USERNAME:-deploy.sh}
|
||||
default_email=${GIT_DEPLOY_EMAIL:-}
|
||||
|
||||
#repository to deploy to. must be readable and writable.
|
||||
repo=origin
|
||||
|
||||
#append commit hash to the end of message by default
|
||||
append_hash=${GIT_DEPLOY_APPEND_HASH:-true}
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
enable_expanded_output
|
||||
|
||||
if ! git diff --exit-code --quiet --cached; then
|
||||
echo Aborting due to uncommitted changes in the index >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
commit_title=`git log -n 1 --format="%s" HEAD`
|
||||
commit_hash=` git log -n 1 --format="%H" HEAD`
|
||||
|
||||
#default commit message uses last title if a custom one is not supplied
|
||||
if [[ -z $commit_message ]]; then
|
||||
commit_message="publish: $commit_title"
|
||||
fi
|
||||
|
||||
#append hash to commit message unless no hash flag was found
|
||||
if [ $append_hash = true ]; then
|
||||
commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash"
|
||||
fi
|
||||
|
||||
previous_branch=`git rev-parse --abbrev-ref HEAD`
|
||||
|
||||
if [ ! -d "$deploy_directory" ]; then
|
||||
echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# must use short form of flag in ls for compatibility with OS X and BSD
|
||||
if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then
|
||||
echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then
|
||||
# deploy_branch exists in $repo; make sure we have the latest version
|
||||
|
||||
disable_expanded_output
|
||||
git fetch --force $repo $deploy_branch:$deploy_branch
|
||||
enable_expanded_output
|
||||
fi
|
||||
|
||||
# check if deploy_branch exists locally
|
||||
if git show-ref --verify --quiet "refs/heads/$deploy_branch"
|
||||
then incremental_deploy
|
||||
else initial_deploy
|
||||
fi
|
||||
|
||||
restore_head
|
||||
}
|
||||
|
||||
initial_deploy() {
|
||||
git --work-tree "$deploy_directory" checkout --orphan $deploy_branch
|
||||
git --work-tree "$deploy_directory" add --all
|
||||
commit+push
|
||||
}
|
||||
|
||||
incremental_deploy() {
|
||||
#make deploy_branch the current branch
|
||||
git symbolic-ref HEAD refs/heads/$deploy_branch
|
||||
#put the previously committed contents of deploy_branch into the index
|
||||
git --work-tree "$deploy_directory" reset --mixed --quiet
|
||||
git --work-tree "$deploy_directory" add --all
|
||||
|
||||
set +o errexit
|
||||
diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$?
|
||||
set -o errexit
|
||||
case $diff in
|
||||
0) echo No changes to files in $deploy_directory. Skipping commit.;;
|
||||
1) commit+push;;
|
||||
*)
|
||||
echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to master, use: git symbolic-ref HEAD refs/heads/master && git reset --mixed >&2
|
||||
return $diff
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
commit+push() {
|
||||
set_user_id
|
||||
git --work-tree "$deploy_directory" commit -m "$commit_message"
|
||||
|
||||
disable_expanded_output
|
||||
#--quiet is important here to avoid outputting the repo URL, which may contain a secret token
|
||||
git push --quiet $repo $deploy_branch
|
||||
enable_expanded_output
|
||||
}
|
||||
|
||||
#echo expanded commands as they are executed (for debugging)
|
||||
enable_expanded_output() {
|
||||
if [ $verbose ]; then
|
||||
set -o xtrace
|
||||
set +o verbose
|
||||
fi
|
||||
}
|
||||
|
||||
#this is used to avoid outputting the repo URL, which may contain a secret token
|
||||
disable_expanded_output() {
|
||||
if [ $verbose ]; then
|
||||
set +o xtrace
|
||||
set -o verbose
|
||||
fi
|
||||
}
|
||||
|
||||
set_user_id() {
|
||||
if [[ -z `git config user.name` ]]; then
|
||||
git config user.name "$default_username"
|
||||
fi
|
||||
if [[ -z `git config user.email` ]]; then
|
||||
git config user.email "$default_email"
|
||||
fi
|
||||
}
|
||||
|
||||
restore_head() {
|
||||
if [[ $previous_branch = "HEAD" ]]; then
|
||||
#we weren't on any branch before, so just set HEAD back to the commit it was on
|
||||
git update-ref --no-deref HEAD $commit_hash $deploy_branch
|
||||
else
|
||||
git symbolic-ref HEAD refs/heads/$previous_branch
|
||||
fi
|
||||
|
||||
git reset --mixed
|
||||
}
|
||||
|
||||
filter() {
|
||||
sed -e "s|$repo|\$repo|g"
|
||||
}
|
||||
|
||||
sanitize() {
|
||||
"$@" 2> >(filter 1>&2) | filter
|
||||
}
|
||||
|
||||
[[ $1 = --source-only ]] || main "$@"
|
||||
@@ -1,295 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<profiles version="12">
|
||||
<profile kind="CodeFormatterProfile" name="Timely" version="12">
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.source" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.8"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
|
||||
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
|
||||
</profile>
|
||||
</profiles>
|
||||
Executable
+148
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"IcoMoonType": "selection",
|
||||
"icons": [
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M438.857 73.143q119.429 0 220.286 58.857t159.714 159.714 58.857 220.286-58.857 220.286-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857zM512 785.714v-108.571q0-8-5.143-13.429t-12.571-5.429h-109.714q-7.429 0-13.143 5.714t-5.714 13.143v108.571q0 7.429 5.714 13.143t13.143 5.714h109.714q7.429 0 12.571-5.429t5.143-13.429zM510.857 589.143l10.286-354.857q0-6.857-5.714-10.286-5.714-4.571-13.714-4.571h-125.714q-8 0-13.714 4.571-5.714 3.429-5.714 10.286l9.714 354.857q0 5.714 5.714 10t13.714 4.286h105.714q8 0 13.429-4.286t6-10z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"tags": [
|
||||
"exclamation-circle"
|
||||
],
|
||||
"defaultCode": 61546,
|
||||
"grid": 14
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 100,
|
||||
"order": 4,
|
||||
"prevSize": 28,
|
||||
"code": 58880,
|
||||
"name": "exclamation-sign",
|
||||
"ligatures": ""
|
||||
},
|
||||
"setIdx": 0,
|
||||
"iconIdx": 0
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M585.143 786.286v-91.429q0-8-5.143-13.143t-13.143-5.143h-54.857v-292.571q0-8-5.143-13.143t-13.143-5.143h-182.857q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h54.857v182.857h-54.857q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h256q8 0 13.143-5.143t5.143-13.143zM512 274.286v-91.429q0-8-5.143-13.143t-13.143-5.143h-109.714q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h109.714q8 0 13.143-5.143t5.143-13.143zM877.714 512q0 119.429-58.857 220.286t-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857 220.286 58.857 159.714 159.714 58.857 220.286z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"tags": [
|
||||
"info-circle"
|
||||
],
|
||||
"defaultCode": 61530,
|
||||
"grid": 14
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 85,
|
||||
"order": 3,
|
||||
"name": "info-sign",
|
||||
"prevSize": 28,
|
||||
"code": 58882
|
||||
},
|
||||
"setIdx": 0,
|
||||
"iconIdx": 2
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M733.714 419.429q0-16-10.286-26.286l-52-51.429q-10.857-10.857-25.714-10.857t-25.714 10.857l-233.143 232.571-129.143-129.143q-10.857-10.857-25.714-10.857t-25.714 10.857l-52 51.429q-10.286 10.286-10.286 26.286 0 15.429 10.286 25.714l206.857 206.857q10.857 10.857 25.714 10.857 15.429 0 26.286-10.857l310.286-310.286q10.286-10.286 10.286-25.714zM877.714 512q0 119.429-58.857 220.286t-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857 220.286 58.857 159.714 159.714 58.857 220.286z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"tags": [
|
||||
"check-circle"
|
||||
],
|
||||
"defaultCode": 61528,
|
||||
"grid": 14
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 83,
|
||||
"order": 9,
|
||||
"prevSize": 28,
|
||||
"code": 58886,
|
||||
"name": "ok-sign"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"iconIdx": 6
|
||||
},
|
||||
{
|
||||
"icon": {
|
||||
"paths": [
|
||||
"M658.286 475.429q0-105.714-75.143-180.857t-180.857-75.143-180.857 75.143-75.143 180.857 75.143 180.857 180.857 75.143 180.857-75.143 75.143-180.857zM950.857 950.857q0 29.714-21.714 51.429t-51.429 21.714q-30.857 0-51.429-21.714l-196-195.429q-102.286 70.857-228 70.857-81.714 0-156.286-31.714t-128.571-85.714-85.714-128.571-31.714-156.286 31.714-156.286 85.714-128.571 128.571-85.714 156.286-31.714 156.286 31.714 128.571 85.714 85.714 128.571 31.714 156.286q0 125.714-70.857 228l196 196q21.143 21.143 21.143 51.429z"
|
||||
],
|
||||
"width": 951,
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"tags": [
|
||||
"search"
|
||||
],
|
||||
"defaultCode": 61442,
|
||||
"grid": 14
|
||||
},
|
||||
"attrs": [],
|
||||
"properties": {
|
||||
"id": 2,
|
||||
"order": 1,
|
||||
"prevSize": 28,
|
||||
"code": 58887,
|
||||
"name": "icon-search"
|
||||
},
|
||||
"setIdx": 0,
|
||||
"iconIdx": 7
|
||||
}
|
||||
],
|
||||
"height": 1024,
|
||||
"metadata": {
|
||||
"name": "slate",
|
||||
"license": "SIL OFL 1.1"
|
||||
},
|
||||
"preferences": {
|
||||
"showGlyphs": true,
|
||||
"showQuickUse": true,
|
||||
"showQuickUse2": true,
|
||||
"showSVGs": true,
|
||||
"fontPref": {
|
||||
"prefix": "icon-",
|
||||
"metadata": {
|
||||
"fontFamily": "slate",
|
||||
"majorVersion": 1,
|
||||
"minorVersion": 0,
|
||||
"description": "Based on FontAwesome",
|
||||
"license": "SIL OFL 1.1"
|
||||
},
|
||||
"metrics": {
|
||||
"emSize": 1024,
|
||||
"baseline": 6.25,
|
||||
"whitespace": 50
|
||||
},
|
||||
"resetPoint": 58880,
|
||||
"showSelector": false,
|
||||
"selector": "class",
|
||||
"classSelector": ".icon",
|
||||
"showMetrics": false,
|
||||
"showMetadata": true,
|
||||
"showVersion": true,
|
||||
"ie7": false
|
||||
},
|
||||
"imagePref": {
|
||||
"prefix": "icon-",
|
||||
"png": true,
|
||||
"useClassSelector": true,
|
||||
"color": 4473924,
|
||||
"bgColor": 16777215
|
||||
},
|
||||
"historySize": 100,
|
||||
"showCodes": true,
|
||||
"gridSize": 16,
|
||||
"showLiga": false
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>timely-app</directory>
|
||||
<outputDirectory>timely-app</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
</assembly>
|
||||
@@ -1,36 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>timely-app</artifactId>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>assemble</id>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<tarLongFileMode>posix</tarLongFileMode>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules/
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadNpmTasks('grunt-execute');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
|
||||
grunt.initConfig({
|
||||
|
||||
clean: ["dist"],
|
||||
|
||||
copy: {
|
||||
src_to_dist: {
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['**/*', '!**/*.js', '!**/*.scss'],
|
||||
dest: 'dist'
|
||||
},
|
||||
img_to_dist: {
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['img/*'],
|
||||
dest: 'dist/src/'
|
||||
},
|
||||
pluginDef: {
|
||||
expand: true,
|
||||
src: ['plugin.json', 'README.md'],
|
||||
dest: 'dist',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
rebuild_all: {
|
||||
files: ['src/**/*', 'plugin.json', 'README.md'],
|
||||
tasks: ['default'],
|
||||
options: {spawn: false}
|
||||
},
|
||||
},
|
||||
|
||||
babel: {
|
||||
options: {
|
||||
sourceMap: true,
|
||||
presets: ["es2015"],
|
||||
plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"],
|
||||
},
|
||||
dist: {
|
||||
files: [{
|
||||
cwd: 'src',
|
||||
expand: true,
|
||||
src: ['**/*.js'],
|
||||
dest: 'dist',
|
||||
ext:'.js'
|
||||
}]
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
grunt.registerTask('default',
|
||||
['clean',
|
||||
'copy:src_to_dist',
|
||||
'copy:pluginDef',
|
||||
'copy:img_to_dist',
|
||||
'babel'
|
||||
]);
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
# Timely
|
||||
|
||||
Timely: a scalable, distributed, **secure** time series database.
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
# Timely
|
||||
|
||||
Timely: a scalable, distributed, **secure** time series database.
|
||||
@@ -1,10 +0,0 @@
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-13">OpenTSDB metrics query</span>
|
||||
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.target' placeholder="events.eventname"></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-13">Show Global Annotations?</span>
|
||||
<editor-checkbox text="" model="ctrl.annotation.isGlobal"></editor-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Timely Login</h1>
|
||||
</div>
|
||||
|
||||
<div ng-if="ctrl.datasources.length === 0">
|
||||
<h4>No Timely datasources are defined.</h4>
|
||||
<i><a href="datasources/new">Create one</a></i>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="source in ctrl.datasources" ng-init="d=source" ng-controller="AuthsCtrl as actrl">
|
||||
<div class="gf-box">
|
||||
<div class="gf-box-header p-x-1 p-y-1">
|
||||
<h3 class="">{{d.name}}
|
||||
<span class="pull-right">
|
||||
<h6><i>https://{{d.jsonData.timelyHost}}:{{d.jsonData.httpsPort}}/login</i></h6>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="gf-box-body">
|
||||
<div ng-if="d.jsonData.basicAuths" class="p-b-1">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">
|
||||
User
|
||||
</span>
|
||||
<input id="timlely-user" class="gf-form-input max-width-21" type="text" ng-model='actrl.basicAuthUser' placeholder="user" required></input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-7">
|
||||
Password
|
||||
</span>
|
||||
<input id="timely-password" class="gf-form-input max-width-21" type="password" ng-model='actrl.basicAuthPassword' placeholder="password" required></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="actrl.fetching" class="gf-row">
|
||||
<div class="alert-{{actrl.fetching.status}} alert">
|
||||
<div class="alert-title">{{actrl.fetching.title}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-button-row center-block">
|
||||
<button type="submit" class="btn btn-success" ng-click="actrl.login(d)">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['lodash'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var _, _createClass, AuthsCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_lodash) {
|
||||
_ = _lodash.default;
|
||||
}],
|
||||
execute: function () {
|
||||
_createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
_export('AuthsCtrl', AuthsCtrl = function () {
|
||||
function AuthsCtrl(backendSrv, $rootScope, $timeout) {
|
||||
_classCallCheck(this, AuthsCtrl);
|
||||
|
||||
this.$rootScope = $rootScope;
|
||||
this.$timeout = $timeout;
|
||||
this.backendSrv = backendSrv;
|
||||
}
|
||||
|
||||
_createClass(AuthsCtrl, [{
|
||||
key: 'login',
|
||||
value: function login(datasource) {
|
||||
this.fetching = {};
|
||||
this.fetching.done = false;
|
||||
this.fetching.show = true;
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = 'Fetching auths...';
|
||||
|
||||
//this.$rootScope.appEvent('alert-success', ['Authorization Cookie Received', '']);
|
||||
//this.$rootScope.appEvent('alert-error', ['Authorization Failed', '']);
|
||||
//this.$rootScope.appEvent('alert-warning', ['Fetching Authorizations', '']);
|
||||
|
||||
var uri = this._getURI(datasource.jsonData);
|
||||
|
||||
if (datasource.jsonData.basicAuths) {
|
||||
if (this.basicAuthUser && this.basicAuthPassword) {
|
||||
var authBlob = { username: this.basicAuthUser, password: this.basicAuthPassword };
|
||||
this.doPostLogin(uri, authBlob);
|
||||
} else {
|
||||
this.$rootScope.appEvent('alert-warning', [datasource.name, 'requires User/Password']);
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = 'User/Password required';
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.doGetLogin(uri);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'doPostLogin',
|
||||
value: function doPostLogin(uri, authBlob) {
|
||||
this.doXHR('POST', uri, authBlob);
|
||||
}
|
||||
}, {
|
||||
key: 'doGetLogin',
|
||||
value: function doGetLogin(uri) {
|
||||
this.doXHR('GET', uri);
|
||||
}
|
||||
}, {
|
||||
key: 'doXHR',
|
||||
value: function doXHR(verb, uri, data) {
|
||||
if (!data) {
|
||||
data = "";
|
||||
}
|
||||
var r = new XMLHttpRequest();
|
||||
r.open(verb, uri, true);
|
||||
r.withCredentials = true;
|
||||
r.setRequestHeader('Content-Type', 'application/json');
|
||||
r.onreadystatechange = function () {
|
||||
this.handleLoginResult(r);
|
||||
}.bind(this);
|
||||
r.send(JSON.stringify(data));
|
||||
}
|
||||
}, {
|
||||
key: 'handleLoginResult',
|
||||
value: function handleLoginResult(response) {
|
||||
// wait for readyState to be '4 => done'
|
||||
if (response.readyState != 4) return;
|
||||
|
||||
this.fetching.done = true;
|
||||
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
var cookie = (document.cookie.match(/^(?:.*;)?TSESSIONID=([^;]+)(?:.*)?$/) || [, null])[1];
|
||||
console.log(cookie);
|
||||
console.log(document.cookie);
|
||||
|
||||
this.fetching.status = 'success';
|
||||
this.fetching.title = 'Login successful';
|
||||
this.$rootScope.appEvent('alert-success', ['Success', '']);
|
||||
break;
|
||||
default:
|
||||
this.fetching.status = 'warning';
|
||||
this.fetching.title = response.statusText;
|
||||
this.$rootScope.appEvent('alert-warning', [response.status + ': ' + response.statusText, '']);
|
||||
break;
|
||||
}
|
||||
|
||||
if (r.readyState != 4 || r.status != 200) return;
|
||||
alert("Success: " + r.getResponseHeader('Set-Cookie'));
|
||||
}
|
||||
}, {
|
||||
key: '_getURI',
|
||||
value: function _getURI(jsonData) {
|
||||
return 'https://' + jsonData.timelyHost + ':' + jsonData.httpsPort + '/login';
|
||||
}
|
||||
}, {
|
||||
key: '_showResults',
|
||||
value: function _showResults() {}
|
||||
}]);
|
||||
|
||||
return AuthsCtrl;
|
||||
}());
|
||||
|
||||
_export('AuthsCtrl', AuthsCtrl);
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=auths.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["../../src/components/auths.js"],"names":["_","AuthsCtrl","backendSrv","$rootScope","$timeout","datasource","fetching","done","show","status","title","uri","_getURI","jsonData","basicAuths","basicAuthUser","basicAuthPassword","authBlob","username","password","doPostLogin","appEvent","name","doGetLogin","doXHR","verb","data","r","XMLHttpRequest","open","withCredentials","setRequestHeader","onreadystatechange","handleLoginResult","bind","send","JSON","stringify","response","readyState","cookie","document","match","console","log","statusText","alert","getResponseHeader","timelyHost","httpsPort"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;;;;;;;;;;;;;;;;;;;;2BAEMC,S;AAEX,2BAAYC,UAAZ,EAAwBC,UAAxB,EAAoCC,QAApC,EAA6C;AAAA;;AAC3C,eAAKD,UAAL,GAAkBA,UAAlB;AACA,eAAKC,QAAL,GAAgBA,QAAhB;AACA,eAAKF,UAAL,GAAkBA,UAAlB;AACD;;;;gCAEKG,U,EAAW;AACf,iBAAKC,QAAL,GAAgB,EAAhB;AACA,iBAAKA,QAAL,CAAcC,IAAd,GAAqB,KAArB;AACA,iBAAKD,QAAL,CAAcE,IAAd,GAAqB,IAArB;AACA,iBAAKF,QAAL,CAAcG,MAAd,GAAuB,SAAvB;AACA,iBAAKH,QAAL,CAAcI,KAAd,GAAsB,mBAAtB;;AAEA;AACA;AACA;;AAEA,gBAAIC,MAAM,KAAKC,OAAL,CAAaP,WAAWQ,QAAxB,CAAV;;AAEA,gBAAGR,WAAWQ,QAAX,CAAoBC,UAAvB,EAAkC;AAChC,kBAAI,KAAKC,aAAL,IAAsB,KAAKC,iBAA/B,EAAiD;AAC/C,oBAAIC,WAAW,EAACC,UAAU,KAAKH,aAAhB,EAA+BI,UAAU,KAAKH,iBAA9C,EAAf;AACA,qBAAKI,WAAL,CAAiBT,GAAjB,EAAsBM,QAAtB;AACD,eAHD,MAGO;AACL,qBAAKd,UAAL,CAAgBkB,QAAhB,CAAyB,eAAzB,EAA0C,CAAChB,WAAWiB,IAAZ,EAAkB,wBAAlB,CAA1C;AACA,qBAAKhB,QAAL,CAAcG,MAAd,GAAuB,SAAvB;AACA,qBAAKH,QAAL,CAAcI,KAAd,GAAsB,wBAAtB;;AAEA;AACD;AACF,aAXD,MAWO;AACL,mBAAKa,UAAL,CAAgBZ,GAAhB;AACD;AAEF;;;sCAEWA,G,EAAKM,Q,EAAS;AACxB,iBAAKO,KAAL,CAAW,MAAX,EAAmBb,GAAnB,EAAwBM,QAAxB;AACD;;;qCAEUN,G,EAAI;AACb,iBAAKa,KAAL,CAAW,KAAX,EAAkBb,GAAlB;AACD;;;gCAEKc,I,EAAMd,G,EAAKe,I,EAAK;AACpB,gBAAI,CAACA,IAAL,EAAU;AACRA,qBAAO,EAAP;AACD;AACD,gBAAIC,IAAI,IAAIC,cAAJ,EAAR;AACAD,cAAEE,IAAF,CAAOJ,IAAP,EAAad,GAAb,EAAkB,IAAlB;AACAgB,cAAEG,eAAF,GAAoB,IAApB;AACAH,cAAEI,gBAAF,CAAmB,cAAnB,EAAmC,kBAAnC;AACAJ,cAAEK,kBAAF,GAAuB,YAAY;AACjC,mBAAKC,iBAAL,CAAuBN,CAAvB;AACD,aAFsB,CAErBO,IAFqB,CAEhB,IAFgB,CAAvB;AAGAP,cAAEQ,IAAF,CAAOC,KAAKC,SAAL,CAAeX,IAAf,CAAP;AACD;;;4CAEiBY,Q,EAAS;AACzB;AACA,gBAAIA,SAASC,UAAT,IAAuB,CAA3B,EAA8B;;AAE9B,iBAAKjC,QAAL,CAAcC,IAAd,GAAqB,IAArB;;AAEA,oBAAO+B,SAAS7B,MAAhB;AACE,mBAAK,GAAL;AACE,oBAAI+B,SAAS,CAACC,SAASD,MAAT,CAAgBE,KAAhB,CAAsB,qCAAtB,KAA8D,GAAE,IAAF,CAA/D,EAAwE,CAAxE,CAAb;AACAC,wBAAQC,GAAR,CAAYJ,MAAZ;AACAG,wBAAQC,GAAR,CAAYH,SAASD,MAArB;;AAEA,qBAAKlC,QAAL,CAAcG,MAAd,GAAuB,SAAvB;AACA,qBAAKH,QAAL,CAAcI,KAAd,GAAsB,kBAAtB;AACA,qBAAKP,UAAL,CAAgBkB,QAAhB,CAAyB,eAAzB,EAA0C,CAAC,SAAD,EAAY,EAAZ,CAA1C;AACA;AACF;AACE,qBAAKf,QAAL,CAAcG,MAAd,GAAuB,SAAvB;AACA,qBAAKH,QAAL,CAAcI,KAAd,GAAsB4B,SAASO,UAA/B;AACA,qBAAK1C,UAAL,CAAgBkB,QAAhB,CAAyB,eAAzB,EAA0C,CAACiB,SAAS7B,MAAT,GAAgB,IAAhB,GAAqB6B,SAASO,UAA/B,EAA0C,EAA1C,CAA1C;AACA;AAdJ;;AAiBA,gBAAIlB,EAAEY,UAAF,IAAgB,CAAhB,IAAqBZ,EAAElB,MAAF,IAAY,GAArC,EAA0C;AAC1CqC,kBAAM,cAAcnB,EAAEoB,iBAAF,CAAoB,YAApB,CAApB;AACD;;;kCAEOlC,Q,EAAS;AACf,mBAAO,aAAWA,SAASmC,UAApB,GAA+B,GAA/B,GAAmCnC,SAASoC,SAA5C,GAAsD,QAA7D;AACD;;;yCAEa,CAEb","file":"auths.js","sourcesContent":["import _ from 'lodash';\n\nexport class AuthsCtrl {\n\n constructor(backendSrv, $rootScope, $timeout){\n this.$rootScope = $rootScope;\n this.$timeout = $timeout;\n this.backendSrv = backendSrv;\n }\n\n login(datasource){\n this.fetching = {};\n this.fetching.done = false;\n this.fetching.show = true;\n this.fetching.status = 'warning';\n this.fetching.title = 'Fetching auths...';\n\n //this.$rootScope.appEvent('alert-success', ['Authorization Cookie Received', '']);\n //this.$rootScope.appEvent('alert-error', ['Authorization Failed', '']);\n //this.$rootScope.appEvent('alert-warning', ['Fetching Authorizations', '']);\n\n var uri = this._getURI(datasource.jsonData);\n\n if(datasource.jsonData.basicAuths){\n if( this.basicAuthUser && this.basicAuthPassword){\n var authBlob = {username: this.basicAuthUser, password: this.basicAuthPassword};\n this.doPostLogin(uri, authBlob);\n } else {\n this.$rootScope.appEvent('alert-warning', [datasource.name, 'requires User/Password']);\n this.fetching.status = 'warning';\n this.fetching.title = 'User/Password required';\n\n return;\n }\n } else {\n this.doGetLogin(uri);\n }\n\n }\n\n doPostLogin(uri, authBlob){\n this.doXHR('POST', uri, authBlob);\n }\n\n doGetLogin(uri){\n this.doXHR('GET', uri)\n }\n\n doXHR(verb, uri, data){\n if( !data){\n data = \"\";\n }\n var r = new XMLHttpRequest();\n r.open(verb, uri, true);\n r.withCredentials = true;\n r.setRequestHeader('Content-Type', 'application/json');\n r.onreadystatechange = function () {\n this.handleLoginResult(r);\n }.bind(this);\n r.send(JSON.stringify(data));\n }\n\n handleLoginResult(response){\n // wait for readyState to be '4 => done'\n if (response.readyState != 4) return;\n\n this.fetching.done = true;\n\n switcLine truncated
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
System.register(['lodash', 'app/core/core', 'plugins/timely-app/components/auths'], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var _, coreModule, appEvents, AuthsCtrl, _createClass, AuthsPageCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [function (_lodash) {
|
||||
_ = _lodash.default;
|
||||
}, function (_appCoreCore) {
|
||||
coreModule = _appCoreCore.coreModule;
|
||||
appEvents = _appCoreCore.appEvents;
|
||||
}, function (_pluginsTimelyAppComponentsAuths) {
|
||||
AuthsCtrl = _pluginsTimelyAppComponentsAuths.AuthsCtrl;
|
||||
}],
|
||||
execute: function () {
|
||||
_createClass = function () {
|
||||
function defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return function (Constructor, protoProps, staticProps) {
|
||||
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
};
|
||||
}();
|
||||
|
||||
_export('AuthsPageCtrl', AuthsPageCtrl = function () {
|
||||
function AuthsPageCtrl(backendSrv, datasourceSrv) {
|
||||
_classCallCheck(this, AuthsPageCtrl);
|
||||
|
||||
this.datasourceSrv = datasourceSrv;
|
||||
this.datasources = this.getTimelyDatasources();
|
||||
coreModule.controller('AuthsCtrl', AuthsCtrl);
|
||||
}
|
||||
|
||||
_createClass(AuthsPageCtrl, [{
|
||||
key: 'getTimelyDatasources',
|
||||
value: function getTimelyDatasources() {
|
||||
var allSources = this.datasourceSrv.getAll();
|
||||
|
||||
var sources = _.map(allSources, function (value, key, col) {
|
||||
var ret = {};
|
||||
ret.meta = {};
|
||||
ret.meta.type = col[key].meta.type;
|
||||
ret.meta.id = col[key].meta.id;
|
||||
ret.name = key;
|
||||
ret.jsonData = col[key].jsonData;
|
||||
return ret;
|
||||
});
|
||||
return _.filter(sources, function (d) {
|
||||
return d.meta.type === 'datasource' && d.meta.id === 'timely-datasource';
|
||||
});
|
||||
}
|
||||
}]);
|
||||
|
||||
return AuthsPageCtrl;
|
||||
}());
|
||||
|
||||
_export('AuthsPageCtrl', AuthsPageCtrl);
|
||||
|
||||
AuthsPageCtrl.templateUrl = 'components/auths.html';
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=authspage.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["../../src/components/authspage.js"],"names":["_","coreModule","appEvents","AuthsCtrl","AuthsPageCtrl","backendSrv","datasourceSrv","datasources","getTimelyDatasources","controller","allSources","getAll","sources","map","value","key","col","ret","meta","type","id","name","jsonData","filter","d","templateUrl"],"mappings":";;;;;;;;;;;;;;;AACOA,O;;AACCC,gB,gBAAAA,U;AAAYC,e,gBAAAA,S;;AACZC,e,oCAAAA,S;;;;;;;;;;;;;;;;;;;;;+BAEKC,a;AAEX,+BAAYC,UAAZ,EAAwBC,aAAxB,EAAsC;AAAA;;AACpC,eAAKA,aAAL,GAAqBA,aAArB;AACA,eAAKC,WAAL,GAAmB,KAAKC,oBAAL,EAAnB;AACAP,qBAAWQ,UAAX,CAAsB,WAAtB,EAAmCN,SAAnC;AACD;;;;iDAEqB;AACpB,gBAAIO,aAAa,KAAKJ,aAAL,CAAmBK,MAAnB,EAAjB;;AAEA,gBAAIC,UAAUZ,EAAEa,GAAF,CAAMH,UAAN,EAAkB,UAASI,KAAT,EAAgBC,GAAhB,EAAqBC,GAArB,EAAyB;AACvD,kBAAIC,MAAM,EAAV;AACAA,kBAAIC,IAAJ,GAAW,EAAX;AACAD,kBAAIC,IAAJ,CAASC,IAAT,GAAgBH,IAAID,GAAJ,EAASG,IAAT,CAAcC,IAA9B;AACAF,kBAAIC,IAAJ,CAASE,EAAT,GAAcJ,IAAID,GAAJ,EAASG,IAAT,CAAcE,EAA5B;AACAH,kBAAII,IAAJ,GAAWN,GAAX;AACAE,kBAAIK,QAAJ,GAAeN,IAAID,GAAJ,EAASO,QAAxB;AACA,qBAAOL,GAAP;AACD,aARa,CAAd;AASA,mBAAOjB,EAAEuB,MAAF,CAASX,OAAT,EAAkB,UAASY,CAAT,EAAW;AAClC,qBAAQA,EAAEN,IAAF,CAAOC,IAAP,KAAgB,YAAhB,IAAgCK,EAAEN,IAAF,CAAOE,EAAP,KAAc,mBAAtD;AACD,aAFM,CAAP;AAGD;;;;;;;;AAGHhB,oBAAcqB,WAAd,GAA4B,uBAA5B","file":"authspage.js","sourcesContent":["\nimport _ from 'lodash';\nimport {coreModule, appEvents} from 'app/core/core';\nimport {AuthsCtrl} from 'plugins/timely-app/components/auths';\n\nexport class AuthsPageCtrl {\n\n constructor(backendSrv, datasourceSrv){\n this.datasourceSrv = datasourceSrv;\n this.datasources = this.getTimelyDatasources();\n coreModule.controller('AuthsCtrl', AuthsCtrl);\n }\n\n getTimelyDatasources(){\n var allSources = this.datasourceSrv.getAll();\n\n var sources = _.map(allSources, function(value, key, col){\n var ret = {};\n ret.meta = {};\n ret.meta.type = col[key].meta.type;\n ret.meta.id = col[key].meta.id;\n ret.name = key;\n ret.jsonData = col[key].jsonData;\n return ret;\n });\n return _.filter(sources, function(d){\n return (d.meta.type === 'datasource' && d.meta.id === 'timely-datasource');\n });\n }\n\n}\nAuthsPageCtrl.templateUrl = 'components/auths.html';\n"]}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
|
||||
<h3 class="page-heading">Timely App Configuration</h3>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">Currently, all configuration is done in the Timely datasource.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
System.register([], function (_export, _context) {
|
||||
"use strict";
|
||||
|
||||
var TimelyAppConfigCtrl;
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setters: [],
|
||||
execute: function () {
|
||||
_export('TimelyAppConfigCtrl', TimelyAppConfigCtrl = function TimelyAppConfigCtrl() {
|
||||
_classCallCheck(this, TimelyAppConfigCtrl);
|
||||
});
|
||||
|
||||
_export('TimelyAppConfigCtrl', TimelyAppConfigCtrl);
|
||||
|
||||
TimelyAppConfigCtrl.templateUrl = 'components/config.html';
|
||||
}
|
||||
};
|
||||
});
|
||||
//# sourceMappingURL=config.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["../../src/components/config.js"],"names":["TimelyAppConfigCtrl","templateUrl"],"mappings":";;;;;;;;;;;;;;;;qCACaA,mB,GAEX,+BAAc;AAAA;AAEb,O;;;;AAGHA,0BAAoBC,WAApB,GAAkC,wBAAlC","file":"config.js","sourcesContent":["\nexport class TimelyAppConfigCtrl {\n\n constructor() {\n \n }\n\n}\nTimelyAppConfigCtrl.templateUrl = 'components/config.html';\n"]}
|
||||
Loaded 100 of 424 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user