ls4 0.9.0
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.
- data/AUTHORS +1 -0
- data/COPYING +661 -0
- data/ChangeLog +9 -0
- data/NOTICE +8 -0
- data/README.rdoc +61 -0
- data/bin/ls4-cs +3 -0
- data/bin/ls4-ds +3 -0
- data/bin/ls4-gw +3 -0
- data/bin/ls4-standalone +3 -0
- data/bin/ls4cmd +3 -0
- data/bin/ls4ctl +3 -0
- data/bin/ls4rpc +3 -0
- data/bin/ls4stat +3 -0
- data/bin/ls4top +3 -0
- data/lib/ls4/command/cmd.rb +241 -0
- data/lib/ls4/command/cs.rb +190 -0
- data/lib/ls4/command/ctl.rb +278 -0
- data/lib/ls4/command/ds.rb +335 -0
- data/lib/ls4/command/gw.rb +256 -0
- data/lib/ls4/command/rpc.rb +172 -0
- data/lib/ls4/command/standalone.rb +318 -0
- data/lib/ls4/command/stat.rb +244 -0
- data/lib/ls4/command/top.rb +291 -0
- data/lib/ls4/default.rb +26 -0
- data/lib/ls4/lib/cclog.rb +220 -0
- data/lib/ls4/lib/ebus.rb +553 -0
- data/lib/ls4/lib/vbcode.rb +228 -0
- data/lib/ls4/logic/fault_detector.rb +212 -0
- data/lib/ls4/logic/membership.rb +253 -0
- data/lib/ls4/logic/node.rb +66 -0
- data/lib/ls4/logic/okey.rb +45 -0
- data/lib/ls4/logic/tsv_data.rb +81 -0
- data/lib/ls4/logic/weight.rb +166 -0
- data/lib/ls4/service/balance.rb +62 -0
- data/lib/ls4/service/base.rb +29 -0
- data/lib/ls4/service/bus.rb +37 -0
- data/lib/ls4/service/config.rb +63 -0
- data/lib/ls4/service/config_cs.rb +33 -0
- data/lib/ls4/service/config_ds.rb +56 -0
- data/lib/ls4/service/config_gw.rb +42 -0
- data/lib/ls4/service/data_client.rb +122 -0
- data/lib/ls4/service/data_server.rb +168 -0
- data/lib/ls4/service/data_server_url.rb +83 -0
- data/lib/ls4/service/gateway.rb +375 -0
- data/lib/ls4/service/gateway_ro.rb +91 -0
- data/lib/ls4/service/gw_http.rb +821 -0
- data/lib/ls4/service/heartbeat.rb +182 -0
- data/lib/ls4/service/log.rb +81 -0
- data/lib/ls4/service/master_select.rb +148 -0
- data/lib/ls4/service/mds.rb +292 -0
- data/lib/ls4/service/mds_cache.rb +294 -0
- data/lib/ls4/service/mds_cache_mem.rb +63 -0
- data/lib/ls4/service/mds_cache_memcached.rb +65 -0
- data/lib/ls4/service/mds_ha.rb +176 -0
- data/lib/ls4/service/mds_memcache.rb +209 -0
- data/lib/ls4/service/mds_tc.rb +508 -0
- data/lib/ls4/service/mds_tt.rb +472 -0
- data/lib/ls4/service/membership.rb +331 -0
- data/lib/ls4/service/process.rb +90 -0
- data/lib/ls4/service/rpc.rb +50 -0
- data/lib/ls4/service/rpc_cs.rb +101 -0
- data/lib/ls4/service/rpc_ds.rb +96 -0
- data/lib/ls4/service/rpc_gw.rb +255 -0
- data/lib/ls4/service/rts.rb +94 -0
- data/lib/ls4/service/rts_file.rb +76 -0
- data/lib/ls4/service/rts_memory.rb +55 -0
- data/lib/ls4/service/slave.rb +132 -0
- data/lib/ls4/service/stat.rb +91 -0
- data/lib/ls4/service/stat_cs.rb +25 -0
- data/lib/ls4/service/stat_ds.rb +40 -0
- data/lib/ls4/service/stat_gw.rb +25 -0
- data/lib/ls4/service/storage.rb +116 -0
- data/lib/ls4/service/storage_dir.rb +201 -0
- data/lib/ls4/service/sync.rb +206 -0
- data/lib/ls4/service/time_check.rb +80 -0
- data/lib/ls4/service/ulog.rb +159 -0
- data/lib/ls4/service/ulog_file.rb +398 -0
- data/lib/ls4/service/ulog_memory.rb +53 -0
- data/lib/ls4/service/weight.rb +134 -0
- data/lib/ls4/version.rb +5 -0
- data/test/01_add_get_remove.rt +84 -0
- data/test/02_read.rt +61 -0
- data/test/03_getd_readd.rt +69 -0
- data/test/04_version_time.rt +170 -0
- data/test/05_version_name.rt +161 -0
- data/test/06_http_get_set_remove_1.rt +119 -0
- data/test/07_http_get_set_remove_2.rt +116 -0
- data/test/08_read_only_time.rt +177 -0
- data/test/09_read_only_name.rt +173 -0
- data/test/10_http_get_set_remove_3.rt +73 -0
- data/test/11_mds_cache_memcached.rt +88 -0
- data/test/12_mds_cache_local_memory.rt +86 -0
- data/test/13_memcache_mds.rt +84 -0
- data/test/14_delete.rt +63 -0
- data/test/15_standalone.rt +71 -0
- data/test/chukan.rb +516 -0
- data/test/common.rb +250 -0
- data/test/load_test.rb +79 -0
- data/test/load_test_offload.rb +86 -0
- metadata +295 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
#
|
2
|
+
# LS4
|
3
|
+
# Copyright (C) 2010-2011 FURUHASHI Sadayuki
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Affero General Public License as
|
7
|
+
# published by the Free Software Foundation, either version 3 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Affero General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
module LS4
|
19
|
+
|
20
|
+
|
21
|
+
class ReadOnlyGatewayService < GatewayService
|
22
|
+
def initialize
|
23
|
+
@rover = ConfigBus.read_only_version
|
24
|
+
end
|
25
|
+
|
26
|
+
def rpc_get_impl(version, key)
|
27
|
+
super(version||@rover, key)
|
28
|
+
end
|
29
|
+
|
30
|
+
def rpc_get_data_impl(version, key)
|
31
|
+
super(version||@rover, key)
|
32
|
+
end
|
33
|
+
|
34
|
+
def rpc_get_attrs_impl(version, key)
|
35
|
+
super(version||@rover, key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def rpc_read_impl(version, key, offset, size)
|
39
|
+
super(version||@rover, key, offset, size)
|
40
|
+
end
|
41
|
+
|
42
|
+
def rpc_add(key, data, attrs)
|
43
|
+
raise_read_only_error
|
44
|
+
end
|
45
|
+
|
46
|
+
def rpc_add_data(key, data)
|
47
|
+
raise_read_only_error
|
48
|
+
end
|
49
|
+
|
50
|
+
def rpc_addv(vname, key, data, attrs)
|
51
|
+
raise_read_only_error
|
52
|
+
end
|
53
|
+
|
54
|
+
def rpc_addv_data(vname, key, attrs)
|
55
|
+
raise_read_only_error
|
56
|
+
end
|
57
|
+
|
58
|
+
def rpc_update_attrs(key, attrs)
|
59
|
+
raise_read_only_error
|
60
|
+
end
|
61
|
+
|
62
|
+
def rpc_delete(key)
|
63
|
+
raise_read_only_error
|
64
|
+
end
|
65
|
+
|
66
|
+
def rpc_deletet(vtime, key)
|
67
|
+
raise_read_only_error
|
68
|
+
end
|
69
|
+
|
70
|
+
def rpc_deletev(vname, key)
|
71
|
+
raise_read_only_error
|
72
|
+
end
|
73
|
+
|
74
|
+
def rpc_remove(key)
|
75
|
+
raise_read_only_error
|
76
|
+
end
|
77
|
+
|
78
|
+
def rpc_url_impl(version, key)
|
79
|
+
super(version||@rover, key)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def raise_read_only_error
|
84
|
+
ar = MessagePack::RPC::AsyncResult.new
|
85
|
+
ar.error("read-only mode")
|
86
|
+
ar
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,821 @@
|
|
1
|
+
#
|
2
|
+
# LS4
|
3
|
+
# Copyright (C) 2010-2011 FURUHASHI Sadayuki
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU Affero General Public License as
|
7
|
+
# published by the Free Software Foundation, either version 3 of the
|
8
|
+
# License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU Affero General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Affero General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
module LS4
|
19
|
+
|
20
|
+
|
21
|
+
class HTTPGatewayService < Service
|
22
|
+
def self.open!
|
23
|
+
require 'rack'
|
24
|
+
require 'webrick'
|
25
|
+
require 'erb'
|
26
|
+
require 'json'
|
27
|
+
require 'thread'
|
28
|
+
instance.init(ConfigBus.http_gateway_address, ConfigBus.http_gateway_error_template_file)
|
29
|
+
end
|
30
|
+
|
31
|
+
DEFAULT_FORMAT = 'json'
|
32
|
+
|
33
|
+
DEFAULT_TEMPLATE =<<EOF
|
34
|
+
<html>
|
35
|
+
<head>
|
36
|
+
<title><%= code %> <%= title %></title>
|
37
|
+
</head>
|
38
|
+
<body>
|
39
|
+
<center><h1><%= code %> <%= title %></h1></center>
|
40
|
+
<hr>
|
41
|
+
<center>LS4 <%= LS4::VERSION %></center>
|
42
|
+
</body>
|
43
|
+
</html>
|
44
|
+
EOF
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@thread = nil
|
48
|
+
@server = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def init(addr, tmplfile)
|
52
|
+
if tmplfile
|
53
|
+
erb = File.read(tmplfile)
|
54
|
+
else
|
55
|
+
erb = DEFAULT_TEMPLATE
|
56
|
+
end
|
57
|
+
@erb = ERB.new(erb)
|
58
|
+
|
59
|
+
opt = {
|
60
|
+
:BindAddress => addr.host,
|
61
|
+
:Port => addr.port,
|
62
|
+
}
|
63
|
+
@server = ::WEBrick::HTTPServer.new(opt)
|
64
|
+
me = self
|
65
|
+
|
66
|
+
app = ::Rack::URLMap.new({
|
67
|
+
'/data' => Proc.new {|env| me.call_data(env) },
|
68
|
+
'/attrs' => Proc.new {|env| me.call_attrs(env) },
|
69
|
+
'/api' => Proc.new {|env| me.call_rpc(env) },
|
70
|
+
'/direct' => Proc.new {|env| me.call_direct(env) },
|
71
|
+
'/redirect' => Proc.new {|env| me.call_redirect(env) },
|
72
|
+
})
|
73
|
+
|
74
|
+
@server.mount("/", ::Rack::Handler::WEBrick, app)
|
75
|
+
@thread = Thread.new do
|
76
|
+
@server.start
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# data/<key>
|
81
|
+
# get(data/key) => get_data
|
82
|
+
# vtime=i => gett_data
|
83
|
+
# vname=s => getv_data
|
84
|
+
# post(data/key, data) => add_data
|
85
|
+
# vname=s => addv_data
|
86
|
+
# attrs=formated => add/addv
|
87
|
+
# format=json/msgpack/tsv
|
88
|
+
# put(data/key, data) => add_data
|
89
|
+
# vname=s => addv_data
|
90
|
+
# attrs=formated => add/addv
|
91
|
+
# format=json/msgpack/tsv
|
92
|
+
# TODO delete(data/key)
|
93
|
+
def call_data(env)
|
94
|
+
case env['REQUEST_METHOD']
|
95
|
+
when 'GET'
|
96
|
+
http_data_get(env)
|
97
|
+
when 'POST'
|
98
|
+
http_data_post(env)
|
99
|
+
when 'PUT'
|
100
|
+
http_data_put(env)
|
101
|
+
else
|
102
|
+
return html_response(405, 'Method Not Allowed')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def http_data_get(env)
|
107
|
+
request = ::Rack::Request.new(env)
|
108
|
+
key = get_key_path_info(env)
|
109
|
+
vtime = optional_int(request, 'vtime')
|
110
|
+
if vtime
|
111
|
+
check_request(request, %w[vtime])
|
112
|
+
else
|
113
|
+
vname = optional_str(request, 'vname')
|
114
|
+
check_request(request, %w[vname])
|
115
|
+
end
|
116
|
+
|
117
|
+
if vtime
|
118
|
+
data = submit(GWRPCBus, :gett_data, vtime, key)
|
119
|
+
elsif vname
|
120
|
+
data = submit(GWRPCBus, :getv_data, vname, key)
|
121
|
+
else
|
122
|
+
data = submit(GWRPCBus, :get_data, key)
|
123
|
+
end
|
124
|
+
|
125
|
+
if data
|
126
|
+
body = [data]
|
127
|
+
return [200, {'Content-Type'=>'application/octet-stream'}, body]
|
128
|
+
else
|
129
|
+
if vtime
|
130
|
+
return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
|
131
|
+
elsif vname
|
132
|
+
return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
|
133
|
+
else
|
134
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def http_data_post(env)
|
140
|
+
request = ::Rack::Request.new(env)
|
141
|
+
key = get_key_path_info(env)
|
142
|
+
data = require_str(request, 'data')
|
143
|
+
vname = optional_str(request, 'vname')
|
144
|
+
attrs = optional_str(request, 'attrs')
|
145
|
+
if attrs
|
146
|
+
format = optional_str(request, 'format')
|
147
|
+
format ||= DEFAULT_FORMAT
|
148
|
+
check_request(request, %w[data vname attrs format])
|
149
|
+
else
|
150
|
+
check_request(request, %w[data vname])
|
151
|
+
end
|
152
|
+
|
153
|
+
if attrs
|
154
|
+
attrs = parse_attrs(attrs, format)
|
155
|
+
unless attrs
|
156
|
+
return html_response(400, 'Bad Request', "unknown format `#{format}'")
|
157
|
+
end
|
158
|
+
if vname
|
159
|
+
okey = submit(GWRPCBus, :addv, vname, key, data, attrs)
|
160
|
+
else
|
161
|
+
okey = submit(GWRPCBus, :add, key, data, attrs)
|
162
|
+
end
|
163
|
+
else
|
164
|
+
if vname
|
165
|
+
okey = submit(GWRPCBus, :addv_data, vname, key, data)
|
166
|
+
else
|
167
|
+
okey = submit(GWRPCBus, :add_data, key, data)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# FIXME return okey?
|
172
|
+
return html_response(200, 'OK')
|
173
|
+
end
|
174
|
+
|
175
|
+
def http_data_put(env)
|
176
|
+
if env['HTTP_EXPECT'] == '100-continue'
|
177
|
+
return html_response(417, 'Exception Failed')
|
178
|
+
end
|
179
|
+
|
180
|
+
request = ::Rack::Request.new(env)
|
181
|
+
key = get_key_path_info(env)
|
182
|
+
vname = optional_str(request, 'vname')
|
183
|
+
attrs = optional_str(request, 'attrs')
|
184
|
+
if attrs
|
185
|
+
format = optional_str(request, 'format')
|
186
|
+
format ||= DEFAULT_FORMAT
|
187
|
+
check_request(request, %w[vname attrs format])
|
188
|
+
else
|
189
|
+
check_request(request, %w[vname])
|
190
|
+
end
|
191
|
+
|
192
|
+
data = env['rack.input'].read
|
193
|
+
|
194
|
+
if attrs
|
195
|
+
attrs = parse_attrs(attrs, format)
|
196
|
+
unless attrs
|
197
|
+
return html_response(400, 'Bad Request', "unknown format `#{format}'")
|
198
|
+
end
|
199
|
+
if vname
|
200
|
+
okey = submit(GWRPCBus, :addv, vname, key, data, attrs)
|
201
|
+
else
|
202
|
+
okey = submit(GWRPCBus, :add, key, data, attrs)
|
203
|
+
end
|
204
|
+
else
|
205
|
+
if vname
|
206
|
+
okey = submit(GWRPCBus, :addv_data, vname, key, data)
|
207
|
+
else
|
208
|
+
okey = submit(GWRPCBus, :add_data, key, data)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# FIXME return okey?
|
213
|
+
return html_response(202, 'Accepted')
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# attrs/<key>
|
218
|
+
# get(data/key) => get_attrs
|
219
|
+
# vtime=i => gett_attrs
|
220
|
+
# vname=s => getv_attrs
|
221
|
+
# format=json/msgpack/tsv
|
222
|
+
# post(attrs/key, json) => update_attrs
|
223
|
+
# put(attrs/key, json) => update_attrs
|
224
|
+
# TODO delete(data/key)
|
225
|
+
def call_attrs(env)
|
226
|
+
case env['REQUEST_METHOD']
|
227
|
+
when 'GET'
|
228
|
+
http_attrs_get(env)
|
229
|
+
when 'POST'
|
230
|
+
http_attrs_post(env)
|
231
|
+
when 'PUT'
|
232
|
+
http_attrs_put(env)
|
233
|
+
else
|
234
|
+
return html_response(405, 'Method Not Allowed')
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def http_attrs_get(env)
|
239
|
+
request = ::Rack::Request.new(env)
|
240
|
+
key = get_key_path_info(env)
|
241
|
+
format = optional_str(request, 'format')
|
242
|
+
format ||= DEFAULT_FORMAT
|
243
|
+
vtime = optional_int(request, 'vtime')
|
244
|
+
if vtime
|
245
|
+
check_request(request, %w[format vtime])
|
246
|
+
else
|
247
|
+
vname = optional_str(request, 'vname')
|
248
|
+
check_request(request, %w[format vname])
|
249
|
+
end
|
250
|
+
|
251
|
+
if vtime
|
252
|
+
attrs = submit(GWRPCBus, :gett_attrs, vtime, key)
|
253
|
+
elsif vname
|
254
|
+
attrs = submit(GWRPCBus, :getv_attrs, vname, key)
|
255
|
+
else
|
256
|
+
attrs = submit(GWRPCBus, :get_attrs, key)
|
257
|
+
end
|
258
|
+
|
259
|
+
if attrs
|
260
|
+
attrs, ct = format_attrs(attrs, format)
|
261
|
+
unless attrs
|
262
|
+
return html_response(400, 'Bad Request', "unknown format `#{format}'")
|
263
|
+
end
|
264
|
+
body = [attrs]
|
265
|
+
return [200, {'Content-Type'=>ct}, body]
|
266
|
+
else
|
267
|
+
if vtime
|
268
|
+
return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
|
269
|
+
elsif vname
|
270
|
+
return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
|
271
|
+
else
|
272
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def http_attrs_post(env)
|
278
|
+
request = ::Rack::Request.new(env)
|
279
|
+
key = get_key_path_info(env)
|
280
|
+
attrs = require_str(request, 'attrs')
|
281
|
+
format = optional_str(request, 'format')
|
282
|
+
format ||= DEFAULT_FORMAT
|
283
|
+
check_request(request, %w[attrs format])
|
284
|
+
|
285
|
+
attrs = parse_attrs(attrs, format)
|
286
|
+
unless attrs
|
287
|
+
return html_response(400, 'Bad Request', "unknown format `#{format}'")
|
288
|
+
end
|
289
|
+
|
290
|
+
okey = submit(GWRPCBus, :update_attrs, key, attrs)
|
291
|
+
|
292
|
+
if okey
|
293
|
+
return html_response(200, 'OK')
|
294
|
+
else
|
295
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def http_attrs_put(env)
|
300
|
+
if env['HTTP_EXPECT'] == '100-continue'
|
301
|
+
return html_response(417, 'Exception Failed')
|
302
|
+
end
|
303
|
+
|
304
|
+
request = ::Rack::Request.new(env)
|
305
|
+
key = get_key_path_info(env)
|
306
|
+
format = optional_str(request, 'format')
|
307
|
+
format ||= DEFAULT_FORMAT
|
308
|
+
check_request(request, %w[format])
|
309
|
+
|
310
|
+
attrs = env['rack.input'].read
|
311
|
+
attrs = parse_attrs(attrs, format)
|
312
|
+
unless attrs
|
313
|
+
return html_response(400, 'Bad Request', "unknown format `#{format}'")
|
314
|
+
end
|
315
|
+
|
316
|
+
okey = submit(GWRPCBus, :update_attrs, key, attrs)
|
317
|
+
|
318
|
+
if okey
|
319
|
+
return html_response(202, 'Accepted')
|
320
|
+
else
|
321
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
# redirect/<key>
|
327
|
+
# get(data/key) => url
|
328
|
+
# vtime=i => urlt
|
329
|
+
# vname=s => urlv
|
330
|
+
def call_redirect(env)
|
331
|
+
case env['REQUEST_METHOD']
|
332
|
+
when 'GET'
|
333
|
+
http_redirect_get(env)
|
334
|
+
else
|
335
|
+
return html_response(405, 'Method Not Allowed')
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def http_redirect_get(env)
|
340
|
+
request = ::Rack::Request.new(env)
|
341
|
+
key = get_key_path_info(env)
|
342
|
+
vtime = optional_int(request, 'vtime')
|
343
|
+
if vtime
|
344
|
+
check_request(request, %w[vtime])
|
345
|
+
else
|
346
|
+
vname = optional_str(request, 'vname')
|
347
|
+
check_request(request, %w[vname])
|
348
|
+
end
|
349
|
+
|
350
|
+
if vtime
|
351
|
+
url = submit(GWRPCBus, :urlt, vtime, key)
|
352
|
+
elsif vname
|
353
|
+
url = submit(GWRPCBus, :urlv, vname, key)
|
354
|
+
else
|
355
|
+
url = submit(GWRPCBus, :url, key)
|
356
|
+
end
|
357
|
+
|
358
|
+
if url
|
359
|
+
body = [url]
|
360
|
+
return [302, {'Content-Type'=>'text/plain', 'Location'=>url}, body]
|
361
|
+
else
|
362
|
+
if vtime
|
363
|
+
return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
|
364
|
+
elsif vname
|
365
|
+
return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
|
366
|
+
else
|
367
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
|
373
|
+
# api/<cmd>
|
374
|
+
# get(api/cmd?k=v)
|
375
|
+
# post(api/cmd?k=v)
|
376
|
+
# cmd:
|
377
|
+
# get_data key= [vtime=] [vname=]
|
378
|
+
# get_attrs key= [vtime=] [vname=] [format=]
|
379
|
+
# add key= [vname=] data= [attrs= [format=]]
|
380
|
+
# add_data (alias of add)
|
381
|
+
# update_attrs key= attrs= [format=]
|
382
|
+
# delete key= [vtimie=] [vname=]
|
383
|
+
# remove key=
|
384
|
+
# url key= [vtime=] [vname=]
|
385
|
+
#
|
386
|
+
def call_rpc(env)
|
387
|
+
m = env['REQUEST_METHOD']
|
388
|
+
if m != 'GET' && m != 'POST'
|
389
|
+
return html_response(405, 'Method Not Allowed')
|
390
|
+
end
|
391
|
+
case get_cmd_path_info(env)
|
392
|
+
when 'get_data'
|
393
|
+
http_rpc_get_data(env)
|
394
|
+
when 'get_attrs'
|
395
|
+
http_rpc_get_attrs(env)
|
396
|
+
when 'add'
|
397
|
+
http_rpc_add(env)
|
398
|
+
when 'add_data'
|
399
|
+
http_rpc_add(env)
|
400
|
+
when 'update_attrs'
|
401
|
+
http_rpc_update_attrs(env)
|
402
|
+
when 'delete'
|
403
|
+
http_rpc_delete(env)
|
404
|
+
when 'remove'
|
405
|
+
http_rpc_remove(env)
|
406
|
+
when 'url'
|
407
|
+
http_rpc_url(env)
|
408
|
+
else
|
409
|
+
return html_response(406, 'Not Acceptable Method')
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
def http_rpc_get_data(env)
|
414
|
+
request = ::Rack::Request.new(env)
|
415
|
+
key = require_str(request, 'key')
|
416
|
+
vtime = optional_int(request, 'vtime')
|
417
|
+
if vtime
|
418
|
+
check_request(request, %w[key vtime])
|
419
|
+
else
|
420
|
+
vname = optional_str(request, 'vname')
|
421
|
+
check_request(request, %w[key vname])
|
422
|
+
end
|
423
|
+
|
424
|
+
if vtime
|
425
|
+
data = submit(GWRPCBus, :gett_data, vtime, key)
|
426
|
+
elsif vname
|
427
|
+
data = submit(GWRPCBus, :getv_data, vname, key)
|
428
|
+
else
|
429
|
+
data = submit(GWRPCBus, :get_data, key)
|
430
|
+
end
|
431
|
+
|
432
|
+
if data
|
433
|
+
body = [data]
|
434
|
+
return [200, {'Content-Type'=>'application/octet-stream'}, body]
|
435
|
+
else
|
436
|
+
if vtime
|
437
|
+
return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
|
438
|
+
elsif vname
|
439
|
+
return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
|
440
|
+
else
|
441
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def http_rpc_get_attrs(env)
|
447
|
+
request = ::Rack::Request.new(env)
|
448
|
+
key = require_str(request, 'key')
|
449
|
+
format = optional_str(request, 'format')
|
450
|
+
format ||= DEFAULT_FORMAT
|
451
|
+
vtime = optional_int(request, 'vtime')
|
452
|
+
if vtime
|
453
|
+
check_request(request, %w[format vtime])
|
454
|
+
else
|
455
|
+
vname = optional_str(request, 'vname')
|
456
|
+
check_request(request, %w[format vname])
|
457
|
+
end
|
458
|
+
|
459
|
+
if vtime
|
460
|
+
attrs = submit(GWRPCBus, :gett_attrs, vtime, key)
|
461
|
+
elsif vname
|
462
|
+
attrs = submit(GWRPCBus, :getv_attrs, vname, key)
|
463
|
+
else
|
464
|
+
attrs = submit(GWRPCBus, :get_attrs, key)
|
465
|
+
end
|
466
|
+
|
467
|
+
if attrs
|
468
|
+
attrs, ct = format_attrs(attrs, format)
|
469
|
+
unless attrs
|
470
|
+
return html_response(400, 'Bad Request', "unknown format `#{format}'")
|
471
|
+
end
|
472
|
+
body = [attrs]
|
473
|
+
return [200, {'Content-Type'=>ct}, body]
|
474
|
+
else
|
475
|
+
if vtime
|
476
|
+
return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
|
477
|
+
elsif vname
|
478
|
+
return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
|
479
|
+
else
|
480
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
def http_rpc_add(env)
|
486
|
+
request = ::Rack::Request.new(env)
|
487
|
+
key = require_str(request, 'key')
|
488
|
+
data = require_str(request, 'data')
|
489
|
+
vname = optional_str(request, 'vname')
|
490
|
+
attrs = optional_str(request, 'attrs')
|
491
|
+
if attrs
|
492
|
+
format = optional_str(request, 'format')
|
493
|
+
format ||= DEFAULT_FORMAT
|
494
|
+
check_request(request, %w[key vname data attrs format])
|
495
|
+
else
|
496
|
+
check_request(request, %w[key vname data])
|
497
|
+
end
|
498
|
+
|
499
|
+
if attrs
|
500
|
+
attrs = parse_attrs(attrs, format)
|
501
|
+
unless attrs
|
502
|
+
return html_response(400, 'Bad Request', "unknown format `#{format}'")
|
503
|
+
end
|
504
|
+
if vname
|
505
|
+
okey = submit(GWRPCBus, :addv, vname, key, data, attrs)
|
506
|
+
else
|
507
|
+
okey = submit(GWRPCBus, :add, key, data, attrs)
|
508
|
+
end
|
509
|
+
else
|
510
|
+
if vname
|
511
|
+
okey = submit(GWRPCBus, :addv_data, vname, key, data)
|
512
|
+
else
|
513
|
+
okey = submit(GWRPCBus, :add_data, key, data)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
# FIXME return okey?
|
518
|
+
return html_response(200, 'OK')
|
519
|
+
end
|
520
|
+
|
521
|
+
def http_rpc_update_attrs(env)
|
522
|
+
request = ::Rack::Request.new(env)
|
523
|
+
key = require_str(request, 'key')
|
524
|
+
attrs = require_str(request, 'attrs')
|
525
|
+
format = optional_str(request, 'format')
|
526
|
+
format ||= DEFAULT_FORMAT
|
527
|
+
check_request(request, %w[attrs format])
|
528
|
+
|
529
|
+
attrs = parse_attrs(attrs, format)
|
530
|
+
unless attrs
|
531
|
+
return html_response(400, 'Bad Request', "unknown format `#{format}'")
|
532
|
+
end
|
533
|
+
|
534
|
+
okey = submit(GWRPCBus, :update_attrs, key, attrs)
|
535
|
+
|
536
|
+
if okey
|
537
|
+
return html_response(200, 'OK')
|
538
|
+
else
|
539
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
def http_rpc_delete(env)
|
544
|
+
request = ::Rack::Request.new(env)
|
545
|
+
key = require_str(request, 'key')
|
546
|
+
vtime = optional_int(request, 'vtime')
|
547
|
+
if vtime
|
548
|
+
check_request(request, %w[key vtime])
|
549
|
+
else
|
550
|
+
vname = optional_str(request, 'vname')
|
551
|
+
check_request(request, %w[key vname])
|
552
|
+
end
|
553
|
+
|
554
|
+
if vtime
|
555
|
+
deleted = submit(GWRPCBus, :deletet, vtime, key)
|
556
|
+
elsif vname
|
557
|
+
deleted = submit(GWRPCBus, :deletev, vname, key)
|
558
|
+
else
|
559
|
+
deleted = submit(GWRPCBus, :delete, key)
|
560
|
+
end
|
561
|
+
|
562
|
+
if deleted
|
563
|
+
return html_response(200, 'OK')
|
564
|
+
else
|
565
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
def http_rpc_remove(env)
|
570
|
+
request = ::Rack::Request.new(env)
|
571
|
+
key = require_str(request, 'key')
|
572
|
+
check_request(request, %w[key])
|
573
|
+
|
574
|
+
removed = submit(GWRPCBus, :remove, key)
|
575
|
+
|
576
|
+
if removed
|
577
|
+
return html_response(200, 'OK')
|
578
|
+
else
|
579
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
def http_rpc_url(env)
|
584
|
+
request = ::Rack::Request.new(env)
|
585
|
+
key = require_str(request, 'key')
|
586
|
+
vtime = optional_int(request, 'vtime')
|
587
|
+
if vtime
|
588
|
+
check_request(request, %w[key vtime])
|
589
|
+
else
|
590
|
+
vname = optional_str(request, 'vname')
|
591
|
+
check_request(request, %w[key vname])
|
592
|
+
end
|
593
|
+
|
594
|
+
if vtime
|
595
|
+
url = submit(GWRPCBus, :urlt, vtime, key)
|
596
|
+
elsif vname
|
597
|
+
url = submit(GWRPCBus, :urlv, vname, key)
|
598
|
+
else
|
599
|
+
url = submit(GWRPCBus, :url, key)
|
600
|
+
end
|
601
|
+
|
602
|
+
if url
|
603
|
+
body = [url]
|
604
|
+
return [200, {'Content-Type'=>'text/plain'}, body]
|
605
|
+
else
|
606
|
+
if vtime
|
607
|
+
return html_response(404, 'Not Found', "key=`#{key}' vtime=#{vtime}")
|
608
|
+
elsif vname
|
609
|
+
return html_response(404, 'Not Found', "key=`#{key}' vname=#{vname}")
|
610
|
+
else
|
611
|
+
return html_response(404, 'Not Found', "key=`#{key}'")
|
612
|
+
end
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
|
617
|
+
# direct/<rsid>/<vtime>/<key> => get_direct
|
618
|
+
def call_direct(env)
|
619
|
+
case env['REQUEST_METHOD']
|
620
|
+
when 'GET'
|
621
|
+
http_direct_get(env)
|
622
|
+
else
|
623
|
+
return html_response(405, 'Method Not Allowed')
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
def http_direct_get(env)
|
628
|
+
request = ::Rack::Request.new(env)
|
629
|
+
path = get_key_path_info(env)
|
630
|
+
rsid, vtime, key = path.split('/', 3)
|
631
|
+
rsid = rsid.to_i
|
632
|
+
vtime = vtime.to_i
|
633
|
+
|
634
|
+
check_request(request, %w[])
|
635
|
+
|
636
|
+
okey = ObjectKey.new(key, vtime, rsid)
|
637
|
+
data = submit(DSRPCBus, :get_direct, okey)
|
638
|
+
|
639
|
+
if data
|
640
|
+
body = [data]
|
641
|
+
return [200, {'Content-Type'=>'application/octet-stream'}, body]
|
642
|
+
else
|
643
|
+
return html_response(404, 'Not Found', "rsid=#{rsid} key=`#{key}' vtime=#{vtime}")
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
|
648
|
+
private
|
649
|
+
def require_str(request, k)
|
650
|
+
str = request.GET[k] || request.POST[k]
|
651
|
+
unless str
|
652
|
+
# FIXME HTTP error code
|
653
|
+
raise "#{k} is required"
|
654
|
+
end
|
655
|
+
str
|
656
|
+
end
|
657
|
+
|
658
|
+
def require_int(request, k)
|
659
|
+
str = require_str(request, k)
|
660
|
+
# FIXME check error
|
661
|
+
str.to_i
|
662
|
+
end
|
663
|
+
|
664
|
+
def optional_str(request, k)
|
665
|
+
request.GET[k] || request.POST[k]
|
666
|
+
end
|
667
|
+
|
668
|
+
def optional_int(request, k)
|
669
|
+
str = optional_str(request, k)
|
670
|
+
if str
|
671
|
+
# FIXME check error
|
672
|
+
str.to_i
|
673
|
+
else
|
674
|
+
nil
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
def get_cmd_path_info(env)
|
679
|
+
env['PATH_INFO'][1..-1] # remove front '/' character
|
680
|
+
end
|
681
|
+
|
682
|
+
def get_key_path_info(env)
|
683
|
+
env['PATH_INFO'][1..-1] # remove front '/' character
|
684
|
+
end
|
685
|
+
|
686
|
+
def check_request(request, accepts)
|
687
|
+
# FIXME check error
|
688
|
+
end
|
689
|
+
|
690
|
+
def parse_attrs(attrs, format)
|
691
|
+
case format
|
692
|
+
when 'json'
|
693
|
+
return JSON.load(attrs)
|
694
|
+
when 'msgpack'
|
695
|
+
return MessagePack.unpack(attrs)
|
696
|
+
when 'tsv'
|
697
|
+
r = {}
|
698
|
+
attrs.split("\n").map {|line|
|
699
|
+
k, v, _ = line.split("\t")
|
700
|
+
r[k] = v || ""
|
701
|
+
}
|
702
|
+
return r
|
703
|
+
else
|
704
|
+
return nil
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
def format_attrs(attrs, format)
|
709
|
+
case format
|
710
|
+
when 'json'
|
711
|
+
return JSON.dump(attrs), 'application/json'
|
712
|
+
when 'msgpack'
|
713
|
+
return MessagePack.pack(attrs), 'application/x-msgpack'
|
714
|
+
when 'tsv'
|
715
|
+
data = attrs.map {|k,v| "#{k}\t#{v}" }.join("\n")
|
716
|
+
return data, 'text/tab-separated-values'
|
717
|
+
else
|
718
|
+
return nil
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
def html_response(code, title, msg=nil)
|
723
|
+
body = @erb.result(LS4.HTTPGatewayService_get_binding(code, title, msg))
|
724
|
+
return [code, {'Content-Type'=>'text/html'}, [body]]
|
725
|
+
#if msg
|
726
|
+
# body = ['<html><head></head><body><h1>',
|
727
|
+
# code.to_s, ' ', title,
|
728
|
+
# '</h1><p>', CGI.escapeHTML(msg.to_s), "</p></body></html>\r\n"]
|
729
|
+
#else
|
730
|
+
# body = ['<html><head></head><body><h1>',
|
731
|
+
# code.to_s, ' ', title,
|
732
|
+
# "</h1></body></html>\r\n"]
|
733
|
+
#end
|
734
|
+
#return [code, {'Content-Type'=>'text/html'}, body]
|
735
|
+
end
|
736
|
+
|
737
|
+
protected
|
738
|
+
class Responder
|
739
|
+
def initialize
|
740
|
+
@mutex = Mutex.new
|
741
|
+
@cond = ConditionVariable.new
|
742
|
+
@sent = false
|
743
|
+
@retval = nil
|
744
|
+
@err = nil
|
745
|
+
end
|
746
|
+
|
747
|
+
attr_reader :retval
|
748
|
+
attr_reader :err
|
749
|
+
|
750
|
+
def wait
|
751
|
+
@mutex.synchronize do
|
752
|
+
until @sent
|
753
|
+
@cond.wait(@mutex)
|
754
|
+
end
|
755
|
+
end
|
756
|
+
end
|
757
|
+
|
758
|
+
def sent?
|
759
|
+
@sent
|
760
|
+
end
|
761
|
+
|
762
|
+
def result(retval, err=nil)
|
763
|
+
@mutex.synchronize do
|
764
|
+
unless @sent
|
765
|
+
@retval = retval
|
766
|
+
@err = err
|
767
|
+
@sent = true
|
768
|
+
@cond.signal
|
769
|
+
end
|
770
|
+
end
|
771
|
+
nil
|
772
|
+
end
|
773
|
+
|
774
|
+
def error(err, retval = nil)
|
775
|
+
result(retval, err)
|
776
|
+
end
|
777
|
+
end
|
778
|
+
|
779
|
+
def submit(bus, name, *args)
|
780
|
+
$log.trace { "http api: #{name} #{args}" }
|
781
|
+
|
782
|
+
r = Responder.new
|
783
|
+
|
784
|
+
ProcessBus.submit {
|
785
|
+
dispatch(bus, name, args, r)
|
786
|
+
}
|
787
|
+
|
788
|
+
r.wait
|
789
|
+
|
790
|
+
if r.err
|
791
|
+
# FIXME
|
792
|
+
raise r.err
|
793
|
+
else
|
794
|
+
return r.retval
|
795
|
+
end
|
796
|
+
|
797
|
+
raise
|
798
|
+
end
|
799
|
+
|
800
|
+
def dispatch(bus, name, args, r)
|
801
|
+
result = bus.__send__(name, *args)
|
802
|
+
if result.is_a?(MessagePack::RPC::AsyncResult)
|
803
|
+
result.set_responder(r)
|
804
|
+
else
|
805
|
+
r.result(result)
|
806
|
+
end
|
807
|
+
rescue => e
|
808
|
+
msg = ["http api error on #{name}: #{e}"]
|
809
|
+
e.backtrace.each {|bt| msg << " #{bt}" }
|
810
|
+
$log.error msg.join("\n")
|
811
|
+
r.error(e)
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
|
816
|
+
def self.HTTPGatewayService_get_binding(code, title, message)
|
817
|
+
binding
|
818
|
+
end
|
819
|
+
|
820
|
+
|
821
|
+
end
|