poweriq_client 0.3.0 → 0.4.0
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.
- data/README.rdoc +120 -11
- data/lib/poweriq_client/resource/base.rb +39 -0
- data/lib/poweriq_client/resource/job.rb +32 -1
- data/lib/poweriq_client/version.rb +1 -1
- data/poweriq_client.gemspec +2 -2
- metadata +3 -3
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.
|
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
|
-
====
|
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
|
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
|
-
|
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
|
-
>>
|
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
|
-
>>
|
58
|
-
>>
|
59
|
-
>> outlet_resource.put
|
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
|
-
|
65
|
+
== Error Handling
|
62
66
|
|
63
|
-
When an error occurs
|
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
|
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
|
|
data/poweriq_client.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{poweriq_client}
|
8
|
-
s.version = "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-
|
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.
|
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-
|
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: -
|
169
|
+
hash: -3128499524696260054
|
170
170
|
segments:
|
171
171
|
- 0
|
172
172
|
version: "0"
|