omf_common 6.0.2.pre.2 → 6.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.
Files changed (62) hide show
  1. data/bin/file_broadcaster.rb +5 -0
  2. data/bin/file_receiver.rb +5 -0
  3. data/bin/omf_keygen +3 -3
  4. data/bin/omf_send_configure +114 -0
  5. data/bin/omf_send_request +19 -4
  6. data/example/engine_alt.rb +13 -7
  7. data/example/viz/garage_monitor.rb +69 -0
  8. data/example/viz/garage_viz.rb +52 -0
  9. data/example/viz/htdocs/image/garage.png +0 -0
  10. data/example/viz/htdocs/template/garage_banner.html +2 -0
  11. data/example/viz/layout.yaml +44 -0
  12. data/example/vm_alt.rb +5 -0
  13. data/lib/omf_common.rb +17 -8
  14. data/lib/omf_common/auth.rb +5 -0
  15. data/lib/omf_common/auth/certificate.rb +21 -2
  16. data/lib/omf_common/auth/certificate_store.rb +50 -20
  17. data/lib/omf_common/auth/ssh_pub_key_convert.rb +7 -0
  18. data/lib/omf_common/comm.rb +6 -1
  19. data/lib/omf_common/comm/amqp/amqp_communicator.rb +88 -12
  20. data/lib/omf_common/comm/amqp/amqp_file_transfer.rb +5 -0
  21. data/lib/omf_common/comm/amqp/amqp_topic.rb +37 -18
  22. data/lib/omf_common/comm/local/local_communicator.rb +5 -0
  23. data/lib/omf_common/comm/local/local_topic.rb +5 -0
  24. data/lib/omf_common/comm/topic.rb +32 -13
  25. data/lib/omf_common/comm/xmpp/communicator.rb +11 -1
  26. data/lib/omf_common/comm/xmpp/topic.rb +5 -0
  27. data/lib/omf_common/comm/xmpp/xmpp_mp.rb +5 -0
  28. data/lib/omf_common/command.rb +5 -0
  29. data/lib/omf_common/core_ext/string.rb +5 -0
  30. data/lib/omf_common/default_logging.rb +23 -5
  31. data/lib/omf_common/eventloop.rb +40 -23
  32. data/lib/omf_common/eventloop/em.rb +18 -5
  33. data/lib/omf_common/eventloop/local_evl.rb +18 -15
  34. data/lib/omf_common/exec_app.rb +44 -24
  35. data/lib/omf_common/key.rb +5 -0
  36. data/lib/omf_common/measure.rb +5 -0
  37. data/lib/omf_common/message.rb +5 -0
  38. data/lib/omf_common/message/json/json_message.rb +13 -5
  39. data/lib/omf_common/message/xml/message.rb +19 -4
  40. data/lib/omf_common/message/xml/relaxng_schema.rb +5 -0
  41. data/lib/omf_common/message/xml/topic_message.rb +5 -0
  42. data/lib/omf_common/version.rb +6 -1
  43. data/omf_common.gemspec +3 -2
  44. data/test/fixture/1st_level.pem +20 -0
  45. data/test/fixture/2nd_level.pem +19 -0
  46. data/test/fixture/3rd_level.pem +19 -0
  47. data/test/fixture/pubsub.rb +5 -0
  48. data/test/fixture/rc.pem +18 -0
  49. data/test/fixture/root.pem +17 -0
  50. data/test/omf_common/auth/certificate_spec.rb +27 -0
  51. data/test/omf_common/auth/certificate_store_spec.rb +58 -0
  52. data/test/omf_common/auth/ssh_pub_key_convert_spec.rb +5 -0
  53. data/test/omf_common/comm/topic_spec.rb +7 -1
  54. data/test/omf_common/comm/xmpp/communicator_spec.rb +5 -0
  55. data/test/omf_common/comm/xmpp/topic_spec.rb +5 -0
  56. data/test/omf_common/comm_spec.rb +5 -0
  57. data/test/omf_common/command_spec.rb +5 -0
  58. data/test/omf_common/core_ext/string_spec.rb +5 -0
  59. data/test/omf_common/message/xml/message_spec.rb +5 -0
  60. data/test/omf_common/message_spec.rb +8 -3
  61. data/test/test_helper.rb +5 -0
  62. metadata +48 -11
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
1
6
 
2
7
 
3
8
  module OmfCommon
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
1
6
  require 'openssl'
2
7
  require 'omf_common/auth'
3
8
  require 'omf_common/auth/ssh_pub_key_convert'
@@ -32,9 +37,11 @@ module OmfCommon::Auth
32
37
  # @param [String] pem is the content of existing x509 cert
33
38
  # @param [OpenSSL::PKey::RSA|String] key is the private key which can be attached to the instance for signing.
34
39
  def self.create_from_x509(pem, key = nil)
35
- unless pem.start_with? BEGIN_CERT
40
+ # Some command list generated cert can use \r\n as newline char
41
+ unless pem =~ /^-----BEGIN CERTIFICATE-----/
36
42
  pem = "#{BEGIN_CERT}#{pem}#{END_CERT}"
37
43
  end
44
+
38
45
  cert = OpenSSL::X509::Certificate.new(pem)
39
46
 
40
47
  key = OpenSSL::PKey::RSA.new(key) if key && key.is_a?(String)
@@ -64,7 +71,9 @@ module OmfCommon::Auth
64
71
 
65
72
  # Create a X509 certificate
66
73
  #
67
- # @param [] address
74
+ # @param [String] address
75
+ # @param [String] subject
76
+ # @param [OpenSSL::PKey::RSA] key
68
77
  # @return {cert, key}
69
78
  #
70
79
  def self._create_x509_cert(address, subject, key, digest = nil,
@@ -127,8 +136,18 @@ module OmfCommon::Auth
127
136
  @cert ||= _create_x509_cert(@address, @subject, @key, @digest)[:cert]
128
137
  end
129
138
 
139
+ # @param [OpenSSL::PKey::RSA|String] key is most likely the public key of the resource.
140
+ #
130
141
  def create_for(address, name, type, domain = DEF_DOMAIN_NAME, duration = 3600, key = nil)
131
142
  raise ArgumentError, "Address required" unless address
143
+
144
+ begin
145
+ key = OpenSSL::PKey::RSA.new(key) if key && key.is_a?(String)
146
+ rescue OpenSSL::PKey::RSAError
147
+ # It might be a SSH pub key, try that
148
+ key = OmfCommon::Auth::SSHPubKeyConvert.convert(key)
149
+ end
150
+
132
151
  cert = self.class.create(address, name, type, domain, self, Time.now, duration, key)
133
152
  CertificateStore.instance.register(cert, address)
134
153
  cert
@@ -1,27 +1,19 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
1
6
  require 'openssl'
7
+ require 'monitor'
2
8
 
3
9
  require 'omf_common/auth'
4
10
 
5
- #require 'singleton'
6
-
7
- # module OmfCommon
8
- # class Key
9
- # include Singleton
10
- #
11
- # attr_accessor :private_key
12
- #
13
- # def import(filename)
14
- # self.private_key = OpenSSL::PKey.read(File.read(filename))
15
- # end
16
- # end
17
- # end
18
-
19
11
  module OmfCommon::Auth
20
12
 
21
13
  class MissingPrivateKeyException < AuthException; end
22
14
 
23
15
  class CertificateStore
24
-
16
+ include MonitorMixin
25
17
 
26
18
  @@instance = nil
27
19
 
@@ -38,12 +30,27 @@ module OmfCommon::Auth
38
30
  end
39
31
 
40
32
  def register(certificate, address = nil)
41
- if address ||= certificate.address
42
- @certs[address] = certificate if address
43
- else
44
- warn "Register certificate without address - #{certificate}"
33
+ @@instance.synchronize do
34
+ begin
35
+ @x509_store.add_cert(certificate.to_x509) if @certs[address].nil? && @certs[certificate.subject].nil?
36
+ rescue OpenSSL::X509::StoreError => e
37
+ if e.message == "cert already in hash table"
38
+ raise "X509 cert '#{address}' already registered in X509 store"
39
+ else
40
+ raise e
41
+ end
42
+ end
43
+
44
+ address ||= certificate.address
45
+
46
+ if address
47
+ @certs[address] ||= certificate
48
+ else
49
+ debug "Register certificate without address - #{certificate}, is it a CA cert?"
50
+ end
51
+
52
+ @certs[certificate.subject] ||= certificate
45
53
  end
46
- @certs[certificate.subject] = certificate
47
54
  end
48
55
 
49
56
  def register_x509(cert_pem, address = nil)
@@ -57,15 +64,38 @@ module OmfCommon::Auth
57
64
  @certs[url]
58
65
  end
59
66
 
67
+ # @param [OpenSSL::X509::Certificate] cert
68
+ #
69
+ def verify(cert)
70
+ cert = cert.to_x509 if cert.kind_of? OmfCommon::Auth::Certificate
71
+ v_result = @x509_store.verify(cert)
72
+ warn "Cert verification failed: '#{@x509_store.error_string}'" unless v_result
73
+ v_result
74
+ end
75
+
76
+ # Load a set of CA certs into cert store from a given location
77
+ #
78
+ # @param [String] folder contains all the CA certs
79
+ #
80
+ def register_default_certs(folder)
81
+ Dir["#{folder}/*"].each do |cert|
82
+ register_x509(File.read(cert))
83
+ end
84
+ end
60
85
 
61
86
  private
87
+
62
88
  def initialize(opts)
89
+ @x509_store = OpenSSL::X509::Store.new
90
+
63
91
  @certs = {}
64
92
  if store = opts[:store]
65
93
  else
66
94
  @store = {private: {}, public: {}}
67
95
  end
68
96
  @serial = 0
97
+
98
+ super()
69
99
  end
70
100
  end # class
71
101
 
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
1
6
  require 'base64'
2
7
  require 'openssl'
3
8
  require 'omf_common/auth'
@@ -42,6 +47,8 @@ module OmfCommon::Auth
42
47
  #
43
48
  def self.convert(keystring)
44
49
  (type, b64, id) = keystring.split(' ')
50
+ raise ArgumentError, "Invalid SSH public key '#{keystring}'" if b64.nil?
51
+
45
52
  decoded_key = Base64.decode64(b64)
46
53
  (n, bytes) = unpack_u32(decoded_key)
47
54
  (keytype, bytes) = unpack_string(bytes, n)
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
1
6
  require 'omf_common/comm/topic'
2
7
 
3
8
  module OmfCommon
@@ -122,7 +127,7 @@ module OmfCommon
122
127
 
123
128
  # Returning connection information
124
129
  #
125
- # @retun [Hash] connection information hash, with type, user and domain.
130
+ # @return [Hash] connection information hash, with type, user and domain.
126
131
  def conn_info
127
132
  { proto: nil, user: nil, domain: nil }
128
133
  end
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
1
6
  require 'amqp'
2
7
  require 'omf_common/comm/amqp/amqp_topic'
3
8
  #require 'omf_common/comm/monkey_patches'
@@ -11,23 +16,25 @@ module OmfCommon
11
16
  # # ignore arguments
12
17
  # end
13
18
 
19
+ attr_reader :channel
20
+
14
21
  # Initialize comms layer
15
22
  #
16
23
  def init(opts = {})
17
- unless (@url = opts[:url])
24
+ @opts = {
25
+ #:ssl (Hash) � TLS (SSL) parameters to use.
26
+ heartbeat: 20, # (Fixnum) � default: 0 � Connection heartbeat, in seconds. 0 means no heartbeat. Can also be configured server-side starting with RabbitMQ 3.0.
27
+ #:on_tcp_connection_failure (#call) � A callable object that will be run if connection to server fails
28
+ #:on_possible_authentication_failure (#call) � A callable object that will be run if authentication fails (see Authentication failure section)
29
+ reconnect_delay: 20 # (Fixnum) Delay in seconds before attempting reconnect on detected failure
30
+ }.merge(opts)
31
+
32
+ unless (@url = @opts.delete(:url))
18
33
  raise "Missing 'url' option for AQMP layer"
19
34
  end
20
35
  @address_prefix = @url + '/'
21
- ::AMQP.connect(@url) do |connection|
22
- @channel = ::AMQP::Channel.new(connection)
23
- @on_connected_procs.each do |proc|
24
- proc.arity == 1 ? proc.call(self) : proc.call
25
- end
26
-
27
- OmfCommon.eventloop.on_stop do
28
- connection.close
29
- end
30
- end
36
+ _connect()
37
+ #AMQP::Session#on_skipped_heartbeats callback that can be used to handle skipped heartbeats
31
38
  super
32
39
  end
33
40
 
@@ -44,13 +51,25 @@ module OmfCommon
44
51
  @on_connected_procs << block
45
52
  end
46
53
 
54
+ # register callbacks to be called when the underlying AMQP layer
55
+ # needs to reconnect to the AMQP server. This may require some additional
56
+ # repairs. If 'block' is nil, the callback is removed
57
+ #
58
+ def on_reconnect(key, &block)
59
+ if block.nil?
60
+ @on_reconnect.delete(key)
61
+ else
62
+ @on_reconnect[key] = block
63
+ end
64
+ end
65
+
47
66
  # Create a new pubsub topic with additional configuration
48
67
  #
49
68
  # @param [String] topic Pubsub topic name
50
69
  def create_topic(topic, opts = {})
51
70
  raise "Topic can't be nil or empty" if topic.nil? || topic.empty?
52
71
  opts = opts.dup
53
- opts[:channel] = @channel
72
+ opts[:communicator] = self
54
73
  topic = topic.to_s
55
74
  if topic.start_with? 'amqp:'
56
75
  # absolute address
@@ -95,8 +114,65 @@ module OmfCommon
95
114
  private
96
115
  def initialize(opts = {})
97
116
  @on_connected_procs = []
117
+ @on_reconnect = {}
98
118
  super
99
119
  end
120
+
121
+ def _connect()
122
+ last_reported_timestamp = nil
123
+ @session = ::AMQP.connect(@url, @opts) do |connection|
124
+ connection.on_tcp_connection_loss do |conn, settings|
125
+ now = Time.now
126
+ if last_reported_timestamp == nil || (now - last_reported_timestamp) > 60
127
+ warn "Lost connectivity. Trying to reconnect..."
128
+ last_reported_timestamp = now
129
+ end
130
+ conn.reconnect(false, 2)
131
+ end
132
+ @channel = ::AMQP::Channel.new(connection)
133
+ @channel.auto_recovery = true
134
+
135
+
136
+ @on_connected_procs.each do |proc|
137
+ proc.arity == 1 ? proc.call(self) : proc.call
138
+ end
139
+
140
+ OmfCommon.eventloop.on_stop do
141
+ connection.close
142
+ end
143
+ end
144
+
145
+ rec_delay = @opts[:reconnect_delay]
146
+ @session.on_tcp_connection_failure do
147
+ warn "Cannot connect to AMQP server '#{@url}'. Attempt to retry in #{rec_delay} sec"
148
+ @session = nil
149
+ OmfCommon.eventloop.after(rec_delay) do
150
+ info 'Retrying'
151
+ _connect
152
+ end
153
+ end
154
+ # @session.on_tcp_connection_loss do
155
+ # _reconnect "Appear to have lost tcp connection. Attempt to reconnect in #{rec_delay} sec"
156
+ # end
157
+ # @session.on_skipped_heartbeats do
158
+ # _reconnect "Appear to have lost heartbeat. Attempt to reconnect in #{rec_delay} sec"
159
+ # end
160
+ @session.on_recovery do
161
+ info 'Recovered!'
162
+ last_reported_timestamp = nil
163
+ @on_reconnect.values.each do |block|
164
+ block.call()
165
+ end
166
+ end
167
+ end
168
+
169
+ # def _reconnect(warn_message = nil)
170
+ # warn(warn_message) if warn_message
171
+ # OmfCommon.eventloop.after(@opts[:reconnect_delay]) do
172
+ # info 'Reconnecting'
173
+ # @session.reconnect
174
+ # end
175
+ # end
100
176
  end
101
177
  end
102
178
  end
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
1
6
  require 'set'
2
7
  require 'monitor'
3
8
 
@@ -1,3 +1,8 @@
1
+ # Copyright (c) 2012 National ICT Australia Limited (NICTA).
2
+ # This software may be used and distributed solely under the terms of the MIT license (License).
3
+ # You should find a copy of the License in LICENSE.TXT or at http://opensource.org/licenses/MIT.
4
+ # By downloading or using this software you accept the terms and the liability disclaimer in the License.
5
+
1
6
 
2
7
 
3
8
  module OmfCommon
@@ -8,17 +13,17 @@ module OmfCommon
8
13
  def to_s
9
14
  "AMQP::Topic<#{id}>"
10
15
  end
11
-
16
+
12
17
  def address
13
18
  @address
14
19
  end
15
-
20
+
16
21
  # Call 'block' when topic is subscribed to underlying messaging
17
- # infrastructure.
22
+ # infrastructure.
18
23
  #
19
24
  def on_subscribed(&block)
20
25
  return unless block
21
-
26
+
22
27
  call_now = false
23
28
  @lock.synchronize do
24
29
  if @subscribed
@@ -30,24 +35,35 @@ module OmfCommon
30
35
  if call_now
31
36
  after(0, &block)
32
37
  end
33
- end
34
-
35
-
38
+ end
39
+
40
+
36
41
  private
37
-
42
+
38
43
  def initialize(id, opts = {})
39
- unless channel = opts.delete(:channel)
40
- raise "Missing :channel option"
44
+ unless @communicator = opts.delete(:communicator)
45
+ raise "Missing :communicator option"
41
46
  end
42
47
  super
43
48
  @address = opts[:address]
44
- @exchange = channel.topic(id, :auto_delete => true)
45
49
  @lock = Monitor.new
46
50
  @subscribed = false
47
51
  @on_subscribed_handlers = []
48
-
49
- # Subscribe as well
50
- #puts "QQ0(#{id})"
52
+
53
+ # @communicator.on_reconnect(self) do
54
+ # info "Resubscribe '#{self}'"
55
+ # _init_amqp
56
+ # end
57
+ _init_amqp
58
+ end
59
+
60
+ def _init_amqp()
61
+ channel = @communicator.channel
62
+ @exchange = channel.topic(id, :auto_delete => true)
63
+ # @exchange.on_connection_interruption do |ex|
64
+ # warn "Exchange #{ex.name} detected connection interruption"
65
+ # @exchange = nil
66
+ # end
51
67
  channel.queue("", :exclusive => true) do |queue|
52
68
  #puts "QQ1(#{id}): #{queue}"
53
69
  queue.bind(@exchange)
@@ -70,15 +86,18 @@ module OmfCommon
70
86
  end
71
87
  end
72
88
  end
73
-
74
-
89
+
75
90
  def _send_message(msg, block = nil)
76
91
  super
77
92
  content_type, content = msg.marshall(self)
78
93
  debug "(#{id}) Send message (#{content_type}) #{msg.inspect}"
79
- @exchange.publish(content, content_type: content_type, message_id: msg.mid)
94
+ if @exchange
95
+ @exchange.publish(content, content_type: content_type, message_id: msg.mid)
96
+ else
97
+ warn "Unavailable AMQP channel. Dropping message '#{msg}'"
98
+ end
80
99
  end
81
100
  end # class
82
- end # module
101
+ end # module
83
102
  end # module
84
103
  end # module