right_agent 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +78 -0
  3. data/Rakefile +86 -0
  4. data/lib/right_agent.rb +66 -0
  5. data/lib/right_agent/actor.rb +163 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +189 -0
  8. data/lib/right_agent/agent.rb +735 -0
  9. data/lib/right_agent/agent_config.rb +403 -0
  10. data/lib/right_agent/agent_identity.rb +209 -0
  11. data/lib/right_agent/agent_tags_manager.rb +213 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/broker_client.rb +683 -0
  14. data/lib/right_agent/command.rb +30 -0
  15. data/lib/right_agent/command/agent_manager_commands.rb +134 -0
  16. data/lib/right_agent/command/command_client.rb +136 -0
  17. data/lib/right_agent/command/command_constants.rb +42 -0
  18. data/lib/right_agent/command/command_io.rb +128 -0
  19. data/lib/right_agent/command/command_parser.rb +87 -0
  20. data/lib/right_agent/command/command_runner.rb +105 -0
  21. data/lib/right_agent/command/command_serializer.rb +63 -0
  22. data/lib/right_agent/console.rb +65 -0
  23. data/lib/right_agent/core_payload_types.rb +42 -0
  24. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  25. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  26. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  27. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  28. data/lib/right_agent/core_payload_types/dev_repositories.rb +90 -0
  29. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  30. data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
  31. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  32. data/lib/right_agent/core_payload_types/login_user.rb +62 -0
  33. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  34. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -0
  35. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  36. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  37. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +73 -0
  38. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  39. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  40. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  41. data/lib/right_agent/daemonize.rb +35 -0
  42. data/lib/right_agent/dispatcher.rb +348 -0
  43. data/lib/right_agent/enrollment_result.rb +217 -0
  44. data/lib/right_agent/exceptions.rb +30 -0
  45. data/lib/right_agent/ha_broker_client.rb +1278 -0
  46. data/lib/right_agent/idempotent_request.rb +140 -0
  47. data/lib/right_agent/log.rb +418 -0
  48. data/lib/right_agent/monkey_patches.rb +29 -0
  49. data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
  50. data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -0
  51. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  52. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  53. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  54. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  55. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  56. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +46 -0
  57. data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -0
  58. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +90 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  64. data/lib/right_agent/multiplexer.rb +91 -0
  65. data/lib/right_agent/operation_result.rb +270 -0
  66. data/lib/right_agent/packets.rb +637 -0
  67. data/lib/right_agent/payload_formatter.rb +104 -0
  68. data/lib/right_agent/pid_file.rb +159 -0
  69. data/lib/right_agent/platform.rb +319 -0
  70. data/lib/right_agent/platform/darwin.rb +227 -0
  71. data/lib/right_agent/platform/linux.rb +268 -0
  72. data/lib/right_agent/platform/windows.rb +1204 -0
  73. data/lib/right_agent/scripts/agent_controller.rb +522 -0
  74. data/lib/right_agent/scripts/agent_deployer.rb +379 -0
  75. data/lib/right_agent/scripts/common_parser.rb +153 -0
  76. data/lib/right_agent/scripts/log_level_manager.rb +193 -0
  77. data/lib/right_agent/scripts/stats_manager.rb +256 -0
  78. data/lib/right_agent/scripts/usage.rb +58 -0
  79. data/lib/right_agent/secure_identity.rb +92 -0
  80. data/lib/right_agent/security.rb +32 -0
  81. data/lib/right_agent/security/cached_certificate_store_proxy.rb +63 -0
  82. data/lib/right_agent/security/certificate.rb +102 -0
  83. data/lib/right_agent/security/certificate_cache.rb +89 -0
  84. data/lib/right_agent/security/distinguished_name.rb +56 -0
  85. data/lib/right_agent/security/encrypted_document.rb +84 -0
  86. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  87. data/lib/right_agent/security/signature.rb +86 -0
  88. data/lib/right_agent/security/static_certificate_store.rb +69 -0
  89. data/lib/right_agent/sender.rb +937 -0
  90. data/lib/right_agent/serialize.rb +29 -0
  91. data/lib/right_agent/serialize/message_pack.rb +102 -0
  92. data/lib/right_agent/serialize/secure_serializer.rb +131 -0
  93. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  94. data/lib/right_agent/serialize/serializable.rb +135 -0
  95. data/lib/right_agent/serialize/serializer.rb +149 -0
  96. data/lib/right_agent/stats_helper.rb +731 -0
  97. data/lib/right_agent/subprocess.rb +38 -0
  98. data/lib/right_agent/tracer.rb +124 -0
  99. data/right_agent.gemspec +60 -0
  100. data/spec/actor_registry_spec.rb +81 -0
  101. data/spec/actor_spec.rb +99 -0
  102. data/spec/agent_config_spec.rb +226 -0
  103. data/spec/agent_identity_spec.rb +75 -0
  104. data/spec/agent_spec.rb +571 -0
  105. data/spec/broker_client_spec.rb +961 -0
  106. data/spec/command/agent_manager_commands_spec.rb +51 -0
  107. data/spec/command/command_io_spec.rb +93 -0
  108. data/spec/command/command_parser_spec.rb +79 -0
  109. data/spec/command/command_runner_spec.rb +72 -0
  110. data/spec/command/command_serializer_spec.rb +51 -0
  111. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  112. data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
  113. data/spec/core_payload_types/login_user_spec.rb +98 -0
  114. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  115. data/spec/core_payload_types/spec_helper.rb +23 -0
  116. data/spec/dispatcher_spec.rb +372 -0
  117. data/spec/enrollment_result_spec.rb +53 -0
  118. data/spec/ha_broker_client_spec.rb +1673 -0
  119. data/spec/idempotent_request_spec.rb +136 -0
  120. data/spec/log_spec.rb +177 -0
  121. data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
  122. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  123. data/spec/monkey_patches/string_patch_spec.rb +99 -0
  124. data/spec/multiplexer_spec.rb +48 -0
  125. data/spec/operation_result_spec.rb +171 -0
  126. data/spec/packets_spec.rb +418 -0
  127. data/spec/platform/platform_spec.rb +60 -0
  128. data/spec/results_mock.rb +45 -0
  129. data/spec/secure_identity_spec.rb +50 -0
  130. data/spec/security/cached_certificate_store_proxy_spec.rb +56 -0
  131. data/spec/security/certificate_cache_spec.rb +71 -0
  132. data/spec/security/certificate_spec.rb +49 -0
  133. data/spec/security/distinguished_name_spec.rb +46 -0
  134. data/spec/security/encrypted_document_spec.rb +55 -0
  135. data/spec/security/rsa_key_pair_spec.rb +55 -0
  136. data/spec/security/signature_spec.rb +66 -0
  137. data/spec/security/static_certificate_store_spec.rb +52 -0
  138. data/spec/sender_spec.rb +887 -0
  139. data/spec/serialize/message_pack_spec.rb +131 -0
  140. data/spec/serialize/secure_serializer_spec.rb +102 -0
  141. data/spec/serialize/serializable_spec.rb +90 -0
  142. data/spec/serialize/serializer_spec.rb +174 -0
  143. data/spec/spec.opts +2 -0
  144. data/spec/spec_helper.rb +77 -0
  145. data/spec/stats_helper_spec.rb +681 -0
  146. data/spec/tracer_spec.rb +114 -0
  147. metadata +320 -0
@@ -0,0 +1,34 @@
1
+ #
2
+ # Copyright (c) 2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'rubygems'
24
+
25
+ begin
26
+ require 'win32ole'
27
+
28
+ # ohai 0.3.6 has a bug which causes WMI data to be imported using the default
29
+ # Windows code page. the workaround is to set the win32ole gem's code page to
30
+ # UTF-8, which is probably a good general Ruby on Windows practice in any case.
31
+ WIN32OLE.codepage = WIN32OLE::CP_UTF8
32
+ rescue LoadError
33
+ # ignore load error and skip monkey-patch if gems are not yet installed.
34
+ end
@@ -0,0 +1,91 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module RightScale
24
+
25
+ # Apply each method call to all registered targets
26
+ class Multiplexer
27
+
28
+ # Access to underlying multiplexed objects
29
+ attr_reader :targets
30
+
31
+ # Undefine warn to prevent Kernel#warn from being called
32
+ undef warn rescue nil
33
+
34
+ # Initialize multiplexer targets
35
+ #
36
+ # === Parameters
37
+ # targets(Object):: Targets that should receive the method calls
38
+ def initialize(*targets)
39
+ @targets = targets || []
40
+ end
41
+
42
+ # Add object to list of multiplexed targets
43
+ #
44
+ # === Parameters
45
+ # target(Object):: Add target to list of multiplexed targets
46
+ #
47
+ # === Return
48
+ # self(RightScale::Multiplexer):: self so operation can be chained
49
+ def add(target)
50
+ @targets << target unless @targets.include?(target)
51
+ self
52
+ end
53
+
54
+ # Remove object from list of multiplexed targets
55
+ #
56
+ # === Parameters
57
+ # target(Object):: Remove target from list of multiplexed targets
58
+ #
59
+ # === Return
60
+ # self(RightScale::Multiplexer):: self so operation can be chained
61
+ def remove(target)
62
+ @targets.delete_if { |t| t == target }
63
+ self
64
+ end
65
+
66
+ # Access target at given index
67
+ #
68
+ # === Parameters
69
+ # index(Integer):: Target index
70
+ #
71
+ # === Return
72
+ # target(Object):: Target at index 'index' or nil if none
73
+ def [](index)
74
+ target = @targets[index]
75
+ end
76
+
77
+ # Forward any method invocation to targets
78
+ #
79
+ # === Parameters
80
+ # m(Symbol):: Method that should be multiplexed
81
+ # args(Array):: Arguments
82
+ #
83
+ # === Return
84
+ # res(Object):: Result of first target in list
85
+ def method_missing(m, *args)
86
+ res = @targets.inject([]) { |res, t| res << t.__send__(m, *args) }
87
+ res[0]
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,270 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #
23
+
24
+ module RightScale
25
+
26
+ # Container for status and result of an operation
27
+ class OperationResult
28
+
29
+ include Serializable
30
+
31
+ # Result status code
32
+ SUCCESS = 0
33
+ ERROR = 1
34
+ CONTINUE = 2
35
+ RETRY = 3
36
+ NON_DELIVERY = 4
37
+ MULTICAST = 5 # Deprecated for agents at version 13 or above
38
+
39
+ # Non-delivery reasons
40
+ NON_DELIVERY_REASONS = [
41
+ NO_TARGET = "no target",
42
+ UNKNOWN_TARGET = "unknown target",
43
+ NO_ROUTE_TO_TARGET = "no route to target",
44
+ TARGET_NOT_CONNECTED = "target not connected",
45
+ TTL_EXPIRATION = "TTL expiration",
46
+ RETRY_TIMEOUT = "retry timeout"
47
+ ]
48
+
49
+ # Maximum characters included in display of error
50
+ MAX_ERROR_SIZE = 60
51
+
52
+ # (Integer) Status code
53
+ attr_accessor :status_code
54
+
55
+ # (Object) Result data, if any
56
+ attr_accessor :content
57
+
58
+ def initialize(*args)
59
+ @status_code = args[0]
60
+ @content = args[1] if args.size > 1
61
+ end
62
+
63
+ # User friendly result
64
+ # Does not include content except in the case of error or non-delivery
65
+ #
66
+ # === Return
67
+ # (String):: Name of result code
68
+ def to_s
69
+ status(reason = true)
70
+ end
71
+
72
+ # User friendly result status
73
+ #
74
+ # === Parameters
75
+ # reason(Boolean):: Whether to include failure reason information, default to false
76
+ #
77
+ # === Return
78
+ # (String):: Name of result code
79
+ def status(reason = false)
80
+ case @status_code
81
+ when SUCCESS then 'success'
82
+ when ERROR then 'error' + (reason ? " (#{truncated_error})" : "")
83
+ when CONTINUE then 'continue'
84
+ when RETRY then 'retry'
85
+ when NON_DELIVERY then 'non-delivery' + (reason ? " (#{@content})" : "")
86
+ when MULTICAST then 'multicast'
87
+ end
88
+ end
89
+
90
+ # Limited length error string
91
+ #
92
+ # === Return
93
+ # e(String):: String of no more than MAX_ERROR_SIZE characters
94
+ def truncated_error
95
+ e = @content.is_a?(String) ? @content : @content.inspect
96
+ e = e[0, MAX_ERROR_SIZE - 3] + "..." if e.size > (MAX_ERROR_SIZE - 3)
97
+ e
98
+ end
99
+
100
+ # Instantiate from request results
101
+ # Ignore all but first result if results is a hash
102
+ #
103
+ # === Parameters
104
+ # results(Result|Hash|OperationResult|nil):: Result or the Result "results" field
105
+ #
106
+ # === Return
107
+ # (RightScale::OperationResult):: Converted operation result
108
+ def self.from_results(results)
109
+ r = results.kind_of?(Result) ? results.results : results
110
+ if r && r.respond_to?(:status_code) && r.respond_to?(:content)
111
+ new(r.status_code, r.content)
112
+ elsif r && r.kind_of?(Hash) && r.values.size > 0
113
+ r = r.values[0]
114
+ if r.respond_to?(:status_code) && r.respond_to?(:content)
115
+ new(r.status_code, r.content)
116
+ else
117
+ error("Invalid operation result content: #{r.inspect}")
118
+ end
119
+ elsif r.nil?
120
+ error("No results")
121
+ else
122
+ error("Invalid operation result type: #{results.inspect}")
123
+ end
124
+ end
125
+
126
+ # Create new success status
127
+ #
128
+ # === Parameters
129
+ # content(Object):: Any data associated with successful results - defaults to nil
130
+ #
131
+ # === Return
132
+ # (OperationResult):: Corresponding result
133
+ def self.success(content = nil)
134
+ OperationResult.new(SUCCESS, content)
135
+ end
136
+
137
+ # Create new error status
138
+ #
139
+ # === Parameters
140
+ # message(String):: Error description
141
+ # exception(Exception|String):: Associated exception or other parenthetical error information
142
+ # backtrace(Symbol):: Exception backtrace extent: :no_trace, :caller, or :trace,
143
+ # defaults to :caller
144
+ #
145
+ # === Return
146
+ # (OperationResult):: Corresponding result
147
+ def self.error(message, exception = nil, backtrace = :caller)
148
+ OperationResult.new(ERROR, Log.format(message, exception, backtrace))
149
+ end
150
+
151
+ # Create new continue status
152
+ #
153
+ # === Parameters
154
+ # content(Object):: Any data associated with continue - defaults to nil
155
+ #
156
+ # === Return
157
+ # (OperationResult):: Corresponding result
158
+ def self.continue(content = nil)
159
+ OperationResult.new(CONTINUE, content)
160
+ end
161
+
162
+ # Create new retry status
163
+ #
164
+ # === Parameters
165
+ # content(Object):: Any data associated with retry - defaults to nil
166
+ #
167
+ # === Return
168
+ # (OperationResult):: Corresponding result
169
+ def self.retry(content = nil)
170
+ OperationResult.new(RETRY, content)
171
+ end
172
+
173
+ # Create new non-delivery status
174
+ #
175
+ # === Parameters
176
+ # reason(String):: Non-delivery reason from NON_DELIVERY_REASONS
177
+ #
178
+ # === Return
179
+ # (OperationResult):: Corresponding result
180
+ def self.non_delivery(reason)
181
+ OperationResult.new(NON_DELIVERY, reason)
182
+ end
183
+
184
+ # Create new multicast status
185
+ # Deprecated for agents at version 13 or above
186
+ #
187
+ # === Parameters
188
+ # targets(Array):: Identity of targets to which request was published
189
+ #
190
+ # === Return
191
+ # (OperationResult):: Corresponding result
192
+ def self.multicast(targets)
193
+ OperationResult.new(MULTICAST, targets)
194
+ end
195
+
196
+ # Was last operation successful?
197
+ #
198
+ # === Return
199
+ # true:: If status is SUCCESS or CONTINUE
200
+ # false:: Otherwise
201
+ def success?
202
+ status_code == SUCCESS || status_code == CONTINUE
203
+ end
204
+
205
+ # Was last operation unsuccessful?
206
+ #
207
+ # === Return
208
+ # true:: If status is ERROR or NON_DELIVERY
209
+ # false:: Otherwise
210
+ def error?
211
+ status_code == ERROR || status_code == NON_DELIVERY
212
+ end
213
+
214
+ # Was last operation status CONTINUE?
215
+ #
216
+ # === Return
217
+ # true:: If status is CONTINUE
218
+ # false:: Otherwise
219
+ def continue?
220
+ status_code == CONTINUE
221
+ end
222
+
223
+ # Was last operation status RETRY?
224
+ #
225
+ # === Return
226
+ # true:: If status is RETRY
227
+ # false:: Otherwise
228
+ def retry?
229
+ status_code == RETRY
230
+ end
231
+
232
+ # Was last operation status NON_DELIVERY?
233
+ #
234
+ # === Return
235
+ # true:: If status is NON_DELIVERY
236
+ # false:: Otherwise
237
+ def non_delivery?
238
+ status_code == NON_DELIVERY
239
+ end
240
+
241
+ # Was last operation status MULTICAST?
242
+ # Deprecated for agents at version 13 or above
243
+ #
244
+ # === Return
245
+ # true:: If status is MULTICAST
246
+ # false:: Otherwise
247
+ def multicast?
248
+ status_code == MULTICAST
249
+ end
250
+
251
+ # Array of serialized fields given to constructor
252
+ def serialized_members
253
+ [@status_code, @content]
254
+ end
255
+
256
+ end # OperationResult
257
+
258
+ # Helper module to simplify result construction
259
+ module OperationResultHelper
260
+
261
+ def success_result(*args) OperationResult.success(*args) end
262
+ def error_result(*args) OperationResult.error(*args) end
263
+ def continue_result(*args) OperationResult.continue(*args) end
264
+ def retry_result(*args) OperationResult.retry(*args) end
265
+ def non_delivery_result(*args) OperationResult.non_delivery(*args) end
266
+ def result_from(*args) OperationResult.from_results(*args) end
267
+
268
+ end # OperationResultHelper
269
+
270
+ end # RightScale
@@ -0,0 +1,637 @@
1
+ # Copyright (c) 2009-2011 RightScale Inc
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+
23
+ # Hack to replace the Nanite namespace from downrev agents with the RightScale namespace
24
+ module JSON
25
+ class << self
26
+ def parse(source, opts = {})
27
+ if source =~ /(.*)json_class":"Nanite::(.*)/
28
+ JSON.parser.new( Regexp.last_match(1) + 'json_class":"RightScale::' + Regexp.last_match(2), opts).parse
29
+ else
30
+ JSON.parser.new(source, opts).parse
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+
37
+ module RightScale
38
+
39
+ # Base class for all packets flowing through the mappers
40
+ # Knows how to dump itself to MessagePack or JSON
41
+ class Packet
42
+
43
+ # Current version of protocol
44
+ VERSION = AgentConfig.protocol_version
45
+
46
+ # Default version for packet senders unaware of this versioning
47
+ DEFAULT_VERSION = 0
48
+
49
+ # Shard scope value meaning restrict sending request only to agents with no shard id
50
+ GLOBAL = 0
51
+
52
+ attr_accessor :size
53
+
54
+ def initialize
55
+ raise NotImplementedError.new("#{self.class.name} is an abstract class.")
56
+ end
57
+
58
+ # Create packet from unmarshalled MessagePack data
59
+ #
60
+ # === Parameters
61
+ # o(Hash):: MessagePack data
62
+ #
63
+ # === Return
64
+ # (Packet):: New packet
65
+ def self.msgpack_create(o)
66
+ create(o)
67
+ end
68
+
69
+ # Create packet from unmarshalled JSON data
70
+ #
71
+ # === Parameters
72
+ # o(Hash):: MessagePack data
73
+ #
74
+ # === Return
75
+ # (Packet):: New packet
76
+ def self.json_create(o)
77
+ create(o)
78
+ end
79
+
80
+ # Marshal packet into MessagePack format
81
+ #
82
+ # === Parameters
83
+ # a(Array):: Arguments
84
+ #
85
+ # === Return
86
+ # msg(String):: Marshalled packet
87
+ def to_msgpack(*a)
88
+ msg = {
89
+ 'msgpack_class' => self.class.name,
90
+ 'data' => instance_variables.inject({}) { |m, ivar| m[ivar.to_s.sub(/@/,'')] = instance_variable_get(ivar); m },
91
+ 'size' => nil
92
+ }.to_msgpack(*a)
93
+ @size = msg.size
94
+ msg.sub!(/size\300/) { |m| "size" + @size.to_msgpack }
95
+ msg
96
+ end
97
+
98
+ # Marshal packet into JSON format
99
+ #
100
+ # === Parameters
101
+ # a(Array):: Arguments
102
+ #
103
+ # === Return
104
+ # js(String):: Marshalled packet
105
+ def to_json(*a)
106
+ # Hack to override RightScale namespace with Nanite for downward compatibility
107
+ class_name = self.class.name
108
+ if class_name =~ /^RightScale::(.*)/
109
+ class_name = "Nanite::" + Regexp.last_match(1)
110
+ end
111
+
112
+ js = {
113
+ 'json_class' => class_name,
114
+ 'data' => instance_variables.inject({}) { |m, ivar| m[ivar.to_s.sub(/@/,'')] = instance_variable_get(ivar); m }
115
+ }.to_json(*a)
116
+ @size = js.size
117
+ js = js.chop + ",\"size\":#{@size}}"
118
+ end
119
+
120
+ # Name of packet in lower snake case
121
+ #
122
+ # === Return
123
+ # (String):: Packet name
124
+ def name
125
+ self.class.to_s.split('::').last.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
126
+ end
127
+
128
+ # Generate log representation
129
+ #
130
+ # === Parameters
131
+ # filter(Array(Symbol)):: Attributes to be included in output
132
+ # version(Symbol|nil):: Version to display: :recv_version, :send_version, or nil meaning none
133
+ #
134
+ # === Return
135
+ # log_msg(String):: Log representation
136
+ def to_s(filter = nil, version = nil)
137
+ v = __send__(version) if version
138
+ v = (v && v != DEFAULT_VERSION) ? " v#{v}" : ""
139
+ log_msg = "[#{name}#{v}]"
140
+ duration = ", #{enough_precision(@duration)} sec" if @duration && (filter.nil? || filter.include?(:duration))
141
+ log_msg += " (#{@size.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")} bytes#{duration})" if @size && !@size.to_s.empty?
142
+ log_msg
143
+ end
144
+
145
+ # Determine enough precision for floating point value to give at least two significant
146
+ # digits and then convert the value to a decimal digit string of that precision
147
+ #
148
+ # === Parameters
149
+ # value(Float):: Value to be converted
150
+ #
151
+ # === Return
152
+ # (String):: Floating point digit string
153
+ def enough_precision(value)
154
+ scale = [1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0]
155
+ enough = lambda { |v| (v >= 10.0 ? 0 :
156
+ (v >= 1.0 ? 1 :
157
+ (v >= 0.1 ? 2 :
158
+ (v >= 0.01 ? 3 :
159
+ (v > 0.001 ? 4 :
160
+ (v > 0.0 ? 5 : 0)))))) }
161
+ digit_str = lambda { |p, v| sprintf("%.#{p}f", (v * scale[p]).round / scale[p])}
162
+ digit_str.call(enough.call(value), value)
163
+ end
164
+
165
+ # Generate log friendly serialized identity
166
+ # Result marked with leading '*' if not same as original identity
167
+ #
168
+ # === Parameters
169
+ # id(String):: Serialized identity
170
+ #
171
+ # === Return
172
+ # (String):: Log friendly serialized identity
173
+ def id_to_s(id)
174
+ modified_id = AgentIdentity.compatible_serialized(id)
175
+ if id == modified_id then modified_id else "*#{modified_id}" end
176
+ end
177
+
178
+ # Convert serialized AgentIdentity to compatible format
179
+ #
180
+ # === Parameters
181
+ # id(String):: Serialized identity
182
+ #
183
+ # === Return
184
+ # (String):: Compatible serialized identity
185
+ def self.compatible(id)
186
+ AgentIdentity.compatible_serialized(id)
187
+ end
188
+
189
+ # Get target to be used for encrypting the packet
190
+ #
191
+ # === Return
192
+ # (String):: Target
193
+ def target_for_encryption
194
+ nil
195
+ end
196
+
197
+ # Whether the packet is one that does not have an associated response
198
+ #
199
+ # === Return
200
+ # (Boolean):: Defaults to true
201
+ def one_way
202
+ true
203
+ end
204
+
205
+ # Generate token used to trace execution of operation across multiple packets
206
+ #
207
+ # === Return
208
+ # tr(String):: Trace token, may be empty
209
+ def trace
210
+ audit_id = self.respond_to?(:payload) && payload.is_a?(Hash) && (payload['audit_id'] || payload[:audit_id])
211
+ tok = self.respond_to?(:token) && token
212
+ tr = ''
213
+ if audit_id || tok
214
+ tr = '<'
215
+ if audit_id
216
+ tr += audit_id.to_s
217
+ tr += ':' if tok
218
+ end
219
+ tr += tok if tok
220
+ tr += '>'
221
+ end
222
+ tr
223
+ end
224
+
225
+ # Retrieve protocol version of original creator of packet
226
+ #
227
+ # === Return
228
+ # (Integer) Received protocol version
229
+ def recv_version
230
+ @version[0]
231
+ end
232
+
233
+ # Retrieve protocol version of packet for use when sending packet
234
+ #
235
+ # === Return
236
+ # (Integer) Send protocol version
237
+ def send_version
238
+ @version[1]
239
+ end
240
+
241
+ # Set protocol version of packet for use when sending packet
242
+ def send_version=(value)
243
+ @version[1] = value
244
+ end
245
+
246
+ end # Packet
247
+
248
+
249
+ # Packet for a work request for an actor node that has an expected result
250
+ class Request < Packet
251
+
252
+ attr_accessor :from, :scope, :payload, :type, :token, :reply_to, :selector, :target, :persistent, :expires_at,
253
+ :tags, :tries
254
+
255
+ DEFAULT_OPTIONS = {:selector => :any}
256
+
257
+ # Create packet
258
+ #
259
+ # === Parameters
260
+ # type(String):: Dispatch route for the request
261
+ # payload(Any):: Arbitrary data that is transferred to actor
262
+ # opts(Hash):: Optional settings:
263
+ # :from(String):: Sender identity
264
+ # :scope(Hash):: Define behavior that should be used to resolve tag based routing
265
+ # :token(String):: Generated request id that a mapper uses to identify replies
266
+ # :reply_to(String):: Identity of the node that actor replies to, usually a mapper itself
267
+ # :selector(Symbol):: Selector used to route the request: :any or :all, defaults to :any,
268
+ # :all deprecated for version 13 and above
269
+ # :target(String):: Target recipient
270
+ # :persistent(Boolean):: Indicates if this request should be saved to persistent storage
271
+ # by the AMQP broker
272
+ # :expires_at(Integer|nil):: Time in seconds in Unix-epoch when this request expires and
273
+ # is to be ignored by the receiver; value 0 means never expire; defaults to 0
274
+ # :tags(Array(Symbol)):: List of tags to be used for selecting target for this request
275
+ # :tries(Array):: List of tokens for previous attempts to send this request
276
+ # version(Array):: Protocol version of the original creator of the packet followed by the
277
+ # protocol version of the packet contents to be used when sending
278
+ # size(Integer):: Size of request in bytes used only for marshalling
279
+ def initialize(type, payload, opts = {}, version = [VERSION, VERSION], size = nil)
280
+ opts = DEFAULT_OPTIONS.merge(opts)
281
+ @type = type
282
+ @payload = payload
283
+ @from = opts[:from]
284
+ @scope = opts[:scope]
285
+ @token = opts[:token]
286
+ @reply_to = opts[:reply_to]
287
+ @selector = opts[:selector]
288
+ @selector = :any if ["least_loaded", "random"].include?(@selector.to_s)
289
+ @target = opts[:target]
290
+ @persistent = opts[:persistent]
291
+ @expires_at = opts[:expires_at] || 0
292
+ @tags = opts[:tags] || []
293
+ @tries = opts[:tries] || []
294
+ @version = version
295
+ @size = size
296
+ end
297
+
298
+ # Test whether the request is being fanned out to multiple targets
299
+ #
300
+ # === Return
301
+ # (Boolean):: true if is multicast, otherwise false
302
+ def fanout?
303
+ @selector.to_s == 'all'
304
+ end
305
+
306
+ # Create packet from unmarshalled data
307
+ #
308
+ # === Parameters
309
+ # o(Hash):: Unmarshalled data
310
+ #
311
+ # === Return
312
+ # (Request):: New packet
313
+ def self.create(o)
314
+ i = o['data']
315
+ expires_at = if i.has_key?('created_at')
316
+ created_at = i['created_at'].to_i
317
+ created_at > 0 ? created_at + (15 * 60) : 0
318
+ else
319
+ i['expires_at']
320
+ end
321
+ new(i['type'], i['payload'], { :from => self.compatible(i['from']), :scope => i['scope'],
322
+ :token => i['token'], :reply_to => self.compatible(i['reply_to']),
323
+ :selector => i['selector'], :target => self.compatible(i['target']),
324
+ :persistent => i['persistent'], :tags => i['tags'],
325
+ :expires_at => expires_at, :tries => i['tries'] },
326
+ i['version'] || [DEFAULT_VERSION, DEFAULT_VERSION], o['size'])
327
+ end
328
+
329
+ # Generate log representation
330
+ #
331
+ # === Parameters
332
+ # filter(Array(Symbol)):: Attributes to be included in output
333
+ # version(Symbol|nil):: Version to display: :recv_version, :send_version, or nil meaning none
334
+ #
335
+ # === Return
336
+ # log_msg(String):: Log representation
337
+ def to_s(filter = nil, version = nil)
338
+ payload = PayloadFormatter.log(@type, @payload)
339
+ log_msg = "#{super(filter, version)} #{trace} #{@type}"
340
+ log_msg += " #{payload}" if payload
341
+ log_msg += " from #{id_to_s(@from)}" if filter.nil? || filter.include?(:from)
342
+ log_msg += ", target #{id_to_s(@target)}" if @target && (filter.nil? || filter.include?(:target))
343
+ log_msg += ", scope #{@scope.inspect}" if @scope && (filter.nil? || filter.include?(:scope))
344
+ log_msg += ", fanout" if (filter.nil? || filter.include?(:fanout)) && fanout?
345
+ log_msg += ", reply_to #{id_to_s(@reply_to)}" if @reply_to && (filter.nil? || filter.include?(:reply_to))
346
+ log_msg += ", tags #{@tags.inspect}" if @tags && !@tags.empty? && (filter.nil? || filter.include?(:tags))
347
+ log_msg += ", persistent" if @persistent && (filter.nil? || filter.include?(:persistent))
348
+ log_msg += ", tries #{tries_to_s}" if @tries && !@tries.empty? && (filter.nil? || filter.include?(:tries))
349
+ log_msg += ", payload #{@payload.inspect}" if filter.nil? || filter.include?(:payload)
350
+ log_msg
351
+ end
352
+
353
+ # Convert tries list to string representation
354
+ #
355
+ # === Return
356
+ # log_msg(String):: Tries list
357
+ def tries_to_s
358
+ @tries.map { |t| "<#{t}>" }.join(", ")
359
+ end
360
+
361
+ # Get target to be used for encrypting the packet
362
+ #
363
+ # === Return
364
+ # (String):: Target
365
+ def target_for_encryption
366
+ @target
367
+ end
368
+
369
+ # Whether the packet is one that does not have an associated response
370
+ #
371
+ # === Return
372
+ # false:: Always return false
373
+ def one_way
374
+ false
375
+ end
376
+
377
+ end # Request
378
+
379
+
380
+ # Packet for a work request for an actor node that has no result, i.e., one-way request
381
+ class Push < Packet
382
+
383
+ attr_accessor :from, :scope, :payload, :type, :token, :selector, :target, :persistent, :expires_at, :tags
384
+
385
+ DEFAULT_OPTIONS = {:selector => :any}
386
+
387
+ # Create packet
388
+ #
389
+ # === Parameters
390
+ # type(String):: Dispatch route for the request
391
+ # payload(Any):: Arbitrary data that is transferred to actor
392
+ # opts(Hash):: Optional settings:
393
+ # :from(String):: Sender identity
394
+ # :scope(Hash):: Define behavior that should be used to resolve tag based routing
395
+ # :token(String):: Generated request id that a mapper uses to identify replies
396
+ # :selector(Symbol):: Selector used to route the request: :any or :all, defaults to :any
397
+ # :target(String):: Target recipient
398
+ # :persistent(Boolean):: Indicates if this request should be saved to persistent storage
399
+ # by the AMQP broker
400
+ # :expires_at(Integer|nil):: Time in seconds in Unix-epoch when this request expires and
401
+ # is to be ignored by the receiver; value 0 means never expire; defaults to 0
402
+ # :tags(Array(Symbol)):: List of tags to be used for selecting target for this request
403
+ # :tries(Array):: List of tokens for previous attempts to send this request (only here
404
+ # for consistency with Request)
405
+ # version(Array):: Protocol version of the original creator of the packet followed by the
406
+ # protocol version of the packet contents to be used when sending
407
+ # size(Integer):: Size of request in bytes used only for marshalling
408
+ def initialize(type, payload, opts = {}, version = [VERSION, VERSION], size = nil)
409
+ opts = DEFAULT_OPTIONS.merge(opts)
410
+ @type = type
411
+ @payload = payload
412
+ @from = opts[:from]
413
+ @scope = opts[:scope]
414
+ @token = opts[:token]
415
+ @selector = opts[:selector]
416
+ @selector = :any if ["least_loaded", "random"].include?(@selector.to_s)
417
+ @target = opts[:target]
418
+ @persistent = opts[:persistent]
419
+ @expires_at = opts[:expires_at] || 0
420
+ @tags = opts[:tags] || []
421
+ @version = version
422
+ @size = size
423
+ end
424
+
425
+ # Test whether the request is being fanned out to multiple targets
426
+ #
427
+ # === Return
428
+ # (Boolean):: true if is fanout, otherwise false
429
+ def fanout?
430
+ @selector.to_s == 'all'
431
+ end
432
+
433
+ # Keep interface consistent with Request packets
434
+ # A push never gets retried
435
+ #
436
+ # === Return
437
+ # []:: Always return empty array
438
+ def tries
439
+ []
440
+ end
441
+
442
+ # Create packet from unmarshalled data
443
+ #
444
+ # === Parameters
445
+ # o(Hash):: Unmarshalled data
446
+ #
447
+ # === Return
448
+ # (Push):: New packet
449
+ def self.create(o)
450
+ i = o['data']
451
+ new(i['type'], i['payload'], { :from => self.compatible(i['from']), :scope => i['scope'],
452
+ :token => i['token'], :selector => i['selector'],
453
+ :target => self.compatible(i['target']), :persistent => i['persistent'],
454
+ :tags => i['tags'], :expires_at => i['expires_at'] },
455
+ i['version'] || [DEFAULT_VERSION, DEFAULT_VERSION], o['size'])
456
+ end
457
+
458
+ # Generate log representation
459
+ #
460
+ # === Parameters
461
+ # filter(Array(Symbol)):: Attributes to be included in output
462
+ # version(Symbol|nil):: Version to display: :recv_version, :send_version, or nil meaning none
463
+ #
464
+ # === Return
465
+ # log_msg(String):: Log representation
466
+ def to_s(filter = nil, version = nil)
467
+ payload = PayloadFormatter.log(@type, @payload)
468
+ log_msg = "#{super(filter, version)} #{trace} #{@type}"
469
+ log_msg += " #{payload}" if payload
470
+ log_msg += " from #{id_to_s(@from)}" if filter.nil? || filter.include?(:from)
471
+ log_msg += ", target #{id_to_s(@target)}" if @target && (filter.nil? || filter.include?(:target))
472
+ log_msg += ", scope #{@scope.inspect}" if @scope && (filter.nil? || filter.include?(:scope))
473
+ log_msg += ", fanout" if (filter.nil? || filter.include?(:fanout)) && fanout?
474
+ log_msg += ", tags #{@tags.inspect}" if @tags && !@tags.empty? && (filter.nil? || filter.include?(:tags))
475
+ log_msg += ", persistent" if @persistent && (filter.nil? || filter.include?(:persistent))
476
+ log_msg += ", payload #{@payload.inspect}" if filter.nil? || filter.include?(:payload)
477
+ log_msg
478
+ end
479
+
480
+ # Get target to be used for encrypting the packet
481
+ #
482
+ # === Return
483
+ # (String):: Target
484
+ def target_for_encryption
485
+ @target
486
+ end
487
+
488
+ end # Push
489
+
490
+
491
+ # Packet for a work result notification sent from actor node
492
+ class Result < Packet
493
+
494
+ attr_accessor :token, :results, :to, :from, :request_from, :tries, :persistent, :duration
495
+
496
+ # Create packet
497
+ #
498
+ # === Parameters
499
+ # token(String):: Generated request id that a mapper uses to identify replies
500
+ # to(String):: Identity of the node to which result should be delivered
501
+ # results(Any):: Arbitrary data that is transferred from actor, a result of actor's work
502
+ # from(String):: Sender identity
503
+ # request_from(String):: Identity of the agent that sent the original request
504
+ # tries(Array):: List of tokens for previous attempts to send associated request
505
+ # persistent(Boolean):: Indicates if this result should be saved to persistent storage
506
+ # by the AMQP broker
507
+ # duration(Float):: Number of seconds required to produce the result
508
+ # version(Array):: Protocol version of the original creator of the packet followed by the
509
+ # protocol version of the packet contents to be used when sending
510
+ # size(Integer):: Size of request in bytes used only for marshalling
511
+ def initialize(token, to, results, from, request_from = nil, tries = nil, persistent = nil, duration = nil,
512
+ version = [VERSION, VERSION], size = nil)
513
+ @token = token
514
+ @to = to
515
+ @results = results
516
+ @from = from
517
+ @request_from = request_from
518
+ @tries = tries || []
519
+ @persistent = persistent
520
+ @duration = duration
521
+ @version = version
522
+ @size = size
523
+ end
524
+
525
+ # Create packet from unmarshalled data
526
+ #
527
+ # === Parameters
528
+ # o(Hash):: Unmarshalled data
529
+ #
530
+ # === Return
531
+ # (Result):: New packet
532
+ def self.create(o)
533
+ i = o['data']
534
+ new(i['token'], self.compatible(i['to']), i['results'], self.compatible(i['from']),
535
+ self.compatible(i['request_from']), i['tries'], i['persistent'], i['duration'],
536
+ i['version'] || [DEFAULT_VERSION, DEFAULT_VERSION], o['size'])
537
+ end
538
+
539
+ # Generate log representation
540
+ #
541
+ # === Parameters
542
+ # filter(Array(Symbol)):: Attributes to be included in output
543
+ # version(Symbol|nil):: Version to display: :recv_version, :send_version, or nil meaning none
544
+ #
545
+ # === Return
546
+ # log_msg(String):: Log representation
547
+ def to_s(filter = nil, version = nil)
548
+ log_msg = "#{super(filter, version)} #{trace}"
549
+ log_msg += " from #{id_to_s(@from)}" if filter.nil? || filter.include?(:from)
550
+ log_msg += " to #{id_to_s(@to)}" if filter.nil? || filter.include?(:to)
551
+ log_msg += ", request_from #{id_to_s(@request_from)}" if @request_from && (filter.nil? || filter.include?(:request_from))
552
+ log_msg += ", persistent" if @persistent && (filter.nil? || filter.include?(:persistent))
553
+ log_msg += ", tries #{tries_to_s}" if @tries && !@tries.empty? && (filter.nil? || filter.include?(:tries))
554
+ if filter.nil? || !filter.include?(:results)
555
+ if !@results.nil?
556
+ if @results.is_a?(RightScale::OperationResult) # Will be true when logging a 'SEND'
557
+ res = @results
558
+ elsif @results.is_a?(Hash) && @results.size == 1 # Will be true when logging a 'RECV' for version 9 or below
559
+ res = @results.values.first
560
+ end
561
+ log_msg += " #{res.to_s}" if res
562
+ end
563
+ else
564
+ log_msg += " results #{@results.inspect}"
565
+ end
566
+ log_msg
567
+ end
568
+
569
+ # Convert tries list to string representation
570
+ #
571
+ # === Return
572
+ # log_msg(String):: Tries list
573
+ def tries_to_s
574
+ log_msg = ""
575
+ @tries.each { |r| log_msg += "<#{r}>, " }
576
+ log_msg = log_msg[0..-3] if log_msg.size > 1
577
+ end
578
+
579
+ # Get target to be used for encrypting the packet
580
+ #
581
+ # === Return
582
+ # (String):: Target
583
+ def target_for_encryption
584
+ @to
585
+ end
586
+
587
+ end # Result
588
+
589
+
590
+ # Packet for carrying statistics
591
+ class Stats < Packet
592
+
593
+ attr_accessor :data, :token, :from
594
+
595
+ # Create packet
596
+ #
597
+ # === Parameters
598
+ # data(Object):: Data
599
+ # from(String):: Identity of sender
600
+ # version(Array):: Protocol version of the original creator of the packet followed by the
601
+ # protocol version of the packet contents to be used when sending
602
+ # size(Integer):: Size of request in bytes used only for marshalling
603
+ def initialize(data, from, version = [VERSION, VERSION], size = nil)
604
+ @data = data
605
+ @from = from
606
+ @version = version
607
+ @size = size
608
+ end
609
+
610
+ # Create packet from unmarshalled data
611
+ #
612
+ # === Parameters
613
+ # o(Hash):: Unmarshalled data
614
+ #
615
+ # === Return
616
+ # (Result):: New packet
617
+ def self.create(o)
618
+ i = o['data']
619
+ new(i['data'], self.compatible(i['from']), i['version'] || [DEFAULT_VERSION, DEFAULT_VERSION], o['size'])
620
+ end
621
+
622
+ # Generate log representation
623
+ #
624
+ # === Parameters
625
+ # filter(Array(Symbol)):: Attributes to be included in output
626
+ # version(Symbol|nil):: Version to display: :recv_version, :send_version, or nil meaning none
627
+ #
628
+ # === Return
629
+ # log_msg(String):: Log representation
630
+ def to_s(filter = nil, version = nil)
631
+ log_msg = "#{super(filter, version)} #{id_to_s(@from)}"
632
+ end
633
+
634
+ end # Stats
635
+
636
+ end # RightScale
637
+