wf_node_api 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/Gemfile +28 -0
  4. data/Gemfile.lock +66 -0
  5. data/LICENSE +340 -0
  6. data/Makefile +19 -0
  7. data/README.md +78 -0
  8. data/Rakefile +2 -0
  9. data/Vagrantfile +102 -0
  10. data/_docs/api/Api/NodeApi.html +123 -0
  11. data/_docs/api/Api.html +149 -0
  12. data/_docs/api/Config/Check.html +331 -0
  13. data/_docs/api/Config.html +115 -0
  14. data/_docs/api/ConfigCheck.html +365 -0
  15. data/_docs/api/ContainerManager.html +1632 -0
  16. data/_docs/api/ContainerManagerAdapter/Lxc.html +1352 -0
  17. data/_docs/api/ContainerManagerAdapter/Vserver.html +1358 -0
  18. data/_docs/api/ContainerManagerAdapter.html +151 -0
  19. data/_docs/api/Error/NotFound.html +134 -0
  20. data/_docs/api/Error.html +115 -0
  21. data/_docs/api/NotFoundError.html +157 -0
  22. data/_docs/api/OS.html +234 -0
  23. data/_docs/api/ResourceManager.html +322 -0
  24. data/_docs/api/ResourceManagerAdapter/Linux.html +326 -0
  25. data/_docs/api/ResourceManagerAdapter.html +149 -0
  26. data/_docs/api/WfNodeApi.html +163 -0
  27. data/_docs/api/_index.html +239 -0
  28. data/_docs/api/class_list.html +58 -0
  29. data/_docs/api/css/common.css +1 -0
  30. data/_docs/api/css/full_list.css +57 -0
  31. data/_docs/api/css/style.css +339 -0
  32. data/_docs/api/file.README.html +146 -0
  33. data/_docs/api/file_list.html +60 -0
  34. data/_docs/api/frames.html +26 -0
  35. data/_docs/api/index.html +146 -0
  36. data/_docs/api/js/app.js +219 -0
  37. data/_docs/api/js/full_list.js +181 -0
  38. data/_docs/api/js/jquery.js +4 -0
  39. data/_docs/api/method_list.html +273 -0
  40. data/_docs/api/top-level-namespace.html +244 -0
  41. data/_docs/rest/api_data.js +1 -0
  42. data/_docs/rest/api_data.json +1 -0
  43. data/_docs/rest/api_project.js +1 -0
  44. data/_docs/rest/api_project.json +1 -0
  45. data/_docs/rest/css/style.css +538 -0
  46. data/_docs/rest/header.md +3 -0
  47. data/_docs/rest/img/favicon.ico +0 -0
  48. data/_docs/rest/img/glyphicons-halflings-white.png +0 -0
  49. data/_docs/rest/img/glyphicons-halflings.png +0 -0
  50. data/_docs/rest/index.html +658 -0
  51. data/_docs/rest/locales/de.js +25 -0
  52. data/_docs/rest/locales/fr.js +25 -0
  53. data/_docs/rest/locales/locale.js +43 -0
  54. data/_docs/rest/locales/nl.js +25 -0
  55. data/_docs/rest/locales/pl.js +25 -0
  56. data/_docs/rest/locales/pt_br.js +25 -0
  57. data/_docs/rest/locales/ru.js +25 -0
  58. data/_docs/rest/locales/zh.js +25 -0
  59. data/_docs/rest/main.js +691 -0
  60. data/_docs/rest/utils/handlebars_helper.js +327 -0
  61. data/_docs/rest/utils/send_sample_request.js +158 -0
  62. data/_docs/rest/vendor/bootstrap-responsive.min.css +9 -0
  63. data/_docs/rest/vendor/bootstrap.min.css +9 -0
  64. data/_docs/rest/vendor/bootstrap.min.js +6 -0
  65. data/_docs/rest/vendor/diff_match_patch.min.js +49 -0
  66. data/_docs/rest/vendor/handlebars.min.js +28 -0
  67. data/_docs/rest/vendor/jquery.min.js +4 -0
  68. data/_docs/rest/vendor/lodash.min.js +61 -0
  69. data/_docs/rest/vendor/path-to-regexp/LICENSE +21 -0
  70. data/_docs/rest/vendor/path-to-regexp/index.js +205 -0
  71. data/_docs/rest/vendor/polyfill.js +100 -0
  72. data/_docs/rest/vendor/prettify/lang-apollo.js +2 -0
  73. data/_docs/rest/vendor/prettify/lang-basic.js +3 -0
  74. data/_docs/rest/vendor/prettify/lang-clj.js +18 -0
  75. data/_docs/rest/vendor/prettify/lang-css.js +2 -0
  76. data/_docs/rest/vendor/prettify/lang-dart.js +3 -0
  77. data/_docs/rest/vendor/prettify/lang-erlang.js +2 -0
  78. data/_docs/rest/vendor/prettify/lang-go.js +1 -0
  79. data/_docs/rest/vendor/prettify/lang-hs.js +2 -0
  80. data/_docs/rest/vendor/prettify/lang-lisp.js +3 -0
  81. data/_docs/rest/vendor/prettify/lang-llvm.js +1 -0
  82. data/_docs/rest/vendor/prettify/lang-lua.js +2 -0
  83. data/_docs/rest/vendor/prettify/lang-matlab.js +6 -0
  84. data/_docs/rest/vendor/prettify/lang-ml.js +2 -0
  85. data/_docs/rest/vendor/prettify/lang-mumps.js +2 -0
  86. data/_docs/rest/vendor/prettify/lang-n.js +4 -0
  87. data/_docs/rest/vendor/prettify/lang-pascal.js +3 -0
  88. data/_docs/rest/vendor/prettify/lang-proto.js +1 -0
  89. data/_docs/rest/vendor/prettify/lang-r.js +2 -0
  90. data/_docs/rest/vendor/prettify/lang-rd.js +1 -0
  91. data/_docs/rest/vendor/prettify/lang-scala.js +2 -0
  92. data/_docs/rest/vendor/prettify/lang-sql.js +2 -0
  93. data/_docs/rest/vendor/prettify/lang-tcl.js +3 -0
  94. data/_docs/rest/vendor/prettify/lang-tex.js +1 -0
  95. data/_docs/rest/vendor/prettify/lang-vb.js +2 -0
  96. data/_docs/rest/vendor/prettify/lang-vhdl.js +3 -0
  97. data/_docs/rest/vendor/prettify/lang-wiki.js +2 -0
  98. data/_docs/rest/vendor/prettify/lang-xq.js +3 -0
  99. data/_docs/rest/vendor/prettify/lang-yaml.js +2 -0
  100. data/_docs/rest/vendor/prettify/prettify.css +1 -0
  101. data/_docs/rest/vendor/prettify/prettify.js +30 -0
  102. data/_docs/rest/vendor/prettify/run_prettify.js +34 -0
  103. data/_docs/rest/vendor/prettify.css +101 -0
  104. data/_docs/rest/vendor/require.min.js +36 -0
  105. data/apidoc.json +23 -0
  106. data/bin/wf_node_api +80 -0
  107. data/lib/wf_node_api/api/node_api.rb +360 -0
  108. data/lib/wf_node_api/config/config_template.erb +50 -0
  109. data/lib/wf_node_api/config_check.rb +50 -0
  110. data/lib/wf_node_api/container_manager.rb +286 -0
  111. data/lib/wf_node_api/container_manager_adapter/lxc.rb +431 -0
  112. data/lib/wf_node_api/container_manager_adapter/vserver.rb +448 -0
  113. data/lib/wf_node_api/error/not_found.rb +27 -0
  114. data/lib/wf_node_api/os.rb +32 -0
  115. data/lib/wf_node_api/resource_manager.rb +49 -0
  116. data/lib/wf_node_api/resource_manager_adapter/linux.rb +53 -0
  117. data/lib/wf_node_api/translations.rb +38 -0
  118. data/lib/wf_node_api/version.rb +28 -0
  119. data/lib/wf_node_api.rb +57 -0
  120. data/manifests/files/dhozac-vserver.repo +4 -0
  121. data/wf-node-api.gemspec +53 -0
  122. metadata +221 -0
@@ -0,0 +1,431 @@
1
+ =begin
2
+ __ ___ _ _ _____ ____ __ __
3
+ \ \ / / |__ (_) |_ ___| ___| _ ___ ___ / ___| \/ |
4
+ \ \ /\ / /| '_ \| | __/ _ \ |_ | | | / __|/ _ \ | | |\/| |
5
+ \ V V / | | | | | || __/ _|| |_| \__ \ __/ |___| | | |
6
+ \_/\_/ |_| |_|_|\__\___|_| \__,_|___/\___|\____|_| |_|
7
+ Container Manager
8
+
9
+ Copyright (C) 2015 David Prandzioch <kontakt@davidprandzioch.de>
10
+
11
+ This program is free software; you can redistribute it and/or
12
+ modify it under the terms of the GNU General Public License
13
+ as published by the Free Software Foundation; either version 2
14
+ of the License, or (at your option) any later version.
15
+
16
+ This program is distributed in the hope that it will be useful,
17
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ GNU General Public License for more details.
20
+
21
+ You should have received a copy of the GNU General Public License
22
+ along with this program; if not, write to the Free Software
23
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24
+ =end
25
+
26
+ module ContainerManagerAdapter
27
+ # Container adapter for LXC. Encapsulates all LXC specific command line
28
+ # wrapping
29
+ class Lxc
30
+ # Lists all available containers
31
+ #
32
+ # @return [Array]
33
+ def containers
34
+ container_list = []
35
+
36
+ self.container_names.each do |item|
37
+ container_list << container(item)
38
+ end
39
+
40
+ container_list
41
+ end
42
+
43
+ # Starts a container with the given name
44
+ #
45
+ # @param name [String] The container name
46
+ #
47
+ # @raise [RuntimeError]
48
+ #
49
+ # @return [String] CLI output
50
+ def start(name)
51
+ res = Open3.capture3($lxc_cmd_start.gsub('[name]', name))
52
+
53
+ if res[1].empty? && res[2].exitstatus == 0
54
+ $logger.info("container " + name + " successfully started")
55
+ return res[0].strip
56
+ end
57
+
58
+ $logger.warn("container " + name + " could not be started")
59
+ raise RuntimeError, res[1].strip
60
+ end
61
+
62
+ # Stops a container with the given name
63
+ #
64
+ # @param name [String] The container name
65
+ #
66
+ # @raise [RuntimeError]
67
+ #
68
+ # @return [String] CLI output
69
+ def stop(name)
70
+ res = Open3.capture3($lxc_cmd_stop.gsub('[name]', name))
71
+
72
+ if res[1].empty? && res[2].exitstatus == 0
73
+ $logger.info("container " + name + " successfully stopped")
74
+ return res[0].strip
75
+ end
76
+
77
+ $logger.warn("container " + name + " could not be stopped")
78
+ raise RuntimeError, res[1].strip
79
+ end
80
+
81
+ # Kills a container with the given name
82
+ #
83
+ # @param name [String] The container name
84
+ #
85
+ # @raise [RuntimeError]
86
+ #
87
+ # @return [String] CLI output
88
+ def kill(name)
89
+ res = Open3.capture3($lxc_cmd_kill.gsub('[name]', name))
90
+
91
+ if res[1].empty? && res[2].exitstatus == 0
92
+ $logger.info("container " + name + " successfully killed")
93
+ return res[0].strip
94
+ end
95
+
96
+ $logger.warn("container " + name + " could not be killed")
97
+ raise RuntimeError, res[1].strip
98
+ end
99
+
100
+ # Deletes a container with the given name
101
+ #
102
+ # @param name [String] The container name
103
+ #
104
+ # @raise [RuntimeError]
105
+ #
106
+ # @return [String] CLI output
107
+ def delete(name)
108
+ res = Open3.capture3($lxc_cmd_destroy.gsub('[name]', name))
109
+
110
+ if res[1].empty? && res[2].exitstatus == 0
111
+ $logger.info("container " + name + " successfully deleted")
112
+ return res[0].strip
113
+ end
114
+
115
+ $logger.warn("container" + name + " could not be deleted")
116
+ raise RuntimeError, res[1].strip
117
+ end
118
+
119
+ # Returns information for a single container
120
+ #
121
+ # @param name [String] The container name
122
+ #
123
+ # @return [Hash] Hash with information about the container
124
+ def container(name)
125
+ data = {}
126
+
127
+ data[:name] = name
128
+ data[:state] = translate_state(state(name))
129
+ data[:ip_address] = ip_addr(name)
130
+ data[:cpu_cores] = assigned_cpu_cores(name).count
131
+ data[:memory_limit_bytes] = memory_limit(name).to_i
132
+ data[:memory_usage_bytes] = memory_usage(name).to_i
133
+ data[:disk_space_gb] = 0
134
+ data[:disk_usage_gb] = 0
135
+ data[:container_type] = 'lxc'
136
+
137
+ data
138
+ end
139
+
140
+ # Creates a container with the given parameters
141
+ #
142
+ # @param name [String] The container name
143
+ # @param ip_address [String] A valid IPv4 address
144
+ # @param disk_size_gb [Integer] The disk size in GB
145
+ # @param memory_limit_mb [Integer] The memory limit in MB
146
+ # @param cpu_core_count [Integer] Amount of Vcores to assign. 0 if no limit should be set
147
+ #
148
+ # @raise [RuntimeError]
149
+ #
150
+ # @return [String] CLI output
151
+ def create_container(name, ip_address, disk_size_gb, memory_limit_mb, cpu_core_count)
152
+ output = ''
153
+
154
+ begin
155
+ if cpu_core_count != 0
156
+ cpuset = self.generate_cpu_set(cpu_core_count, ResourceManager.new('linux'))
157
+ end
158
+
159
+ create_result = Open3.capture3($lxc_cmd_create.gsub('[name]', name))
160
+
161
+ output += create_result[0]
162
+ output += create_result[1]
163
+
164
+ config_path = $lxc_container_config_path.gsub('[name]', name)
165
+
166
+ open(config_path, 'a') do |f|
167
+ f << "lxc.network.ipv4=#{ip_address}/#{$lxc_ip_netmask}\n"
168
+ f << "lxc.start.auto=1\n"
169
+ f << "lxc.start.delay=5\n"
170
+
171
+ if cpu_core_count != 0
172
+ f << "lxc.cgroup.cpuset.cpus=#{cpuset}\n"
173
+ end
174
+
175
+ f << "lxc.cgroup.memory.limit_in_bytes=#{memory_limit_mb.to_s}M\n"
176
+ end
177
+
178
+ $logger.info("creation of container " + name + " successful")
179
+ return output.strip
180
+ rescue => e
181
+ $logger.warn("container " + name + " could not be created, rolling back...")
182
+
183
+ # rollback
184
+ delete(name) if exist?(name)
185
+
186
+ output += e.message
187
+ raise RuntimeError, output.strip
188
+ end
189
+ end
190
+
191
+ # Checks if a container with the given name exists
192
+ #
193
+ # @param name [String] The container name
194
+ #
195
+ # @return [Boolean] Existing/not existing
196
+ def exist?(name)
197
+ self.container_names().include?(name)
198
+ end
199
+
200
+ # Returns the amount of free cpu cores
201
+ #
202
+ # @param resman [ResourceManager] A ResourceManager instance
203
+ #
204
+ # @return [Integer] The amount of free cpu cores
205
+ def free_cpu_core_count(resman)
206
+ self.free_cpu_cores(resman).count
207
+ end
208
+
209
+ protected
210
+ # Reads the config file of a LXC container
211
+ #
212
+ # @param name [String] The container name
213
+ #
214
+ # @raise [StandardError]
215
+ #
216
+ # @return [String] Contents of the configuration file
217
+ def read_config(name)
218
+ config = config_path(name)
219
+
220
+ if File.exists?(config) == false
221
+ raise 'config file does not exist'
222
+ end
223
+
224
+ if File.readable?(config) == false
225
+ raise 'config file not readable'
226
+ end
227
+
228
+ File.read(config)
229
+ end
230
+
231
+ # Returns the config path for a container
232
+ #
233
+ # @param name [String] The container name
234
+ #
235
+ # @return [String] The config path
236
+ def config_path(name)
237
+ $lxc_container_config_path.gsub('[name]', name)
238
+ end
239
+
240
+ # Returns the info string for a container using lxc-info
241
+ #
242
+ # @param name [String] The container name
243
+ #
244
+ # @return [String] info string
245
+ def get_info(name)
246
+ output = `#{$lxc_cmd_info.gsub('[name]', name)}`
247
+
248
+ if output.nil?
249
+ return ''
250
+ end
251
+
252
+ return output
253
+ end
254
+
255
+ # Returns the assigned IP addresses for the container
256
+ #
257
+ # @param name [String] The container name
258
+ #
259
+ # @return [String|NilClass] IP address
260
+ def ip_addr(name)
261
+ result = get_info(name).scan(/^IP:\s+([0-9.]+)$/).flatten
262
+
263
+ result.first.nil? || result.first.empty? ? nil : result.first
264
+ end
265
+
266
+ # Returns the state of the container
267
+ #
268
+ # @param name [String] The container name
269
+ #
270
+ # @return [String|NilClass] State
271
+ def state(name)
272
+ result = get_info(name).scan(/^State:\s+([A-Za-z]+)$/).flatten
273
+
274
+ result.first.nil? || result.first.empty? ? nil : result.first
275
+ end
276
+
277
+ # Returns cgroup information for a container
278
+ #
279
+ # @param name [String] The container name
280
+ # @param param [String] The cgroup parameter to read
281
+ #
282
+ # @return [String|NilClass] The result
283
+ def cgroup_info(name, param)
284
+ res = `lxc-cgroup -n #{name} #{param}`
285
+ res.empty? ? nil : res.strip
286
+ end
287
+
288
+ # Returns the current cpu usage for a container
289
+ #
290
+ # @param name [String] The container name
291
+ #
292
+ # @return [String|NilClass] CPU usage
293
+ def cpu_usage(name)
294
+ cgroup_info(name, 'cpuacct.usage')
295
+ end
296
+
297
+ # Returns the memory limit for a container
298
+ #
299
+ # @param name [String] The container name
300
+ #
301
+ # @return [Integer|NilClass] Memory limit in bytes
302
+ def memory_limit(name)
303
+ cgroup_info(name, 'memory.limit_in_bytes').to_i
304
+ end
305
+
306
+ # Returns the memory usage for a container
307
+ #
308
+ # @param name [String] The container name
309
+ #
310
+ # @return [Integer|NilClass] Memory usage in bytes
311
+ def memory_usage(name)
312
+ cgroup_info(name, 'memory.usage_in_bytes').to_i
313
+ end
314
+
315
+ # Returns a list of all container names
316
+ #
317
+ # @return [Array] Container names
318
+ def container_names
319
+ `#{$lxc_cmd_ls}`.lines.map(&:strip).uniq
320
+ end
321
+
322
+ # Returns an array with assigned CPU core ids
323
+ #
324
+ # return [Array] assigned cpu cores
325
+ def globally_assigned_cpu_cores
326
+ assigned_cores = []
327
+
328
+ container_names.each do |c|
329
+ self.assigned_cpu_cores(c).each do |cpu|
330
+ assigned_cores.push(cpu)
331
+ end
332
+ end
333
+
334
+ assigned_cores
335
+ end
336
+
337
+ # Reads a config value from the containers config file
338
+ #
339
+ # @param name [String] The container name
340
+ # @param regex [Regexp] The pattern to search for
341
+ #
342
+ # @return [Boolean|String] The config result
343
+ def read_config_value(name, regex)
344
+ config = self.read_config(name)
345
+ line = config.scan(regex)
346
+
347
+ if line.empty?
348
+ return false
349
+ end
350
+
351
+ value = line.flatten.first.strip
352
+
353
+ value
354
+ end
355
+
356
+ # Returns an array with Vcores assigend to a container
357
+ #
358
+ # @param name [String] The container name
359
+ #
360
+ # @return [Array] Array with cpu core ids
361
+ def assigned_cpu_cores(name)
362
+ value = self.read_config_value(name, /lxc\.cgroup\.cpuset\.cpus\s*\=(.*)$/)
363
+ used_cores = []
364
+
365
+ if value == false
366
+ return used_cores
367
+ end
368
+
369
+ value.split(',').each do |cpu|
370
+ if cpu.include?('-')
371
+ range = Range.new(*cpu.split('-').map(&:to_i))
372
+
373
+ range.each do |i|
374
+ used_cores.push(i)
375
+ end
376
+ else
377
+ used_cores.push(cpu.to_i)
378
+ end
379
+ end
380
+
381
+ used_cores
382
+ end
383
+
384
+ # Returns an array of unassigned cpu cores
385
+ #
386
+ # @param resman [ResourceManager] A ResourceManager instance
387
+ #
388
+ # @raise [ArgumentError]
389
+ #
390
+ # @return [Array]
391
+ def free_cpu_cores(resman)
392
+ raise ArgumentError, 'no valid ResourceManager instance' unless resman.is_a?(ResourceManager)
393
+
394
+ free_cores = []
395
+ used_cores = self.globally_assigned_cpu_cores()
396
+
397
+ (0..(resman.total_cpu_cores-1)).each do |i|
398
+ if used_cores.include?(i) == false
399
+ free_cores.push(i)
400
+ end
401
+ end
402
+
403
+ free_cores
404
+ end
405
+
406
+ # Generates a cpu set for a container
407
+ #
408
+ # @param count [Integer] Number of cpu cores to use
409
+ # @param resman [ResourceManager] A ResourceManager instance
410
+ #
411
+ # @raise [RuntimeError]
412
+ #
413
+ # @return [String] cpuset string
414
+ def generate_cpu_set(count, resman)
415
+ count = count.to_i
416
+ free_cores = self.free_cpu_cores(resman)
417
+
418
+ if count > free_cores.count
419
+ raise RuntimeError, 'not enough cpu cores left'
420
+ end
421
+
422
+ cores_to_use = []
423
+
424
+ count.times do
425
+ cores_to_use.push(free_cores.shift)
426
+ end
427
+
428
+ cores_to_use.join(',')
429
+ end
430
+ end
431
+ end