norikra 0.1.4-java → 0.1.5-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +23 -0
- data/lib/norikra/cli.rb +60 -4
- data/lib/norikra/field.rb +17 -1
- data/lib/norikra/query.rb +7 -5
- data/lib/norikra/query/ast.rb +205 -28
- data/lib/norikra/rpc/gatekeeper.rb +18 -0
- data/lib/norikra/rpc/http.rb +2 -0
- data/lib/norikra/server.rb +15 -29
- data/lib/norikra/stats.rb +22 -9
- data/lib/norikra/version.rb +1 -1
- data/lib/norikra/webui/handler.rb +19 -0
- data/spec/field_spec.rb +13 -0
- data/spec/query_spec.rb +35 -0
- data/spec/stats_spec.rb +0 -10
- data/views/index.erb +37 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30a26c93805e62b832b47ade931907e800c35c01
|
4
|
+
data.tar.gz: f8fe030eacb414c84aeebd01b40351965391a53b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 243a44cf8003f1843df7137c4c78c9e72765e3da51f7f33e961531cd9017c612389d44051fdf2a1cf98c57149b97a9978c250dacc98a4cd324206e9866d4cdbd
|
7
|
+
data.tar.gz: 7351f6ee5139a5323adfb9708abe8291f253e47a783d2efcc4fdf682c35f706ea48ae7c179dcfa8bb6bea73137eb95bdd2b578c164ec4af4de24e237cb2efe9b
|
data/README.md
CHANGED
@@ -69,6 +69,29 @@ See: http://norikra.github.io/
|
|
69
69
|
|
70
70
|
## Changes
|
71
71
|
|
72
|
+
* v0.1.5:
|
73
|
+
* Add RPC port monitoring handler (GET /)
|
74
|
+
* Add `Pattern` support
|
75
|
+
* Changes for GC/JVM
|
76
|
+
* Server starts with default(recommended) JVM options for GC configurations
|
77
|
+
* Add option not to use default JVM options
|
78
|
+
* Add option to configure JVM to print GC logs
|
79
|
+
* Stats file changes
|
80
|
+
* Changed to use SIGUSR2 (SIGUSR1 is used by JVM on linux platform)
|
81
|
+
* Changed no to contain server process configurations (ports, threads, logs ...)
|
82
|
+
* WebUI improvements
|
83
|
+
* Add button to close target on WebUI
|
84
|
+
* Add link to download stats file
|
85
|
+
* Fix bugs
|
86
|
+
* Fieldname handlings about names with non-alphabetic chars
|
87
|
+
* Query parser bug for some built-in functions (ex: `rate(10)`)
|
88
|
+
* Write stats file in more safe way
|
89
|
+
* WebUI: Incorrect memory bar
|
90
|
+
* v0.1.4:
|
91
|
+
* Stat dump option on runtime per specified intervals (`--dump-stat-interval`)
|
92
|
+
* Stat dump on demand by SIGUSR1
|
93
|
+
* `norikra-client event see` command (and API call) to get query results, but not remove it
|
94
|
+
* Last modified time for each target on WebUI
|
72
95
|
* v0.1.3:
|
73
96
|
* Fix critical bug about query de-registration
|
74
97
|
* v0.1.2:
|
data/lib/norikra/cli.rb
CHANGED
@@ -13,11 +13,12 @@ module Norikra
|
|
13
13
|
### Server options
|
14
14
|
option :host, :type => :string, :default => nil, :aliases => "-H", :desc => 'host address that server listen [0.0.0.0]'
|
15
15
|
option :port, :type => :numeric, :default => nil, :aliases => "-P", :desc => 'port that server uses [26571]'
|
16
|
+
option :'ui-port', :type => :numeric, :default => nil, :desc => 'port that Web UI server uses [26578]'
|
16
17
|
|
17
18
|
option :stats, :type => :string, :default => nil, :aliases => "-s", \
|
18
|
-
:desc => 'status file path to load/dump targets
|
19
|
+
:desc => 'status file path to load/dump targets and queries [none]'
|
19
20
|
option :'suppress-dump-stat', :type => :boolean, :default => false, \
|
20
|
-
:desc => 'specify not to update stat file with updated targets/queries
|
21
|
+
:desc => 'specify not to update stat file with updated targets/queries on runtime [false]'
|
21
22
|
option :'dump-stat-interval', :type => :numeric, :default => nil, \
|
22
23
|
:desc => 'interval(seconds) of status file dumps on runtime [none (on shutdown only)]'
|
23
24
|
|
@@ -29,6 +30,10 @@ module Norikra
|
|
29
30
|
option :outfile, :type => :string, :default => nil, \
|
30
31
|
:desc => "stdout redirect file when daemonized [${logdir}/norikra.out]"
|
31
32
|
|
33
|
+
### JVM options
|
34
|
+
option :'bare-jvm', :type => :boolean, :default => false, :desc => "use JVM without any recommended options"
|
35
|
+
option :'gc-log', :type => :string, :default => nil, :desc => "output gc logs on specified file path"
|
36
|
+
|
32
37
|
### Performance options
|
33
38
|
# performance predefined configuration sets
|
34
39
|
option :micro, :type => :boolean, :default => false, \
|
@@ -71,6 +76,40 @@ module Norikra
|
|
71
76
|
end
|
72
77
|
|
73
78
|
class CLI < Thor
|
79
|
+
# JVM defaults w/ ConcurrentGC:
|
80
|
+
# java -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version
|
81
|
+
# (jruby does not modify flags)
|
82
|
+
#
|
83
|
+
# CMSIncrementalMode: not recommended in Java8
|
84
|
+
#
|
85
|
+
# CMSInitiatingOccupancyFraction: CMS threshold
|
86
|
+
# CMSInitiatingOccupancyFraction = 100 - MinHeapFreeRatio + CMSTriggerRatio * MinHeapFreeRatio / 100
|
87
|
+
# default: MinHeapFreeRatio=40 CMSTriggerRatio=80 -> CMSInitiatingOccupancyFraction=92
|
88
|
+
#
|
89
|
+
# NewRatio=7 (New:Old = 1:7)
|
90
|
+
# InitialSurvivorRatio=8 MinSurvivorRatio=3 SurvivorRatio=8 (Eden:Survivor0:survivor1 = 8:1:1)
|
91
|
+
#
|
92
|
+
# MaxTenuringThreshold=4
|
93
|
+
# TargetSurvivorRatio=50
|
94
|
+
# (InitialTenuringThreshold=7 is for Parallel GC/UseAdaptiveSizePolicy)
|
95
|
+
#
|
96
|
+
# SoftRefLRUPolicyMSPerMB=1000
|
97
|
+
# ( gc_interval > free_heap * ms_per_mb : clear softref )
|
98
|
+
|
99
|
+
JVM_OPTIONS = [
|
100
|
+
'-XX:-UseGCOverheadLimit',
|
101
|
+
'-XX:+UseConcMarkSweepGC', '-XX:+UseCompressedOops',
|
102
|
+
'-XX:CMSInitiatingOccupancyFraction=70', '-XX:+UseCMSInitiatingOccupancyOnly',
|
103
|
+
'-XX:NewRatio=1',
|
104
|
+
'-XX:SurvivorRatio=2', '-XX:MaxTenuringThreshold=15', '-XX:TargetSurvivorRatio=80',
|
105
|
+
'-XX:SoftRefLRUPolicyMSPerMB=200',
|
106
|
+
]
|
107
|
+
|
108
|
+
JVM_GC_OPTIONS = [
|
109
|
+
'-verbose:gc', '-XX:+PrintGCDetails', '-XX:+PrintGCDateStamps',
|
110
|
+
'-XX:+HeapDumpOnOutOfMemoryError',
|
111
|
+
]
|
112
|
+
|
74
113
|
### 'start' and 'serverprocess' have almost same option set (for parse/help)
|
75
114
|
### DIFF: jvm options (-X)
|
76
115
|
Norikra::CLIUtil.register_common_start_options(self)
|
@@ -85,11 +124,22 @@ module Norikra
|
|
85
124
|
ARGV.shift # shift head "start"
|
86
125
|
|
87
126
|
argv = ["serverproc"]
|
88
|
-
jruby_options = ['-J-server'
|
127
|
+
jruby_options = ['-J-server']
|
128
|
+
|
129
|
+
unless options[:'bare-jvm']
|
130
|
+
jruby_options += JVM_OPTIONS.map{|opt| '-J' + opt }
|
131
|
+
end
|
132
|
+
|
133
|
+
if options[:'gc-log']
|
134
|
+
jruby_options += JVM_GC_OPTIONS.map{|opt| '-J' + opt }
|
135
|
+
jruby_options.push "-J-Xloggc:#{options[:'gc-log']}"
|
136
|
+
end
|
89
137
|
|
90
138
|
ARGV.each do |arg|
|
91
139
|
if arg =~ /^-X(.+)$/
|
92
140
|
jruby_options.push('-J-X' + $1)
|
141
|
+
elsif arg =~ /^-verbose:gc$/
|
142
|
+
jruby_options.push('-J-verbose:gc')
|
93
143
|
else
|
94
144
|
argv.push(arg)
|
95
145
|
end
|
@@ -171,7 +221,13 @@ module Norikra
|
|
171
221
|
level: loglevel, dir: options[:logdir], filesize: options[:'log-filesize'], backups: options[:'log-backups'],
|
172
222
|
}
|
173
223
|
|
174
|
-
|
224
|
+
server_options = {
|
225
|
+
host: options[:host],
|
226
|
+
port: options[:port],
|
227
|
+
ui_port: options[:'ui-port'],
|
228
|
+
}
|
229
|
+
|
230
|
+
server = Norikra::Server.new( server_options, conf )
|
175
231
|
server.run
|
176
232
|
server.shutdown
|
177
233
|
end
|
data/lib/norikra/field.rb
CHANGED
@@ -218,7 +218,23 @@ module Norikra
|
|
218
218
|
raise ArgumentError, "container_accessor must be a String or Interger, but #{accessor.class.to_s}"
|
219
219
|
end
|
220
220
|
if v.is_a?(Hash)
|
221
|
-
v[accessor] || v[accessor.to_s]
|
221
|
+
# v[accessor] || v[accessor.to_s]
|
222
|
+
if v.has_key?(accessor)
|
223
|
+
v[accessor]
|
224
|
+
elsif v.has_key?(accessor.to_s)
|
225
|
+
v[accessor.to_s]
|
226
|
+
elsif accessor.is_a?(String)
|
227
|
+
val = v.keys.reduce(nil) do |r, key|
|
228
|
+
if key.is_a?(String)
|
229
|
+
r || ( key.gsub(/[^$_a-zA-Z0-9]/, '_') == accessor.to_s ? v[key] : nil )
|
230
|
+
else
|
231
|
+
r
|
232
|
+
end
|
233
|
+
end
|
234
|
+
val # valid value or nil
|
235
|
+
else # v does not have key "#{accessor}" and accessor is Fixnum
|
236
|
+
nil
|
237
|
+
end
|
222
238
|
elsif v.is_a?(Array)
|
223
239
|
if accessor.is_a?(Fixnum)
|
224
240
|
v[accessor]
|
data/lib/norikra/query.rb
CHANGED
@@ -58,13 +58,13 @@ module Norikra
|
|
58
58
|
|
59
59
|
def targets
|
60
60
|
return @targets if @targets
|
61
|
-
@targets = (self.ast.listup(:stream).map(&:
|
61
|
+
@targets = (self.ast.listup(:stream).map(&:targets).flatten + self.subqueries.map(&:targets).flatten).sort.uniq
|
62
62
|
@targets
|
63
63
|
end
|
64
64
|
|
65
65
|
def aliases
|
66
66
|
return @aliases if @aliases
|
67
|
-
@aliases = (self.ast.listup(:stream).map(&:
|
67
|
+
@aliases = (self.ast.listup(:stream).map{|s| s.aliases.map(&:first) }.flatten + self.subqueries.map(&:aliases).flatten).sort.uniq
|
68
68
|
@aliases
|
69
69
|
end
|
70
70
|
|
@@ -81,10 +81,12 @@ module Norikra
|
|
81
81
|
all = []
|
82
82
|
unknowns = []
|
83
83
|
self.ast.listup(:stream).each do |node|
|
84
|
-
|
85
|
-
alias_map[
|
84
|
+
node.aliases.each do |alias_name, target|
|
85
|
+
alias_map[alias_name] = target
|
86
|
+
end
|
87
|
+
node.targets.each do |target|
|
88
|
+
fields[target] ||= []
|
86
89
|
end
|
87
|
-
fields[node.target] = []
|
88
90
|
end
|
89
91
|
|
90
92
|
dup_aliases = (alias_map.keys & fields.keys)
|
data/lib/norikra/query/ast.rb
CHANGED
@@ -79,34 +79,84 @@ module Norikra
|
|
79
79
|
# ["LIB_FUNC_CHAIN", ["LIB_FUNCTION", "opts.num.seq", "length", "("]],
|
80
80
|
# "0"]]]]
|
81
81
|
|
82
|
+
### SELECT a.name, a.content, b.content
|
83
|
+
### FROM pattern[every a=EventA -> b=EventA(name = a.name, type = 'TYPE') WHERE timer:within(1 min)].win:time(2 hour)
|
84
|
+
### WHERE a.source in ('A', 'B')
|
85
|
+
|
86
|
+
# ["EPL_EXPR",
|
87
|
+
# ["SELECTION_EXPR",
|
88
|
+
# ["SELECTION_ELEMENT_EXPR",
|
89
|
+
# ["EVENT_PROP_EXPR",
|
90
|
+
# ["EVENT_PROP_SIMPLE", "a"],
|
91
|
+
# ["EVENT_PROP_SIMPLE", "name"]]],
|
92
|
+
# ["SELECTION_ELEMENT_EXPR",
|
93
|
+
# ["EVENT_PROP_EXPR",
|
94
|
+
# ["EVENT_PROP_SIMPLE", "a"],
|
95
|
+
# ["EVENT_PROP_SIMPLE", "content"]]],
|
96
|
+
# ["SELECTION_ELEMENT_EXPR",
|
97
|
+
# ["EVENT_PROP_EXPR",
|
98
|
+
# ["EVENT_PROP_SIMPLE", "b"],
|
99
|
+
# ["EVENT_PROP_SIMPLE", "content"]]]],
|
100
|
+
# ["STREAM_EXPR",
|
101
|
+
# ["PATTERN_INCL_EXPR",
|
102
|
+
# ["FOLLOWED_BY_EXPR",
|
103
|
+
# ["FOLLOWED_BY_ITEM", ["every", ["PATTERN_FILTER_EXPR", "a", "EventA"]]],
|
104
|
+
# ["FOLLOWED_BY_ITEM",
|
105
|
+
# ["GUARD_EXPR",
|
106
|
+
# ["PATTERN_FILTER_EXPR",
|
107
|
+
# "b",
|
108
|
+
# "EventA",
|
109
|
+
# ["EVAL_EQUALS_EXPR",
|
110
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "name"]],
|
111
|
+
# ["EVENT_PROP_EXPR",
|
112
|
+
# ["EVENT_PROP_SIMPLE", "a"],
|
113
|
+
# ["EVENT_PROP_SIMPLE", "name"]]],
|
114
|
+
# ["EVAL_EQUALS_EXPR",
|
115
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "type"]],
|
116
|
+
# "'TYPE'"]],
|
117
|
+
# "timer",
|
118
|
+
# "within",
|
119
|
+
# ["TIME_PERIOD", ["MINUTE_PART", "1"]]]]]],
|
120
|
+
# ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["HOUR_PART", "2"]]]],
|
121
|
+
# ["WHERE_EXPR",
|
122
|
+
# ["in",
|
123
|
+
# ["EVENT_PROP_EXPR",
|
124
|
+
# ["EVENT_PROP_SIMPLE", "a"],
|
125
|
+
# ["EVENT_PROP_SIMPLE", "source"]],
|
126
|
+
# "(",
|
127
|
+
# "'A'",
|
128
|
+
# "'B'",
|
129
|
+
# ")"]]]
|
130
|
+
|
82
131
|
def astnode(tree)
|
83
132
|
children = if tree.children
|
84
133
|
tree.children.map{|c| astnode(c)}
|
85
134
|
else
|
86
135
|
[]
|
87
136
|
end
|
88
|
-
case tree.text
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
137
|
+
cls = case tree.text
|
138
|
+
when 'EVENT_PROP_EXPR' then ASTEventPropNode
|
139
|
+
when 'SELECTION_ELEMENT_EXPR' then ASTSelectionElementNode
|
140
|
+
when 'LIB_FUNCTION' then ASTLibFunctionNode
|
141
|
+
when 'STREAM_EXPR' then ASTStreamNode
|
142
|
+
when 'PATTERN_FILTER_EXPR' then ASTPatternNode
|
143
|
+
when 'SUBSELECT_EXPR' then ASTSubSelectNode
|
144
|
+
else ASTNode
|
145
|
+
end
|
146
|
+
if cls.respond_to?(:generate)
|
147
|
+
cls.generate(tree.text, children, tree)
|
99
148
|
else
|
100
|
-
|
149
|
+
cls.new(tree.text, children, tree)
|
101
150
|
end
|
102
151
|
end
|
103
152
|
|
104
153
|
class ASTNode
|
105
|
-
attr_accessor :name, :children
|
154
|
+
attr_accessor :name, :children, :tree
|
106
155
|
|
107
|
-
def initialize(name, children)
|
156
|
+
def initialize(name, children, tree)
|
108
157
|
@name = name
|
109
158
|
@children = children
|
159
|
+
@tree = tree
|
110
160
|
end
|
111
161
|
|
112
162
|
def nodetype?(*sym)
|
@@ -132,7 +182,12 @@ module Norikra
|
|
132
182
|
nil
|
133
183
|
end
|
134
184
|
|
135
|
-
def listup(type) # search all nodes that has 'type'
|
185
|
+
def listup(*type) # search all nodes that has 'type'
|
186
|
+
if type.size > 1
|
187
|
+
return type.map{|t| self.listup(t) }.reduce(&:+)
|
188
|
+
end
|
189
|
+
type = type.first
|
190
|
+
|
136
191
|
result = []
|
137
192
|
result.push(self) if type.is_a?(String) && @name == type || nodetype?(type)
|
138
193
|
|
@@ -184,15 +239,22 @@ module Norikra
|
|
184
239
|
end
|
185
240
|
|
186
241
|
class ASTLibFunctionNode < ASTNode # LIB_FUNCTION
|
187
|
-
|
188
|
-
# "
|
189
|
-
# "
|
190
|
-
# "
|
191
|
-
# "
|
242
|
+
### foo is function
|
243
|
+
# "foo()" => ["LIB_FUNCTION", "foo", "("]
|
244
|
+
# "foo(10)" => ["LIB_FUNCTION", "foo", "10", "("]
|
245
|
+
# "foo(10,0)" => ["LIB_FUNCTION", "foo", "10", "0", "("]
|
246
|
+
# "foo(bar)" => ["LIB_FUNCTION", "foo", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bar"]], "("]
|
247
|
+
|
248
|
+
### foo is property
|
249
|
+
# "foo.bar()" => ["LIB_FUNCTION", "foo", "bar", "("]
|
250
|
+
# "foo.bar(0)" => ["LIB_FUNCTION", "foo", "bar", "0", "("]
|
251
|
+
# "foo.bar(0,8)" => ["LIB_FUNCTION", "foo", "bar", "0", "8", "("]
|
192
252
|
|
193
|
-
|
253
|
+
### nested field access
|
254
|
+
# "foo.bar.$0.baz()" => ["LIB_FUNCTION", "foo.bar.$0", "baz", "("]
|
194
255
|
|
195
|
-
#
|
256
|
+
# 2nd child is bare-word (like [a-z][a-z0-9]*) -> this is function -> 1st child is receiver -> property
|
257
|
+
# 2nd child is literal or property or none -> 1st child is built-in function
|
196
258
|
|
197
259
|
def nodetype?(*sym)
|
198
260
|
sym.include?(:lib) || sym.include?(:libfunc)
|
@@ -207,7 +269,7 @@ module Norikra
|
|
207
269
|
# first element should be func name if second element is property, library call or subqueries
|
208
270
|
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
|
209
271
|
|
210
|
-
elsif @children[1].name =~ /^(-)?\d+(\.\d+)
|
272
|
+
elsif @children[1].name =~ /^(-)?\d+(\.\d+)?$/ || @children[1].name =~ /^'[^']*'$/ || @children[1].name =~ /^"[^"]*"$/
|
211
273
|
# first element should be func name if secod element is number/string literal
|
212
274
|
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
|
213
275
|
|
@@ -216,7 +278,7 @@ module Norikra
|
|
216
278
|
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target, known_targets_aliases)}.reduce(&:+) || []
|
217
279
|
|
218
280
|
else
|
219
|
-
# first element may be property
|
281
|
+
# first element may be property ########## or function
|
220
282
|
# * simple 'fieldname.funcname()'
|
221
283
|
# * fully qualified 'target.fieldname.funcname()'
|
222
284
|
# * simple/fully-qualified container field access 'fieldname.key.$0.funcname()' or 'target.fieldname.$1.funcname()'
|
@@ -237,31 +299,146 @@ module Norikra
|
|
237
299
|
end
|
238
300
|
|
239
301
|
class ASTStreamNode < ASTNode # STREAM_EXPR
|
302
|
+
def self.generate(name, children, tree)
|
303
|
+
if children.first.name == 'EVENT_FILTER_EXPR'
|
304
|
+
ASTStreamEventNode.new(name, children, tree)
|
305
|
+
elsif children.first.name == 'PATTERN_INCL_EXPR'
|
306
|
+
ASTStreamPatternNode.new(name, children, tree)
|
307
|
+
else
|
308
|
+
raise "unexpected stream node type! report to norikra developer!: #{children.map(&:name).join(',')}"
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def nodetype?(*sym)
|
313
|
+
sym.include?(:stream)
|
314
|
+
end
|
315
|
+
|
316
|
+
def targets
|
317
|
+
# ["TARGET_NAME"]
|
318
|
+
raise NotImplementedError, "ASTStreamNode#targets MUST be overridden by subclass"
|
319
|
+
end
|
320
|
+
|
321
|
+
def aliases
|
322
|
+
# [ [ "ALIAS_NAME", "TARGET_NAME" ], ... ]
|
323
|
+
raise NotImplementedError, "ASTStreamNode#aliases MUST be overridden by subclass"
|
324
|
+
end
|
325
|
+
|
326
|
+
def fields(default_target=nil, known_targets_aliases=[])
|
327
|
+
raise NotImplementedError, "ASTStreamNode#fields MUST be overridden by subclass"
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
class ASTStreamEventNode < ASTStreamNode
|
332
|
+
##### from stream_def [as name] [unidirectional] [retain-union | retain-intersection],
|
333
|
+
##### [ stream_def ... ]
|
334
|
+
#
|
335
|
+
# single Event stream name ( ex: FROM events.win:time(...) AS e )
|
336
|
+
#
|
240
337
|
# ["STREAM_EXPR",
|
241
338
|
# ["EVENT_FILTER_EXPR", "FraudWarningEvent"],
|
242
339
|
# ["VIEW_EXPR", "win", "keepall"],
|
243
340
|
# "fraud"],
|
341
|
+
#
|
244
342
|
# ["STREAM_EXPR",
|
245
343
|
# ["EVENT_FILTER_EXPR",
|
246
344
|
# "PINChangeEvent",
|
247
345
|
# [">", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "10"]],
|
248
346
|
# ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["SECOND_PART", "20"]]]],
|
249
347
|
|
348
|
+
NON_ALIAS_NODES = ['EVENT_FILTER_EXPR','VIEW_EXPR','unidirectional','retain-union','retain-intersection']
|
349
|
+
|
350
|
+
def targets
|
351
|
+
[self.find('EVENT_FILTER_EXPR').child.name]
|
352
|
+
end
|
353
|
+
|
354
|
+
def aliases
|
355
|
+
alias_nodes = children.select{|n| not NON_ALIAS_NODES.include?(n.name) }
|
356
|
+
if alias_nodes.size < 1
|
357
|
+
[]
|
358
|
+
elsif alias_nodes.size > 1
|
359
|
+
raise "unexpected FROM clause (includes 2 or more alias words): #{alias_nodes.map(&:name).join(',')}"
|
360
|
+
else
|
361
|
+
[ [ alias_nodes.first.name, self.targets.first ] ]
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def fields(default_target=nil, known_targets_aliases=[])
|
366
|
+
this_target = self.targets.first
|
367
|
+
self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target,known_targets_aliases)}.reduce(&:+) || []
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
class ASTStreamPatternNode < ASTStreamNode
|
372
|
+
## MEMO: Pattern itself can have alias name, but it makes no sense. So we ignore it.
|
373
|
+
## ('x' is ignored): pattern [... ] AS x
|
374
|
+
#
|
375
|
+
# pattern ( ex: FROM pattern[ every a=events1 -> b=Events1(name=a.name, type='T') where timer:within(1 min) ].win:time(2 hour) )
|
376
|
+
#
|
377
|
+
# ["STREAM_EXPR",
|
378
|
+
# ["PATTERN_INCL_EXPR",
|
379
|
+
# ["FOLLOWED_BY_EXPR",
|
380
|
+
# ["FOLLOWED_BY_ITEM", ["every", ["PATTERN_FILTER_EXPR", "a", "EventA"]]],
|
381
|
+
# ["FOLLOWED_BY_ITEM",
|
382
|
+
# ["GUARD_EXPR",
|
383
|
+
# ["PATTERN_FILTER_EXPR",
|
384
|
+
# "b",
|
385
|
+
# "EventA",
|
386
|
+
# ["EVAL_EQUALS_EXPR",
|
387
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "name"]],
|
388
|
+
# ["EVENT_PROP_EXPR",
|
389
|
+
# ["EVENT_PROP_SIMPLE", "a"],
|
390
|
+
# ["EVENT_PROP_SIMPLE", "name"]]],
|
391
|
+
# ["EVAL_EQUALS_EXPR",
|
392
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "type"]],
|
393
|
+
# "'TYPE'"]],
|
394
|
+
# "timer",
|
395
|
+
# "within",
|
396
|
+
# ["TIME_PERIOD", ["MINUTE_PART", "1"]]]]]],
|
397
|
+
# ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["HOUR_PART", "2"]]]],
|
398
|
+
|
399
|
+
def targets
|
400
|
+
self.listup(:pattern).map(&:target)
|
401
|
+
end
|
402
|
+
|
403
|
+
def aliases
|
404
|
+
self.listup(:pattern).map{|p| [ p.alias, p.target ] }
|
405
|
+
end
|
406
|
+
|
407
|
+
def fields(default_target=nil, known_targets_aliases=[])
|
408
|
+
self.listup(:pattern).map{|p| p.fields(default_target, known_targets_aliases) }.reduce(&:+) || []
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
class ASTPatternNode < ASTNode
|
413
|
+
# ["PATTERN_FILTER_EXPR", "a", "EventA"]
|
414
|
+
#
|
415
|
+
# ["PATTERN_FILTER_EXPR",
|
416
|
+
# "b",
|
417
|
+
# "EventA",
|
418
|
+
# ["EVAL_EQUALS_EXPR",
|
419
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "name"]],
|
420
|
+
# ["EVENT_PROP_EXPR",
|
421
|
+
# ["EVENT_PROP_SIMPLE", "a"],
|
422
|
+
# ["EVENT_PROP_SIMPLE", "name"]]],
|
423
|
+
# ["EVAL_EQUALS_EXPR",
|
424
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "type"]],
|
425
|
+
# "'TYPE'"]],
|
426
|
+
|
250
427
|
def nodetype?(*sym)
|
251
|
-
sym.include?(:
|
428
|
+
sym.include?(:pattern)
|
252
429
|
end
|
253
430
|
|
254
431
|
def target
|
255
|
-
|
432
|
+
children[1].name
|
256
433
|
end
|
257
434
|
|
258
435
|
def alias
|
259
|
-
|
436
|
+
children[0].name
|
260
437
|
end
|
261
438
|
|
262
439
|
def fields(default_target=nil, known_targets_aliases=[])
|
263
440
|
this_target = self.target
|
264
|
-
self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target,known_targets_aliases)}.reduce(&:+) || []
|
441
|
+
self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target, known_targets_aliases) }.reduce(&:+) || []
|
265
442
|
end
|
266
443
|
end
|
267
444
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'norikra/logger'
|
2
|
+
include Norikra::Log
|
3
|
+
|
4
|
+
require 'norikra/rpc'
|
5
|
+
|
6
|
+
class Norikra::RPC::Gatekeeper
|
7
|
+
# handler of "GET /" to respond for monitor tools
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
if env["REQUEST_METHOD"] == 'GET' && env["PATH_INFO"] == '/'
|
14
|
+
return [ 200, {'Content-Type' => 'text/plain'}, ['Norikra alive!'] ]
|
15
|
+
end
|
16
|
+
return @app.call(env)
|
17
|
+
end
|
18
|
+
end
|
data/lib/norikra/rpc/http.rb
CHANGED
@@ -3,6 +3,7 @@ require 'rack/builder'
|
|
3
3
|
require 'msgpack-rpc-over-http-jruby'
|
4
4
|
|
5
5
|
require_relative 'handler'
|
6
|
+
require_relative 'gatekeeper'
|
6
7
|
|
7
8
|
require 'norikra/logger'
|
8
9
|
include Norikra::Log
|
@@ -25,6 +26,7 @@ module Norikra::RPC
|
|
25
26
|
@threads = opts[:threads] || DEFAULT_THREADS
|
26
27
|
handler = Norikra::RPC::Handler.new(@engine)
|
27
28
|
@app = Rack::Builder.new {
|
29
|
+
use Norikra::RPC::Gatekeeper
|
28
30
|
run MessagePack::RPCOverHTTP::Server.app(handler)
|
29
31
|
}
|
30
32
|
end
|
data/lib/norikra/server.rb
CHANGED
@@ -43,13 +43,13 @@ module Norikra
|
|
43
43
|
:web => { threads: 2 },
|
44
44
|
}
|
45
45
|
|
46
|
-
def self.threading_configuration(conf
|
46
|
+
def self.threading_configuration(conf)
|
47
47
|
threads = case conf[:predefined]
|
48
48
|
when :micro then MICRO_PREDEFINED
|
49
49
|
when :small then SMALL_PREDEFINED
|
50
50
|
when :middle then MIDDLE_PREDEFINED
|
51
51
|
when :large then LARGE_PREDEFINED
|
52
|
-
else
|
52
|
+
else MICRO_PREDEFINED
|
53
53
|
end
|
54
54
|
[:inbound, :outbound, :route_exec, :timer_exec].each do |type|
|
55
55
|
[:threads, :capacity].each do |item|
|
@@ -61,15 +61,15 @@ module Norikra
|
|
61
61
|
threads
|
62
62
|
end
|
63
63
|
|
64
|
-
def self.log_configuration(conf
|
65
|
-
logconf =
|
64
|
+
def self.log_configuration(conf)
|
65
|
+
logconf = { level: nil, dir: nil, filesize: nil, backups: nil }
|
66
66
|
[:level, :dir, :filesize, :backups].each do |sym|
|
67
67
|
logconf[sym] = conf[sym] if conf[sym]
|
68
68
|
end
|
69
69
|
logconf
|
70
70
|
end
|
71
71
|
|
72
|
-
def initialize(
|
72
|
+
def initialize(server_options, conf={})
|
73
73
|
if conf[:daemonize]
|
74
74
|
outfile_path = conf[:daemonize][:outfile] || File.join(conf[:log][:dir], 'norikra.out')
|
75
75
|
Dir.chdir("/")
|
@@ -89,12 +89,12 @@ module Norikra
|
|
89
89
|
nil
|
90
90
|
end
|
91
91
|
|
92
|
-
@host = host ||
|
93
|
-
@port = port ||
|
94
|
-
@ui_port =
|
92
|
+
@host = server_options[:host] || Norikra::RPC::HTTP::DEFAULT_LISTEN_HOST
|
93
|
+
@port = server_options[:port] || Norikra::RPC::HTTP::DEFAULT_LISTEN_PORT
|
94
|
+
@ui_port = server_options[:ui_port] || Norikra::WebUI::HTTP::DEFAULT_LISTEN_PORT
|
95
95
|
|
96
|
-
@thread_conf = self.class.threading_configuration(conf[:thread]
|
97
|
-
@log_conf = self.class.log_configuration(conf[:log]
|
96
|
+
@thread_conf = self.class.threading_configuration(conf[:thread])
|
97
|
+
@log_conf = self.class.log_configuration(conf[:log])
|
98
98
|
|
99
99
|
Norikra::Log.init(@log_conf[:level], @log_conf[:dir], {:filesize => @log_conf[:filesize], :backups => @log_conf[:backups]})
|
100
100
|
|
@@ -144,7 +144,7 @@ module Norikra
|
|
144
144
|
info "Norikra server started."
|
145
145
|
|
146
146
|
shutdown_proc = ->{ @running = false }
|
147
|
-
# JVM uses SIGQUIT for thread/heap state dumping
|
147
|
+
# JVM uses SIGQUIT / SIGUSR1 for thread/heap state dumping
|
148
148
|
[:INT, :TERM].each do |s|
|
149
149
|
Signal.trap(s, shutdown_proc)
|
150
150
|
end
|
@@ -155,14 +155,14 @@ module Norikra
|
|
155
155
|
else
|
156
156
|
nil
|
157
157
|
end
|
158
|
-
Signal.trap(:
|
158
|
+
Signal.trap(:USR2, ->{ @dump_stats = true })
|
159
159
|
|
160
|
-
#TODO: SIGHUP?(dynamic plugin loading?)
|
160
|
+
#TODO: SIGHUP?(dynamic plugin loading?)
|
161
161
|
|
162
162
|
while @running
|
163
163
|
sleep 0.3
|
164
164
|
|
165
|
-
if @
|
165
|
+
if @stats_path && !@stats_suppress_dump
|
166
166
|
if @dump_stats || (@dump_next_time && Time.now > @dump_next_time)
|
167
167
|
dump_stats
|
168
168
|
@dump_stats = false
|
@@ -203,21 +203,7 @@ module Norikra
|
|
203
203
|
end
|
204
204
|
|
205
205
|
def dump_stats
|
206
|
-
|
207
|
-
host: @host,
|
208
|
-
port: @port,
|
209
|
-
threads: @thread_conf,
|
210
|
-
log: @log_conf,
|
211
|
-
targets: @engine.targets.map{|t|
|
212
|
-
{
|
213
|
-
:name => t.name,
|
214
|
-
:fields => @engine.typedef_manager.dump_target(t.name),
|
215
|
-
:auto_field => t.auto_field
|
216
|
-
}
|
217
|
-
},
|
218
|
-
queries: @engine.queries.map(&:dump)
|
219
|
-
)
|
220
|
-
stats.dump(@stats_path)
|
206
|
+
Norikra::Stats.generate(@engine).dump(@stats_path)
|
221
207
|
info "Current status saved", :path => @stats_path
|
222
208
|
end
|
223
209
|
end
|
data/lib/norikra/stats.rb
CHANGED
@@ -2,27 +2,40 @@ require 'json'
|
|
2
2
|
|
3
3
|
module Norikra
|
4
4
|
class Stats
|
5
|
-
attr_accessor :host, :port, :ui_port, :threads, :log
|
6
5
|
attr_accessor :targets, :queries
|
7
6
|
|
7
|
+
def self.generate(engine)
|
8
|
+
Norikra::Stats.new(
|
9
|
+
targets: engine.targets.map{|t|
|
10
|
+
{
|
11
|
+
:name => t.name,
|
12
|
+
:fields => engine.typedef_manager.dump_target(t.name),
|
13
|
+
:auto_field => t.auto_field
|
14
|
+
}
|
15
|
+
},
|
16
|
+
queries: engine.queries.map(&:dump)
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
8
20
|
def initialize(opts={})
|
9
|
-
@host = opts[:host]
|
10
|
-
@port = opts[:port]
|
11
|
-
@ui_port = opts[:ui_port]
|
12
|
-
@threads = opts[:threads]
|
13
|
-
@log = opts[:log]
|
14
21
|
@targets = opts[:targets] || []
|
15
22
|
@queries = opts[:queries] || []
|
16
23
|
end
|
17
24
|
|
18
25
|
def to_hash
|
19
|
-
{
|
26
|
+
{targets: @targets, queries: @queries}
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_json
|
30
|
+
JSON.pretty_generate(self.to_hash)
|
20
31
|
end
|
21
32
|
|
22
33
|
def dump(path)
|
23
|
-
|
24
|
-
|
34
|
+
tmp_path = path + '.tmp'
|
35
|
+
File.open(tmp_path, 'w') do |file|
|
36
|
+
file.write(self.to_json)
|
25
37
|
end
|
38
|
+
File.rename(tmp_path, path)
|
26
39
|
end
|
27
40
|
|
28
41
|
def self.load(path)
|
data/lib/norikra/version.rb
CHANGED
@@ -4,6 +4,8 @@ require 'norikra/logger'
|
|
4
4
|
|
5
5
|
require 'norikra/webui'
|
6
6
|
|
7
|
+
require 'norikra/stats'
|
8
|
+
|
7
9
|
require 'sinatra/base'
|
8
10
|
require 'sinatra/json'
|
9
11
|
require 'erubis'
|
@@ -87,6 +89,14 @@ class Norikra::WebUI::Handler < Sinatra::Base
|
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
92
|
+
post '/close' do
|
93
|
+
target_name = params[:target]
|
94
|
+
logging(:manage, :close, [target_name]) do
|
95
|
+
engine.close(target_name)
|
96
|
+
redirect '/'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
90
100
|
post '/register' do
|
91
101
|
query_name,query_group,expression = params[:query_name], params[:query_group], params[:expression]
|
92
102
|
|
@@ -120,6 +130,15 @@ class Norikra::WebUI::Handler < Sinatra::Base
|
|
120
130
|
end
|
121
131
|
end
|
122
132
|
|
133
|
+
get '/stat/dump' do
|
134
|
+
logging(:show, :stat_dump) do
|
135
|
+
stats = Norikra::Stats.generate(engine)
|
136
|
+
date = Time.now.strftime('%Y-%m-%d')
|
137
|
+
attachment "stats.dump.#{date}.json"
|
138
|
+
stats.to_json
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
123
142
|
get '/json/query/:name' do
|
124
143
|
query_name = params[:name]
|
125
144
|
logging(:show, :json_query, [query_name]) do
|
data/spec/field_spec.rb
CHANGED
@@ -275,5 +275,18 @@ describe Norikra::Field do
|
|
275
275
|
f.define_value_accessor('foo.bar.1.baz', true)
|
276
276
|
expect(f.value({'foo' => {'bar' => [{'baz' => 'value1'}, {'baz' => 'value2'}]}})).to eql('value2')
|
277
277
|
end
|
278
|
+
|
279
|
+
it 'defines chain value accessor with field name including escaped field' do
|
280
|
+
f = Norikra::Field.new('foo', 'hash')
|
281
|
+
f.define_value_accessor('foo.bar_baz.bar_baz', true)
|
282
|
+
data = {
|
283
|
+
'foo' => {
|
284
|
+
'bar baz' => {
|
285
|
+
'bar/baz' => 'ichi',
|
286
|
+
}
|
287
|
+
}
|
288
|
+
}
|
289
|
+
expect(f.value(data)).to eql('ichi')
|
290
|
+
end
|
278
291
|
end
|
279
292
|
end
|
data/spec/query_spec.rb
CHANGED
@@ -24,6 +24,23 @@ describe Norikra::Query do
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
context 'with top-level built-in functions' do
|
28
|
+
it 'returns query instances collectly parsed' do
|
29
|
+
expression = 'SELECT rate(10) FROM TestTable output snapshot every 2 sec'
|
30
|
+
q = Norikra::Query.new(
|
31
|
+
:name => 'TestTable query1', :expression => expression
|
32
|
+
)
|
33
|
+
expect(q.name).to eql('TestTable query1')
|
34
|
+
expect(q.group).to be_nil
|
35
|
+
expect(q.expression).to eql(expression)
|
36
|
+
expect(q.targets).to eql(['TestTable'])
|
37
|
+
|
38
|
+
expect(q.fields).to eql([])
|
39
|
+
expect(q.fields('TestTable')).to eql([])
|
40
|
+
expect(q.fields(nil)).to eql([])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
27
44
|
context 'with order by' do
|
28
45
|
it 'returns query instances, collectly parsed, without AS_names for fields' do
|
29
46
|
expression = 'SELECT name.string, count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND size > 100 and param.length() > 0 GROUP BY name.string ORDER BY cnt'
|
@@ -159,6 +176,24 @@ describe Norikra::Query do
|
|
159
176
|
expect(q.fields(nil)).to eql([])
|
160
177
|
end
|
161
178
|
end
|
179
|
+
|
180
|
+
context 'with query with patterns' do
|
181
|
+
it 'returns query instances collectly parsed' do
|
182
|
+
|
183
|
+
expression = "select a.name, a.content, b.content from pattern [every a=EventA -> b=EventA(name = a.name, type = 'TYPE') where timer:within(1 min)].win:time(2 hour) where a.source in ('A', 'B')"
|
184
|
+
q = Norikra::Query.new(
|
185
|
+
:name => 'TestTable query9', :expression => expression
|
186
|
+
)
|
187
|
+
expect(q.name).to eql('TestTable query9')
|
188
|
+
expect(q.group).to be_nil
|
189
|
+
expect(q.expression).to eql(expression)
|
190
|
+
expect(q.targets).to eql(['EventA'])
|
191
|
+
expect(q.aliases).to eql(['a', 'b'])
|
192
|
+
expect(q.fields).to eql(['name', 'content', 'type', 'source'].sort)
|
193
|
+
expect(q.fields('EventA')).to eql(['name', 'content', 'type', 'source'].sort)
|
194
|
+
expect(q.fields(nil)).to eql([])
|
195
|
+
end
|
196
|
+
end
|
162
197
|
end
|
163
198
|
|
164
199
|
describe '#dup' do
|
data/spec/stats_spec.rb
CHANGED
@@ -9,11 +9,6 @@ describe Norikra::Stats do
|
|
9
9
|
describe '#to_hash' do
|
10
10
|
it 'returns internal stats as hash with symbolized keys' do
|
11
11
|
args = {
|
12
|
-
host: nil,
|
13
|
-
port: nil,
|
14
|
-
ui_port: nil,
|
15
|
-
threads: Norikra::Server::SMALL_PREDEFINED,
|
16
|
-
log: {level: nil, dir: nil, filesize: nil, backups: nil},
|
17
12
|
targets: [],
|
18
13
|
queries: [],
|
19
14
|
}
|
@@ -27,11 +22,6 @@ describe Norikra::Stats do
|
|
27
22
|
Dir.mktmpdir do |dir|
|
28
23
|
File.open("#{dir}/stats.json", 'w') do |file|
|
29
24
|
args = {
|
30
|
-
host: nil,
|
31
|
-
port: nil,
|
32
|
-
ui_port: nil,
|
33
|
-
threads: Norikra::Server::LARGE_PREDEFINED,
|
34
|
-
log: {level: 'WARN', dir: '/var/log/norikra', filesize: '50MB', backups: 300},
|
35
25
|
targets: [
|
36
26
|
{ name: 'test1', fields: { id: { name: 'id', type: 'int', optional: false}, data: { name: 'data', type: 'string', optional: true } } },
|
37
27
|
],
|
data/views/index.erb
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
<div class="progress-bar progress-bar-<%= usage_bar_color %>" style="width: <%= used_p.to_i %>%;">
|
15
15
|
<span class="sr-only"><%= used_p %> % used</span>
|
16
16
|
</div>
|
17
|
-
<div class="progress-bar progress-bar-success" style="width: <%= committed_p.to_i %>%;">
|
17
|
+
<div class="progress-bar progress-bar-success" style="width: <%= (committed_p - used_p).to_i %>%;">
|
18
18
|
<span class="sr-only"><%= committed_p %> % used</span>
|
19
19
|
</div>
|
20
20
|
</div>
|
@@ -151,8 +151,8 @@
|
|
151
151
|
|
152
152
|
<% if targets.size > 0 %>
|
153
153
|
<table class="table">
|
154
|
-
<tr><th>Target</th><th>Auto field</th><th>Fields</th><th
|
155
|
-
<% targets.
|
154
|
+
<tr><th>Target</th><th>Auto field</th><th>Fields</th><th>modified</th><th></th></tr>
|
155
|
+
<% targets.each_with_index do |target, index| %>
|
156
156
|
<tr>
|
157
157
|
<td><%= target[:name] %></td>
|
158
158
|
<td><%= target[:auto_field] %></td>
|
@@ -163,11 +163,44 @@
|
|
163
163
|
(lazy target)
|
164
164
|
<% end %>
|
165
165
|
</td>
|
166
|
-
<td style="font-size: small; color: #c0c0c0;">TODO: change auto_fields, reserve fields, remove targets</td>
|
167
166
|
<td style="text-align: right;"><%= target[:modified] %></td>
|
167
|
+
<td style="font-size: small; color: #c0c0c0;">
|
168
|
+
<a class="btn btn-danger btn-xs" data-toggle="modal" href="#removeTarget<%= index %>">
|
169
|
+
<span class="glyphicon glyphicon-trash"></span>
|
170
|
+
</a>
|
171
|
+
<div class="modal fade"
|
172
|
+
id="removeTarget<%= index %>"
|
173
|
+
tabindex="-1" role="dialog" aria-labelledby="removeTargetLabel<%= index %>" aria-hidden="true">
|
174
|
+
<div class="modal-dialog">
|
175
|
+
<div class="modal-content">
|
176
|
+
<div class="modal-header">
|
177
|
+
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
178
|
+
<h4 class="modal-title">Remove Target <%= target[:name] %></h4>
|
179
|
+
</div>
|
180
|
+
<div class="modal-body">
|
181
|
+
<p>Remove all definitions and queries?</p>
|
182
|
+
<p>name: <%= target[:name] %></p>
|
183
|
+
</div>
|
184
|
+
<div class="modal-footer">
|
185
|
+
<form class="form-inline" action="/close" method="POST">
|
186
|
+
<input type="hidden" name="target" value="<%= target[:name] %>" />
|
187
|
+
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
188
|
+
<button type="submit" class="btn btn-danger">Remove</button>
|
189
|
+
</form>
|
190
|
+
</div>
|
191
|
+
</div><!-- /.modal-content -->
|
192
|
+
</div><!-- /.modal-dialog -->
|
193
|
+
</div><!-- /.modal -->
|
194
|
+
</td>
|
168
195
|
</tr>
|
169
196
|
<% end %>
|
170
197
|
</table>
|
171
198
|
<% else %>
|
172
199
|
<p>No targets found.</p>
|
173
200
|
<% end %>
|
201
|
+
|
202
|
+
<div class="page-header">
|
203
|
+
<h1 id="stats">Target/Query stat dump</h1>
|
204
|
+
</div>
|
205
|
+
|
206
|
+
<a href="/stat/dump" class="btn btn-info btn-lg" role="button">Download JSON</a>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: norikra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- TAGOMORI Satoshi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mizuno
|
@@ -292,6 +292,7 @@ files:
|
|
292
292
|
- lib/norikra/query/ast.rb
|
293
293
|
- lib/norikra/rpc.rb
|
294
294
|
- lib/norikra/rpc/error.rb
|
295
|
+
- lib/norikra/rpc/gatekeeper.rb
|
295
296
|
- lib/norikra/rpc/handler.rb
|
296
297
|
- lib/norikra/rpc/http.rb
|
297
298
|
- lib/norikra/rubyudf.rb
|