wf_node_api 0.6.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 (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