mcollective-client 1.3.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mcollective-client might be problematic. Click here for more details.

Files changed (103) hide show
  1. data/bin/mc-call-agent +54 -0
  2. data/bin/mco +27 -0
  3. data/lib/mcollective.rb +70 -0
  4. data/lib/mcollective/agents.rb +160 -0
  5. data/lib/mcollective/application.rb +354 -0
  6. data/lib/mcollective/applications.rb +145 -0
  7. data/lib/mcollective/client.rb +292 -0
  8. data/lib/mcollective/config.rb +202 -0
  9. data/lib/mcollective/connector.rb +18 -0
  10. data/lib/mcollective/connector/base.rb +24 -0
  11. data/lib/mcollective/facts.rb +39 -0
  12. data/lib/mcollective/facts/base.rb +86 -0
  13. data/lib/mcollective/log.rb +103 -0
  14. data/lib/mcollective/logger.rb +5 -0
  15. data/lib/mcollective/logger/base.rb +73 -0
  16. data/lib/mcollective/logger/console_logger.rb +61 -0
  17. data/lib/mcollective/logger/file_logger.rb +46 -0
  18. data/lib/mcollective/logger/syslog_logger.rb +53 -0
  19. data/lib/mcollective/matcher.rb +16 -0
  20. data/lib/mcollective/matcher/parser.rb +93 -0
  21. data/lib/mcollective/matcher/scanner.rb +123 -0
  22. data/lib/mcollective/message.rb +201 -0
  23. data/lib/mcollective/monkey_patches.rb +104 -0
  24. data/lib/mcollective/optionparser.rb +164 -0
  25. data/lib/mcollective/pluginmanager.rb +180 -0
  26. data/lib/mcollective/pluginpackager.rb +26 -0
  27. data/lib/mcollective/pluginpackager/agent_definition.rb +79 -0
  28. data/lib/mcollective/pluginpackager/standard_definition.rb +59 -0
  29. data/lib/mcollective/registration.rb +16 -0
  30. data/lib/mcollective/registration/base.rb +75 -0
  31. data/lib/mcollective/rpc.rb +188 -0
  32. data/lib/mcollective/rpc/actionrunner.rb +142 -0
  33. data/lib/mcollective/rpc/agent.rb +441 -0
  34. data/lib/mcollective/rpc/audit.rb +38 -0
  35. data/lib/mcollective/rpc/client.rb +793 -0
  36. data/lib/mcollective/rpc/ddl.rb +258 -0
  37. data/lib/mcollective/rpc/helpers.rb +339 -0
  38. data/lib/mcollective/rpc/progress.rb +63 -0
  39. data/lib/mcollective/rpc/reply.rb +61 -0
  40. data/lib/mcollective/rpc/request.rb +51 -0
  41. data/lib/mcollective/rpc/result.rb +41 -0
  42. data/lib/mcollective/rpc/stats.rb +185 -0
  43. data/lib/mcollective/runnerstats.rb +90 -0
  44. data/lib/mcollective/security.rb +26 -0
  45. data/lib/mcollective/security/base.rb +237 -0
  46. data/lib/mcollective/shell.rb +87 -0
  47. data/lib/mcollective/ssl.rb +246 -0
  48. data/lib/mcollective/unix_daemon.rb +37 -0
  49. data/lib/mcollective/util.rb +274 -0
  50. data/lib/mcollective/vendor.rb +41 -0
  51. data/lib/mcollective/vendor/require_vendored.rb +2 -0
  52. data/lib/mcollective/windows_daemon.rb +25 -0
  53. data/spec/Rakefile +16 -0
  54. data/spec/fixtures/application/test.rb +7 -0
  55. data/spec/fixtures/test-cert.pem +15 -0
  56. data/spec/fixtures/test-private.pem +15 -0
  57. data/spec/fixtures/test-public.pem +6 -0
  58. data/spec/monkey_patches/instance_variable_defined.rb +7 -0
  59. data/spec/spec.opts +1 -0
  60. data/spec/spec_helper.rb +25 -0
  61. data/spec/unit/agents_spec.rb +280 -0
  62. data/spec/unit/application_spec.rb +636 -0
  63. data/spec/unit/applications_spec.rb +155 -0
  64. data/spec/unit/array.rb +30 -0
  65. data/spec/unit/config_spec.rb +148 -0
  66. data/spec/unit/facts/base_spec.rb +118 -0
  67. data/spec/unit/facts_spec.rb +39 -0
  68. data/spec/unit/log_spec.rb +71 -0
  69. data/spec/unit/logger/base_spec.rb +110 -0
  70. data/spec/unit/logger/syslog_logger_spec.rb +86 -0
  71. data/spec/unit/matcher/parser_spec.rb +106 -0
  72. data/spec/unit/matcher/scanner_spec.rb +71 -0
  73. data/spec/unit/message_spec.rb +401 -0
  74. data/spec/unit/optionparser_spec.rb +113 -0
  75. data/spec/unit/pluginmanager_spec.rb +173 -0
  76. data/spec/unit/pluginpackager/agent_definition_spec.rb +130 -0
  77. data/spec/unit/pluginpackager/standard_definition_spec.rb +75 -0
  78. data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +533 -0
  79. data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +34 -0
  80. data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +417 -0
  81. data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +229 -0
  82. data/spec/unit/plugins/mcollective/security/psk_spec.rb +156 -0
  83. data/spec/unit/registration/base_spec.rb +77 -0
  84. data/spec/unit/rpc/actionrunner_spec.rb +213 -0
  85. data/spec/unit/rpc/agent_spec.rb +155 -0
  86. data/spec/unit/rpc/client_spec.rb +523 -0
  87. data/spec/unit/rpc/ddl_spec.rb +388 -0
  88. data/spec/unit/rpc/helpers_spec.rb +55 -0
  89. data/spec/unit/rpc/reply_spec.rb +143 -0
  90. data/spec/unit/rpc/request_spec.rb +115 -0
  91. data/spec/unit/rpc/result_spec.rb +66 -0
  92. data/spec/unit/rpc/stats_spec.rb +288 -0
  93. data/spec/unit/runnerstats_spec.rb +40 -0
  94. data/spec/unit/security/base_spec.rb +279 -0
  95. data/spec/unit/shell_spec.rb +144 -0
  96. data/spec/unit/ssl_spec.rb +244 -0
  97. data/spec/unit/symbol.rb +11 -0
  98. data/spec/unit/unix_daemon.rb +41 -0
  99. data/spec/unit/util_spec.rb +342 -0
  100. data/spec/unit/vendor_spec.rb +34 -0
  101. data/spec/unit/windows_daemon.rb +43 -0
  102. data/spec/windows_spec.opts +1 -0
  103. metadata +242 -0
@@ -0,0 +1,53 @@
1
+ module MCollective
2
+ module Logger
3
+ # Implements a syslog based logger using the standard ruby syslog class
4
+ class Syslog_logger<Base
5
+ require 'syslog'
6
+
7
+ include Syslog::Constants
8
+
9
+ def start
10
+ config = Config.instance
11
+
12
+ facility = syslog_facility(config.logfacility)
13
+ level = config.loglevel.to_sym
14
+
15
+ Syslog.close if Syslog.opened?
16
+ Syslog.open(File.basename($0), 3, facility)
17
+
18
+ set_level(level)
19
+ end
20
+
21
+ def syslog_facility(facility)
22
+ begin
23
+ Syslog.const_get("LOG_#{facility.upcase}")
24
+ rescue NameError => e
25
+ STDERR.puts "Invalid syslog facility #{facility} supplied, reverting to USER"
26
+ Syslog::LOG_USER
27
+ end
28
+ end
29
+
30
+ def set_logging_level(level)
31
+ # noop
32
+ end
33
+
34
+ def valid_levels
35
+ {:info => :info,
36
+ :warn => :warning,
37
+ :debug => :debug,
38
+ :fatal => :crit,
39
+ :error => :err}
40
+ end
41
+
42
+ def log(level, from, msg)
43
+ if @known_levels.index(level) >= @known_levels.index(@active_level)
44
+ Syslog.send(map_level(level), "#{from} #{msg}")
45
+ end
46
+ rescue
47
+ # if this fails we probably cant show the user output at all,
48
+ # STDERR it as last resort
49
+ STDERR.puts("#{level}: #{msg}")
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,16 @@
1
+ module MCollective
2
+ # A parser and scanner that creates a stack machine for a simple
3
+ # fact and class matching language used on the CLI to facilitate
4
+ # a rich discovery language
5
+ #
6
+ # Language EBNF
7
+ #
8
+ # compound = ["("] expression [")"] {["("] expression [")"]}
9
+ # expression = [!|not]statement ["and"|"or"] [!|not] statement
10
+ # char = A-Z | a-z | < | > | => | =< | _ | - |* | / { A-Z | a-z | < | > | => | =< | _ | - | * | / | }
11
+ # int = 0|1|2|3|4|5|6|7|8|9{|0|1|2|3|4|5|6|7|8|9|0}
12
+ module Matcher
13
+ autoload :Parser, "mcollective/matcher/parser"
14
+ autoload :Scanner, "mcollective/matcher/scanner"
15
+ end
16
+ end
@@ -0,0 +1,93 @@
1
+ module MCollective
2
+ module Matcher
3
+ class Parser
4
+ attr_reader :scanner, :execution_stack
5
+
6
+ def initialize(args)
7
+ @scanner = Scanner.new(args)
8
+ @execution_stack = []
9
+ parse
10
+ end
11
+
12
+ # Parse the input string, one token at a time a contruct the call stack
13
+ def parse
14
+ p_token,p_token_value = nil
15
+ c_token,c_token_value = @scanner.get_token
16
+ parenth = 0
17
+
18
+ while (c_token != nil)
19
+ @scanner.token_index += 1
20
+ n_token, n_token_value = @scanner.get_token
21
+
22
+ unless n_token == " "
23
+ case c_token
24
+ when "and"
25
+ unless (n_token =~ /not|statement|\(/) || (scanner.token_index == scanner.arguments.size)
26
+ raise "Error at column #{scanner.token_index}. \nExpected 'not', 'statement' or '('. Found '#{n_token_value}'"
27
+ end
28
+
29
+ if p_token == nil
30
+ raise "Error at column #{scanner.token_index}. \n Expression cannot start with 'and'"
31
+ elsif (p_token == "and" || p_token == "or")
32
+ raise "Error at column #{scanner.token_index}. \n #{p_token} cannot be followed by 'and'"
33
+ end
34
+
35
+ when "or"
36
+ unless (n_token =~ /not|statement|\(/) || (scanner.token_index == scanner.arguments.size)
37
+ raise "Error at column #{scanner.token_index}. \nExpected 'not', 'statement', '('. Found '#{n_token_value}'"
38
+ end
39
+
40
+ if p_token == nil
41
+ raise "Error at column #{scanner.token_index}. \n Expression cannot start with 'or'"
42
+ elsif (p_token == "and" || p_token == "or")
43
+ raise "Error at column #{scanner.token_index}. \n #{p_token} cannot be followed by 'or'"
44
+ end
45
+
46
+ when "not"
47
+ unless n_token =~ /statement|\(|not/
48
+ raise "Error at column #{scanner.token_index}. \nExpected 'statement' or '('. Found '#{n_token_value}'"
49
+ end
50
+
51
+ when "statement"
52
+ unless n_token =~ /and|or|\)/
53
+ unless scanner.token_index == scanner.arguments.size
54
+ raise "Error at column #{scanner.token_index}. \nExpected 'and', 'or', ')'. Found '#{n_token_value}'"
55
+ end
56
+ end
57
+
58
+ when ")"
59
+ unless (n_token =~ /|and|or|not|\(/)
60
+ unless(scanner.token_index == scanner.arguments.size)
61
+ raise "Error at column #{scanner.token_index}. \nExpected 'and', 'or', 'not' or '('. Found '#{n_token_value}'"
62
+ end
63
+ end
64
+ parenth += 1
65
+
66
+ when "("
67
+ unless n_token =~ /statement|not|\(/
68
+ raise "Error at column #{scanner.token_index}. \nExpected 'statement', '(', not. Found '#{n_token_value}'"
69
+ end
70
+ parenth -= 1
71
+
72
+ else
73
+ raise "Unexpected token found at column #{scanner.token_index}. '#{c_token_value}'"
74
+ end
75
+
76
+ unless n_token == " "
77
+ @execution_stack << {c_token => c_token_value}
78
+ end
79
+
80
+ p_token, p_token_value = c_token, c_token_value
81
+ c_token, c_token_value = n_token, n_token_value
82
+ end
83
+ end
84
+
85
+ if parenth < 0
86
+ raise "Error. Missing parentheses ')'."
87
+ elsif parenth > 0
88
+ raise "Error. Missing parentheses '('."
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,123 @@
1
+ module MCollective
2
+ module Matcher
3
+ class Scanner
4
+ attr_accessor :arguments, :token_index
5
+
6
+ def initialize(arguments)
7
+ @token_index = 0
8
+ @arguments = arguments
9
+ end
10
+
11
+ # Scans the input string and identifies single language tokens
12
+ def get_token
13
+ if @token_index >= @arguments.size
14
+ return nil
15
+ end
16
+
17
+ begin
18
+ case @arguments.split("")[@token_index]
19
+ when "("
20
+ return "(", "("
21
+
22
+ when ")"
23
+ return ")", ")"
24
+
25
+ when "n"
26
+ if (@arguments.split("")[@token_index + 1] == "o") && (@arguments.split("")[@token_index + 2] == "t") && ((@arguments.split("")[@token_index + 3] == " ") || (@arguments.split("")[@token_index + 3] == "("))
27
+ @token_index += 2
28
+ return "not", "not"
29
+ else
30
+ gen_statement
31
+ end
32
+
33
+ when "!"
34
+ return "not", "not"
35
+
36
+ when "a"
37
+ if (@arguments.split("")[@token_index + 1] == "n") && (@arguments.split("")[@token_index + 2] == "d") && ((@arguments.split("")[@token_index + 3] == " ") || (@arguments.split("")[@token_index + 3] == "("))
38
+ @token_index += 2
39
+ return "and", "and"
40
+ else
41
+ gen_statement
42
+ end
43
+
44
+ when "o"
45
+ if (@arguments.split("")[@token_index + 1] == "r") && ((@arguments.split("")[@token_index + 2] == " ") || (@arguments.split("")[@token_index + 2] == "("))
46
+ @token_index += 1
47
+ return "or", "or"
48
+ else
49
+ gen_statement
50
+ end
51
+
52
+ when " "
53
+ return " ", " "
54
+
55
+ else
56
+ gen_statement
57
+ end
58
+ end
59
+ rescue NoMethodError => e
60
+ pp e
61
+ raise "Cannot end statement with 'and', 'or', 'not'"
62
+ end
63
+
64
+ private
65
+ # Helper generates a statement token
66
+ def gen_statement
67
+ current_token_value = ""
68
+ j = @token_index
69
+
70
+ begin
71
+ if (@arguments.split("")[j] == "/")
72
+ begin
73
+ current_token_value << @arguments.split("")[j]
74
+ j += 1
75
+ if @arguments.split("")[j] == "/"
76
+ current_token_value << "/"
77
+ break
78
+ end
79
+ end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\//)
80
+ elsif (@arguments.split("")[j] =~ /=|<|>/)
81
+ while !(@arguments.split("")[j] =~ /=|<|>/)
82
+ current_token_value << @arguments.split("")[j]
83
+ j += 1
84
+ end
85
+
86
+ current_token_value << @arguments.split("")[j]
87
+ j += 1
88
+
89
+ if @arguments.split("")[j] == "/"
90
+ begin
91
+ current_token_value << @arguments.split("")[j]
92
+ j += 1
93
+ if @arguments.split("")[j] == "/"
94
+ current_token_value << "/"
95
+ break
96
+ end
97
+ end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\//)
98
+ else
99
+ while (j < @arguments.size) && ((@arguments.split("")[j] != " ") && (@arguments.split("")[j] != ")"))
100
+ current_token_value << @arguments.split("")[j]
101
+ j += 1
102
+ end
103
+ end
104
+ else
105
+ begin
106
+ current_token_value << @arguments.split("")[j]
107
+ j += 1
108
+ end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\s|\)/)
109
+ end
110
+ rescue Exception => e
111
+ raise "Invalid token found - '#{current_token_value}'"
112
+ end
113
+
114
+ if current_token_value =~ /^(and|or|not|!)$/
115
+ raise "Class name cannot be 'and', 'or', 'not'. Found '#{current_token_value}'"
116
+ end
117
+
118
+ @token_index += current_token_value.size - 1
119
+ return "statement", current_token_value
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,201 @@
1
+ module MCollective
2
+ # container for a message, its headers, agent, collective and other meta data
3
+ class Message
4
+ attr_reader :message, :request, :validated, :msgtime, :payload, :type, :expected_msgid, :reply_to
5
+ attr_accessor :headers, :agent, :collective, :filter
6
+ attr_accessor :requestid, :discovered_hosts, :options, :ttl
7
+
8
+ VALIDTYPES = [:message, :request, :direct_request, :reply]
9
+
10
+ # payload - the message body without headers etc, just the text
11
+ # message - the original message received from the middleware
12
+ # options[:base64] - if the body base64 encoded?
13
+ # options[:agent] - the agent the message is for/from
14
+ # options[:collective] - the collective its for/from
15
+ # options[:headers] - the message headers
16
+ # options[:type] - an indicator about the type of message, :message, :request, :direct_request or :reply
17
+ # options[:request] - if this is a reply this should old the message we are replying to
18
+ # options[:filter] - for requests, the filter to encode into the message
19
+ # options[:options] - the normal client options hash
20
+ # options[:ttl] - the maximum amount of seconds this message can be valid for
21
+ # options[:expected_msgid] - in the case of replies this is the msgid it is expecting in the replies
22
+ def initialize(payload, message, options = {})
23
+ options = {:base64 => false,
24
+ :agent => nil,
25
+ :headers => {},
26
+ :type => :message,
27
+ :request => nil,
28
+ :filter => Util.empty_filter,
29
+ :options => {},
30
+ :ttl => 60,
31
+ :expected_msgid => nil,
32
+ :collective => nil}.merge(options)
33
+
34
+ @payload = payload
35
+ @message = message
36
+ @requestid = nil
37
+ @discovered_hosts = nil
38
+ @reply_to = nil
39
+
40
+ @type = options[:type]
41
+ @headers = options[:headers]
42
+ @base64 = options[:base64]
43
+ @filter = options[:filter]
44
+ @expected_msgid = options[:expected_msgid]
45
+ @options = options[:options]
46
+
47
+ @ttl = @options[:ttl] || Config.instance.ttl
48
+ @msgtime = 0
49
+
50
+ @validated = false
51
+
52
+ if options[:request]
53
+ @request = options[:request]
54
+ @agent = request.agent
55
+ @collective = request.collective
56
+ @type = :reply
57
+ else
58
+ @agent = options[:agent]
59
+ @collective = options[:collective]
60
+ end
61
+
62
+ base64_decode!
63
+ end
64
+
65
+ # Sets the message type to one of the known types. In the case of :direct_request
66
+ # the list of hosts to communicate with should have been set with #discovered_hosts
67
+ # else an exception will be raised. This is for extra security, we never accidentally
68
+ # want to send a direct request without a list of hosts or something weird like that
69
+ # as it might result in a filterless broadcast being sent.
70
+ #
71
+ # Additionally you simply cannot set :direct_request if direct_addressing was not enabled
72
+ # this is to force a workflow that doesnt not yield in a mistake when someone might assume
73
+ # direct_addressing is enabled when its not.
74
+ def type=(type)
75
+ if type == :direct_request
76
+ raise "Direct requests is not enabled using the direct_addressing config option" unless Config.instance.direct_addressing
77
+
78
+ unless @discovered_hosts && !@discovered_hosts.empty?
79
+ raise "Can only set type to :direct_request if discovered_hosts have been set"
80
+ end
81
+ end
82
+
83
+ raise "Unknown message type #{type}" unless VALIDTYPES.include?(type)
84
+
85
+ @type = type
86
+ end
87
+
88
+ # Sets a custom reply-to target for requests. The connector plugin should inspect this
89
+ # when constructing requests and set this header ensuring replies will go to the custom target
90
+ # otherwise the connector should just do what it usually does
91
+ def reply_to=(target)
92
+ raise "Custom reply targets can only be set on requests" unless [:request, :direct_request].include?(@type)
93
+
94
+ @reply_to = target
95
+ end
96
+
97
+ # in the case of reply messages we are expecting replies to a previously
98
+ # created message. This stores a hint to that previously sent message id
99
+ # and can be used by other classes like the security plugins as a means
100
+ # of optimizing their behavior like by ignoring messages not directed
101
+ # at us.
102
+ def expected_msgid=(msgid)
103
+ raise "Can only store the expected msgid for reply messages" unless @type == :reply
104
+ @expected_msgid = msgid
105
+ end
106
+
107
+ def base64_decode!
108
+ return unless @base64
109
+
110
+ @payload = SSL.base64_decode(@payload)
111
+ @base64 = false
112
+ end
113
+
114
+ def base64_encode!
115
+ return if @base64
116
+
117
+ @payload = SSL.base64_encode(@payload)
118
+ @base64 = true
119
+ end
120
+
121
+ def base64?
122
+ @base64
123
+ end
124
+
125
+ def encode!
126
+ case type
127
+ when :reply
128
+ raise "Cannot encode a reply message if no request has been associated with it" unless request
129
+ raise 'callerid in original request is not valid, surpressing reply to potentially forged request' unless PluginManager["security_plugin"].valid_callerid?(request.payload[:callerid])
130
+
131
+ @requestid = request.payload[:requestid]
132
+ @payload = PluginManager["security_plugin"].encodereply(agent, payload, requestid, request.payload[:callerid])
133
+ when :request, :direct_request
134
+ @requestid = create_reqid
135
+ @payload = PluginManager["security_plugin"].encoderequest(Config.instance.identity, payload, requestid, filter, agent, collective, ttl)
136
+ else
137
+ raise "Cannot encode #{type} messages"
138
+ end
139
+ end
140
+
141
+ def decode!
142
+ raise "Cannot decode message type #{type}" unless [:request, :reply].include?(type)
143
+
144
+ @payload = PluginManager["security_plugin"].decodemsg(self)
145
+
146
+ if type == :request
147
+ raise 'callerid in request is not valid, surpressing reply to potentially forged request' unless PluginManager["security_plugin"].valid_callerid?(payload[:callerid])
148
+ end
149
+
150
+ [:collective, :agent, :filter, :requestid, :ttl, :msgtime].each do |prop|
151
+ instance_variable_set("@#{prop}", payload[prop]) if payload.include?(prop)
152
+ end
153
+ end
154
+
155
+ # Perform validation against the message by checking filters and ttl
156
+ def validate
157
+ raise "Can only validate request messages" unless type == :request
158
+
159
+ msg_age = Time.now.utc.to_i - msgtime
160
+
161
+ if msg_age > ttl
162
+ cid = ""
163
+ cid += payload[:callerid] + "@" if payload.include?(:callerid)
164
+ cid += payload[:senderid]
165
+
166
+ if msg_age > ttl
167
+ PluginManager["global_stats"].ttlexpired
168
+
169
+ raise(MsgTTLExpired, "Message #{requestid} from #{cid} created at #{msgtime} is #{msg_age} seconds old, TTL is #{ttl}")
170
+ end
171
+ end
172
+
173
+ raise(NotTargettedAtUs, "Received message is not targetted to us") unless PluginManager["security_plugin"].validate_filter?(payload[:filter])
174
+
175
+ @validated = true
176
+ end
177
+
178
+ # publish a reply message by creating a target name and sending it
179
+ def publish
180
+ Timeout.timeout(2) do
181
+ # If we've been specificaly told about hosts that were discovered
182
+ # use that information to do P2P calls if appropriate else just
183
+ # send it as is.
184
+ if @discovered_hosts && Config.instance.direct_addressing
185
+ if @discovered_hosts.size <= Config.instance.direct_addressing_threshold
186
+ @type = :direct_request
187
+ Log.debug("Handling #{requestid} as a direct request")
188
+ end
189
+
190
+ PluginManager["connector_plugin"].publish(self)
191
+ else
192
+ PluginManager["connector_plugin"].publish(self)
193
+ end
194
+ end
195
+ end
196
+
197
+ def create_reqid
198
+ Digest::MD5.hexdigest("#{Config.instance.identity}-#{Time.now.to_f}-#{agent}-#{collective}")
199
+ end
200
+ end
201
+ end