ganeti_client 0.0.1 → 0.0.2
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/README +13 -6
- data/lib/ganeti_client/client.rb +242 -79
- data/lib/ganeti_client.rb +24 -0
- metadata +3 -3
data/README
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
== License
|
2
|
-
|
3
|
-
This Ruby Ganeti Client is release under AGPL licence (http://www.gnu.org/licenses/agpl-3.0.html)
|
4
|
-
|
5
|
-
|
6
1
|
== Todo
|
7
2
|
|
8
3
|
1. Manually test the following methods + fix when bugs found
|
@@ -17,7 +12,6 @@
|
|
17
12
|
1.9. instance_activate_disks
|
18
13
|
1.10. instance_create_tags
|
19
14
|
1.11. instance_delete_tags
|
20
|
-
1.12. job_get
|
21
15
|
1.13. job_delete
|
22
16
|
1.14. node_evaluate
|
23
17
|
1.15. node_migrate
|
@@ -61,6 +55,7 @@
|
|
61
55
|
info.name
|
62
56
|
=> "hostname"
|
63
57
|
|
58
|
+
|
64
59
|
== Contributing
|
65
60
|
|
66
61
|
1. Fork the project
|
@@ -69,6 +64,18 @@
|
|
69
64
|
4. Send a pull request
|
70
65
|
|
71
66
|
|
67
|
+
== Author
|
68
|
+
|
69
|
+
Michaël Rigart
|
70
|
+
|
71
|
+
|
72
|
+
== License
|
73
|
+
|
74
|
+
This Ruby Ganeti Client is release under AGPL licence (http://www.gnu.org/licenses/agpl-3.0.html)
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
72
79
|
|
73
80
|
|
74
81
|
|
data/lib/ganeti_client/client.rb
CHANGED
@@ -1,23 +1,61 @@
|
|
1
|
+
# The program makes use of the Google Ganeti RAPI to access diffrent resources.
|
2
|
+
#
|
3
|
+
# The Client is mainly developed for usage with the Ganeti RAPI version 2
|
4
|
+
#
|
5
|
+
# The protocol used is JSON over HTTP designed afther the REST principle. HTTP Basic authentication as per RFC2617 is supported
|
6
|
+
#
|
7
|
+
# A few generic refered parameter types and the values they allow:
|
8
|
+
#
|
9
|
+
# bool:
|
10
|
+
# A boolean option will accept 1 or 0 as numbers but not i.e. True or False
|
11
|
+
#
|
12
|
+
# A few parameter mean the same thing accross all resources which implement it:
|
13
|
+
#
|
14
|
+
# bulk:
|
15
|
+
# Bulk-mode means that for the resources which usually return just a list of child resources (e.g. /2/instances which returns just instance names),
|
16
|
+
# the output will instead contain detailed data for all these subresources. This is more efficient than query-ing the sub-resources themselves.
|
17
|
+
#
|
18
|
+
# dry-run:
|
19
|
+
# The boolean dry-run argument, if provided and set, signals to Ganeti that the job should not be executed, only the pre-execution checks will be done.
|
20
|
+
# This is useful in trying to determine (without guarantees though, as in the meantime the cluster state could have changed) if the operation
|
21
|
+
# is likely to succeed or at least start executing.
|
22
|
+
#
|
23
|
+
# force:
|
24
|
+
# Force operation to continue even if it will cause the cluster to become inconsistent (e.g. because there are not enough master candidates).
|
25
|
+
#
|
26
|
+
# Author:: Michaël Rigart (mailto:michael@netronix.be)
|
27
|
+
# Copyright:: Copyright (c) 2010 Michaël Rigart
|
28
|
+
# License:: Distributes under AGPL Licence
|
29
|
+
|
1
30
|
module GanetiClient
|
31
|
+
|
32
|
+
# This class contains all active resources available in Ganeti RAPI
|
2
33
|
class Client
|
3
34
|
|
4
|
-
attr_accessor :
|
35
|
+
attr_accessor :host, :username, :password, :version, :show_response
|
5
36
|
|
37
|
+
# Create the client object
|
38
|
+
#
|
39
|
+
# Parameters:
|
40
|
+
# host: hostname and port
|
41
|
+
# username: username that has access to RAPI
|
42
|
+
# password: password of the user provided
|
43
|
+
# show_response: show response data (optional)
|
6
44
|
def initialize(host, username, password, show_response = false)
|
7
|
-
self.
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
self.
|
12
|
-
|
13
|
-
self.headers = authenticate(username, password)
|
45
|
+
self.host = host
|
46
|
+
self.username = username
|
47
|
+
self.password = password
|
48
|
+
|
49
|
+
self.show_response = show_response
|
14
50
|
|
15
|
-
self.version = self.
|
51
|
+
self.version = self.version_get
|
16
52
|
end
|
17
53
|
|
18
54
|
|
19
|
-
#
|
20
|
-
#
|
55
|
+
# Get the cluster information
|
56
|
+
#
|
57
|
+
# Return:
|
58
|
+
# GanetiInfo object
|
21
59
|
def info_get
|
22
60
|
url = get_url("info")
|
23
61
|
response_body = JSON.parse(send_request("GET", url))
|
@@ -28,7 +66,9 @@ module GanetiClient
|
|
28
66
|
end
|
29
67
|
|
30
68
|
# Redistrite configuration to all nodes
|
31
|
-
#
|
69
|
+
#
|
70
|
+
# Return:
|
71
|
+
# job id.
|
32
72
|
def redistribute_config
|
33
73
|
url = get_url("redistribute-config")
|
34
74
|
response_body = send_request("PUT", url)
|
@@ -36,9 +76,13 @@ module GanetiClient
|
|
36
76
|
return response_body
|
37
77
|
end
|
38
78
|
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
79
|
+
# Get all instances on the cluster
|
80
|
+
#
|
81
|
+
# Parameters:
|
82
|
+
# bulk: 0|1 (optional)
|
83
|
+
#
|
84
|
+
# Return:
|
85
|
+
# Array of all available instances. The array items contain a GanetiInstance object
|
42
86
|
def instances_get(bulk = 0)
|
43
87
|
url = get_url("instances", {"bulk" => bulk})
|
44
88
|
response_body = JSON.parse(send_request("GET", url))
|
@@ -51,23 +95,57 @@ module GanetiClient
|
|
51
95
|
return list
|
52
96
|
end
|
53
97
|
|
54
|
-
#
|
98
|
+
# Create an instance
|
55
99
|
# If the options bool dry-run argument is provided, the job will not be actually executed, only the pre-execution checks will be done.
|
56
100
|
# Query-ing the job result will return, in boty dry-run and normal case, the list of nodes selected for the instance
|
57
|
-
# Returns: a job ID that can be used later for polling
|
58
101
|
#
|
59
|
-
#
|
60
|
-
#
|
102
|
+
# Build parameters dict, optional parameters need to be
|
103
|
+
# excluded to not cause issues with rapi.
|
104
|
+
#
|
105
|
+
# Example:
|
106
|
+
# info = {
|
107
|
+
# 'hypervisor' => 'kvm' , 'disk_template' => 'plain',
|
108
|
+
# 'pnode' => 'node.netronix.be', 'name' => 'vm1.netronix.be', 'os' => 'debootstrap+lucid',
|
109
|
+
# 'vcpus' => '4', 'memory' => '4096', 'disks' => ['25600'],
|
110
|
+
# 'kernel-path' => '/boot/vmlinuz-2.6-kvmU'
|
111
|
+
# }
|
112
|
+
#
|
113
|
+
# Parameters:
|
114
|
+
# info: hash of data needed for the instance creation
|
115
|
+
# dry_run: 0|1 (optional)
|
116
|
+
#
|
117
|
+
# Return:
|
118
|
+
# job_id
|
61
119
|
def instance_create(info, dry_run = 0)
|
120
|
+
params = {
|
121
|
+
'hypervisor' => info['hypervisor'], 'disk_template' => info['disk_template'],
|
122
|
+
'pnode' => info['pnode'], 'name' => info['iname'], 'os' => info['os'],
|
123
|
+
'vcpus' => info['vcpus'], 'memory' => info['memory'], 'disks' => [info['size']]
|
124
|
+
}
|
125
|
+
|
126
|
+
# Add secondary node
|
127
|
+
params['snode'] = info['snode'] if info['disk_template'] == 'drbd' && info['snode']
|
128
|
+
|
129
|
+
# Add PVM parameters
|
130
|
+
if info['hypervisor']
|
131
|
+
params['kernel_path'] = info['kernel_path'] if info['kernel_path']
|
132
|
+
params['initrd_path'] = info['initrd_path'] if info['initrd_path']
|
133
|
+
end
|
134
|
+
|
62
135
|
url = get_url("instances", {"dry-run" => dry_run})
|
63
|
-
body = JSON.generate(
|
136
|
+
body = JSON.generate(params)
|
64
137
|
response_body = send_request("POST", url, body)
|
65
138
|
|
66
139
|
return response_body
|
67
140
|
end
|
68
141
|
|
69
|
-
#
|
70
|
-
#
|
142
|
+
# Get instance specific information, similar to the bulk output from the instance list
|
143
|
+
#
|
144
|
+
# Parameters:
|
145
|
+
# name: name of the instance
|
146
|
+
#
|
147
|
+
# Return
|
148
|
+
# GanetiInstance object
|
71
149
|
def instance_get(name)
|
72
150
|
url = get_url("instances/#{name}")
|
73
151
|
response_body = JSON.parse(send_request("GET", url))
|
@@ -77,19 +155,29 @@ module GanetiClient
|
|
77
155
|
return GanetiInstance.new(response_body)
|
78
156
|
end
|
79
157
|
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
|
158
|
+
# Delete a specific instance
|
159
|
+
#
|
160
|
+
# Parameters:
|
161
|
+
# name: name of the instance
|
162
|
+
# dry_run: 0|1 (optional)
|
163
|
+
#
|
164
|
+
# Return:
|
165
|
+
# ?
|
166
|
+
def instance_delete(name, dry_run = 0)
|
84
167
|
url = get_url("instances/#{name}", {"dry-run" => dry_run})
|
85
168
|
response_body = send_request("DELETE", url)
|
86
169
|
|
87
170
|
return response_body
|
88
171
|
end
|
89
172
|
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
173
|
+
# Get detailed information about an instance. Static parameter can be set to return only static information from the configuration without querying the instance's nodes
|
174
|
+
#
|
175
|
+
# Parameters:
|
176
|
+
# name: name of the instance
|
177
|
+
# static: 0|1 (optional)
|
178
|
+
#
|
179
|
+
# Return:
|
180
|
+
# job id
|
93
181
|
def instance_get_info(name, static = 0)
|
94
182
|
url = get_url("instances/#{name}/info", {"static" => static})
|
95
183
|
response_body = send_request("GET", url)
|
@@ -97,8 +185,7 @@ module GanetiClient
|
|
97
185
|
return response_body
|
98
186
|
end
|
99
187
|
|
100
|
-
#
|
101
|
-
# Reboots the instance
|
188
|
+
# Reboot a specific instance
|
102
189
|
# The URI takes optional type=soft|hard|full and ignore_secondaries=0|1 parameters
|
103
190
|
#
|
104
191
|
# type defines the reboot type.
|
@@ -107,86 +194,130 @@ module GanetiClient
|
|
107
194
|
# full is like hard but also recreates the configuration from ground up as if you would have don a gnt-instance shutdown and gnt-instance start on it
|
108
195
|
#
|
109
196
|
# it supports the dry-run argument
|
197
|
+
#
|
198
|
+
# Parameters:
|
199
|
+
# name: name of the instance
|
200
|
+
# type: soft|hard|full (optional)
|
201
|
+
# ignore_secondaries: 0|1 (optional)
|
202
|
+
# dry_run: 0|1 (optional)
|
203
|
+
#
|
204
|
+
# Return:
|
205
|
+
# ?
|
110
206
|
def instance_reboot(name, type = "soft", ignore_secondaries = 0, dry_run = 0)
|
111
207
|
url = get_url("instances/#{name}/reboot", {"type" => type, "ignore_secondaries" => ignore_secondaries, "dry_run" => 0})
|
112
|
-
response_body =
|
113
|
-
|
114
|
-
create_class("GanetiInstanceReboot")
|
208
|
+
response_body = send_request("POST", url)
|
115
209
|
|
116
|
-
return
|
210
|
+
return response_body
|
117
211
|
end
|
118
212
|
|
119
213
|
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
214
|
+
# Shutdown an instance
|
215
|
+
#
|
216
|
+
# Parameters:
|
217
|
+
# name: name of the instance
|
218
|
+
# dry_run: 0|1 (optional)
|
219
|
+
#
|
220
|
+
# Return:
|
221
|
+
# ?
|
123
222
|
def instance_shutdown(name, dry_run = 0)
|
124
223
|
url = get_url("instances/#{name}/shutdown", {"dry-run" => dry_run})
|
125
|
-
response_body =
|
126
|
-
|
127
|
-
create_class("GanetiInstanceShutdown")
|
224
|
+
response_body = send_request("PUT", url)
|
128
225
|
|
129
|
-
return
|
226
|
+
return response_body
|
130
227
|
end
|
131
228
|
|
132
|
-
# Instance startup URI
|
133
229
|
# Startup an instance
|
134
230
|
# The URI takes an optional force=1|0 parameter to start the instance even if secondary disks are failing
|
135
|
-
#
|
231
|
+
#
|
232
|
+
# Parameters:
|
233
|
+
# name: name of the instance
|
234
|
+
# force: 0|1 (optional)
|
235
|
+
# dry_run: 0|1 (optional)
|
236
|
+
#
|
237
|
+
# Return:
|
238
|
+
# ?
|
136
239
|
def instance_startup(name, force = 0, dry_run=0)
|
137
240
|
url = get_url("instances/#{name}/startup", {"force" => force, "dry-run" => dry_run})
|
138
241
|
body = "" # force parameter
|
139
242
|
response_body = send_request("PUT", url, body)
|
140
243
|
|
141
|
-
|
142
|
-
|
143
|
-
return GanetiInstanceStartup.new(response_body)
|
244
|
+
return response_body
|
144
245
|
end
|
145
246
|
|
146
247
|
# Install the operating system again
|
147
|
-
#
|
148
|
-
|
248
|
+
#
|
249
|
+
# Parameters:
|
250
|
+
# name: name of the instance
|
251
|
+
# os_name: name of the os
|
252
|
+
# nostartup: 0|1 (optional)
|
253
|
+
#
|
254
|
+
# Return:
|
255
|
+
# ?
|
256
|
+
def instance_reinstall(name, os_name, nostartup = 0)
|
149
257
|
url = get_url("instances/#{name}/reinstall", {"os" => os_name, "nostartup" => nostartup})
|
150
258
|
response_body = send_request("POST", url)
|
151
259
|
|
152
|
-
|
153
|
-
|
154
|
-
return GanetiInstanceReinstall.new(response_body)
|
260
|
+
return response_body
|
155
261
|
end
|
156
262
|
|
157
263
|
# Replaces disks on an instance
|
158
264
|
# Takes the parameters mode (one of replace_on_primary, replace_on_secondary or replace_auto), disks (comma seperated list of disk indexes), remote_node and iallocator
|
159
265
|
# Either remote_node or iallocator needs to be defined when using mode=replace_new_secondary
|
160
266
|
# mode is a mandatory parameter. replace_auto tries to determine the broken disk(s) on its own and replacing it
|
267
|
+
#
|
268
|
+
# Parameters:
|
269
|
+
# name: name of the instance
|
270
|
+
# mode replace_on_primary|replace_on_secondary|replace_auto (optional)
|
271
|
+
# ialllocator:
|
272
|
+
# remote_node:
|
273
|
+
# disks: comma seperated list of disk indexes
|
274
|
+
#
|
275
|
+
# Return:
|
276
|
+
# ?
|
161
277
|
def instance_replace_disks(name, mode = "replace_auto", iallocator = "", remote_node = "", disks = "")
|
162
278
|
url = get_url("instances/#{name}/replace-disks", {"mode" => mode, "iallocator" => iallocator, "remote_node" => remote_node, "disks" => disks})
|
163
279
|
response_body = send_request("POST", url)
|
164
280
|
|
165
|
-
return
|
281
|
+
return response_body
|
166
282
|
end
|
167
283
|
|
168
284
|
# Activate disks on an instance
|
169
285
|
# Takes the bool parameter ignore_size. When set ignore the recorded size (useful for forcing activation when recoreded size is wrong)
|
286
|
+
#
|
287
|
+
# Parameters:
|
288
|
+
# name: name of the instance
|
289
|
+
# ignore_size: 0|1 (optional)
|
290
|
+
#
|
291
|
+
# Return:
|
292
|
+
# ?
|
170
293
|
def intance_activate_disks(name, ignore_size = 0)
|
171
294
|
url = get_url("instances/#{name}/activate-disks", {"ignore_size" => ignore_size})
|
172
295
|
response_body = send_request("PUT", url)
|
173
296
|
|
174
|
-
return
|
297
|
+
return response_body
|
175
298
|
end
|
176
299
|
|
177
300
|
# Deactivate disks on an instance
|
178
|
-
#
|
301
|
+
#
|
302
|
+
# Parameters:
|
303
|
+
# name: name of the instance
|
304
|
+
#
|
305
|
+
# Return:
|
306
|
+
# ?
|
179
307
|
def instance_deactivate_disks(name)
|
180
308
|
url = get_url("instances/#{name}/deactivate-disks")
|
181
309
|
response_body = send_request("PUT", url)
|
182
310
|
|
183
|
-
return
|
311
|
+
return response_body
|
184
312
|
end
|
185
313
|
|
186
314
|
# Returns a list of tags
|
187
315
|
#
|
188
|
-
#
|
189
|
-
#
|
316
|
+
# Parameters:
|
317
|
+
# name: name of the instance
|
318
|
+
#
|
319
|
+
# Return:
|
320
|
+
# Array of tags
|
190
321
|
def instance_get_tags(name)
|
191
322
|
url = get_url("instances/#{name}/tags")
|
192
323
|
response_body = JSON.parse(send_request("GET", url))
|
@@ -195,28 +326,41 @@ module GanetiClient
|
|
195
326
|
end
|
196
327
|
|
197
328
|
# Add a set of tags
|
198
|
-
#
|
199
|
-
#
|
200
|
-
|
201
|
-
|
329
|
+
#
|
330
|
+
# Parameters:
|
331
|
+
# name: name of the instance
|
332
|
+
# tags: Array of tags
|
333
|
+
# dry_run: 0|1 (optional)
|
334
|
+
#
|
335
|
+
# Return:
|
336
|
+
# ?
|
337
|
+
def instance_create_tags(name, tags, dry_run = 0)
|
338
|
+
url = get_url("instances/#{name}/tags", {"tags" => tags, "dry-run" => dry_run})
|
202
339
|
response_body = send_request("PUT", url)
|
203
340
|
|
204
341
|
return response_body
|
205
342
|
end
|
206
343
|
|
207
|
-
# Delete a tag
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
|
212
|
-
|
344
|
+
# Delete (a) tag(s) on an instance
|
345
|
+
#
|
346
|
+
# Parameters:
|
347
|
+
# name: name of the instance
|
348
|
+
# tags: Array of tags
|
349
|
+
# dry_run: 0|1 (optional)
|
350
|
+
#
|
351
|
+
# Return:
|
352
|
+
# ?
|
353
|
+
def instance_delete_tags(name, tags, dry_run = 0)
|
354
|
+
url = get_url("instances/#{name}/tags", {"tags" => tags, "dry-run" => dry_run})
|
213
355
|
response_body = send_request("DELETE", url)
|
214
356
|
|
215
|
-
return
|
357
|
+
return response_body
|
216
358
|
end
|
217
359
|
|
218
360
|
# Returns a dictionary of jobs
|
219
|
-
#
|
361
|
+
#
|
362
|
+
# Return:
|
363
|
+
# Array of GanetiJob objects
|
220
364
|
def jobs_get
|
221
365
|
url = get_url("jobs")
|
222
366
|
response_body = JSON.parse(send_request("GET", url))
|
@@ -287,11 +431,17 @@ module GanetiClient
|
|
287
431
|
end
|
288
432
|
|
289
433
|
# Cancel a not-yet-started job
|
434
|
+
#
|
435
|
+
# Parameters:
|
436
|
+
# job_id: id of a job
|
437
|
+
#
|
438
|
+
# Return:
|
439
|
+
# ?
|
290
440
|
def job_delete(job_id)
|
291
441
|
url = get_url("jobs/#{job_id}")
|
292
442
|
response = send_request("DELETE", url)
|
293
443
|
|
294
|
-
return
|
444
|
+
return response_body
|
295
445
|
end
|
296
446
|
|
297
447
|
# Nodes resource
|
@@ -331,7 +481,7 @@ module GanetiClient
|
|
331
481
|
url = get_url("nodes/#{name}/evacuate", {"iallocator" => iallocator, "remote_node" => remote_node})
|
332
482
|
response_body = send_request("POST", url)
|
333
483
|
|
334
|
-
return
|
484
|
+
return response_body
|
335
485
|
end
|
336
486
|
|
337
487
|
# Migrates all primary instances of a node
|
@@ -343,7 +493,7 @@ module GanetiClient
|
|
343
493
|
url = get_url("nodes/#{name}/migrate", {"live" => live})
|
344
494
|
response_body = send_request("POST", url)
|
345
495
|
|
346
|
-
return
|
496
|
+
return response_body
|
347
497
|
end
|
348
498
|
|
349
499
|
# Get the node role
|
@@ -515,9 +665,9 @@ module GanetiClient
|
|
515
665
|
private
|
516
666
|
|
517
667
|
def authenticate(username, password)
|
518
|
-
basic = Base64.
|
668
|
+
basic = Base64.encode64("#{username}:#{password}").strip
|
519
669
|
headers = {'Authorization' => "Basic #{basic}"}
|
520
|
-
|
670
|
+
|
521
671
|
return headers
|
522
672
|
end
|
523
673
|
|
@@ -528,20 +678,33 @@ module GanetiClient
|
|
528
678
|
params.each do |key, value|
|
529
679
|
if value.kind_of?(Array)
|
530
680
|
value.each do |svalue|
|
531
|
-
param_string += "#{key}=#{svalue}"
|
681
|
+
param_string += "#{key}=#{svalue}&"
|
532
682
|
end
|
533
683
|
else
|
534
684
|
param_string += "#{key}=#{value}&"
|
535
685
|
end
|
536
686
|
end
|
537
687
|
end
|
538
|
-
|
688
|
+
|
689
|
+
url = (self.version)? "/#{self.version}/#{path}?#{param_string}" : "/#{path}?#{param_string}"
|
690
|
+
|
691
|
+
return url.chop
|
539
692
|
end
|
540
693
|
|
541
694
|
def send_request(method, url, body = nil)
|
542
|
-
|
695
|
+
uri = URI.parse(host)
|
696
|
+
|
697
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
698
|
+
http.use_ssl = (uri.scheme == "http")? false : true
|
699
|
+
|
700
|
+
headers = {}
|
701
|
+
headers = authenticate(self.username, self.password) if method != 'GET'
|
702
|
+
|
703
|
+
response = http.send_request(method, url, body, headers)
|
704
|
+
|
705
|
+
|
706
|
+
puts "Response #{response.code} #{response.message}: #{response.body}" if self.show_response
|
543
707
|
|
544
|
-
puts "Response #{response.code} #{response.message}: #{response.body}" if self.show_reponse
|
545
708
|
return response.body.strip
|
546
709
|
end
|
547
710
|
|
data/lib/ganeti_client.rb
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010 Michaël Rigart
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
|
1
25
|
require 'net/http'
|
2
26
|
require 'net/https'
|
3
27
|
require 'uri'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ganeti_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Micha\xC3\xABl Rigart"
|