leo_manager_client 0.4.7

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.
@@ -0,0 +1,275 @@
1
+ # ======================================================================
2
+ #
3
+ # LeoFS Manager Client
4
+ #
5
+ # Copyright (c) 2012 Rakuten, Inc.
6
+ #
7
+ # This file is provided to you under the Apache License,
8
+ # Version 2.0 (the "License"); you may not use this file
9
+ # except in compliance with the License. You may obtain
10
+ # a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing,
15
+ # software distributed under the License is distributed on an
16
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ # KIND, either express or implied. See the License for the
18
+ # specific language governing permissions and limitations
19
+ # under the License.
20
+ #
21
+ # ======================================================================
22
+ module LeoManager
23
+
24
+ # ==========================
25
+ # System Information Model
26
+ # ==========================
27
+ class Status
28
+ # Node
29
+ attr_reader :node_stat
30
+ # System
31
+ attr_reader :system_info
32
+ # Array of Node
33
+ attr_reader :node_list
34
+
35
+ def initialize(h)
36
+ @node_stat = NodeStat.new(h[:node_stat]) if h.has_key?(:node_stat)
37
+ @system_info = System.new(h[:system_info]) if h.has_key?(:system_info)
38
+ @node_list = h[:node_list].map {|node| Node.new(node) } if h.has_key?(:node_list)
39
+ end
40
+
41
+ class System
42
+ attr_reader :version, :ring_size, :ring_cur, :ring_prev
43
+
44
+ # number of replicas
45
+ attr_reader :n
46
+ # number of replicas needed for a successful READ operation
47
+ attr_reader :r
48
+ # number of replicas needed for a successful WRITE operation
49
+ attr_reader :w
50
+ # number of replicas needed for a successful DELETE operation
51
+ attr_reader :d
52
+
53
+ def initialize(h)
54
+ @version = h[:version]
55
+ @n = Integer(h[:n])
56
+ @r = Integer(h[:r])
57
+ @w = Integer(h[:w])
58
+ @d = Integer(h[:d])
59
+ @ring_size = Integer(h[:ring_size])
60
+ #XXX: leo_manager returns ring_hash_(cur|prev) as decimal (not hex)
61
+ @ring_cur = Integer(h[:ring_hash_cur]).to_s(16)
62
+ @ring_prev = Integer(h[:ring_hash_prev]).to_s(16)
63
+ end
64
+ end
65
+
66
+ # Node Status Model
67
+ class Node
68
+ attr_reader :type, :node, :state, :ring_cur, :ring_prev, :when
69
+
70
+ def initialize(h)
71
+ @type = h[:type]
72
+ @node = h[:node]
73
+ @when = Time.parse(h[:when])
74
+ @state = h[:state]
75
+ #XXX: these are written in hex
76
+ @ring_cur = h[:ring_cur]
77
+ @ring_prev = h[:ring_prev]
78
+ end
79
+
80
+ alias joined_at when
81
+ end
82
+
83
+ class NodeStat
84
+ @@properties = [
85
+ :version, :log_dir, :ring_cur, :ring_prev, :vm_version,
86
+ :total_mem_usage, :system_mem_usage, :procs_mem_usage,
87
+ :ets_mem_usage, :num_of_procs, :limit_of_procs, :thread_pool_size,
88
+ :replication_msgs, :sync_vnode_msgs, :rebalance_msgs, :kernel_poll
89
+ ]
90
+
91
+ attr_reader *@@properties
92
+
93
+ def initialize(h)
94
+ @@properties.each do |property|
95
+ instance_variable_set("@#{property}", h[property])
96
+ end
97
+ @kernel_poll = (h[:kernel_poll] == "true") if h.has_key?(:kernel_poll)
98
+ end
99
+ end
100
+ end
101
+
102
+ # ==========================
103
+ # Assigned file info Model
104
+ # ==========================
105
+ class AssignedFile
106
+ attr_reader :node, :vnode_id, :size, :clock, :checksum, :timestamp, :delete, :num_of_chunks
107
+
108
+ def initialize(h)
109
+ @node = h[:node]
110
+ @vnode_id = h[:vnode_id]
111
+ @size = h[:size]
112
+ @clock = h[:clock]
113
+ @checksum = h[:checksum]
114
+ timestamp = h[:timestamp]
115
+ @timestamp = timestamp.empty? ? timestamp : Time.parse(timestamp)
116
+ @delete = h[:delete] != 0 # bool
117
+ @num_of_chunks = h[:num_of_chunks]
118
+ end
119
+ end
120
+
121
+ # ==========================
122
+ # Storage Status Model
123
+ # ==========================
124
+ class StorageStat
125
+ attr_reader :active_num_of_objects, :total_num_of_objects,
126
+ :active_size_of_objects, :total_size_of_objects,
127
+ :ratio_of_active_size,
128
+ :last_compaction_start, :last_compaction_end
129
+
130
+ alias total_of_objects total_num_of_objects # for compatibility
131
+
132
+ def initialize(h)
133
+ @active_num_of_objects = h[:active_num_of_objects]
134
+ @total_num_of_objects = h[:total_num_of_objects]
135
+ @active_size_of_objects = h[:active_size_of_objects]
136
+ @total_size_of_objects = h[:total_size_of_objects]
137
+ @ratio_of_active_size = h[:ratio_of_active_size]
138
+
139
+ last_compaction_start = h[:last_compaction_start]
140
+ if last_compaction_start == "____-__-__ __:__:__"
141
+ @last_compaction_start = nil # you have never done compaction
142
+ else
143
+ @last_compaction_start = Time.parse(last_compaction_start)
144
+ end
145
+
146
+ last_compaction_end = h[:last_compaction_end]
147
+ if last_compaction_end == "____-__-__ __:__:__"
148
+ @last_compaction_end = nil
149
+ else
150
+ @last_compaction_end = Time.parse(last_compaction_end)
151
+ end
152
+ end
153
+
154
+ def file_size
155
+ warn "property 'file_size' is deprecated"
156
+ end
157
+ end
158
+
159
+ # ==========================
160
+ # S3 Credential Model
161
+ # ==========================
162
+ class Credential
163
+ # AWS_ACCESS_KEY_ID
164
+ attr_reader :access_key_id
165
+ # AWS_SECRET_ACCESS_KEY
166
+ attr_reader :secret_access_key
167
+
168
+ def initialize(h)
169
+ @access_key_id = h[:access_key_id]
170
+ @secret_access_key = h[:secret_access_key]
171
+ end
172
+ end
173
+
174
+ # ==========================
175
+ # Login Info Model
176
+ # ==========================
177
+ RoleDef = {
178
+ 1 => :general,
179
+ 9 => :admin
180
+ }
181
+ RoleDef.default_proc = proc {|_, key| raise "invalid @user_id: #{key}" }
182
+ RoleDef.freeze
183
+
184
+ class LoginInfo
185
+ attr_reader :id, :role_id, :access_key_id, :secret_key, :created_at
186
+
187
+ def initialize(h)
188
+ h = h[:user]
189
+ @id = h[:id]
190
+ @role_id = h[:role_id]
191
+ @access_key_id = h[:access_key_id]
192
+ @secret_key = h[:secret_key]
193
+ @created_at = Time.parse(h[:created_at])
194
+ end
195
+
196
+ def role
197
+ RoleDef[@role_id]
198
+ end
199
+ end
200
+
201
+ # ==========================
202
+ # User Info Model
203
+ # ==========================
204
+ class User
205
+ attr_reader :user_id, :role_id, :access_key_id, :created_at
206
+
207
+ def initialize(h)
208
+ @user_id = h[:user_id]
209
+ @role_id = h[:role_id]
210
+ @access_key_id = h[:access_key_id]
211
+ @created_at = Time.parse(h[:created_at])
212
+ end
213
+
214
+ def role
215
+ RoleDef[@role_id]
216
+ end
217
+ end
218
+
219
+ # ==========================
220
+ # Endpoint Model
221
+ # ==========================
222
+ class Endpoint
223
+ # host of the endpoint
224
+ attr_reader :endpoint
225
+ # When the endpoint created at
226
+ attr_reader :created_at
227
+
228
+ def initialize(h)
229
+ @endpoint = h[:endpoint]
230
+ @created_at = Time.parse(h[:created_at])
231
+ end
232
+ end
233
+
234
+ # ==========================
235
+ # S3-Bucket Model
236
+ # ==========================
237
+ class Bucket
238
+ # name of bucket
239
+ attr_reader :name
240
+ # name of the bucket's owner
241
+ attr_reader :owner
242
+ # when the bucket created at
243
+ attr_reader :created_at
244
+
245
+ def initialize(h)
246
+ @name = h[:bucket]
247
+ @owner = h[:owner]
248
+ @created_at = Time.parse(h[:created_at])
249
+ end
250
+ end
251
+
252
+ # ==========================
253
+ # Compaction Status Model
254
+ # ==========================
255
+ class CompactionStatus
256
+ attr_reader :status, :last_compaction_start,
257
+ :total_targets, :num_of_pending_targets,
258
+ :num_of_ongoing_targets, :num_of_out_of_targets
259
+
260
+ def initialize(h)
261
+ @status = h[:status]
262
+ @total_targets = h[:total_targets]
263
+ @num_of_pending_targets = h[:num_of_pending_targets]
264
+ @num_of_ongoing_targets = h[:num_of_ongoing_targets]
265
+ @num_of_out_of_targets = h[:num_of_out_of_targets]
266
+
267
+ last_compaction_start = h[:last_compaction_start]
268
+ if last_compaction_start == "____-__-__ __:__:__"
269
+ @last_compaction_start = nil # you have never done compaction
270
+ else
271
+ @last_compaction_start = Time.parse(last_compaction_start)
272
+ end
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,162 @@
1
+ # ======================================================================
2
+ #
3
+ # LeoFS Manager Client
4
+ #
5
+ # Copyright (c) 2012 Rakuten, Inc.
6
+ #
7
+ # This file is provided to you under the Apache License,
8
+ # Version 2.0 (the "License"); you may not use this file
9
+ # except in compliance with the License. You may obtain
10
+ # a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing,
15
+ # software distributed under the License is distributed on an
16
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ # KIND, either express or implied. See the License for the
18
+ # specific language governing permissions and limitations
19
+ # under the License.
20
+ #
21
+ # ======================================================================
22
+
23
+ require "socket"
24
+
25
+ module Dummy
26
+ module Response
27
+ Login = {
28
+ :user => {
29
+ :id => "foo",
30
+ :role_id => 0,
31
+ :access_key_id => "855d2f9bf21f51b4fd38",
32
+ :secret_key => "ea6d9540d6385f32d674c925929748e00e0e961a",
33
+ :created_at => "2012-11-29 17:18:39 +0900"
34
+ }
35
+ }.to_json
36
+
37
+ Status = {
38
+ :system_info => {
39
+ :version => "0.10.1",
40
+ :n => "1",
41
+ :r => "1",
42
+ :w => "1",
43
+ :d => "1",
44
+ :ring_size => "128",
45
+ :ring_hash_cur => "2688134336",
46
+ :ring_hash_prev => "2688134336"},
47
+ :node_list => [
48
+ {
49
+ :type => "S",
50
+ :node => "storage_0@127.0.0.1",
51
+ :state => "running",
52
+ :ring_cur => "a039acc0",
53
+ :ring_prev => "a039acc0",
54
+ :when => "2012-09-21 15:08:22 +0900"
55
+ }, {
56
+ :type => "G",
57
+ :node => "gateway_0@127.0.0.1",
58
+ :state => "running",
59
+ :ring_cur => "a039acc0",
60
+ :ring_prev => "a039acc0",
61
+ :when => "2012-09-21 15:08:25 +0900"
62
+ }
63
+ ]
64
+ }.to_json
65
+
66
+ Whereis = {
67
+ :assigned_info => [
68
+ {
69
+ :node => "storage_0@127.0.0.1",
70
+ :vnode_id => "",
71
+ :size => "",
72
+ :clock => "",
73
+ :checksum => "",
74
+ :timestamp => "2012-12-07 16:51:08 +0900",
75
+ :delete => 0,
76
+ :num_of_chunks => 0
77
+ }
78
+ ]
79
+ }.to_json
80
+
81
+ StorageStat = {
82
+ :active_num_of_objects => 0,
83
+ :total_num_of_objects => 0,
84
+ :active_size_of_objects => 0,
85
+ :total_size_of_objects => 0,
86
+ :ratio_of_active_size => 0,
87
+ :last_compaction_start => "2013-03-13 09:11:04 +0900",
88
+ :last_compaction_end => "2013-03-13 09:11:04 +0900"
89
+ }.to_json
90
+
91
+ GetEndpoints = {:endpoints => [{:endpoint => "s3.amazonaws.com", :created_at=>"2012-09-21 15:08:11 +0900"},
92
+ {:endpoint => "localhost", :created_at=>"2012-09-21 15:08:11 +0900"},
93
+ {:endpoint => "foo", :created_at=>"2012-09-21 18:51:08 +0900"},
94
+ {:endpoint => "leofs.org", :created_at=>"2012-09-21 15:08:11 +0900"}
95
+ ]}.to_json
96
+ GetBuckets = {:buckets => [{:bucket => "test",
97
+ :owner => "test",
98
+ :created_at => "2012-09-24 15:38:49 +0900"
99
+ }]}.to_json
100
+
101
+ GetUsers = {:users => [{:access_key_id => "05236",
102
+ :user_id => "_test_leofs_",
103
+ :role_id => 1,
104
+ :created_at => "2012-11-20 15:13:20 +0900"}]}.to_json
105
+
106
+ CreateUser = {:access_key_id => "xxxxxxxxxxxxxxxxxxxx",
107
+ :secret_access_key => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}.to_json
108
+ end
109
+
110
+ Argument = "arg" # passed to command which requires some arguments.
111
+
112
+ # dummy Manager
113
+ class Manager
114
+ def initialize
115
+ t = Thread.new do
116
+ TCPServer.open(Host, Port) do |server|
117
+ loop do
118
+ begin
119
+ socket = server.accept
120
+ while line = socket.readline.split.first
121
+ response = process_line(line)
122
+ socket.puts(response)
123
+ end
124
+ rescue EOFError
125
+ end
126
+ end
127
+ end
128
+ end
129
+ nil until t.stop? # wait server start
130
+ end
131
+
132
+ def process_line(line)
133
+ line.rstrip!
134
+ begin
135
+ case line
136
+ when "login"
137
+ Response::Login
138
+ when "status"
139
+ Response::Status
140
+ when "get-buckets"
141
+ Response::GetBuckets
142
+ when "whereis"
143
+ Response::Whereis
144
+ when "get-endpoints"
145
+ Response::GetEndpoints
146
+ when "get-buckets"
147
+ Response::GetBuckets
148
+ when "get-users"
149
+ Response::GetUsers
150
+ when "create-user"
151
+ Response::CreateUser
152
+ when /^du/
153
+ Response::StorageStat
154
+ else
155
+ { :result => line }.to_json
156
+ end
157
+ rescue => ex
158
+ { :error => ex.message }.to_json
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,281 @@
1
+ # ======================================================================
2
+ #
3
+ # LeoFS Manager Client
4
+ #
5
+ # Copyright (c) 2012 Rakuten, Inc.
6
+ #
7
+ # This file is provided to you under the Apache License,
8
+ # Version 2.0 (the "License"); you may not use this file
9
+ # except in compliance with the License. You may obtain
10
+ # a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing,
15
+ # software distributed under the License is distributed on an
16
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17
+ # KIND, either express or implied. See the License for the
18
+ # specific language governing permissions and limitations
19
+ # under the License.
20
+ #
21
+ # ======================================================================
22
+
23
+ require "json"
24
+ require_relative "dummy_tcp_server"
25
+ require_relative "../lib/leo_manager_client"
26
+
27
+ Host = "localhost"
28
+ Port = 50000
29
+
30
+ $DEBUG = false
31
+
32
+ # key: api_name, value: num of args
33
+ NoResultAPIs = {
34
+ :start => 0,
35
+ :detach => 1,
36
+ :rebalance => 0,
37
+ :purge => 1,
38
+ :set_endpoint => 1,
39
+ :del_endpoint => 1,
40
+ :add_bucket => 2,
41
+ :delete_bucket => 2
42
+ }
43
+
44
+ include LeoManager
45
+
46
+ describe LeoManager do
47
+ describe StorageStat do
48
+ shared_examples_for StorageStat do
49
+ its(:active_num_of_objects) { should == 0 }
50
+ its(:total_num_of_objects) { should == 0 }
51
+ its(:active_size_of_objects) { should == 0 }
52
+ its(:total_size_of_objects) { should == 0 }
53
+ end
54
+
55
+ context "there is no last compaction" do
56
+ subject do
57
+ StorageStat.new(:active_num_of_objects => 0,
58
+ :total_num_of_objects => 0,
59
+ :active_size_of_objects => 0,
60
+ :total_size_of_objects => 0,
61
+ :ratio_of_active_size => "0.0%",
62
+ :last_compaction_start => "____-__-__ __:__:__",
63
+ :last_compaction_end => "____-__-__ __:__:__"
64
+ )
65
+ end
66
+
67
+ it_behaves_like StorageStat
68
+
69
+ its(:last_compaction_start) { should be_nil }
70
+ its(:last_compaction_end) { should be_nil }
71
+ end
72
+
73
+ context "there is last compaction" do
74
+ let(:time_str) { Time.now.to_s }
75
+
76
+ subject do
77
+ StorageStat.new(
78
+ :active_num_of_objects => 0,
79
+ :total_num_of_objects => 0,
80
+ :active_size_of_objects => 0,
81
+ :total_size_of_objects => 0,
82
+ :last_compaction_start => time_str,
83
+ :last_compaction_end => time_str
84
+ )
85
+ end
86
+
87
+ it_behaves_like StorageStat
88
+
89
+ its(:last_compaction_start) { subject.to_s == time_str }
90
+ its(:last_compaction_end) { subject.to_s == time_str }
91
+ end
92
+ end
93
+
94
+ describe Status do
95
+ describe Status::System do
96
+ let(:ring_hash) { "1679896365" }
97
+
98
+ subject do
99
+ Status::System.new({
100
+ :version => "0.14.0",
101
+ :n => "2",
102
+ :r => "1",
103
+ :w => "1",
104
+ :d => "1",
105
+ :ring_size => "128",
106
+ :ring_hash_cur => ring_hash,
107
+ :ring_hash_prev => ring_hash
108
+ })
109
+ end
110
+
111
+ [:n, :r, :w, :d, :ring_size].each do |property|
112
+ its(property) { should be_a Integer }
113
+ end
114
+
115
+ [:ring_cur, :ring_prev].each do |property|
116
+ its(property) { should be_a String }
117
+ its(property) { should == Integer(ring_hash).to_s(16) }
118
+ end
119
+ end
120
+
121
+ describe Status::NodeStat do
122
+ config = {
123
+ :version => "0.14.0-RC2",
124
+ :log_dir => "./log",
125
+ :ring_cur => "64212f2d",
126
+ :ring_prev => "64212f2d",
127
+ :vm_version => "5.9.3.1",
128
+ :total_mem_usage => 29281552,
129
+ :system_mem_usage => 11874433,
130
+ :procs_mem_usage => 17411895,
131
+ :ets_mem_usage => 1022392,
132
+ :num_of_procs => 325,
133
+ :limit_of_procs => 1048576,
134
+ :kernel_poll => "true",
135
+ :thread_pool_size => 32,
136
+ :replication_msgs => 0,
137
+ :sync_vnode_msgs => 0,
138
+ :rebalance_msgs => 0
139
+ }
140
+
141
+ subject do
142
+ Status::NodeStat.new(config)
143
+ end
144
+
145
+ its(:kernel_poll) { should == (config[:kernel_poll] == "true") }
146
+ _config = config.reject {|key| key == :kernel_poll }
147
+ _config.each {|key, value| its(key) { should == value } }
148
+ end
149
+ end
150
+
151
+ describe Client do
152
+ before(:all) do
153
+ Dummy::Manager.new
154
+ @manager = Client.new("#{Host}:#{Port}")
155
+ end
156
+ subject { @manager }
157
+
158
+ it "raises error when it is passed invalid params" do
159
+ lambda { Client.new }.should raise_error
160
+ end
161
+
162
+ describe "#login" do
163
+ it "returns LoginInfo" do
164
+ subject.login("user_id", "pass").should be_a LoginInfo
165
+ end
166
+ end
167
+
168
+ describe "#status" do
169
+ its(:status) { should be_a Status }
170
+ its("status.system_info") { should be_a Status::System }
171
+ its("status.node_list") do
172
+ should be_a Array
173
+ should be_all {|node| node.is_a?(Status::Node) }
174
+ end
175
+ end
176
+
177
+ describe "#whereis" do
178
+ it "returns Array of WhereInfo" do
179
+ result = subject.whereis("path")
180
+ result.should be_a Array
181
+ result.each do |where_info|
182
+ where_info.should be_a AssignedFile
183
+ where_info.num_of_chunks.should be_a Integer
184
+ end
185
+ end
186
+ end
187
+
188
+ describe "#du" do
189
+ it "returns DiskUsage" do
190
+ subject.du("node").should be_a StorageStat
191
+ end
192
+ end
193
+
194
+ describe "#create_user" do
195
+ it "returns Credential" do
196
+ subject.create_user("user_id", "password").should be_a Credential
197
+ end
198
+
199
+ it "goes with only user_id" do
200
+ subject.create_user("user_id").should be_a Credential
201
+ end
202
+ end
203
+
204
+ describe "#update_user_role" do
205
+ it "returns nil" do
206
+ subject.update_user_role("user_id", "admin").should be_nil
207
+ end
208
+ end
209
+
210
+ describe "#update_user_password" do
211
+ it "returns nil" do
212
+ subject.update_user_password("user_id", "new_password").should be_nil
213
+ end
214
+ end
215
+
216
+ describe "#delete_user" do
217
+ it "returns nil" do
218
+ subject.delete_user("user_id").should be_nil
219
+ end
220
+ end
221
+
222
+ its(:get_users) do
223
+ should be_a Array
224
+ should be_all do |account|
225
+ account.is_a?(User)
226
+ end
227
+ end
228
+
229
+ its(:get_endpoints) do
230
+ should be_a Array
231
+ should be_all do |endpoint|
232
+ endpoint.is_a?(Endpoint)
233
+ end
234
+ end
235
+
236
+ its(:get_buckets) do
237
+ should be_a Array
238
+ should be_all do |bucket|
239
+ bucket.is_a?(Bucket)
240
+ end
241
+ end
242
+
243
+ describe "#recover_file" do
244
+ it do
245
+ subject.recover_file("path").should be_nil
246
+ end
247
+ end
248
+
249
+ describe "#recover_node" do
250
+ it do
251
+ subject.recover_node("node").should be_nil
252
+ end
253
+ end
254
+
255
+ describe "#recover_ring" do
256
+ it do
257
+ subject.recover_ring("node").should be_nil
258
+ end
259
+ end
260
+
261
+ NoResultAPIs.each do |api, num_of_args|
262
+ describe "##{api}" do
263
+ it "returns nil" do
264
+ subject.send(api, *(["argument"] * num_of_args)).should be_nil
265
+ end
266
+ end
267
+ end
268
+
269
+ describe "#disconnect!" do
270
+ it "returns nil" do
271
+ subject.disconnect!.should be_nil
272
+ end
273
+
274
+ it "accepts no more requests" do
275
+ lambda {
276
+ subject.status
277
+ }.should raise_error
278
+ end
279
+ end
280
+ end
281
+ end