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.
Files changed (100) hide show
  1. data/AUTHORS +1 -0
  2. data/COPYING +661 -0
  3. data/ChangeLog +9 -0
  4. data/NOTICE +8 -0
  5. data/README.rdoc +61 -0
  6. data/bin/ls4-cs +3 -0
  7. data/bin/ls4-ds +3 -0
  8. data/bin/ls4-gw +3 -0
  9. data/bin/ls4-standalone +3 -0
  10. data/bin/ls4cmd +3 -0
  11. data/bin/ls4ctl +3 -0
  12. data/bin/ls4rpc +3 -0
  13. data/bin/ls4stat +3 -0
  14. data/bin/ls4top +3 -0
  15. data/lib/ls4/command/cmd.rb +241 -0
  16. data/lib/ls4/command/cs.rb +190 -0
  17. data/lib/ls4/command/ctl.rb +278 -0
  18. data/lib/ls4/command/ds.rb +335 -0
  19. data/lib/ls4/command/gw.rb +256 -0
  20. data/lib/ls4/command/rpc.rb +172 -0
  21. data/lib/ls4/command/standalone.rb +318 -0
  22. data/lib/ls4/command/stat.rb +244 -0
  23. data/lib/ls4/command/top.rb +291 -0
  24. data/lib/ls4/default.rb +26 -0
  25. data/lib/ls4/lib/cclog.rb +220 -0
  26. data/lib/ls4/lib/ebus.rb +553 -0
  27. data/lib/ls4/lib/vbcode.rb +228 -0
  28. data/lib/ls4/logic/fault_detector.rb +212 -0
  29. data/lib/ls4/logic/membership.rb +253 -0
  30. data/lib/ls4/logic/node.rb +66 -0
  31. data/lib/ls4/logic/okey.rb +45 -0
  32. data/lib/ls4/logic/tsv_data.rb +81 -0
  33. data/lib/ls4/logic/weight.rb +166 -0
  34. data/lib/ls4/service/balance.rb +62 -0
  35. data/lib/ls4/service/base.rb +29 -0
  36. data/lib/ls4/service/bus.rb +37 -0
  37. data/lib/ls4/service/config.rb +63 -0
  38. data/lib/ls4/service/config_cs.rb +33 -0
  39. data/lib/ls4/service/config_ds.rb +56 -0
  40. data/lib/ls4/service/config_gw.rb +42 -0
  41. data/lib/ls4/service/data_client.rb +122 -0
  42. data/lib/ls4/service/data_server.rb +168 -0
  43. data/lib/ls4/service/data_server_url.rb +83 -0
  44. data/lib/ls4/service/gateway.rb +375 -0
  45. data/lib/ls4/service/gateway_ro.rb +91 -0
  46. data/lib/ls4/service/gw_http.rb +821 -0
  47. data/lib/ls4/service/heartbeat.rb +182 -0
  48. data/lib/ls4/service/log.rb +81 -0
  49. data/lib/ls4/service/master_select.rb +148 -0
  50. data/lib/ls4/service/mds.rb +292 -0
  51. data/lib/ls4/service/mds_cache.rb +294 -0
  52. data/lib/ls4/service/mds_cache_mem.rb +63 -0
  53. data/lib/ls4/service/mds_cache_memcached.rb +65 -0
  54. data/lib/ls4/service/mds_ha.rb +176 -0
  55. data/lib/ls4/service/mds_memcache.rb +209 -0
  56. data/lib/ls4/service/mds_tc.rb +508 -0
  57. data/lib/ls4/service/mds_tt.rb +472 -0
  58. data/lib/ls4/service/membership.rb +331 -0
  59. data/lib/ls4/service/process.rb +90 -0
  60. data/lib/ls4/service/rpc.rb +50 -0
  61. data/lib/ls4/service/rpc_cs.rb +101 -0
  62. data/lib/ls4/service/rpc_ds.rb +96 -0
  63. data/lib/ls4/service/rpc_gw.rb +255 -0
  64. data/lib/ls4/service/rts.rb +94 -0
  65. data/lib/ls4/service/rts_file.rb +76 -0
  66. data/lib/ls4/service/rts_memory.rb +55 -0
  67. data/lib/ls4/service/slave.rb +132 -0
  68. data/lib/ls4/service/stat.rb +91 -0
  69. data/lib/ls4/service/stat_cs.rb +25 -0
  70. data/lib/ls4/service/stat_ds.rb +40 -0
  71. data/lib/ls4/service/stat_gw.rb +25 -0
  72. data/lib/ls4/service/storage.rb +116 -0
  73. data/lib/ls4/service/storage_dir.rb +201 -0
  74. data/lib/ls4/service/sync.rb +206 -0
  75. data/lib/ls4/service/time_check.rb +80 -0
  76. data/lib/ls4/service/ulog.rb +159 -0
  77. data/lib/ls4/service/ulog_file.rb +398 -0
  78. data/lib/ls4/service/ulog_memory.rb +53 -0
  79. data/lib/ls4/service/weight.rb +134 -0
  80. data/lib/ls4/version.rb +5 -0
  81. data/test/01_add_get_remove.rt +84 -0
  82. data/test/02_read.rt +61 -0
  83. data/test/03_getd_readd.rt +69 -0
  84. data/test/04_version_time.rt +170 -0
  85. data/test/05_version_name.rt +161 -0
  86. data/test/06_http_get_set_remove_1.rt +119 -0
  87. data/test/07_http_get_set_remove_2.rt +116 -0
  88. data/test/08_read_only_time.rt +177 -0
  89. data/test/09_read_only_name.rt +173 -0
  90. data/test/10_http_get_set_remove_3.rt +73 -0
  91. data/test/11_mds_cache_memcached.rt +88 -0
  92. data/test/12_mds_cache_local_memory.rt +86 -0
  93. data/test/13_memcache_mds.rt +84 -0
  94. data/test/14_delete.rt +63 -0
  95. data/test/15_standalone.rt +71 -0
  96. data/test/chukan.rb +516 -0
  97. data/test/common.rb +250 -0
  98. data/test/load_test.rb +79 -0
  99. data/test/load_test_offload.rb +86 -0
  100. 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