ibm_power_hmc 0.3.0 → 0.7.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.
@@ -4,16 +4,26 @@ require 'time'
4
4
  require 'uri'
5
5
 
6
6
  module IbmPowerHmc
7
- # Parser for HMC feeds and entries.
7
+ ##
8
+ # Generic parser for HMC K2 XML responses.
8
9
  class Parser
9
10
  def initialize(body)
10
11
  @doc = REXML::Document.new(body)
11
12
  end
12
13
 
14
+ ##
15
+ # @!method entry
16
+ # Return the first K2 entry element in the response.
17
+ # @return [REXML::Element, nil] The first entry element.
13
18
  def entry
14
19
  @doc.elements["entry"]
15
20
  end
16
21
 
22
+ ##
23
+ # @!method object(filter_type = nil)
24
+ # Parse the first K2 entry element into an object.
25
+ # @param filter_type [String] Entry type must match the specified type.
26
+ # @return [IbmPowerHmc::AbstractRest, nil] The parsed object.
17
27
  def object(filter_type = nil)
18
28
  self.class.to_obj(entry, filter_type)
19
29
  end
@@ -21,19 +31,28 @@ module IbmPowerHmc
21
31
  def self.to_obj(entry, filter_type = nil)
22
32
  return if entry.nil?
23
33
 
24
- content = entry.elements["content"]
34
+ content = entry.elements["content[@type]"]
25
35
  return if content.nil?
26
36
 
27
- type = content.attributes["type"]
28
- return if type.nil?
29
-
30
- type = type.split("=").last
37
+ type = content.attributes["type"].split("=").last
31
38
  return unless filter_type.nil? || filter_type.to_s == type
32
39
 
33
40
  Module.const_get("IbmPowerHmc::#{type}").new(entry)
34
41
  end
35
42
  end
36
43
 
44
+ ##
45
+ # Parser for HMC K2 feeds.
46
+ # A feed encapsulates a list of entries like this:
47
+ # <feed>
48
+ # <entry>
49
+ # <!-- entry #1 -->
50
+ # </entry>
51
+ # <entry>
52
+ # <!-- entry #2 -->
53
+ # </entry>
54
+ # ...
55
+ # </feed>
37
56
  class FeedParser < Parser
38
57
  def entries
39
58
  objs = []
@@ -43,6 +62,11 @@ module IbmPowerHmc
43
62
  objs
44
63
  end
45
64
 
65
+ ##
66
+ # @!method objects(filter_type = nil)
67
+ # Parse feed entries into objects.
68
+ # @param filter_type [String] Filter entries based on content type.
69
+ # @return [Array<IbmPowerHmc::AbstractRest>] The list of objects.
46
70
  def objects(filter_type = nil)
47
71
  entries do |entry|
48
72
  self.class.to_obj(entry, filter_type)
@@ -53,38 +77,111 @@ module IbmPowerHmc
53
77
  private_constant :Parser
54
78
  private_constant :FeedParser
55
79
 
56
- # HMC generic XML entry
57
- class AbstractRest
80
+ ##
81
+ # HMC generic K2 non-REST object.
82
+ # @abstract
83
+ # @attr_reader [REXML::Document] xml The XML document representing this object.
84
+ class AbstractNonRest
58
85
  ATTRS = {}.freeze
59
- attr_reader :uuid, :published, :etag, :xml
86
+ attr_reader :xml
60
87
 
61
- def initialize(doc)
62
- @uuid = doc.elements["id"]&.text
63
- @published = Time.xmlschema(doc.elements["published"]&.text)
64
- @etag = doc.elements["etag:etag"]&.text&.strip
65
- type = self.class.name.split("::").last
66
- @xml = doc.elements["content/#{type}:#{type}"]
67
- define_attrs(self.class::ATTRS)
88
+ def initialize(xml)
89
+ @xml = xml
90
+ self.class::ATTRS.each { |varname, xpath| define_attr(varname, xpath) }
68
91
  end
69
92
 
93
+ ##
94
+ # @!method define_attr(varname, xpath)
95
+ # Define an instance variable using the text of an XML element as value.
96
+ # @param varname [String] The name of the instance variable.
97
+ # @param xpath [String] The XPath of the XML element containing the text.
70
98
  def define_attr(varname, xpath)
71
- value = text_element(xpath)
99
+ value = singleton(xpath)
72
100
  self.class.__send__(:attr_reader, varname)
73
101
  instance_variable_set("@#{varname}", value)
74
102
  end
103
+ private :define_attr
104
+
105
+ ##
106
+ # @!method singleton(xpath, attr = nil)
107
+ # Get the text (or the value of a specified attribute) of an XML element.
108
+ # @param xpath [String] The XPath of the XML element.
109
+ # @param attr [String] The name of the attribute.
110
+ # @return [String, nil] The text or attribute value of the XML element or nil.
111
+ # @example lpar.singleton("PartitionProcessorConfiguration/*/MaximumVirtualProcessors").to_i
112
+ def singleton(xpath, attr = nil)
113
+ elem = xml.elements[xpath]
114
+ return if elem.nil?
115
+
116
+ attr.nil? ? elem.text&.strip : elem.attributes[attr]
117
+ end
75
118
 
76
- def define_attrs(hash)
77
- hash.each do |key, value|
78
- define_attr(key, value)
119
+ def to_s
120
+ str = +"#{self.class.name}:\n"
121
+ self.class::ATTRS.each do |varname, _|
122
+ value = instance_variable_get("@#{varname}")
123
+ value = value.nil? ? "null" : "'#{value}'"
124
+ str << " #{varname}: #{value}\n"
79
125
  end
126
+ str
127
+ end
128
+
129
+ def uuid_from_href(href, index = -1)
130
+ URI(href).path.split('/')[index]
80
131
  end
81
132
 
82
- def text_element(xpath)
83
- xml.elements[xpath]&.text&.strip
133
+ def uuids_from_links(elem, index = -1)
134
+ xml.get_elements("#{elem}/link[@href]").map do |link|
135
+ uuid_from_href(link.attributes["href"], index)
136
+ end.compact
137
+ end
138
+ end
139
+
140
+ ##
141
+ # HMC generic K2 REST object.
142
+ # Encapsulate data for a single REST object.
143
+ # The XML looks like this:
144
+ # <entry>
145
+ # <id>uuid</id>
146
+ # <published>timestamp</published>
147
+ # <link rel="SELF" href="https://..."/>
148
+ # <etag:etag>ETag</etag:etag>
149
+ # <content type="type">
150
+ # <!-- actual content here -->
151
+ # </content>
152
+ # </entry>
153
+ #
154
+ # @abstract
155
+ # @attr_reader [String] uuid The UUID of the object contained in the entry.
156
+ # @attr_reader [Time] published The time at which the entry was published.
157
+ # @attr_reader [URI::HTTPS] href The URL of the object itself.
158
+ # @attr_reader [String] etag The entity tag of the entry.
159
+ # @attr_reader [String] content_type The content type of the object contained in the entry.
160
+ class AbstractRest < AbstractNonRest
161
+ attr_reader :uuid, :published, :href, :etag, :content_type
162
+
163
+ def initialize(entry)
164
+ if entry.name != "entry"
165
+ # We are inlined.
166
+ super(entry)
167
+ return
168
+ end
169
+
170
+ @uuid = entry.elements["id"]&.text
171
+ @published = Time.xmlschema(entry.elements["published"]&.text)
172
+ link = entry.elements["link[@rel='SELF']"]
173
+ @href = URI(link.attributes["href"]) unless link.nil?
174
+ @etag = entry.elements["etag:etag"]&.text&.strip
175
+ content = entry.elements["content"]
176
+ @content_type = content.attributes["type"]
177
+ super(content.elements.first)
84
178
  end
85
179
 
86
- def extract_uuid_from_href(href)
87
- URI(href).path.split('/').last
180
+ def to_s
181
+ str = super
182
+ str << " uuid: '#{uuid}'\n" if defined?(@uuid)
183
+ str << " published: '#{published}'\n" if defined?(@published)
184
+ str
88
185
  end
89
186
  end
90
187
 
@@ -97,9 +194,7 @@ module IbmPowerHmc
97
194
  }.freeze
98
195
 
99
196
  def managed_systems_uuids
100
- xml.get_elements("ManagedSystems/link").map do |link|
101
- extract_uuid_from_href(link.attributes["href"])
102
- end.compact
197
+ uuids_from_links("ManagedSystems")
103
198
  end
104
199
  end
105
200
 
@@ -117,26 +212,55 @@ module IbmPowerHmc
117
212
  :avail_cpus => "AssociatedSystemProcessorConfiguration/CurrentAvailableSystemProcessorUnits",
118
213
  :mtype => "MachineTypeModelAndSerialNumber/MachineType",
119
214
  :model => "MachineTypeModelAndSerialNumber/Model",
120
- :serial => "MachineTypeModelAndSerialNumber/SerialNumber"
215
+ :serial => "MachineTypeModelAndSerialNumber/SerialNumber",
216
+ :vtpm_version => "AssociatedSystemSecurity/VirtualTrustedPlatformModuleVersion",
217
+ :vtpm_lpars => "AssociatedSystemSecurity/AvailableVirtualTrustedPlatformModulePartitions"
121
218
  }.freeze
122
219
 
123
- def lpars_uuids
124
- xml.get_elements("AssociatedLogicalPartitions/link").map do |link|
125
- extract_uuid_from_href(link.attributes["href"])
220
+ def cpu_compat_modes
221
+ xml.get_elements("AssociatedSystemProcessorConfiguration/SupportedPartitionProcessorCompatibilityModes").map do |elem|
222
+ elem.text&.strip
126
223
  end.compact
127
224
  end
128
225
 
226
+ def lpars_uuids
227
+ uuids_from_links("AssociatedLogicalPartitions")
228
+ end
229
+
129
230
  def vioses_uuids
130
- xml.get_elements("AssociatedVirtualIOServers/link").map do |link|
131
- extract_uuid_from_href(link.attributes["href"])
132
- end.compact
231
+ uuids_from_links("AssociatedVirtualIOServers")
133
232
  end
233
+
234
+ def io_adapters
235
+ xml.get_elements("AssociatedSystemIOConfiguration/IOSlots/IOSlot/RelatedIOAdapter/IOAdapter").map do |elem|
236
+ IOAdapter.new(elem)
237
+ end
238
+ end
239
+
240
+ def vswitches_uuids
241
+ uuids_from_links("AssociatedSystemIOConfiguration/AssociatedSystemVirtualNetwork/VirtualSwitches")
242
+ end
243
+
244
+ def networks_uuids
245
+ uuids_from_links("AssociatedSystemIOConfiguration/AssociatedSystemVirtualNetwork/VirtualNetworks")
246
+ end
247
+ end
248
+
249
+ # I/O Adapter information
250
+ class IOAdapter < AbstractNonRest
251
+ ATTRS = {
252
+ :id => "AdapterID",
253
+ :description => "Description",
254
+ :name => "DeviceName",
255
+ :type => "DeviceType",
256
+ :dr_name => "DynamicReconfigurationConnectorName",
257
+ :udid => "UniqueDeviceID"
258
+ }.freeze
134
259
  end
135
260
 
136
261
  # Common class for LPAR and VIOS
137
262
  class BasePartition < AbstractRest
138
263
  ATTRS = {
139
- :os => "OperatingSystemVersion",
140
264
  :name => "PartitionName",
141
265
  :id => "PartitionID",
142
266
  :state => "PartitionState",
@@ -145,25 +269,563 @@ module IbmPowerHmc
145
269
  :dedicated => "PartitionProcessorConfiguration/HasDedicatedProcessors",
146
270
  :rmc_state => "ResourceMonitoringControlState",
147
271
  :rmc_ipaddr => "ResourceMonitoringIPAddress",
148
- :ref_code => "ReferenceCode"
272
+ :os => "OperatingSystemVersion",
273
+ :ref_code => "ReferenceCode",
274
+ :procs => "PartitionProcessorConfiguration/CurrentDedicatedProcessorConfiguration/CurrentProcessors",
275
+ :proc_units => "PartitionProcessorConfiguration/CurrentSharedProcessorConfiguration/CurrentProcessingUnits",
276
+ :vprocs => "PartitionProcessorConfiguration/CurrentSharedProcessorConfiguration/AllocatedVirtualProcessors"
149
277
  }.freeze
150
278
 
151
279
  def sys_uuid
152
- sys_href = xml.elements["AssociatedManagedSystem"].attributes["href"]
153
- extract_uuid_from_href(sys_href)
280
+ sys_href = singleton("AssociatedManagedSystem", "href")
281
+ uuid_from_href(sys_href)
282
+ end
283
+
284
+ def net_adap_uuids
285
+ uuids_from_links("ClientNetworkAdapters")
286
+ end
287
+
288
+ def lhea_ports
289
+ xml.get_elements("HostEthernetAdapterLogicalPorts/HostEthernetAdapterLogicalPort").map do |elem|
290
+ HostEthernetAdapterLogicalPort.new(elem)
291
+ end
292
+ end
293
+
294
+ def sriov_elp_uuids
295
+ uuids_from_links("SRIOVEthernetLogicalPorts")
296
+ end
297
+
298
+ # Setters
299
+
300
+ def name=(name)
301
+ xml.elements[ATTRS[:name]].text = name
302
+ @name = name
154
303
  end
155
304
  end
156
305
 
157
306
  # Logical Partition information
158
307
  class LogicalPartition < BasePartition
308
+ def vnic_dedicated_uuids
309
+ uuids_from_links("DedicatedVirtualNICs")
310
+ end
311
+
312
+ def vscsi_client_uuids
313
+ uuids_from_links("VirtualSCSIClientAdapters")
314
+ end
315
+
316
+ def vfc_client_uuids
317
+ uuids_from_links("VirtualFibreChannelClientAdapters")
318
+ end
159
319
  end
160
320
 
161
321
  # VIOS information
162
322
  class VirtualIOServer < BasePartition
323
+ def pvs
324
+ xml.get_elements("PhysicalVolumes/PhysicalVolume").map do |elem|
325
+ PhysicalVolume.new(elem)
326
+ end
327
+ end
328
+
329
+ def rep
330
+ elem = xml.elements["MediaRepositories/VirtualMediaRepository"]
331
+ VirtualMediaRepository.new(elem) unless elem.nil?
332
+ end
333
+
334
+ def vscsi_mappings
335
+ xml.get_elements("VirtualSCSIMappings/VirtualSCSIMapping").map do |elem|
336
+ VirtualSCSIMapping.new(elem)
337
+ end
338
+ end
339
+
340
+ def vfc_mappings
341
+ xml.get_elements("VirtualFibreChannelMappings/VirtualFibreChannelMapping").map do |elem|
342
+ VirtualFibreChannelMapping.new(elem)
343
+ end
344
+ end
345
+ end
346
+
347
+ # Empty parent class to match K2 schema definition
348
+ class VirtualSCSIStorage < AbstractNonRest; end
349
+
350
+ # Physical Volume information
351
+ class PhysicalVolume < VirtualSCSIStorage
352
+ ATTRS = {
353
+ :location => "LocationCode",
354
+ :description => "Description",
355
+ :is_available => "AvailableForUsage",
356
+ :capacity => "VolumeCapacity",
357
+ :name => "VolumeName",
358
+ :is_fc => "IsFibreChannelBacked",
359
+ :udid => "VolumeUniqueID"
360
+ }.freeze
361
+ end
362
+
363
+ # Logical Volume information
364
+ class VirtualDisk < VirtualSCSIStorage
365
+ ATTRS = {
366
+ :name => "DiskName",
367
+ :label => "DiskLabel",
368
+ :capacity => "DiskCapacity", # In GiB
369
+ :psize => "PartitionSize",
370
+ :vg => "VolumeGroup",
371
+ :udid => "UniqueDeviceID"
372
+ }.freeze
373
+ end
374
+
375
+ # Virtual CD-ROM information
376
+ class VirtualOpticalMedia < VirtualSCSIStorage
377
+ ATTRS = {
378
+ :name => "MediaName",
379
+ :udid => "MediaUDID",
380
+ :mount_opts => "MountType",
381
+ :size => "Size" # in GiB
382
+ }.freeze
383
+ end
384
+
385
+ # Virtual Media Repository information
386
+ class VirtualMediaRepository < AbstractNonRest
387
+ ATTRS = {
388
+ :name => "RepositoryName",
389
+ :size => "RepositorySize" # in GiB
390
+ }.freeze
391
+
392
+ def vopts
393
+ xml.get_elements("OpticalMedia/VirtualOpticalMedia").map do |elem|
394
+ VirtualOpticalMedia.new(elem)
395
+ end
396
+ end
397
+ end
398
+
399
+ # Virtual Switch information
400
+ class VirtualSwitch < AbstractRest
401
+ ATTRS = {
402
+ :id => "SwitchID",
403
+ :mode => "SwitchMode", # "VEB", "VEPA"
404
+ :name => "SwitchName"
405
+ }.freeze
406
+
407
+ def sys_uuid
408
+ href.path.split('/')[-3]
409
+ end
410
+
411
+ def networks_uuids
412
+ uuids_from_links("VirtualNetworks")
413
+ end
414
+ end
415
+
416
+ # Virtual Network information
417
+ class VirtualNetwork < AbstractRest
418
+ ATTRS = {
419
+ :name => "NetworkName",
420
+ :vlan_id => "NetworkVLANID",
421
+ :vswitch_id => "VswitchID",
422
+ :tagged => "TaggedNetwork"
423
+ }.freeze
424
+
425
+ def vswitch_uuid
426
+ href = singleton("AssociatedSwitch", "href")
427
+ uuid_from_href(href)
428
+ end
429
+
430
+ def lpars_uuids
431
+ uuids_from_links("ConnectedPartitions")
432
+ end
433
+ end
434
+
435
+ # Virtual I/O Adapter information
436
+ class VirtualIOAdapter < AbstractRest
437
+ ATTRS = {
438
+ :type => "AdapterType", # "Server", "Client", "Unknown"
439
+ :location => "LocationCode",
440
+ :slot => "VirtualSlotNumber",
441
+ :required => "RequiredAdapter"
442
+ }.freeze
443
+ end
444
+
445
+ # Virtual Ethernet Adapter information
446
+ class VirtualEthernetAdapter < VirtualIOAdapter
447
+ ATTRS = ATTRS.merge({
448
+ :macaddr => "MACAddress",
449
+ :vswitch_id => "VirtualSwitchID",
450
+ :vlan_id => "PortVLANID",
451
+ :location => "LocationCode"
452
+ }.freeze)
453
+
454
+ def vswitch_uuid
455
+ uuids_from_links("AssociatedVirtualSwitch").first
456
+ end
457
+ end
458
+
459
+ # Client Network Adapter information
460
+ class ClientNetworkAdapter < VirtualEthernetAdapter
461
+ def networks_uuids
462
+ uuids_from_links("VirtualNetworks")
463
+ end
464
+ end
465
+
466
+ # LP-HEA information
467
+ class EthernetBackingDevice < IOAdapter; end
468
+ class HostEthernetAdapterLogicalPort < EthernetBackingDevice
469
+ ATTRS = ATTRS.merge({
470
+ :macaddr => "MACAddress",
471
+ :port_id => "LogicalPortID",
472
+ :state => "PortState",
473
+ :location => "HEALogicalPortPhysicalLocation"
474
+ }.freeze)
475
+ end
476
+
477
+ # Virtual NIC dedicated information
478
+ class VirtualNICDedicated < VirtualIOAdapter
479
+ ATTRS = ATTRS.merge({
480
+ :location => "DynamicReconfigurationConnectorName", # overrides VirtualIOAdapter
481
+ :macaddr => "Details/MACAddress",
482
+ :os_devname => "Details/OSDeviceName",
483
+ :port_vlan_id => "Details/PortVLANID"
484
+ }.freeze)
485
+ end
486
+
487
+ # SR-IOV Configured Logical Port information
488
+ class SRIOVConfiguredLogicalPort < AbstractRest
489
+ ATTRS = {
490
+ :port_id => "LogicalPortID",
491
+ :port_vlan_id => "PortVLANID",
492
+ :location => "LocationCode",
493
+ :dr_name => "DynamicReconfigurationConnectorName",
494
+ :devname => "DeviceName",
495
+ :capacity => "ConfiguredCapacity"
496
+ }.freeze
497
+
498
+ def lpars_uuids
499
+ uuids_from_links("AssociatedLogicalPartitions")
500
+ end
501
+ end
502
+
503
+ # SR-IOV Ethernet Logical Port information
504
+ class SRIOVEthernetLogicalPort < SRIOVConfiguredLogicalPort
505
+ ATTRS = ATTRS.merge({
506
+ :macaddr => "MACAddress"
507
+ }.freeze)
508
+ end
509
+
510
+ # Virtual SCSI mapping information
511
+ class VirtualSCSIMapping < AbstractNonRest
512
+ def lpar_uuid
513
+ href = singleton("AssociatedLogicalPartition", "href")
514
+ uuid_from_href(href)
515
+ end
516
+
517
+ def client
518
+ elem = xml.elements["ClientAdapter"]
519
+ VirtualSCSIClientAdapter.new(elem) unless elem.nil?
520
+ end
521
+
522
+ def server
523
+ elem = xml.elements["ServerAdapter"]
524
+ VirtualSCSIServerAdapter.new(elem) unless elem.nil?
525
+ end
526
+
527
+ def storage
528
+ # Possible storage types are:
529
+ # LogicalUnit, PhysicalVolume, VirtualDisk, VirtualOpticalMedia
530
+ elem = xml.elements["Storage/*[1]"]
531
+ Module.const_get("IbmPowerHmc::#{elem.name}").new(elem) unless elem.nil?
532
+ end
533
+
534
+ def device
535
+ # Possible backing device types are:
536
+ # LogicalVolumeVirtualTargetDevice, PhysicalVolumeVirtualTargetDevice,
537
+ # SharedStoragePoolLogicalUnitVirtualTargetDevice, VirtualOpticalTargetDevice
538
+ elem = xml.elements["TargetDevice/*[1]"]
539
+ Module.const_get("IbmPowerHmc::#{elem.name}").new(elem) unless elem.nil?
540
+ end
541
+ end
542
+
543
+ # Virtual SCSI adapter (common class for Client and Server)
544
+ class VirtualSCSIAdapter < VirtualIOAdapter
545
+ ATTRS = ATTRS.merge({
546
+ :name => "AdapterName",
547
+ :backdev => "BackingDeviceName",
548
+ :remote_backdev => "RemoteBackingDeviceName",
549
+ :remote_lpar_id => "RemoteLogicalPartitionID",
550
+ :remote_slot => "RemoteSlotNumber",
551
+ :server_location => "ServerLocationCode",
552
+ :udid => "UniqueDeviceID"
553
+ }.freeze)
554
+ end
555
+
556
+ # Virtual SCSI client adapter information
557
+ class VirtualSCSIClientAdapter < VirtualSCSIAdapter
558
+ def server
559
+ elem = xml.elements["ServerAdapter"]
560
+ VirtualSCSIServerAdapter.new(elem) unless elem.nil?
561
+ end
562
+
563
+ def vios_uuid
564
+ href = singleton("ConnectingPartition", "href")
565
+ uuid_from_href(href)
566
+ end
567
+ end
568
+
569
+ # Virtual SCSI server adapter information
570
+ class VirtualSCSIServerAdapter < VirtualSCSIAdapter; end
571
+
572
+ # Virtual target device information
573
+ class VirtualTargetDevice < AbstractNonRest
574
+ ATTRS = {
575
+ :lun => "LogicalUnitAddress",
576
+ :parent => "ParentName",
577
+ :target => "TargetName",
578
+ :udid => "UniqueDeviceID"
579
+ }.freeze
580
+ end
581
+
582
+ # LV backing device information
583
+ class LogicalVolumeVirtualTargetDevice < VirtualTargetDevice; end
584
+
585
+ # PV backing device information
586
+ class PhysicalVolumeVirtualTargetDevice < VirtualTargetDevice; end
587
+
588
+ # LU backing device information
589
+ class SharedStoragePoolLogicalUnitVirtualTargetDevice < VirtualTargetDevice
590
+ ATTRS = ATTRS.merge({
591
+ :cluster_id => "ClusterID",
592
+ :path => "PathName",
593
+ :raid_level => "RAIDLevel"
594
+ }.freeze)
595
+ end
596
+
597
+ # Virtual CD backing device information
598
+ class VirtualOpticalTargetDevice < VirtualTargetDevice
599
+ def media
600
+ elem = xml.elements["VirtualOpticalMedia"]
601
+ VirtualOpticalMedia.new(elem) unless elem.nil?
602
+ end
603
+ end
604
+
605
+ # VFC mapping information
606
+ class VirtualFibreChannelMapping < AbstractNonRest
607
+ def lpar_uuid
608
+ href = singleton("AssociatedLogicalPartition", "href")
609
+ uuid_from_href(href)
610
+ end
611
+
612
+ def client
613
+ elem = xml.elements["ClientAdapter"]
614
+ VirtualFibreChannelClientAdapter.new(elem) unless elem.nil?
615
+ end
616
+
617
+ def server
618
+ elem = xml.elements["ServerAdapter"]
619
+ VirtualFibreChannelServerAdapter.new(elem) unless elem.nil?
620
+ end
621
+
622
+ def port
623
+ elem = xml.elements["Port"]
624
+ PhysicalFibreChannelPort.new(elem) unless elem.nil?
625
+ end
626
+ end
627
+
628
+ # VFC adapter information
629
+ class VirtualFibreChannelAdapter < VirtualIOAdapter
630
+ ATTRS = ATTRS.merge({
631
+ :name => "AdapterName",
632
+ :lpar_id => "ConnectingPartitionID",
633
+ :slot => "ConnectingVirtualSlotNumber",
634
+ :udid => "UniqueDeviceID"
635
+ }.freeze)
636
+
637
+ def lpar_uuid
638
+ href = singleton("ConnectingPartition", "href")
639
+ uuid_from_href(href)
640
+ end
641
+ end
642
+
643
+ # VFC client information
644
+ class VirtualFibreChannelClientAdapter < VirtualFibreChannelAdapter
645
+ def nport_loggedin
646
+ xml.get_elements("NportLoggedInStatus/VirtualFibreChannelNPortLoginStatus").map do |elem|
647
+ VirtualFibreChannelNPortLoginStatus.new(elem)
648
+ end
649
+ end
650
+
651
+ def server
652
+ elem = xml.elements["ServerAdapter"]
653
+ VirtualFibreChannelServerAdapter.new(elem) unless elem.nil?
654
+ end
655
+
656
+ def wwpns
657
+ singleton("WWPNs")&.split
658
+ end
659
+
660
+ def os_disks
661
+ xml.get_elements("OperatingSystemDisks/OperatingSystemDisk/Name").map do |elem|
662
+ elem.text&.strip
663
+ end.compact
664
+ end
665
+ end
666
+
667
+ # VFC port status
668
+ class VirtualFibreChannelNPortLoginStatus < AbstractNonRest
669
+ ATTRS = {
670
+ :wwpn => "WWPN",
671
+ :wwpn_status => "WWPNStatus",
672
+ :loggedin_by => "LoggedInBy",
673
+ :reason => "StatusReason"
674
+ }.freeze
675
+ end
676
+
677
+ # VFC server information
678
+ class VirtualFibreChannelServerAdapter < VirtualFibreChannelAdapter
679
+ ATTRS = ATTRS.merge({
680
+ :map_port => "MapPort"
681
+ }.freeze)
682
+
683
+ def port
684
+ elem = xml.elements["PhysicalPort"]
685
+ PhysicalFibreChannelPort.new(elem) unless elem.nil?
686
+ end
687
+ end
688
+
689
+ # FC port information
690
+ class PhysicalFibreChannelPort < AbstractNonRest
691
+ ATTRS = {
692
+ :location => "LocationCode",
693
+ :name => "PortName",
694
+ :udid => "UniqueDeviceID",
695
+ :wwpn => "WWPN",
696
+ :wwnn => "WWNN",
697
+ :avail_ports => "AvailablePorts",
698
+ :total_ports => "TotalPorts"
699
+ }.freeze
700
+
701
+ def pvs
702
+ xml.get_elements("PhysicalVolumes/PhysicalVolume").map do |elem|
703
+ PhysicalVolume.new(elem)
704
+ end
705
+ end
706
+ end
707
+
708
+ # Cluster information
709
+ class Cluster < AbstractRest
710
+ ATTRS = {
711
+ :name => "ClusterName",
712
+ :id => "ClusterID",
713
+ :tier_capable => "ClusterCapabilities/IsTierCapable"
714
+ }.freeze
715
+
716
+ def ssp_uuid
717
+ href = singleton("ClusterSharedStoragePool", "href")
718
+ uuid_from_href(href)
719
+ end
720
+
721
+ def nodes
722
+ xml.get_elements("Node/Node").map do |elem|
723
+ Node.new(elem)
724
+ end
725
+ end
726
+ end
727
+
728
+ # Cluster node information
729
+ class Node < AbstractNonRest
730
+ ATTRS = {
731
+ :hostname => "HostName",
732
+ :lpar_id => "PartitionID",
733
+ :state => "State",
734
+ :ioslevel => "VirtualIOServerLevel"
735
+ }.freeze
736
+
737
+ def vios_uuid
738
+ href = singleton("VirtualIOServer", "href")
739
+ uuid_from_href(href)
740
+ end
741
+ end
742
+
743
+ # SSP information
744
+ class SharedStoragePool < AbstractRest
745
+ ATTRS = {
746
+ :name => "StoragePoolName",
747
+ :udid => "UniqueDeviceID",
748
+ :capacity => "Capacity",
749
+ :free_space => "FreeSpace",
750
+ :overcommit => "OverCommitSpace",
751
+ :total_lu_size => "TotalLogicalUnitSize",
752
+ :alert_threshold => "AlertThreshold"
753
+ }.freeze
754
+
755
+ def cluster_uuid
756
+ href = singleton("AssociatedCluster", "href")
757
+ uuid_from_href(href)
758
+ end
759
+
760
+ def pvs
761
+ xml.get_elements("PhysicalVolumes/PhysicalVolume").map do |elem|
762
+ PhysicalVolume.new(elem)
763
+ end
764
+ end
765
+
766
+ def tiers_uuids
767
+ uuids_from_links("AssociatedTiers")
768
+ end
769
+
770
+ def lus
771
+ xml.get_elements("LogicalUnits/LogicalUnit").map do |elem|
772
+ LogicalUnit.new(elem)
773
+ end
774
+ end
775
+ end
776
+
777
+ # SSP tier information
778
+ class Tier < AbstractRest
779
+ ATTRS = {
780
+ :name => "Name",
781
+ :udid => "UniqueDeviceID",
782
+ :type => "Type",
783
+ :capacity => "Capacity",
784
+ :total_lu_size => "TotalLogicalUnitSize",
785
+ :is_default => "IsDefault",
786
+ :free_space => "FreeSpace"
787
+ }.freeze
788
+
789
+ def ssp_uuid
790
+ href = singleton("AssociatedSharedStoragePool", "href")
791
+ uuid_from_href(href)
792
+ end
793
+
794
+ def lus_uuids
795
+ uuids_from_links("AssociatedLogicalUnits")
796
+ end
797
+ end
798
+
799
+ # SSP LU information
800
+ class LogicalUnit < VirtualSCSIStorage
801
+ ATTRS = {
802
+ :name => "UnitName",
803
+ :capacity => "UnitCapacity",
804
+ :udid => "UniqueDeviceID",
805
+ :thin => "ThinDevice",
806
+ :type => "LogicalUnitType",
807
+ :in_use => "InUse"
808
+ }.freeze
809
+ end
810
+
811
+ class PartitionTemplateSummary < AbstractRest
812
+ ATTRS = {
813
+ :name => "partitionTemplateName"
814
+ }.freeze
815
+ end
816
+
817
+ class PartitionTemplate < AbstractRest
818
+ ATTRS = {
819
+ :name => "partitionTemplateName",
820
+ :description => "description",
821
+ :os => "logicalPartitionConfig/osVersion",
822
+ :memory => "logicalPartitionConfig/memoryConfiguration/currMemory"
823
+ }.freeze
163
824
  end
164
825
 
165
826
  # HMC Event
166
827
  class Event < AbstractRest
828
+ attr_accessor :usertask
167
829
  ATTRS = {
168
830
  :id => "EventID",
169
831
  :type => "EventType",
@@ -192,9 +854,9 @@ module IbmPowerHmc
192
854
 
193
855
  def results
194
856
  results = {}
195
- xml.each_element("Results/JobParameter") do |result|
196
- name = result.elements["ParameterName"]&.text&.strip
197
- value = result.elements["ParameterValue"]&.text&.strip
857
+ xml.each_element("Results/JobParameter") do |jobparam|
858
+ name = jobparam.elements["ParameterName"]&.text&.strip
859
+ value = jobparam.elements["ParameterValue"]&.text&.strip
198
860
  results[name] = value unless name.nil?
199
861
  end
200
862
  results