right_agent 0.5.1

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 (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
+