norikra 0.1.4-java → 0.1.5-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|