solusvm 0.10.1 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/README.markdown +75 -25
  2. data/bin/solusvm +3 -153
  3. data/lib/solusvm/base.rb +17 -10
  4. data/lib/solusvm/cli/base_cli.rb +47 -0
  5. data/lib/solusvm/cli/client_cli.rb +49 -0
  6. data/lib/solusvm/cli/general_cli.rb +28 -0
  7. data/lib/solusvm/cli/node_cli.rb +43 -0
  8. data/lib/solusvm/cli/reseller_cli.rb +73 -0
  9. data/lib/solusvm/cli/server_cli.rb +168 -0
  10. data/lib/solusvm/cli.rb +22 -0
  11. data/lib/solusvm/client.rb +26 -12
  12. data/lib/solusvm/general.rb +25 -17
  13. data/lib/solusvm/node.rb +60 -0
  14. data/lib/solusvm/reseller.rb +72 -0
  15. data/lib/solusvm/server.rb +116 -2
  16. data/lib/solusvm/version.rb +1 -1
  17. data/lib/solusvm.rb +8 -8
  18. data/solusvm.gemspec +3 -0
  19. data/test/cli/test_client_cli.rb +80 -0
  20. data/test/cli/test_general_cli.rb +35 -0
  21. data/test/cli/test_node_cli.rb +64 -0
  22. data/test/cli/test_reseller_cli.rb +139 -0
  23. data/test/cli/test_server_cli.rb +267 -0
  24. data/test/fixtures/client_delete_success.txt +2 -0
  25. data/test/fixtures/client_list_success.txt +14 -0
  26. data/test/fixtures/client_list_success_empty.txt +2 -0
  27. data/test/fixtures/general_isos_success.txt +3 -0
  28. data/test/fixtures/general_node_virtualservers_success.txt +15 -0
  29. data/test/fixtures/general_node_virtualservers_success_empty.txt +2 -0
  30. data/test/fixtures/general_node_xenresources_success.txt +4 -0
  31. data/test/fixtures/general_nodes_ids_success.txt +3 -0
  32. data/test/fixtures/general_plans_success.txt +3 -0
  33. data/test/fixtures/reseller_change_resources_success.txt +16 -0
  34. data/test/fixtures/reseller_create_success.txt +23 -0
  35. data/test/fixtures/reseller_delete_success.txt +2 -0
  36. data/test/fixtures/reseller_info_success.txt +23 -0
  37. data/test/fixtures/reseller_list_success.txt +3 -0
  38. data/test/fixtures/server_bootorder_success.txt +2 -0
  39. data/test/fixtures/server_change_consolepass_success.txt +3 -0
  40. data/test/fixtures/server_change_owner_success.txt +2 -0
  41. data/test/fixtures/server_change_vncpass_success.txt +3 -0
  42. data/test/fixtures/server_console_success.txt +7 -0
  43. data/test/fixtures/server_hostname_success.txt +3 -0
  44. data/test/fixtures/server_mountiso_success.txt +2 -0
  45. data/test/fixtures/server_network_disable_success.txt +2 -0
  46. data/test/fixtures/server_network_enable_success.txt +2 -0
  47. data/test/fixtures/server_pae_success.txt +2 -0
  48. data/test/fixtures/server_rootpassword_success.txt +3 -0
  49. data/test/fixtures/server_status_success.txt +2 -0
  50. data/test/fixtures/server_tun_disable_success.txt +2 -0
  51. data/test/fixtures/server_tun_enable_success.txt +2 -0
  52. data/test/fixtures/server_unmountiso_success.txt +2 -0
  53. data/test/fixtures/server_vnc_success.txt +6 -0
  54. data/test/helper.rb +6 -0
  55. data/test/test_cli.rb +17 -0
  56. data/test/test_client.rb +50 -6
  57. data/test/test_general.rb +24 -48
  58. data/test/test_node.rb +107 -0
  59. data/test/test_reseller.rb +75 -0
  60. data/test/test_server.rb +95 -1
  61. metadata +132 -17
data/test/test_server.rb CHANGED
@@ -76,6 +76,36 @@ class TestServer < Test::Unit::TestCase
76
76
  assert_equal 'Virtual server terminated', @server.statusmsg
77
77
  end
78
78
 
79
+ def test_tun_enable
80
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-tun-enable&vserverid=1", :body => load_response('server_tun_enable_success'))
81
+ assert @server.tun_enable(1)
82
+ end
83
+
84
+ def test_tun_disable
85
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-tun-disable&vserverid=1", :body => load_response('server_tun_disable_success'))
86
+ assert @server.tun_disable(1)
87
+ end
88
+
89
+ def test_network_enable
90
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-network-enable&vserverid=1", :body => load_response('server_network_enable_success'))
91
+ assert @server.network_enable(1)
92
+ end
93
+
94
+ def test_network_disable
95
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-network-disable&vserverid=1", :body => load_response('server_network_disable_success'))
96
+ assert @server.network_disable(1)
97
+ end
98
+
99
+ def test_pae_enable
100
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-pae&vserverid=1&pae=on", :body => load_response('server_pae_success'))
101
+ assert @server.pae_enable(1)
102
+ end
103
+
104
+ def test_pae_disable
105
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-pae&vserverid=1&pae=off", :body => load_response('server_pae_success'))
106
+ assert @server.pae_disable(1)
107
+ end
108
+
79
109
  def test_exists
80
110
  FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-checkexists&vserverid=1", :body => load_response('server_exists_success'))
81
111
  assert @server.exists?(1)
@@ -83,7 +113,8 @@ class TestServer < Test::Unit::TestCase
83
113
  end
84
114
 
85
115
  def test_status
86
- #flunk "Implement this"
116
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-status&vserverid=1", :body => load_response('server_status_success'))
117
+ assert_equal 'online', @server.status(1)
87
118
  end
88
119
 
89
120
  def test_add_ip
@@ -96,6 +127,36 @@ class TestServer < Test::Unit::TestCase
96
127
  assert_equal 'Virtual server updated', @server.statusmsg
97
128
  end
98
129
 
130
+ def test_change_owner
131
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-changeowner&vserverid=1&clientid=2", :body => load_response('server_change_owner_success'))
132
+ assert @server.change_owner(1, 2)
133
+ end
134
+
135
+ def test_change_consolepass
136
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-consolepass&vserverid=1&consolepassword=thepassword", :body => load_response('server_change_consolepass_success'))
137
+ assert @server.change_consolepass(1, "thepassword")
138
+ end
139
+
140
+ def test_change_vncpass
141
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-vncpass&vserverid=1&vncpassword=thepassword", :body => load_response('server_change_vncpass_success'))
142
+ assert @server.change_vncpass(1, "thepassword")
143
+ end
144
+
145
+ def test_change_rootpassword
146
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-rootpassword&vserverid=1&rootpassword=thepassword", :body => load_response('server_rootpassword_success'))
147
+ assert @server.change_rootpassword(1, "thepassword")
148
+ end
149
+
150
+ def test_change_bootorder
151
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-bootorder&vserverid=1&bootorder=c", :body => load_response('server_bootorder_success'))
152
+ assert @server.change_bootorder(1, :c)
153
+ end
154
+
155
+ def test_change_hostname
156
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-hostname&vserverid=1&hostname=thehostname", :body => load_response('server_hostname_success'))
157
+ assert @server.change_hostname(1, "thehostname")
158
+ end
159
+
99
160
  def test_info
100
161
  FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-info&vserverid=1&reboot=true", :body => load_response('server_info_success'))
101
162
  assert @server.info(1, true)
@@ -115,6 +176,29 @@ class TestServer < Test::Unit::TestCase
115
176
  assert_equal 'swp', info['swap-burst']
116
177
  assert_equal 'xenhvm', info['type']
117
178
  end
179
+
180
+ def test_vnc
181
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-vnc&vserverid=1", :body => load_response('server_vnc_success'))
182
+ info = @server.vnc(1)
183
+
184
+ assert info
185
+ assert_equal 'thetype', info['type']
186
+ assert_equal 'thevncip', info['vncip']
187
+ assert_equal 'thevncport', info['vncport']
188
+ assert_equal 'thevncpassword', info['vncpassword']
189
+ end
190
+
191
+ def test_console
192
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-console&vserverid=1", :body => load_response('server_console_success'))
193
+ info = @server.console(1)
194
+
195
+ assert info
196
+ assert_equal 'thetype', info['type']
197
+ assert_equal 'theconsoleip', info['consoleip']
198
+ assert_equal 'theconsoleport', info['consoleport']
199
+ assert_equal 'theconsolepassword', info['consolepassword']
200
+ assert_equal 'theconsoleusername', info['consoleusername']
201
+ end
118
202
 
119
203
  def test_info_all
120
204
  FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-infoall&vserverid=1", :body => load_response('server_infoall_success'))
@@ -130,4 +214,14 @@ class TestServer < Test::Unit::TestCase
130
214
  assert_equal "/graphs/9/214/214-load-8f7daef90bc75037489af4217af674a67df545ba05c8a6bcd5341d5894f2f905bf23976f52c0104415c1694135d51f204ddfd7b11bbe87c195a5de4a-86400.png", info["loadgraph"]
131
215
  assert_equal "/graphs/9/214/214-mem-8f7daef90bc75037489af4217af674a67df545ba05c8a6bcd5341d5894f2f905bf23976f52c0104415c1694135d51f204ddfd7b11bbe87c195a5de4a-86400.png", info["memorygraph"]
132
216
  end
217
+
218
+ def test_mountiso
219
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-mountiso&vserverid=1&iso=theiso", :body => load_response('server_mountiso_success'))
220
+ assert @server.mountiso(1, "theiso")
221
+ end
222
+
223
+ def test_unmountiso
224
+ FakeWeb.register_uri(:get, "#{base_uri}&action=vserver-unmountiso&vserverid=1", :body => load_response('server_unmountiso_success'))
225
+ assert @server.unmountiso(1)
226
+ end
133
227
  end
metadata CHANGED
@@ -1,20 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solusvm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
5
- prerelease:
4
+ version: 1.0.0.beta
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Justin Mazzi
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-03 00:00:00.000000000 -05:00
12
+ date: 2012-02-09 00:00:00.000000000 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: xml-simple
17
- requirement: &11769180 !ruby/object:Gem::Requirement
17
+ requirement: &14661020 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,21 @@ dependencies:
22
22
  version: '0'
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *11769180
25
+ version_requirements: *14661020
26
+ - !ruby/object:Gem::Dependency
27
+ name: thor
28
+ requirement: &14656840 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *14656840
26
37
  - !ruby/object:Gem::Dependency
27
38
  name: redgreen
28
- requirement: &11768300 !ruby/object:Gem::Requirement
39
+ requirement: &14656060 !ruby/object:Gem::Requirement
29
40
  none: false
30
41
  requirements:
31
42
  - - ~>
@@ -33,10 +44,10 @@ dependencies:
33
44
  version: 1.2.2
34
45
  type: :development
35
46
  prerelease: false
36
- version_requirements: *11768300
47
+ version_requirements: *14656060
37
48
  - !ruby/object:Gem::Dependency
38
49
  name: fakeweb
39
- requirement: &11766820 !ruby/object:Gem::Requirement
50
+ requirement: &14655100 !ruby/object:Gem::Requirement
40
51
  none: false
41
52
  requirements:
42
53
  - - ~>
@@ -44,10 +55,32 @@ dependencies:
44
55
  version: 1.3.0
45
56
  type: :development
46
57
  prerelease: false
47
- version_requirements: *11766820
58
+ version_requirements: *14655100
59
+ - !ruby/object:Gem::Dependency
60
+ name: mocha
61
+ requirement: &14654280 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ~>
65
+ - !ruby/object:Gem::Version
66
+ version: 0.10.3
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *14654280
70
+ - !ruby/object:Gem::Dependency
71
+ name: rdoc
72
+ requirement: &14653500 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '3.12'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *14653500
48
81
  - !ruby/object:Gem::Dependency
49
82
  name: rake
50
- requirement: &11765820 !ruby/object:Gem::Requirement
83
+ requirement: &14652820 !ruby/object:Gem::Requirement
51
84
  none: false
52
85
  requirements:
53
86
  - - ~>
@@ -55,7 +88,7 @@ dependencies:
55
88
  version: 0.9.2.2
56
89
  type: :development
57
90
  prerelease: false
58
- version_requirements: *11765820
91
+ version_requirements: *14652820
59
92
  description: Solusvm allows for easy interaction with the SolusVM Admin::API.
60
93
  email:
61
94
  - jmazzi@gmail.com
@@ -76,13 +109,27 @@ files:
76
109
  - bin/solusvm
77
110
  - lib/solusvm.rb
78
111
  - lib/solusvm/base.rb
112
+ - lib/solusvm/cli.rb
113
+ - lib/solusvm/cli/base_cli.rb
114
+ - lib/solusvm/cli/client_cli.rb
115
+ - lib/solusvm/cli/general_cli.rb
116
+ - lib/solusvm/cli/node_cli.rb
117
+ - lib/solusvm/cli/reseller_cli.rb
118
+ - lib/solusvm/cli/server_cli.rb
79
119
  - lib/solusvm/client.rb
80
120
  - lib/solusvm/exceptions.rb
81
121
  - lib/solusvm/general.rb
82
122
  - lib/solusvm/hash.rb
123
+ - lib/solusvm/node.rb
124
+ - lib/solusvm/reseller.rb
83
125
  - lib/solusvm/server.rb
84
126
  - lib/solusvm/version.rb
85
127
  - solusvm.gemspec
128
+ - test/cli/test_client_cli.rb
129
+ - test/cli/test_general_cli.rb
130
+ - test/cli/test_node_cli.rb
131
+ - test/cli/test_reseller_cli.rb
132
+ - test/cli/test_server_cli.rb
86
133
  - test/fixtures/base_bad_key.txt
87
134
  - test/fixtures/base_node_does_not_exist.txt
88
135
  - test/fixtures/base_unauthorized_ip.txt
@@ -92,30 +139,63 @@ files:
92
139
  - test/fixtures/client_change_password_success.txt
93
140
  - test/fixtures/client_create_error.txt
94
141
  - test/fixtures/client_create_success.txt
142
+ - test/fixtures/client_delete_success.txt
95
143
  - test/fixtures/client_exists_success.txt
144
+ - test/fixtures/client_list_success.txt
145
+ - test/fixtures/client_list_success_empty.txt
96
146
  - test/fixtures/error.txt
147
+ - test/fixtures/general_isos_success.txt
97
148
  - test/fixtures/general_node_list_all_ips_available.txt
98
149
  - test/fixtures/general_node_list_all_ips_not_available.txt
99
150
  - test/fixtures/general_node_statistics_success.txt
151
+ - test/fixtures/general_node_virtualservers_success.txt
152
+ - test/fixtures/general_node_virtualservers_success_empty.txt
153
+ - test/fixtures/general_node_xenresources_success.txt
154
+ - test/fixtures/general_nodes_ids_success.txt
100
155
  - test/fixtures/general_nodes_success.txt
156
+ - test/fixtures/general_plans_success.txt
101
157
  - test/fixtures/general_templates_success.txt
158
+ - test/fixtures/reseller_change_resources_success.txt
159
+ - test/fixtures/reseller_create_success.txt
160
+ - test/fixtures/reseller_delete_success.txt
161
+ - test/fixtures/reseller_info_success.txt
162
+ - test/fixtures/reseller_list_success.txt
102
163
  - test/fixtures/server_boot_success.txt
164
+ - test/fixtures/server_bootorder_success.txt
165
+ - test/fixtures/server_change_consolepass_success.txt
166
+ - test/fixtures/server_change_owner_success.txt
103
167
  - test/fixtures/server_change_success.txt
168
+ - test/fixtures/server_change_vncpass_success.txt
169
+ - test/fixtures/server_console_success.txt
104
170
  - test/fixtures/server_create_success.txt
105
171
  - test/fixtures/server_exists_success.txt
172
+ - test/fixtures/server_hostname_success.txt
106
173
  - test/fixtures/server_info_success.txt
107
174
  - test/fixtures/server_infoall_success.txt
175
+ - test/fixtures/server_mountiso_success.txt
176
+ - test/fixtures/server_network_disable_success.txt
177
+ - test/fixtures/server_network_enable_success.txt
178
+ - test/fixtures/server_pae_success.txt
108
179
  - test/fixtures/server_reboot_success.txt
109
180
  - test/fixtures/server_rebuild_success.txt
110
181
  - test/fixtures/server_resume_success.txt
182
+ - test/fixtures/server_rootpassword_success.txt
111
183
  - test/fixtures/server_shutdown_success.txt
184
+ - test/fixtures/server_status_success.txt
112
185
  - test/fixtures/server_suspend_success.txt
113
186
  - test/fixtures/server_terminate_success.txt
187
+ - test/fixtures/server_tun_disable_success.txt
188
+ - test/fixtures/server_tun_enable_success.txt
189
+ - test/fixtures/server_unmountiso_success.txt
190
+ - test/fixtures/server_vnc_success.txt
114
191
  - test/helper.rb
115
192
  - test/test_base.rb
193
+ - test/test_cli.rb
116
194
  - test/test_client.rb
117
195
  - test/test_general.rb
118
196
  - test/test_hash.rb
197
+ - test/test_node.rb
198
+ - test/test_reseller.rb
119
199
  - test/test_server.rb
120
200
  - test/test_solusvm.rb
121
201
  has_rdoc: true
@@ -133,16 +213,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
213
  version: '0'
134
214
  segments:
135
215
  - 0
136
- hash: 1996623877103118428
216
+ hash: -2671600862802291558
137
217
  required_rubygems_version: !ruby/object:Gem::Requirement
138
218
  none: false
139
219
  requirements:
140
- - - ! '>='
220
+ - - ! '>'
141
221
  - !ruby/object:Gem::Version
142
- version: '0'
143
- segments:
144
- - 0
145
- hash: 1996623877103118428
222
+ version: 1.3.1
146
223
  requirements: []
147
224
  rubyforge_project: solusvm
148
225
  rubygems_version: 1.6.2
@@ -150,6 +227,11 @@ signing_key:
150
227
  specification_version: 3
151
228
  summary: Wrapper for the SolusVM Admin::API
152
229
  test_files:
230
+ - test/cli/test_client_cli.rb
231
+ - test/cli/test_general_cli.rb
232
+ - test/cli/test_node_cli.rb
233
+ - test/cli/test_reseller_cli.rb
234
+ - test/cli/test_server_cli.rb
153
235
  - test/fixtures/base_bad_key.txt
154
236
  - test/fixtures/base_node_does_not_exist.txt
155
237
  - test/fixtures/base_unauthorized_ip.txt
@@ -159,29 +241,62 @@ test_files:
159
241
  - test/fixtures/client_change_password_success.txt
160
242
  - test/fixtures/client_create_error.txt
161
243
  - test/fixtures/client_create_success.txt
244
+ - test/fixtures/client_delete_success.txt
162
245
  - test/fixtures/client_exists_success.txt
246
+ - test/fixtures/client_list_success.txt
247
+ - test/fixtures/client_list_success_empty.txt
163
248
  - test/fixtures/error.txt
249
+ - test/fixtures/general_isos_success.txt
164
250
  - test/fixtures/general_node_list_all_ips_available.txt
165
251
  - test/fixtures/general_node_list_all_ips_not_available.txt
166
252
  - test/fixtures/general_node_statistics_success.txt
253
+ - test/fixtures/general_node_virtualservers_success.txt
254
+ - test/fixtures/general_node_virtualservers_success_empty.txt
255
+ - test/fixtures/general_node_xenresources_success.txt
256
+ - test/fixtures/general_nodes_ids_success.txt
167
257
  - test/fixtures/general_nodes_success.txt
258
+ - test/fixtures/general_plans_success.txt
168
259
  - test/fixtures/general_templates_success.txt
260
+ - test/fixtures/reseller_change_resources_success.txt
261
+ - test/fixtures/reseller_create_success.txt
262
+ - test/fixtures/reseller_delete_success.txt
263
+ - test/fixtures/reseller_info_success.txt
264
+ - test/fixtures/reseller_list_success.txt
169
265
  - test/fixtures/server_boot_success.txt
266
+ - test/fixtures/server_bootorder_success.txt
267
+ - test/fixtures/server_change_consolepass_success.txt
268
+ - test/fixtures/server_change_owner_success.txt
170
269
  - test/fixtures/server_change_success.txt
270
+ - test/fixtures/server_change_vncpass_success.txt
271
+ - test/fixtures/server_console_success.txt
171
272
  - test/fixtures/server_create_success.txt
172
273
  - test/fixtures/server_exists_success.txt
274
+ - test/fixtures/server_hostname_success.txt
173
275
  - test/fixtures/server_info_success.txt
174
276
  - test/fixtures/server_infoall_success.txt
277
+ - test/fixtures/server_mountiso_success.txt
278
+ - test/fixtures/server_network_disable_success.txt
279
+ - test/fixtures/server_network_enable_success.txt
280
+ - test/fixtures/server_pae_success.txt
175
281
  - test/fixtures/server_reboot_success.txt
176
282
  - test/fixtures/server_rebuild_success.txt
177
283
  - test/fixtures/server_resume_success.txt
284
+ - test/fixtures/server_rootpassword_success.txt
178
285
  - test/fixtures/server_shutdown_success.txt
286
+ - test/fixtures/server_status_success.txt
179
287
  - test/fixtures/server_suspend_success.txt
180
288
  - test/fixtures/server_terminate_success.txt
289
+ - test/fixtures/server_tun_disable_success.txt
290
+ - test/fixtures/server_tun_enable_success.txt
291
+ - test/fixtures/server_unmountiso_success.txt
292
+ - test/fixtures/server_vnc_success.txt
181
293
  - test/helper.rb
182
294
  - test/test_base.rb
295
+ - test/test_cli.rb
183
296
  - test/test_client.rb
184
297
  - test/test_general.rb
185
298
  - test/test_hash.rb
299
+ - test/test_node.rb
300
+ - test/test_reseller.rb
186
301
  - test/test_server.rb
187
302
  - test/test_solusvm.rb