ibm_power_hmc 0.1.0 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 397feab84db9812185ab1125cd6778b52a1848519d6bae5950d69cb31e9b4845
4
- data.tar.gz: 27dd4d94cd61a19c66d0d6e438c17987ea5926af78b7677b5044a11a50acdb18
3
+ metadata.gz: 214dfa7f9a89d236f1f9bc4ee927b619596a0fabe59c2f596359637e891601ab
4
+ data.tar.gz: 2affd160febc3952014ab522bb9b0f89b0ebef6d785720d2d8d2a875cd1df918
5
5
  SHA512:
6
- metadata.gz: 1171816de48bed97b1837ab39d3bd639772928ebee98fb8f287c85006196bdf068320599b921a239d10d5ce74e98629cde754eca14b12c6cf6be85477e7a7fb3
7
- data.tar.gz: 10f7397e58acab288715283de98480caf915fb628021b2f8c5261b13b2c7b273cdec5e1d7a52b35560acbe5cae6dfed5c9e68516673d97ff5f91a9fb44daa370
6
+ metadata.gz: 3e6e6ae8b4c7d84eea70e3328ea44834a5b95ecd98fbe6a31c045e575951b6c17751ca7d6517d3ad58647b5c8b7e1321470c368c48018c31a450a28ba40463e5
7
+ data.tar.gz: f9139f2ea1a83d0abedc3c73832450e20fa4fcf7c56cc65c50ec5b14750bb5bb2eb17d7948ffc22d5887984d0f412ae8213a237892350e55f0cd55a49af80f30
data/.gitignore CHANGED
File without changes
data/.rubocop.yml ADDED
@@ -0,0 +1,4 @@
1
+ inherit_gem:
2
+ manageiq-style: ".rubocop_base.yml"
3
+ inherit_from:
4
+ - ".rubocop_local.yml"
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ Exclude:
3
+ - '*.gemspec'
4
+ Layout/HashAlignment:
5
+ EnforcedHashRocketStyle: key
6
+ Style/ConditionalAssignment:
7
+ EnforcedStyle: assign_inside_condition
data/CHANGELOG.md CHANGED
File without changes
data/Gemfile CHANGED
File without changes
data/Gemfile.lock CHANGED
File without changes
data/LICENSE CHANGED
File without changes
data/README.md CHANGED
File without changes
data/Rakefile CHANGED
File without changes
File without changes
@@ -2,8 +2,22 @@
2
2
 
3
3
  # Module for IBM HMC Rest API Client
4
4
  module IbmPowerHmc
5
- # HMC REST Client connection
5
+ require_relative 'pcm.rb'
6
+
7
+ class Error < StandardError; end
8
+
9
+ ##
10
+ # HMC REST Client connection.
6
11
  class Connection
12
+ ##
13
+ # @!method initialize(host:, username: "hscroot", password:, port: 12_443, validate_ssl: true)
14
+ # Create a new HMC connection.
15
+ #
16
+ # @param host [String] Hostname of the HMC.
17
+ # @param username [String] User name.
18
+ # @param password [String] Password.
19
+ # @param port [Integer] TCP port number.
20
+ # @param validate_ssl [Boolean] Verify SSL certificates.
7
21
  def initialize(host:, username: "hscroot", password:, port: 12_443, validate_ssl: true)
8
22
  # Damien: use URI::HTTPS
9
23
  @hostname = "#{host}:#{port}"
@@ -13,10 +27,14 @@ module IbmPowerHmc
13
27
  @api_session_token = nil
14
28
  end
15
29
 
30
+ ##
31
+ # @!method logon
32
+ # Establish a trusted session with the Web Services APIs.
33
+ # @return [String] The X-API-Session token.
16
34
  def logon
17
35
  method_url = "/rest/api/web/Logon"
18
36
  headers = {
19
- content_type: "application/vnd.ibm.powervm.web+xml; type=LogonRequest"
37
+ :content_type => "application/vnd.ibm.powervm.web+xml; type=LogonRequest"
20
38
  }
21
39
  doc = REXML::Document.new("")
22
40
  doc.add_element("LogonRequest", {
@@ -26,41 +44,84 @@ module IbmPowerHmc
26
44
  doc.root.add_element("UserID").text = @username
27
45
  doc.root.add_element("Password").text = @password
28
46
 
29
- # Damien: begin/rescue
30
47
  @api_session_token = ""
31
48
  response = request(:put, method_url, headers, doc.to_s)
32
49
  doc = REXML::Document.new(response.body)
33
- @api_session_token = doc.root.elements["X-API-Session"].text
50
+ elem = doc.elements["LogonResponse/X-API-Session"]
51
+ raise Error, "LogonResponse/X-API-Session not found" if elem.nil?
52
+
53
+ @api_session_token = elem.text
34
54
  end
35
55
 
56
+ ##
57
+ # @!method logoff
58
+ # Close the session.
36
59
  def logoff
60
+ # Don't want to trigger automatic logon here!
61
+ return if @api_session_token.nil?
62
+
37
63
  method_url = "/rest/api/web/Logon"
38
- request(:delete, method_url)
64
+ begin
65
+ request(:delete, method_url)
66
+ rescue
67
+ # Ignore exceptions as this is best effort attempt to log off.
68
+ end
39
69
  @api_session_token = nil
40
70
  end
41
71
 
72
+ def parse_feed(doc, myclass)
73
+ objs = []
74
+ doc.each_element("feed/entry") do |entry|
75
+ objs << myclass.new(entry)
76
+ end
77
+ objs
78
+ end
79
+ private :parse_feed
80
+
81
+ ##
82
+ # @!method management_console
83
+ # Retrieve information about the management console.
84
+ # @return [IbmPowerHmc::ManagementConsole] The management console.
42
85
  def management_console
43
86
  method_url = "/rest/api/uom/ManagementConsole"
44
87
  response = request(:get, method_url)
45
88
  doc = REXML::Document.new(response.body)
46
- entry = doc.root.elements["entry"]
47
- ManagementConsole.new(entry)
89
+ # This request returns a feed with a single entry.
90
+ parse_feed(doc, ManagementConsole).first
48
91
  end
49
92
 
93
+ ##
94
+ # @!method managed_systems
95
+ # Retrieve the list of systems managed by the HMC.
96
+ # @return [Array<IbmPowerHmc::ManagedSystem>] The list of managed systems.
50
97
  def managed_systems
51
98
  method_url = "/rest/api/uom/ManagedSystem"
52
99
  response = request(:get, method_url)
53
100
  doc = REXML::Document.new(response.body)
54
- systems = []
55
- return systems if doc.root.nil?
101
+ parse_feed(doc, ManagedSystem)
102
+ end
56
103
 
57
- doc.root.each_element("entry") do |entry|
58
- system = ManagedSystem.new(entry)
59
- systems += [system]
60
- end
61
- systems
104
+ ##
105
+ # @!method managed_system(lpar_uuid, sys_uuid = nil, group_name = nil)
106
+ # Retrieve information about a managed system.
107
+ # @param sys_uuid [String] The UUID of the managed system.
108
+ # @param group_name [String] The extended group attributes.
109
+ # @return [IbmPowerHmc::ManagedSystem] The logical partition.
110
+ def managed_system(sys_uuid, group_name = nil)
111
+ method_url = "/rest/api/uom/ManagedSystem/#{sys_uuid}"
112
+ method_url += "?group=#{group_name}" unless group_name.nil?
113
+
114
+ response = request(:get, method_url)
115
+ doc = REXML::Document.new(response.body)
116
+ entry = doc.elements["entry"]
117
+ ManagedSystem.new(entry)
62
118
  end
63
119
 
120
+ ##
121
+ # @!method lpars(sys_uuid = nil)
122
+ # Retrieve the list of logical partitions managed by the HMC.
123
+ # @param sys_uuid [String] The UUID of the managed system.
124
+ # @return [Array<IbmPowerHmc::LogicalPartition>] The list of logical partitions.
64
125
  def lpars(sys_uuid = nil)
65
126
  if sys_uuid.nil?
66
127
  method_url = "/rest/api/uom/LogicalPartition"
@@ -69,16 +130,16 @@ module IbmPowerHmc
69
130
  end
70
131
  response = request(:get, method_url)
71
132
  doc = REXML::Document.new(response.body)
72
- lpars = []
73
- return lpars if doc.root.nil?
74
-
75
- doc.root.each_element("entry") do |entry|
76
- lpar = LogicalPartition.new(entry)
77
- lpars += [lpar]
78
- end
79
- lpars
133
+ parse_feed(doc, LogicalPartition)
80
134
  end
81
135
 
136
+ ##
137
+ # @!method lpar(lpar_uuid, sys_uuid = nil, group_name = nil)
138
+ # Retrieve information about a logical partition.
139
+ # @param lpar_uuid [String] The UUID of the logical partition.
140
+ # @param sys_uuid [String] The UUID of the managed system.
141
+ # @param group_name [String] The extended group attributes.
142
+ # @return [IbmPowerHmc::LogicalPartition] The logical partition.
82
143
  def lpar(lpar_uuid, sys_uuid = nil, group_name = nil)
83
144
  if sys_uuid.nil?
84
145
  method_url = "/rest/api/uom/LogicalPartition/#{lpar_uuid}"
@@ -93,6 +154,11 @@ module IbmPowerHmc
93
154
  LogicalPartition.new(entry)
94
155
  end
95
156
 
157
+ ##
158
+ # @!method lpar_quick_property(lpar_uuid, property_name)
159
+ # Retrieve a quick property of a logical partition.
160
+ # @param lpar_uuid [String] The UUID of the logical partition.
161
+ # @return [String] The quick property value.
96
162
  def lpar_quick_property(lpar_uuid, property_name)
97
163
  method_url = "/rest/api/uom/LogicalPartition/#{lpar_uuid}/quick/#{property_name}"
98
164
 
@@ -100,141 +166,223 @@ module IbmPowerHmc
100
166
  response.body[1..-2]
101
167
  end
102
168
 
169
+ ##
170
+ # @!method vioses(sys_uuid = nil)
171
+ # Retrieve the list of virtual I/O servers managed by the HMC.
172
+ # @param sys_uuid [String] The UUID of the managed system.
173
+ # @return [Array<IbmPowerHmc::VirtualIOServer>] The list of virtual I/O servers.
103
174
  def vioses(sys_uuid = nil)
104
175
  if sys_uuid.nil?
105
176
  method_url = "/rest/api/uom/VirtualIOServer"
106
177
  else
107
178
  method_url = "/rest/api/uom/ManagedSystem/#{sys_uuid}/VirtualIOServer"
108
179
  end
109
- begin
110
- response = request(:get, method_url)
111
- rescue StandardError
112
- return []
113
- end
180
+ response = request(:get, method_url)
114
181
  doc = REXML::Document.new(response.body)
115
- vioses = []
116
- return vioses if doc.root.nil?
117
-
118
- doc.root.each_element("entry") do |entry|
119
- vios = VirtualIOServer.new(entry)
120
- vioses += [vios]
121
- end
122
- vioses
182
+ parse_feed(doc, VirtualIOServer)
123
183
  end
124
184
 
125
- # Damien: share the same method for VIOS and LPAR?
126
- def lpar_profiles(lpar_uuid)
127
- method_url = "/rest/api/uom/LogicalPartition/#{lpar_uuid}/LogicalPartitionProfile"
128
- begin
129
- response = request(:get, method_url)
130
- rescue StandardError
131
- return []
185
+ ##
186
+ # @!method vios(vios_uuid, sys_uuid = nil, group_name = nil)
187
+ # Retrieve information about a virtual I/O server.
188
+ # @param vios_uuid [String] The UUID of the virtual I/O server.
189
+ # @param sys_uuid [String] The UUID of the managed system.
190
+ # @param group_name [String] The extended group attributes.
191
+ # @return [IbmPowerHmc::VirtualIOServer] The virtual I/O server.
192
+ def vios(vios_uuid, sys_uuid = nil, group_name = nil)
193
+ if sys_uuid.nil?
194
+ method_url = "/rest/api/uom/VirtualIOServer/#{vios_uuid}"
195
+ else
196
+ method_url = "/rest/api/uom/ManagedSystem/#{sys_uuid}/VirtualIOServer/#{vios_uuid}"
132
197
  end
133
- doc = REXML::Document.new(response.body)
134
- profiles = []
135
- return profiles if doc.root.nil?
198
+ method_url += "?group=#{group_name}" unless group_name.nil?
136
199
 
137
- doc.root.each_element("entry") do |entry|
138
- profile = LogicalPartitionProfile.new(lpar_uuid, entry)
139
- profiles += [profile]
140
- end
141
- profiles
200
+ response = request(:get, method_url)
201
+ doc = REXML::Document.new(response.body)
202
+ entry = doc.elements["entry"]
203
+ VirtualIOServer.new(entry)
142
204
  end
143
205
 
144
- def poweron_lpar(lpar_uuid, params = {})
206
+ ##
207
+ # @!method poweron_lpar(lpar_uuid, params = {}, sync = true)
208
+ # Power on a logical partition.
209
+ # @param lpar_uuid [String] The UUID of the logical partition.
210
+ # @param params [Hash] Job parameters.
211
+ # @param sync [Boolean] Start the job and wait for its completion.
212
+ # @return [IbmPowerHmc::HmcJob] The HMC job.
213
+ def poweron_lpar(lpar_uuid, params = {}, sync = true)
145
214
  method_url = "/rest/api/uom/LogicalPartition/#{lpar_uuid}/do/PowerOn"
146
215
 
147
216
  job = HmcJob.new(self, method_url, "PowerOn", "LogicalPartition", params)
148
- job.start
149
- job.wait
150
- job.delete
217
+ job.run if sync
218
+ job
151
219
  end
152
220
 
153
- def poweroff_lpar(lpar_uuid, params = {})
221
+ ##
222
+ # @!method poweroff_lpar(lpar_uuid, params = {}, sync = true)
223
+ # Power off a logical partition.
224
+ # @param lpar_uuid [String] The UUID of the logical partition.
225
+ # @param params [Hash] Job parameters.
226
+ # @param sync [Boolean] Start the job and wait for its completion.
227
+ # @return [IbmPowerHmc::HmcJob] The HMC job.
228
+ def poweroff_lpar(lpar_uuid, params = {}, sync = true)
154
229
  method_url = "/rest/api/uom/LogicalPartition/#{lpar_uuid}/do/PowerOff"
155
230
 
156
231
  job = HmcJob.new(self, method_url, "PowerOff", "LogicalPartition", params)
157
- job.start
158
- job.wait
159
- job.delete
232
+ job.run if sync
233
+ job
160
234
  end
161
235
 
162
- # Damien: share with poweron_lpar?
163
- def poweron_vios(vios_uuid, params = {})
236
+ ##
237
+ # @!method poweron_vios(vios_uuid, params = {}, sync = true)
238
+ # Power on a virtual I/O server.
239
+ # @param vios_uuid [String] The UUID of the virtual I/O server.
240
+ # @param params [Hash] Job parameters.
241
+ # @param sync [Boolean] Start the job and wait for its completion.
242
+ # @return [IbmPowerHmc::HmcJob] The HMC job.
243
+ def poweron_vios(vios_uuid, params = {}, sync = true)
164
244
  method_url = "/rest/api/uom/VirtualIOServer/#{vios_uuid}/do/PowerOn"
165
245
 
166
246
  job = HmcJob.new(self, method_url, "PowerOn", "VirtualIOServer", params)
167
- job.start
168
- job.wait
169
- job.delete
247
+ job.run if sync
248
+ job
170
249
  end
171
250
 
172
- # Damien: share with poweroff_lpar?
173
- def poweroff_vios(vios_uuid, params = {})
251
+ ##
252
+ # @!method poweroff_vios(vios_uuid, params = {}, sync = true)
253
+ # Power off a virtual I/O server.
254
+ # @param vios_uuid [String] The UUID of the virtual I/O server.
255
+ # @param params [Hash] Job parameters.
256
+ # @param sync [Boolean] Start the job and wait for its completion.
257
+ # @return [IbmPowerHmc::HmcJob] The HMC job.
258
+ def poweroff_vios(vios_uuid, params = {}, sync = true)
174
259
  method_url = "/rest/api/uom/VirtualIOServer/#{vios_uuid}/do/PowerOff"
175
260
 
176
261
  job = HmcJob.new(self, method_url, "PowerOff", "VirtualIOServer", params)
177
- job.start
178
- job.wait
179
- job.delete
262
+ job.run if sync
263
+ job
180
264
  end
181
265
 
182
- def poweron_managed_system(sys_uuid, params = {})
266
+ ##
267
+ # @!method poweron_managed_system(sys_uuid, params = {}, sync = true)
268
+ # Power on a managed system.
269
+ # @param sys_uuid [String] The UUID of the managed system.
270
+ # @param params [Hash] Job parameters.
271
+ # @param sync [Boolean] Start the job and wait for its completion.
272
+ # @return [IbmPowerHmc::HmcJob] The HMC job.
273
+ def poweron_managed_system(sys_uuid, params = {}, sync = true)
183
274
  method_url = "/rest/api/uom/ManagedSystem/#{sys_uuid}/do/PowerOn"
184
275
 
185
276
  job = HmcJob.new(self, method_url, "PowerOn", "ManagedSystem", params)
186
- job.start
187
- job.wait
188
- job.delete
277
+ job.run if sync
278
+ job
189
279
  end
190
280
 
191
- def poweroff_managed_system(sys_uuid, params = {})
281
+ ##
282
+ # @!method poweroff_managed_system(sys_uuid, params = {}, sync = true)
283
+ # Power off a managed system.
284
+ # @param sys_uuid [String] The UUID of the managed system.
285
+ # @param params [Hash] Job parameters.
286
+ # @param sync [Boolean] Start the job and wait for its completion.
287
+ # @return [IbmPowerHmc::HmcJob] The HMC job.
288
+ def poweroff_managed_system(sys_uuid, params = {}, sync = true)
192
289
  method_url = "/rest/api/uom/ManagedSystem/#{sys_uuid}/do/PowerOff"
193
290
 
194
291
  job = HmcJob.new(self, method_url, "PowerOff", "ManagedSystem", params)
195
- job.start
196
- job.wait
197
- job.delete
292
+ job.run if sync
293
+ job
198
294
  end
199
295
 
200
- # Blocks until new events occur.
201
- def next_events
296
+ ##
297
+ # @!method cli_run(hmc_uuid, cmd, sync = true)
298
+ # Run a CLI command on the HMC as a job.
299
+ # @param hmc_uuid [String] The UUID of the management console.
300
+ # @param cmd [String] The command to run.
301
+ # @param sync [Boolean] Start the job and wait for its completion.
302
+ # @return [IbmPowerHmc::HmcJob] The HMC job.
303
+ def cli_run(hmc_uuid, cmd, sync = true)
304
+ method_url = "/rest/api/uom/ManagementConsole/#{hmc_uuid}/do/CLIRunner"
305
+
306
+ params = {
307
+ "cmd" => cmd,
308
+ "acknowledgeThisAPIMayGoAwayInTheFuture" => "true",
309
+ }
310
+ job = HmcJob.new(self, method_url, "CLIRunner", "ManagementConsole", params)
311
+ job.run if sync
312
+ job
313
+ end
314
+
315
+ ##
316
+ # @!method next_events(wait = true)
317
+ # Retrieve a list of events that occured since last call.
318
+ # @param wait [Boolean] If no event is available, block until new events occur.
319
+ # @return [Array<IbmPowerHmc::Event>] The list of events.
320
+ def next_events(wait = true)
202
321
  method_url = "/rest/api/uom/Event"
203
322
 
204
- events = []
323
+ response = nil
205
324
  loop do
206
325
  response = request(:get, method_url)
207
- next if response.code == 204
326
+ # No need to sleep as the HMC already waits a bit before returning 204
327
+ break if response.code != 204 || !wait
328
+ end
329
+ doc = REXML::Document.new(response.body)
330
+ parse_feed(doc, Event)
331
+ end
208
332
 
209
- doc = REXML::Document.new(response.body)
210
- doc.root.each_element("entry") do |entry|
211
- event = Event.new(entry)
212
- events += [event]
333
+ class HttpError < Error
334
+ attr_reader :status, :uri, :reason, :message
335
+
336
+ ##
337
+ # @!method initialize(err)
338
+ # Create a new HttpError exception.
339
+ # @param err [RestClient::Exception] The REST client exception.
340
+ def initialize(err)
341
+ super
342
+ @status = err.http_code
343
+
344
+ # Try to parse body as an HttpErrorResponse
345
+ doc = REXML::Document.new(err.response.body)
346
+ entry = doc.elements["entry"]
347
+ unless entry.nil?
348
+ resp = HttpErrorResponse.new(entry)
349
+ @uri = resp.uri
350
+ @reason = resp.reason
351
+ @message = resp.message
213
352
  end
214
- break
215
353
  end
216
- events
217
354
  end
218
355
 
356
+ ##
357
+ # @!method request(method, url, headers = {}, payload = nil)
358
+ # Perform a REST API request.
359
+ # @param method [String] The HTTP method.
360
+ # @param url [String] The method URL.
361
+ # @param headers [Hash] HTTP headers.
362
+ # @param payload [String] HTTP request payload.
363
+ # @return [RestClient::Response] The response from the HMC.
219
364
  def request(method, url, headers = {}, payload = nil)
220
365
  logon if @api_session_token.nil?
221
-
222
- headers = headers.merge({ "X-API-Session" => @api_session_token })
223
- # Damien: use URI module and prepare in initialize?
224
- response = RestClient::Request.execute(
225
- method: method,
226
- url: "https://" + @hostname + url,
227
- verify_ssl: @verify_ssl,
228
- payload: payload,
229
- headers: headers
230
- )
231
- if response.code == 403
232
- # Damien: if token expires, reauth?
233
- @api_session_token = nil
234
- logon
235
- # Damien: retry TBD
366
+ reauth = false
367
+ begin
368
+ headers = headers.merge({"X-API-Session" => @api_session_token})
369
+ RestClient::Request.execute(
370
+ :method => method,
371
+ :url => "https://" + @hostname + url,
372
+ :verify_ssl => @verify_ssl,
373
+ :payload => payload,
374
+ :headers => headers
375
+ )
376
+ rescue RestClient::Exception => e
377
+ # Do not retry on failed logon attempts
378
+ if e.http_code == 401 && @api_session_token != "" && !reauth
379
+ # Try to reauth
380
+ reauth = true
381
+ logon
382
+ retry
383
+ end
384
+ raise HttpError.new(e), "REST request failed"
236
385
  end
237
- response
238
386
  end
239
387
  end
240
388
  end
@@ -1,20 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Module for IBM HMC Rest API Client
4
3
  module IbmPowerHmc
5
- # HMC Job for long running operations
4
+ ##
5
+ # HMC Job for long running operations.
6
6
  class HmcJob
7
- def initialize(hc, method_url, operation, group, params)
8
- @hc = hc
7
+ class JobNotStarted < StandardError; end
8
+
9
+ ##
10
+ # @!method initialize(conn, method_url, operation, group, params = {})
11
+ # Construct a new HMC Job.
12
+ #
13
+ # @param conn [IbmPowerHmc::Connection] The connection to the HMC.
14
+ # @param method_url [String] The method URL.
15
+ # @param operation [String] The name of the requested operation.
16
+ # @param group [String] The name of the group.
17
+ # @param params [Hash] The job name/value parameters.
18
+ def initialize(conn, method_url, operation, group, params = {})
19
+ @conn = conn
9
20
  @method_url = method_url
10
21
  @operation = operation
11
22
  @group = group
12
23
  @params = params
13
24
  end
14
25
 
26
+ ##
27
+ # @!method status
28
+ # Start the job asynchronously.
29
+ # @return [String] The ID of the job.
15
30
  def start
16
31
  headers = {
17
- content_type: "application/vnd.ibm.powervm.web+xml; type=JobRequest"
32
+ :content_type => "application/vnd.ibm.powervm.web+xml; type=JobRequest"
18
33
  }
19
34
  doc = REXML::Document.new("")
20
35
  doc.add_element("JobRequest:JobRequest", {
@@ -22,49 +37,95 @@ module IbmPowerHmc
22
37
  "xmlns" => "http://www.ibm.com/xmlns/systems/power/firmware/web/mc/2012_10/",
23
38
  "schemaVersion" => "V1_1_0"
24
39
  })
25
- op = doc.root.add_element("RequestedOperation", { "schemaVersion" => "V1_1_0" })
40
+ op = doc.root.add_element("RequestedOperation", {"schemaVersion" => "V1_1_0"})
26
41
  op.add_element("OperationName").text = @operation
27
42
  op.add_element("GroupName").text = @group
28
43
  # Damien: ProgressType?
29
- jobparams = doc.root.add_element("JobParameters", { "schemaVersion" => "V1_1_0" })
44
+ jobparams = doc.root.add_element("JobParameters", {"schemaVersion" => "V1_1_0"})
30
45
  @params.each do |key, value|
31
- jobparam = jobparams.add_element("JobParameter", { "schemaVersion" => "V1_1_0" })
46
+ jobparam = jobparams.add_element("JobParameter", {"schemaVersion" => "V1_1_0"})
32
47
  jobparam.add_element("ParameterName").text = key
33
48
  jobparam.add_element("ParameterValue").text = value
34
49
  end
35
- response = @hc.request(:put, @method_url, headers, doc.to_s)
50
+ response = @conn.request(:put, @method_url, headers, doc.to_s)
36
51
  doc = REXML::Document.new(response.body)
37
52
  info = doc.root.elements["content/JobResponse:JobResponse"]
38
53
  @id = info.elements["JobID"].text
39
54
  end
40
55
 
56
+ # @return [Hash] The job results returned by the HMC.
57
+ attr_reader :results
58
+
59
+ ##
60
+ # @!method status
61
+ # Return the status of the job.
62
+ # @return [String] The status of the job.
41
63
  def status
42
- # Damien: check id is defined
64
+ raise JobNotStarted unless defined?(@id)
65
+
43
66
  method_url = "/rest/api/uom/jobs/#{@id}"
44
67
  headers = {
45
- content_type: "application/vnd.ibm.powervm.web+xml; type=JobRequest"
68
+ :content_type => "application/vnd.ibm.powervm.web+xml; type=JobRequest"
46
69
  }
47
- response = @hc.request(:get, method_url, headers)
70
+ response = @conn.request(:get, method_url, headers)
48
71
  doc = REXML::Document.new(response.body)
49
72
  info = doc.root.elements["content/JobResponse:JobResponse"]
50
73
  status = info.elements["Status"].text
51
- # Damien: also retrieve "ResponseException/Message"
74
+ # Damien: also retrieve "ResponseException/Message"?
75
+
76
+ # Gather Job results returned by the HMC.
77
+ @results = {}
78
+ info.each_element("Results/JobParameter") do |result|
79
+ name = result.elements["ParameterName"].text.strip
80
+ value = result.elements["ParameterValue"].text.strip
81
+ @results[name] = value
82
+ end
83
+
52
84
  status
53
85
  end
54
86
 
55
- def wait(timeout = 120, poll_interval = 30)
56
- endtime = Time.now + timeout
57
- while Time.now < endtime do
87
+ ##
88
+ # @!method wait(timeout = 120, poll_interval = 0)
89
+ # Wait for the job to complete.
90
+ # @param timeout [Integer] The maximum time in seconds to wait for the job to complete.
91
+ # @param poll_interval [Integer] The interval in seconds between status queries (0 means auto).
92
+ # @return [String] The status of the job.
93
+ def wait(timeout = 120, poll_interval = 0)
94
+ endtime = Time.now.utc + timeout
95
+ auto = poll_interval == 0
96
+ poll_interval = 1 if auto
97
+ while Time.now.utc < endtime
58
98
  status = self.status
59
- break if status != "RUNNING" # Damien: and != "STARTING"?
99
+ return status if status != "RUNNING" && status != "NOT_STARTED"
100
+
101
+ poll_interval *= 2 if auto && poll_interval < 30
60
102
  sleep(poll_interval)
61
103
  end
104
+ "TIMEDOUT"
62
105
  end
63
106
 
107
+ ##
108
+ # @!method run(timeout = 120, poll_interval = 0)
109
+ # Run the job synchronously.
110
+ # @param timeout [Integer] The maximum time in seconds to wait for the job to complete.
111
+ # @param poll_interval [Integer] The interval in seconds between status queries (0 means auto).
112
+ # @return [String] The status of the job.
113
+ def run(timeout = 120, poll_interval = 0)
114
+ start
115
+ status = wait(timeout, poll_interval)
116
+ status
117
+ ensure
118
+ delete if defined?(@id)
119
+ end
120
+
121
+ ##
122
+ # @!method delete
123
+ # Delete the job from the HMC.
64
124
  def delete
65
- # Damien: check id is defined
125
+ raise JobNotStarted unless defined?(@id)
126
+
66
127
  method_url = "/rest/api/uom/jobs/#{@id}"
67
- @hc.request(:delete, method_url)
128
+ @conn.request(:delete, method_url)
68
129
  # Returns HTTP 204 if ok
69
130
  end
70
131
  end
@@ -16,7 +16,7 @@ module IbmPowerHmc
16
16
  value = doc.elements[xpath]
17
17
  value = value.text unless value.nil?
18
18
  value = value.strip unless value.nil?
19
- self.class.__send__(:attr_reader, "#{varname}")
19
+ self.class.__send__(:attr_reader, varname)
20
20
  instance_variable_set("@#{varname}", value)
21
21
  end
22
22
 
@@ -25,10 +25,6 @@ module IbmPowerHmc
25
25
  get_value(doc, key, value)
26
26
  end
27
27
  end
28
-
29
- def to_s
30
- "uuid=#{@uuid}"
31
- end
32
28
  end
33
29
 
34
30
  # HMC information
@@ -44,10 +40,6 @@ module IbmPowerHmc
44
40
  info = doc.elements["content/ManagementConsole:ManagementConsole"]
45
41
  get_values(info, XMLMAP)
46
42
  end
47
-
48
- def to_s
49
- "hmc name=#{@name} version=#{@version} build_level=#{@build_level}"
50
- end
51
43
  end
52
44
 
53
45
  # Managed System information
@@ -60,7 +52,10 @@ module IbmPowerHmc
60
52
  "AssociatedSystemMemoryConfiguration/InstalledSystemMemory" => "memory",
61
53
  "AssociatedSystemMemoryConfiguration/CurrentAvailableSystemMemory" => "avail_mem",
62
54
  "AssociatedSystemProcessorConfiguration/InstalledSystemProcessorUnits" => "cpus",
63
- "AssociatedSystemProcessorConfiguration/CurrentAvailableSystemProcessorUnits" => "avail_cpus"
55
+ "AssociatedSystemProcessorConfiguration/CurrentAvailableSystemProcessorUnits" => "avail_cpus",
56
+ "MachineTypeModelAndSerialNumber/MachineType" => "mtype",
57
+ "MachineTypeModelAndSerialNumber/Model" => "model",
58
+ "MachineTypeModelAndSerialNumber/SerialNumber" => "serial",
64
59
  }.freeze
65
60
 
66
61
  def initialize(doc)
@@ -68,10 +63,6 @@ module IbmPowerHmc
68
63
  info = doc.elements["content/ManagedSystem:ManagedSystem"]
69
64
  get_values(info, XMLMAP)
70
65
  end
71
-
72
- def to_s
73
- "sys name=#{@name} state=#{@state} ip=#{@ipaddr} mem=#{@memory}MB avail=#{@avail_mem}MB CPUs=#{@cpus} avail=#{@avail_cpus}"
74
- end
75
66
  end
76
67
 
77
68
  # Logical Partition information
@@ -96,10 +87,6 @@ module IbmPowerHmc
96
87
  @sys_uuid = URI(sys_href).path.split('/').last
97
88
  get_values(info, XMLMAP)
98
89
  end
99
-
100
- def to_s
101
- "lpar name=#{@name} id=#{@id} state=#{@state} type=#{@type} memory=#{@memory}MB dedicated cpus=#{@dedicated}"
102
- end
103
90
  end
104
91
 
105
92
  # VIOS information
@@ -122,21 +109,12 @@ module IbmPowerHmc
122
109
  @sys_uuid = URI(sys_href).path.split('/').last
123
110
  get_values(info, XMLMAP)
124
111
  end
125
-
126
- def to_s
127
- "vios name=#{@name} id=#{@id} state=#{@state} type=#{@type} memory=#{@memory}MB dedicated cpus=#{@dedicated}"
128
- end
129
- end
130
-
131
- # LPAR profile
132
- class LogicalPartitionProfile < HmcObject
133
- attr_reader :lpar_uuid
134
-
135
- # Damien: TBD
136
112
  end
137
113
 
138
114
  # HMC Event
139
115
  class Event < HmcObject
116
+ attr_reader :published
117
+
140
118
  XMLMAP = {
141
119
  "EventID" => "id",
142
120
  "EventType" => "type",
@@ -146,12 +124,25 @@ module IbmPowerHmc
146
124
 
147
125
  def initialize(doc)
148
126
  super(doc)
127
+ @published = doc.elements["published"].text
149
128
  info = doc.elements["content/Event:Event"]
150
129
  get_values(info, XMLMAP)
151
130
  end
131
+ end
132
+
133
+ # Error response from HMC
134
+ class HttpErrorResponse < HmcObject
135
+ XMLMAP = {
136
+ "HTTPStatus" => "status",
137
+ "RequestURI" => "uri",
138
+ "ReasonCode" => "reason",
139
+ "Message" => "message",
140
+ }.freeze
152
141
 
153
- def to_s
154
- "event id=#{@id} type=#{@type} data=#{@data} detail=#{@detail}"
142
+ def initialize(doc)
143
+ super(doc)
144
+ info = doc.elements["content/HttpErrorResponse:HttpErrorResponse"]
145
+ get_values(info, XMLMAP)
155
146
  end
156
147
  end
157
148
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'uri'
5
+
6
+ module IbmPowerHmc
7
+ class Connection
8
+ ##
9
+ # @!method managed_system_metrics(sys_uuid:, start_ts: nil, end_ts: nil, no_samples: nil)
10
+ # Retrieve metrics for a managed system.
11
+ # @param sys_uuid [String] The managed system UUID.
12
+ # @param start_ts [String] Start timestamp.
13
+ # @param end_ts [String] End timestamp.
14
+ # @param no_samples [Integer] Number of samples.
15
+ # @return [Array<Hash>] The processed metrics for the managed system.
16
+ def managed_system_metrics(sys_uuid:, start_ts: nil, end_ts: nil, no_samples: nil)
17
+ method_url = "/rest/api/pcm/ManagedSystem/#{sys_uuid}/ProcessedMetrics"
18
+ query = []
19
+ query << "StartTS=#{start_ts}" unless start_ts.nil?
20
+ query << "EndTS=#{end_ts}" unless end_ts.nil?
21
+ query << "NoOfSamples=#{no_samples}" unless no_samples.nil?
22
+ method_url += "?" + query.compact.join('&') unless query.empty?
23
+
24
+ response = request(:get, method_url)
25
+ doc = REXML::Document.new(response.body)
26
+ metrics = []
27
+ doc.each_element("feed/entry") do |entry|
28
+ category = entry.elements["category"]
29
+ next if category.nil?
30
+
31
+ term = category.attributes["term"]
32
+ next if term.nil? || term != "ManagedSystem"
33
+
34
+ link = entry.elements["link"]
35
+ next if link.nil?
36
+
37
+ href = link.attributes["href"]
38
+ next if href.nil?
39
+
40
+ # Validate URI
41
+ begin
42
+ href = URI(href)
43
+ rescue
44
+ next
45
+ end
46
+ # Remove https://hostname:port prefix.
47
+ href.scheme = href.host = href.port = nil
48
+
49
+ response = request(:get, href.to_s)
50
+ metrics << JSON.parse(response.body)
51
+ end
52
+ metrics
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IbmPowerHmc
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
data/lib/ibm_power_hmc.rb CHANGED
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ibm_power_hmc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - IBM Power
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-06 00:00:00.000000000 Z
11
+ date: 2021-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
@@ -25,12 +25,14 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.1'
27
27
  description: A Ruby gem for interacting with the IBM Hardware Management Console (HMC).
28
- email:
28
+ email:
29
29
  executables: []
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
33
  - ".gitignore"
34
+ - ".rubocop.yml"
35
+ - ".rubocop_local.yml"
34
36
  - CHANGELOG.md
35
37
  - Gemfile
36
38
  - Gemfile.lock
@@ -44,6 +46,7 @@ files:
44
46
  - lib/ibm_power_hmc/connection.rb
45
47
  - lib/ibm_power_hmc/job.rb
46
48
  - lib/ibm_power_hmc/objects.rb
49
+ - lib/ibm_power_hmc/pcm.rb
47
50
  - lib/ibm_power_hmc/version.rb
48
51
  homepage: http://github.com/IBM/ibm_power_hmc_sdk_ruby
49
52
  licenses:
@@ -53,7 +56,7 @@ metadata:
53
56
  homepage_uri: http://github.com/IBM/ibm_power_hmc_sdk_ruby
54
57
  source_code_uri: http://github.com/IBM/ibm_power_hmc_sdk_ruby
55
58
  changelog_uri: http://github.com/IBM/ibm_power_hmc_sdk_ruby/blob/master/CHANGELOG.md
56
- post_install_message:
59
+ post_install_message:
57
60
  rdoc_options: []
58
61
  require_paths:
59
62
  - lib
@@ -68,8 +71,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
71
  - !ruby/object:Gem::Version
69
72
  version: '0'
70
73
  requirements: []
71
- rubygems_version: 3.1.2
72
- signing_key:
74
+ rubygems_version: 3.0.3
75
+ signing_key:
73
76
  specification_version: 4
74
77
  summary: IBM Power HMC Ruby gem.
75
78
  test_files: []