corosync 0.0.3 → 0.1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec6cba2dc8f6d3d85accfe0821274b1bc3268e2a
4
- data.tar.gz: 05257c44ede07405190a84caaf509be8216095d6
3
+ metadata.gz: d3b3eae3af47a1cbdf65addeeb1a19236e86836c
4
+ data.tar.gz: 70b0ad25954babd0459a34564688779de7aa5946
5
5
  SHA512:
6
- metadata.gz: 16c7e8a4d0ac77525d5ba950d9f04b025d0424c07b39ba613ec70843f5ce80acf2eab56bc1762923fc5b0a22ad29710314553e8841322890338118755c12eebf
7
- data.tar.gz: e818f76f4e96fa0bf648a62767c3ca7208646b36d319d4ae81c58d578a4a62a6c5fac6a08a22ed573e2994ba635c812a4b944f9b9749f75dd0382258a697bffe
6
+ metadata.gz: 0cc2801a58368731e1411b12f834980a90c9931df3e8c2c7d1b6ef9c79dcc840def720919f4d3b4db0c7890c070aa1cdacdfb1e371001a0c35cdbb20cff3b5f0
7
+ data.tar.gz: 88c1f83e655f0c8e30e723cc857a034f2d0296fc0e75b8371daec93e2e3bf4350746b9eab0eabf1981d2baf9beb176bf60ad7db4ea46700f33d988fe8bce5e3c
data/Gemfile.lock CHANGED
@@ -1,6 +1,6 @@
1
1
  GIT
2
2
  remote: git://github.com/ffi/ffi-swig-generator.git
3
- revision: 818e921fc85aa58550c15e558af81edd1e8e9f69
3
+ revision: b420db1b6f2b9530ed88fc641de350cac92d47ac
4
4
  specs:
5
5
  ffi-swig-generator (0.3.3)
6
6
  nokogiri (>= 1.6.0)
@@ -10,12 +10,12 @@ GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
12
  diff-lcs (1.2.4)
13
- ffi (1.9.0)
13
+ ffi (1.9.3)
14
14
  json (1.8.1)
15
15
  mini_portile (0.5.2)
16
- nokogiri (1.6.0)
16
+ nokogiri (1.6.1)
17
17
  mini_portile (~> 0.5.0)
18
- rake (10.1.0)
18
+ rake (10.1.1)
19
19
  rdoc (4.0.1)
20
20
  json (~> 1.4)
21
21
  rspec (2.14.1)
data/README.md CHANGED
@@ -10,26 +10,45 @@ Corosync is a cluster communication engine for communication between nodes opera
10
10
  * **SAM** - SAM is a service which is meant to ensure processes remain operational. You can instruct corosync to start a process, and should that process die, corosync can notify you, and start it back up.
11
11
  * **Quorum** - The quorum service is a very basic service used for keeping track of whether the cluster has quorum or not.
12
12
  * **Votequorum** - The votequorum service is a more powerful version of the quorum service. It allows you to control the number of votes provided by any one node such that you can control the logic that determines whether the cluster is quorate.
13
- * **CMAP** - CMAP is a key/value store. It allows you to store keys & their values, and subscribe to changes made to those keys.
13
+ * **CMAP** - CMAP is a key/value store of the Corosync configuration database. You can get/set keys/values and watch them for changes.
14
14
 
15
15
 
16
16
  Corosync offers these services through a C API. This gem utilizes [ffi](http://github.com/ffi/ffi) to provide the interface to that API.
17
17
 
18
+
18
19
  ## State
19
20
 
20
- Currently the only supported service is CPG. It is fully functional, though it may change as it is very young.
21
+ CPG, Quorum, Votequorum, and CMAP are feature complete. Possibly with bugs, but I personally use CPG very heavily, along the basic features of Quorum and CMAP.
21
22
 
22
23
  ## Examples
23
24
 
24
- ### CPG
25
+ There are fully functional example scripts in the `examples` directory. But below are some brief snippets.
25
26
 
27
+ ### CPG
26
28
  require 'corosync/cpg'
27
29
  cpg = Corosync::CPG.new('mygroup')
28
- cpg.on_message do |message, membership|
29
- puts "Received #{message}"
30
+ cpg.on_message do |message, sender|
31
+ puts "Received #{message} from #{sender.nodeid}:#{sender.pid}"
30
32
  end
31
33
  puts "Member node IDs: #{cpg.members.map {|m| m.nodeid}.join(" ")}"
32
34
  cpg.send "hello"
33
35
  loop do
34
36
  cpg.dispatch
35
37
  end
38
+
39
+ ### CMAP
40
+ require 'corosync/cmap'
41
+ cmap = Corosync::CMAP.new
42
+ cmap.set('mykey.foo', :int32, -1234)
43
+ puts "mykey.foo is #{cmap.get('mykey.foo')}"
44
+
45
+
46
+ ## Contributions
47
+ I welcome any contributions in the form of bugs or pull requests. One thing to be aware of in terms of new features is that the goal of this gem is to provide a low level interface to the Corosync library. Features which simplify or abstract things should be implemented as separate gems. For example, my own [corosync commander](http://github.com/phemmer/ruby-corosync-commander).
48
+
49
+
50
+ ## Versioning
51
+
52
+ I use a slight variant of [semantic versioning](http://semver.org). The scheme is: DESIGN.MAJOR.MINOR.PATCH
53
+
54
+ The DESIGN is used to indicate when not only is it no longer backwards compatable, but the fundamental design has changed. Meaning that it may take more than a little tweaking to accomidate for the changes. This should rarely, if ever, happen.
data/Rakefile CHANGED
@@ -22,7 +22,7 @@ rule '.rb' => ['.i'] do |t|
22
22
  end
23
23
  end
24
24
 
25
- corosync_services = ['cpg']
25
+ corosync_services = ['cpg','quorum','votequorum', 'cmap']
26
26
  multitask :ffi => ['ffi/common.rb'] + corosync_services.map{|s| "ffi/#{s}.rb"}
27
27
 
28
28
  ########################################
@@ -32,3 +32,64 @@ RSpec::Core::RakeTask.new(:test) do |t|
32
32
  t.pattern = 'spec/**/*.rb'
33
33
  t.rspec_opts = '-c -f d --fail-fast'
34
34
  end
35
+
36
+ ########################################
37
+ @gemspec_file = Dir.glob('*.gemspec').first
38
+ def spec
39
+ require 'rubygems' unless defined? Gem::Specification
40
+ @spec ||= eval(File.read(@gemspec_file))
41
+ end
42
+
43
+ desc 'Bump version'
44
+ task 'version' do
45
+ current_version = File.read('VERSION').chomp
46
+ current_version_commit = %x{git rev-parse --verify #{current_version} 2>/dev/null}.chomp
47
+ current_head_commit = %x{git rev-parse HEAD}.chomp
48
+ if current_version_commit != '' and current_version_commit != current_head_commit then
49
+ # there have been commits since the current version
50
+
51
+ next_version = current_version.split('.')
52
+ next_version[-1] = next_version.last.to_i + 1
53
+ next_version = next_version.join('.')
54
+ print "Next version? (#{next_version}): "
55
+ response = STDIN.gets.chomp
56
+ if response != '' then
57
+ raise StandardError, "Not a valid version" unless response.match(/^[0-9\.]+$/)
58
+ next_version = response
59
+ end
60
+
61
+ File.open('VERSION', 'w') do |file|
62
+ file.puts next_version
63
+ end
64
+ message = %x{git log #{current_version_commit}..HEAD --pretty=format:'* %s%n %an (%ai) - @%h%n'}.gsub(/'/, "'\\\\''")
65
+
66
+ sh "git commit -m 'Version: #{next_version}\n\n#{message}' VERSION"
67
+ sh "git tag #{next_version}"
68
+
69
+ @spec = nil
70
+ end
71
+ end
72
+
73
+ desc 'Build gem file'
74
+ task 'build' do
75
+ sh "gem build #{@gemspec_file}"
76
+ end
77
+
78
+ desc 'Publish gem file'
79
+ task 'publish' do
80
+ gem_file = "#{spec.name}-#{spec.version}.gem"
81
+ sh "git push"
82
+ sh "gem push #{gem_file}"
83
+ end
84
+
85
+ desc 'Release a new version'
86
+ task 'release' do
87
+ raise StandardError, "Not on master branch" if %x{git rev-parse --abbrev-ref HEAD}.chomp != "master"
88
+ raise StandardError, "Uncommitted files" if %x{git status --porcelain}.chomp.size != 0
89
+
90
+ [:test, :version, :build, :publish].each do |task|
91
+ puts "# #{task}\n"
92
+ Rake::Task[task].execute
93
+ puts "\n"
94
+ end
95
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0.0
data/corosync.gemspec CHANGED
@@ -1,6 +1,4 @@
1
- require File.expand_path('../lib/version.rb', __FILE__)
2
-
3
- Gem::Specification.new 'corosync', Corosync::GEM_VERSION do |s|
1
+ Gem::Specification.new 'corosync', File.read('VERSION').chomp do |s|
4
2
  s.description = 'An interface to the Corosync clustering services.'
5
3
  s.summary = 'Corosync library interface'
6
4
  s.homepage = 'http://github.com/phemmer/ruby-corosync/'
@@ -0,0 +1,72 @@
1
+ $:.unshift(File.expand_path('../../lib', __FILE__))
2
+ require 'corosync/cmap'
3
+
4
+ def list
5
+ cmap = Corosync::CMAP.new(true)
6
+
7
+ cmap.keys.each do |key|
8
+ type, value = cmap.get(key)
9
+ puts "#{key} (#{type}) = #{value.inspect}"
10
+ end
11
+ end
12
+ def get(key)
13
+ cmap = Corosync::CMAP.new(true)
14
+
15
+ type, value = cmap.get(key)
16
+ puts "#{key} (#{type}) = #{value.inspect}"
17
+ end
18
+ def set(key, type, value)
19
+ cmap = Corosync::CMAP.new(true)
20
+
21
+ if type == 'float' or type == 'double' then
22
+ value = value.to_f
23
+ elsif type.match(/^u?int(8|16|32|64)$/) then
24
+ value = value.to_i
25
+ end
26
+
27
+ cmap.set(key, type.to_sym, value)
28
+ end
29
+ def delete(key)
30
+ cmap = Corosync::CMAP.new(true)
31
+
32
+ cmap.delete(key)
33
+ end
34
+ def inc(key)
35
+ cmap = Corosync::CMAP.new(true)
36
+
37
+ cmap.inc(key)
38
+ end
39
+ def dec(key)
40
+ cmap = Corosync::CMAP.new(true)
41
+
42
+ cmap.dec(key)
43
+ end
44
+ def track(key)
45
+ cmap = Corosync::CMAP.new(true)
46
+
47
+ cmap.track_add(key, [:add, :delete, :modify]) do |action, key, value_type, value, value_old_type, value_old|
48
+ puts "action=#{action.inspect} key=#{key.inspect} value_type=#{value_type.inspect} value=#{value.inspect} value_old_type=#{value_old_type.inspect} value_old=#{value_old.inspect}"
49
+ end
50
+
51
+ loop do
52
+ cmap.dispatch
53
+ end
54
+ end
55
+
56
+ args = ARGV.dup
57
+ action = args.shift
58
+ if action == 'list' then
59
+ list(*args)
60
+ elsif action == 'get' then
61
+ get(*args)
62
+ elsif action == 'set' then
63
+ set(*args)
64
+ elsif action == 'delete' then
65
+ delete(*args)
66
+ elsif action == 'inc' then
67
+ inc(*args)
68
+ elsif action == 'dec' then
69
+ dec(*args)
70
+ elsif action == 'track' then
71
+ track(*args)
72
+ end
@@ -0,0 +1,75 @@
1
+ $:.unshift(File.expand_path('../../lib', __FILE__))
2
+ require 'corosync/cpg'
3
+ require 'json'
4
+
5
+ require 'zlib'
6
+ def makeid(member)
7
+ Zlib::crc32("#{member.nodeid} #{member.pid}").to_s(36)
8
+ end
9
+
10
+ names = {}
11
+
12
+ cpg = Corosync::CPG.new
13
+ cpg.on_message do |message, sender|
14
+ id = makeid(sender)
15
+ message = JSON.parse(message)
16
+ if message['type'] == 'name' then
17
+ $stdout.puts "! #{names[id] || id} is now known as #{message['data']}" if names[id] != message['data']
18
+ names[id] = message['data']
19
+ elsif message['type'] == 'text' then
20
+ name = names[id] || id
21
+ $stdout.puts "#{name}: #{message['data']}"
22
+ end
23
+ end
24
+
25
+ cpg.on_confchg do |member_list, left_list, join_list|
26
+ if join_list.include?(cpg.member) then
27
+ # we just joined the cluster
28
+ other_list = member_list.to_a - [cpg.member]
29
+ $stdout.puts "! we joined the cluster. Our name: #{makeid(cpg.member)}. Other members: #{other_list.map{|m| makeid(m)}.join(', ')}"
30
+ elsif join_list.size > 0 then
31
+ # someone else joined the cluster
32
+ join_list.each do |m|
33
+ id = makeid(m)
34
+ $stdout.puts "! #{names[id] || id} joined the cluster"
35
+ names.delete(m)
36
+ end
37
+
38
+ # send our name if we have one
39
+ id = makeid(cpg.member)
40
+ cpg.send({'type' => 'name', 'data' => names[id]}.to_json) if names[id]
41
+ end
42
+ left_list.each do |m|
43
+ id = makeid(m)
44
+ $stdout.puts "! #{names[id] || id} left the cluster"
45
+ names.delete(m)
46
+ end
47
+ end
48
+
49
+ cpg.join($0)
50
+
51
+ ios = [$stdin, cpg.fd]
52
+ inbuf = ''
53
+ while ios.size > 0 do
54
+ ios_ready = IO.select(ios)
55
+ if ios_ready[0].include?(cpg.fd) then
56
+ cpg.dispatch
57
+ end
58
+ if ios_ready[0].include?($stdin) then
59
+ inbuf << $stdin.read_nonblock(1024)
60
+ while line = inbuf.slice!(/^(.*)#{$/}/) do
61
+ if line.match(/^\/name (.+)/) then
62
+ cpg.send({'type' => 'name', 'data' => $1}.to_json)
63
+ elsif line.match(/^\/names/) then
64
+ members = []
65
+ cpg.members.each do |m|
66
+ id = makeid(m)
67
+ members << (names[id] || id)
68
+ end
69
+ $stdout.puts "! names: #{members.join(', ')}"
70
+ else
71
+ cpg.send({'type' => 'text', 'data' => line}.to_json)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -2,7 +2,7 @@ $:.unshift(File.expand_path('../../lib', __FILE__))
2
2
  require 'corosync/cpg'
3
3
 
4
4
  cpg = Corosync::CPG.new
5
- cpg.on_message do |message, nodeid, pid|
5
+ cpg.on_message do |message, sender|
6
6
  puts "MESSAGE: #{message.inspect}"
7
7
  end
8
8
  cpg.on_confchg do |member_list, left_list, joined_list|
data/ffi/cmap.i ADDED
@@ -0,0 +1,14 @@
1
+ %module Corosync
2
+ %{
3
+ require File.expand_path('../common.rb', __FILE__)
4
+
5
+ module Corosync
6
+ extend FFI::Library
7
+ ffi_lib 'libcmap'
8
+
9
+ %}
10
+ %import "common.i"
11
+ %include <corosync/cmap.h>
12
+ %{
13
+ end
14
+ %}
data/ffi/cmap.rb ADDED
@@ -0,0 +1,91 @@
1
+
2
+ require File.expand_path('../common.rb', __FILE__)
3
+
4
+ module Corosync
5
+ extend FFI::Library
6
+ ffi_lib 'libcmap'
7
+
8
+ typedef :uint64, :cmap_handle_t
9
+ typedef :uint64, :cmap_iter_handle_t
10
+ typedef :uint64, :cmap_track_handle_t
11
+ CMAP_KEYNAME_MAXLEN = 255
12
+ CMAP_KEYNAME_MINLEN = 3
13
+ CMAP_TRACK_ADD = 4
14
+ CMAP_TRACK_DELETE = 1
15
+ CMAP_TRACK_MODIFY = 2
16
+ CMAP_TRACK_PREFIX = 8
17
+ CMAP_VALUETYPE_INT8 = 1
18
+ CMAP_VALUETYPE_UINT8 = 2
19
+ CMAP_VALUETYPE_INT16 = 3
20
+ CMAP_VALUETYPE_UINT16 = 4
21
+ CMAP_VALUETYPE_INT32 = 5
22
+ CMAP_VALUETYPE_UINT32 = 6
23
+ CMAP_VALUETYPE_INT64 = 7
24
+ CMAP_VALUETYPE_UINT64 = 8
25
+ CMAP_VALUETYPE_FLOAT = 9
26
+ CMAP_VALUETYPE_DOUBLE = 10
27
+ CMAP_VALUETYPE_STRING = 11
28
+ CMAP_VALUETYPE_BINARY = 12
29
+ cmap_value_types_t = enum :cmap_value_types_t, [
30
+ :int8, 1,
31
+ :uint8, 2,
32
+ :int16, 3,
33
+ :uint16, 4,
34
+ :int32, 5,
35
+ :uint32, 6,
36
+ :int64, 7,
37
+ :uint64, 8,
38
+ :float, 9,
39
+ :double, 10,
40
+ :string, 11,
41
+ :binary, 12,
42
+ ]
43
+
44
+ class CmapNotifyValue < FFI::Struct
45
+ layout(
46
+ :type, :cmap_value_types_t,
47
+ :len, :size_t,
48
+ :data, :pointer
49
+ )
50
+ end
51
+ Callback_cmap_notify_fn_t = callback(:cmap_notify_fn_t, [ :cmap_handle_t, :cmap_track_handle_t, :int32, :string, CmapNotifyValue.by_value, CmapNotifyValue.by_value, :pointer ], :void)
52
+ attach_function :cmap_initialize, :cmap_initialize, [ :pointer ], :cs_error_t
53
+ attach_function :cmap_finalize, :cmap_finalize, [ :cmap_handle_t ], :cs_error_t
54
+ attach_function :cmap_fd_get, :cmap_fd_get, [ :cmap_handle_t, :pointer ], :cs_error_t
55
+ attach_function :cmap_dispatch, :cmap_dispatch, [ :cmap_handle_t, :cs_dispatch_flags_t ], :cs_error_t
56
+ attach_function :cmap_context_get, :cmap_context_get, [ :cmap_handle_t, :pointer ], :cs_error_t
57
+ attach_function :cmap_context_set, :cmap_context_set, [ :cmap_handle_t, :pointer ], :cs_error_t
58
+ attach_function :cmap_set, :cmap_set, [ :cmap_handle_t, :string, :pointer, :size_t, :cmap_value_types_t ], :cs_error_t
59
+ attach_function :cmap_set_int8, :cmap_set_int8, [ :cmap_handle_t, :string, :int8 ], :cs_error_t
60
+ attach_function :cmap_set_uint8, :cmap_set_uint8, [ :cmap_handle_t, :string, :uint8 ], :cs_error_t
61
+ attach_function :cmap_set_int16, :cmap_set_int16, [ :cmap_handle_t, :string, :int16 ], :cs_error_t
62
+ attach_function :cmap_set_uint16, :cmap_set_uint16, [ :cmap_handle_t, :string, :uint16 ], :cs_error_t
63
+ attach_function :cmap_set_int32, :cmap_set_int32, [ :cmap_handle_t, :string, :int32 ], :cs_error_t
64
+ attach_function :cmap_set_uint32, :cmap_set_uint32, [ :cmap_handle_t, :string, :uint32 ], :cs_error_t
65
+ attach_function :cmap_set_int64, :cmap_set_int64, [ :cmap_handle_t, :string, :int64 ], :cs_error_t
66
+ attach_function :cmap_set_uint64, :cmap_set_uint64, [ :cmap_handle_t, :string, :uint64 ], :cs_error_t
67
+ attach_function :cmap_set_float, :cmap_set_float, [ :cmap_handle_t, :string, :float ], :cs_error_t
68
+ attach_function :cmap_set_double, :cmap_set_double, [ :cmap_handle_t, :string, :double ], :cs_error_t
69
+ attach_function :cmap_set_string, :cmap_set_string, [ :cmap_handle_t, :string, :string ], :cs_error_t
70
+ attach_function :cmap_delete, :cmap_delete, [ :cmap_handle_t, :string ], :cs_error_t
71
+ attach_function :cmap_get, :cmap_get, [ :cmap_handle_t, :string, :pointer, :pointer, :pointer ], :cs_error_t
72
+ attach_function :cmap_get_int8, :cmap_get_int8, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
73
+ attach_function :cmap_get_uint8, :cmap_get_uint8, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
74
+ attach_function :cmap_get_int16, :cmap_get_int16, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
75
+ attach_function :cmap_get_uint16, :cmap_get_uint16, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
76
+ attach_function :cmap_get_int32, :cmap_get_int32, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
77
+ attach_function :cmap_get_uint32, :cmap_get_uint32, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
78
+ attach_function :cmap_get_int64, :cmap_get_int64, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
79
+ attach_function :cmap_get_uint64, :cmap_get_uint64, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
80
+ attach_function :cmap_get_float, :cmap_get_float, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
81
+ attach_function :cmap_get_double, :cmap_get_double, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
82
+ attach_function :cmap_get_string, :cmap_get_string, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
83
+ attach_function :cmap_inc, :cmap_inc, [ :cmap_handle_t, :string ], :cs_error_t
84
+ attach_function :cmap_dec, :cmap_dec, [ :cmap_handle_t, :string ], :cs_error_t
85
+ attach_function :cmap_iter_init, :cmap_iter_init, [ :cmap_handle_t, :string, :pointer ], :cs_error_t
86
+ attach_function :cmap_iter_next, :cmap_iter_next, [ :cmap_handle_t, :cmap_iter_handle_t, :pointer, :pointer, :pointer ], :cs_error_t
87
+ attach_function :cmap_iter_finalize, :cmap_iter_finalize, [ :cmap_handle_t, :cmap_iter_handle_t ], :cs_error_t
88
+ attach_function :cmap_track_add, :cmap_track_add, [ :cmap_handle_t, :string, :int32, Callback_cmap_notify_fn_t, :pointer, :pointer ], :cs_error_t
89
+ attach_function :cmap_track_delete, :cmap_track_delete, [ :cmap_handle_t, :cmap_track_handle_t ], :cs_error_t
90
+
91
+ end
data/ffi/common.rb CHANGED
@@ -8,7 +8,7 @@ module Corosync
8
8
  class Iovec < FFI::Struct
9
9
  layout(
10
10
  :iov_base, :pointer,
11
- :iov_len, :uint
11
+ :iov_len, :size_t
12
12
  )
13
13
  end
14
14
  typedef :int64, :cs_time_t