poweriq_client 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -9,11 +9,12 @@ see the online Power IQ documentation (http://www.raritan.com/support/power-iq).
9
9
 
10
10
  == Console
11
11
 
12
- The Power IQ client comes with a interactive console. To run the console:
12
+ The Power IQ client comes with a interactive console. From the console you can make REST API calls to Power IQ.
13
+ To run the console:
13
14
 
14
15
  % poweriq_client
15
16
 
16
- ==== Console Options
17
+ ==== Options
17
18
 
18
19
  Usage: poweriq_client [options]
19
20
  -l, --less-typing Put resource objects in global namespace
@@ -25,7 +26,7 @@ The Power IQ client comes with a interactive console. To run the console:
25
26
 
26
27
  ==== Configuration
27
28
 
28
- Create a file named .poweriq_client in your home directory. The contents of the file
29
+ Create the file $HOME/.poweriq_client. The contents of the file
29
30
  should look similiar to below, adjusted to your Power IQ server:
30
31
 
31
32
  default:
@@ -39,28 +40,31 @@ need to provide the configuration manually:
39
40
  % poweriq_client
40
41
  >> PowerIQ::Configuration.configure("user"=>"foo","password"=>"bar","host"=>"vm150")
41
42
 
42
- ==== Examples
43
+ == Examples
44
+
45
+ Start the client console:
46
+ % poweriq_client -u admin -p raritan -s vm163
43
47
 
44
48
  Retrieve all outlets:
45
49
  >> outlets_resource = PowerIQ::Resource::Outlet.new
46
50
  https://vm163/api/v2/outlets
47
- >> outlets_resource.get
51
+ >> outlets_json = outlets_resource.get
48
52
 
49
53
  Retrieve one outlet:
50
54
  >> outlet_resource = PowerIQ::Resource::Outlet.new('/381')
51
55
  https://vm163/api/v2/outlets/381
52
- >> outlet = outlet_resource.get
56
+ >> outlet_json = outlet_resource.get
53
57
 
54
58
  Update an outlet:
55
59
  >> outlet_resource = PowerIQ::Resource::Outlet.new('/381')
56
60
  https://vm163/api/v2/outlets/381
57
- >> outlet = outlet_resource.get
58
- >> outlet['outlet']['outlet_name'] = "New Name"
59
- >> outlet_resource.put outlet.to_json
61
+ >> outlet_json = outlet_resource.get
62
+ >> outlet_json['outlet']['outlet_name'] = "New Name"
63
+ >> outlet_resource.put outlet_json.to_json
60
64
 
61
- === Error Handling
65
+ == Error Handling
62
66
 
63
- When an error occurs with API calls, the Power IQ server will return an appropriate HTTP response code, and include
67
+ When an error occurs during an API call, the Power IQ server will return an appropriate HTTP response code, and include
64
68
  a json response with error details. On the client side, an exception is thrown. Either the exception or the
65
69
  resource object can be used to reveal details about what went wrong:
66
70
 
@@ -145,6 +149,111 @@ You can also catch the exception, and the exception will have the response objec
145
149
  "trace" => "/var/oculan/home/tba/rails/src/lib/messaging/job_polling.rb:31:...."
146
150
  }
147
151
 
152
+ == Jobs
153
+
154
+ Some resource actions will return a Job JSON structure. These actions either will always, or optionally execute
155
+ asynchronously. In order to determine when a job has been completed, the server must be polled for job
156
+ completeness. Methods on the job resource simplify this for you:
157
+
158
+ >> job = Job.new('/5')
159
+ https://vm163/api/v2/jobs/5
160
+ >> job.get
161
+ {
162
+ "job" => {
163
+ "id" => 5,
164
+ "user_id" => 1,
165
+ "status" => "ACTIVE",
166
+ "description" => nil,
167
+ "start_time" => "2011/10/07 14:54:32 +0000",
168
+ "end_time" => "2011/10/07 14:54:33 +0000",
169
+ "has_errors" => false,
170
+ "percent_complete" => .5,
171
+ "completed" => false,
172
+ "last_message" => "Job started",
173
+ "error_count" => 0
174
+ }
175
+ }
176
+ >> job.poll(:max=>12,:delay=>5)
177
+ true
178
+ >> job.response.json
179
+ {
180
+ "job" => {
181
+ "id" => 5,
182
+ "user_id" => 1,
183
+ "status" => "COMPLETED",
184
+ "description" => nil,
185
+ "start_time" => "2011/11/06 00:56:28 +0000",
186
+ "end_time" => "2011/11/06 00:56:47 +0000",
187
+ "has_errors" => true,
188
+ "percent_complete" => 1.0,
189
+ "completed" => true,
190
+ "last_message" => "Failed to set outlet id: 381 label on PDU 192.168.45.234. PowerIQ was unable to reach 192.168.45.234.",
191
+ "error_count" => 1
192
+ },
193
+ "job_messages" => [
194
+ [0] {
195
+ "id" => 15,
196
+ "unit_of_work" => 0.25,
197
+ "job_id" => 5,
198
+ "level" => "INFO",
199
+ "trace" => nil,
200
+ "start_time" => "2011/11/06 00:56:30 +0000",
201
+ "end_time" => nil,
202
+ "message_key" => ":magic.outlet_get_outlet_success",
203
+ "message_vars" => "{\"new_outlet_name\":\"foo\",\"_pdu_outlet_id\":\"381\"}",
204
+ "aborted" => false,
205
+ "message" => "Confirmed existence of Outlet 11"
206
+ },
207
+ [1] {
208
+ "id" => 16,
209
+ "unit_of_work" => 0.25,
210
+ "job_id" => 5,
211
+ "level" => "INFO",
212
+ "trace" => nil,
213
+ "start_time" => "2011/11/06 00:56:32 +0000",
214
+ "end_time" => nil,
215
+ "message_key" => ":magic.outlet_get_pdu_success",
216
+ "message_vars" => "{\"_pdu_id\":\"40\",\"new_outlet_name\":\"foo\",\"_pdu_outlet_id\":\"381\"}",
217
+ "aborted" => false,
218
+ "message" => "Confirmed existence of 192.168.45.234 for outlet Outlet 11."
219
+ },
220
+ [2] {
221
+ "id" => 17,
222
+ "unit_of_work" => 0.25,
223
+ "job_id" => 5,
224
+ "level" => "INFO",
225
+ "trace" => nil,
226
+ "start_time" => "2011/11/06 00:56:34 +0000",
227
+ "end_time" => nil,
228
+ "message_key" => ":magic.outlet_get_pdu_metadata_success",
229
+ "message_vars" => "{\"_pdu_id\":\"40\",\"new_outlet_name\":\"foo\",\"_pdu_outlet_id\":\"381\"}",
230
+ "aborted" => false,
231
+ "message" => "Retrieved plugin for 192.168.45.234."
232
+ },
233
+ [3] {
234
+ "id" => 18,
235
+ "unit_of_work" => 0.25,
236
+ "job_id" => 5,
237
+ "level" => "ERROR",
238
+ "trace" => "SNMP_ERROR_TIMEOUT",
239
+ "start_time" => "2011/11/06 00:56:47 +0000",
240
+ "end_time" => nil,
241
+ "message_key" => ":magic.outlet_rename_snmp_timeout",
242
+ "message_vars" => "{\"_pdu_id\":\"40\",\"new_outlet_name\":\"foo\",\"_pdu_outlet_id\":\"381\"}",
243
+ "aborted" => false,
244
+ "message" => "Failed to set outlet id: 381 label on PDU 192.168.45.234. PowerIQ was unable to reach 192.168.45.234."
245
+ }
246
+ ]
247
+ }
248
+
249
+ * job.poll(:max=>5,:delay=>10) where :max is the maximum number of times to poll for job completeness, and :delay is the
250
+ amount of seconds to pause between each poll
251
+ * job.poll returns true if the job completed, false otherwise
252
+ * job.poll will also automatically retrieve all job_messages after completion
253
+
254
+ Please refer to the online Power IQ documentation (http://www.raritan.com/support/power-iq) for a list of resources
255
+ that optionally or require asynchrounous execution.
256
+
148
257
  == Development
149
258
 
150
259
  The Power IQ rest client uses the rest-client gem, with some
@@ -3,6 +3,14 @@ require 'rest_client'
3
3
  module PowerIQ
4
4
  module Resource
5
5
 
6
+ class ResourceError < StandardError
7
+ attr_accessor :resource
8
+ def initialize(msg=nil,resource=nil)
9
+ super(msg)
10
+ self.resource = resource
11
+ end
12
+ end
13
+
6
14
  module ResponseHandler
7
15
  def self.create(resource)
8
16
  Proc.new { |response,request,result,&block|
@@ -42,6 +50,7 @@ module PowerIQ
42
50
 
43
51
  class << self
44
52
  attr_writer :host,:singular,:user,:password
53
+
45
54
  def host
46
55
  if(self == Base)
47
56
  @host
@@ -49,6 +58,7 @@ module PowerIQ
49
58
  @host || Base.host
50
59
  end
51
60
  end
61
+
52
62
  def user
53
63
  if(self == Base)
54
64
  @user
@@ -56,6 +66,7 @@ module PowerIQ
56
66
  @user || Base.user
57
67
  end
58
68
  end
69
+
59
70
  def password
60
71
  if(self == Base)
61
72
  @password
@@ -63,6 +74,7 @@ module PowerIQ
63
74
  @password || Base.password
64
75
  end
65
76
  end
77
+
66
78
  def resource_name(pluralize=false)
67
79
  base = self.name.demodulize
68
80
  if(pluralize)
@@ -70,15 +82,42 @@ module PowerIQ
70
82
  end
71
83
  base.underscore
72
84
  end
85
+
73
86
  def resource_url
74
87
  "https://#{self.host}/api/v2/#{self.resource_name(!self.singular?)}"
75
88
  end
89
+
76
90
  def singular?
77
91
  @singular
78
92
  end
93
+ end
79
94
 
95
+ def root_key(collection=false)
96
+ self.class.resource_name(collection)
80
97
  end
81
98
 
99
+ def member?
100
+ !!url.match(%r{#{self.class.resource_url}/\d+$})
101
+ end
102
+
103
+ def json_available?
104
+ self.response && self.response.json && !self.response.json.empty? && self.response.json.has_key?(self.root_key)
105
+ end
106
+
107
+ protected
108
+
109
+ def require_json!
110
+ unless(json_available?)
111
+ raise ResourceError.new("JSON response required. Did you GET the resource first?",self)
112
+ end
113
+ end
114
+
115
+ def require_member!
116
+ unless(member?)
117
+ raise ResourceError.new("Member resource required. Not a valid member resource specifier: #{url}",self)
118
+ end
119
+ end
120
+
82
121
  end
83
122
  end
84
123
  end
@@ -1,6 +1,37 @@
1
1
  module PowerIQ
2
2
  module Resource
3
- class Job < PowerIQ::Resource::Base; end
3
+ class Job < PowerIQ::Resource::Base
4
+
5
+ def poll(options={:delay=>5,:max=>12})
6
+ require_member!
7
+ options.symbolize_keys!
8
+ poll_count = 0
9
+ unless(completed? || poll_count < options[:max])
10
+ poll_count += 1
11
+ get
12
+ Kernel.sleep(options[:delay])
13
+ end
14
+ begin
15
+ messages_json = Job.new(url+"/messages").get
16
+ self.response.json.merge!(messages_json)
17
+ rescue
18
+ end
19
+ completed? == true
20
+ end
21
+
22
+ def completed?
23
+ require_member!
24
+ require_json!
25
+ self.response.json[root_key]["completed"] == true
26
+ end
27
+
28
+ def has_errors?
29
+ require_member!
30
+ require_json!
31
+ self.response.json[root_key]["has_errors"] == true
32
+ end
33
+
34
+ end
4
35
  end
5
36
  end
6
37
 
@@ -1,7 +1,7 @@
1
1
  module PowerIQ
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 3
4
+ MINOR = 4
5
5
  PATCH = 0
6
6
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
7
7
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{poweriq_client}
8
- s.version = "0.3.0"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = [%q{Trent Albright}]
12
- s.date = %q{2011-11-06}
12
+ s.date = %q{2011-11-07}
13
13
  s.description = %q{Power IQ Rest API client for Power IQ 3.1}
14
14
  s.email = %q{trent.albright@raritan.com}
15
15
  s.executables = [%q{poweriq_client}]
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: poweriq_client
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.3.0
5
+ version: 0.4.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Trent Albright
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-11-06 00:00:00 Z
13
+ date: 2011-11-07 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rest-client
@@ -166,7 +166,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
166
166
  requirements:
167
167
  - - ">="
168
168
  - !ruby/object:Gem::Version
169
- hash: -3473045824434038177
169
+ hash: -3128499524696260054
170
170
  segments:
171
171
  - 0
172
172
  version: "0"