ibm_power_hmc 0.18.0 → 0.19.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.
@@ -0,0 +1,232 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require 'uri'
5
+
6
+ module IbmPowerHmc
7
+ ##
8
+ # Generic parser for HMC K2 XML responses.
9
+ class Parser
10
+ def initialize(body)
11
+ @doc = REXML::Document.new(body)
12
+ end
13
+
14
+ ##
15
+ # @!method entry
16
+ # Return the first K2 entry element in the response.
17
+ # @return [REXML::Element, nil] The first entry element.
18
+ def entry
19
+ @doc.elements["entry"]
20
+ end
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.
27
+ def object(filter_type = nil)
28
+ self.class.to_obj(entry, filter_type)
29
+ end
30
+
31
+ def self.to_obj(entry, filter_type = nil)
32
+ return if entry.nil?
33
+
34
+ content = entry.elements["content[@type]"]
35
+ return if content.nil?
36
+
37
+ type = content.attributes["type"].split("=")[1] || filter_type.to_s
38
+ return unless filter_type.nil? || filter_type.to_s == type
39
+
40
+ Module.const_get("IbmPowerHmc::#{type}").new(entry)
41
+ end
42
+ end
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>
56
+ class FeedParser < Parser
57
+ def entries
58
+ objs = []
59
+ @doc.each_element("feed/entry") do |entry|
60
+ objs << yield(entry)
61
+ end
62
+ objs
63
+ end
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.
70
+ def objects(filter_type = nil)
71
+ entries do |entry|
72
+ self.class.to_obj(entry, filter_type)
73
+ end.compact
74
+ end
75
+ end
76
+
77
+ ##
78
+ # HMC generic K2 non-REST object.
79
+ # @abstract
80
+ # @attr_reader [REXML::Document] xml The XML document representing this object.
81
+ class AbstractNonRest
82
+ ATTRS = {}.freeze
83
+ attr_reader :xml
84
+
85
+ def initialize(xml)
86
+ @xml = xml
87
+ self.class::ATTRS.each { |varname, xpath| define_attr(varname, xpath) }
88
+ end
89
+
90
+ ##
91
+ # @!method define_attr(varname, xpath)
92
+ # Define an instance variable using the text of an XML element as value.
93
+ # @param varname [String] The name of the instance variable.
94
+ # @param xpath [String] The XPath of the XML element containing the text.
95
+ def define_attr(varname, xpath)
96
+ value = singleton(xpath)
97
+ self.class.__send__(:attr_reader, varname)
98
+ self.class.__send__(:define_method, "#{varname}=") do |v|
99
+ if v.nil?
100
+ xml.elements.delete(xpath)
101
+ else
102
+ create_element(xpath) if xml.elements[xpath].nil?
103
+ xml.elements[xpath].text = v
104
+ end
105
+ instance_variable_set("@#{varname}", v)
106
+ end
107
+ instance_variable_set("@#{varname}", value)
108
+ end
109
+ private :define_attr
110
+
111
+ ##
112
+ # @!method create_element(xpath)
113
+ # Create a new XML element.
114
+ # @param xpath [String] The XPath of the XML element to create.
115
+ def create_element(xpath)
116
+ cur = xml
117
+ xpath.split("/").each do |el|
118
+ p = cur.elements[el]
119
+ if p.nil?
120
+ cur = cur.add_element(el)
121
+ else
122
+ cur = p
123
+ end
124
+ end
125
+ end
126
+
127
+ ##
128
+ # @!method singleton(xpath, attr = nil)
129
+ # Get the text (or the value of a specified attribute) of an XML element.
130
+ # @param xpath [String] The XPath of the XML element.
131
+ # @param attr [String] The name of the attribute.
132
+ # @return [String, nil] The text or attribute value of the XML element or nil.
133
+ # @example lpar.singleton("PartitionProcessorConfiguration/*/MaximumVirtualProcessors").to_i
134
+ def singleton(xpath, attr = nil)
135
+ elem = xml.elements[xpath]
136
+ return if elem.nil?
137
+
138
+ attr.nil? ? elem.text&.strip : elem.attributes[attr]
139
+ end
140
+
141
+ def to_s
142
+ str = +"#{self.class.name}:\n"
143
+ self.class::ATTRS.each do |varname, _|
144
+ value = instance_variable_get("@#{varname}")
145
+ value = value.nil? ? "null" : "'#{value}'"
146
+ str << " #{varname}: #{value}\n"
147
+ end
148
+ str
149
+ end
150
+
151
+ def uuid_from_href(href, index = -1)
152
+ URI(href).path.split('/')[index]
153
+ end
154
+
155
+ def uuids_from_links(elem, index = -1)
156
+ xml.get_elements("#{elem}/link[@href]").map do |link|
157
+ uuid_from_href(link.attributes["href"], index)
158
+ end.compact
159
+ end
160
+
161
+ def timestamp(xpath)
162
+ # XML element containing a number of milliseconds since the Epoch.
163
+ Time.at(0, singleton(xpath).to_i, :millisecond).utc
164
+ end
165
+
166
+ def collection_of(name, type)
167
+ xml.get_elements([name, type].compact.join("/")).map do |elem|
168
+ Module.const_get("IbmPowerHmc::#{elem.name}").new(elem)
169
+ rescue NameError
170
+ nil
171
+ end.compact
172
+ end
173
+ end
174
+
175
+ ##
176
+ # HMC generic K2 REST object.
177
+ # Encapsulate data for a single REST object.
178
+ # The XML looks like this:
179
+ # <entry>
180
+ # <id>uuid</id>
181
+ # <published>timestamp</published>
182
+ # <link rel="SELF" href="https://..."/>
183
+ # <etag:etag>ETag</etag:etag>
184
+ # <content type="type">
185
+ # <!-- actual content here -->
186
+ # </content>
187
+ # </entry>
188
+ #
189
+ # @abstract
190
+ # @attr_reader [String] uuid The UUID of the object contained in the entry.
191
+ # @attr_reader [Time] published The time at which the entry was published.
192
+ # @attr_reader [URI::HTTPS] href The URL of the object itself.
193
+ # @attr_reader [String] etag The entity tag of the entry.
194
+ # @attr_reader [String] content_type The content type of the object contained in the entry.
195
+ class AbstractRest < AbstractNonRest
196
+ attr_reader :uuid, :published, :href, :etag, :content_type
197
+
198
+ def initialize(entry)
199
+ if entry.name != "entry"
200
+ # We are inlined.
201
+ super(entry)
202
+ return
203
+ end
204
+
205
+ @uuid = entry.elements["id"]&.text
206
+ @published = Time.xmlschema(entry.elements["published"]&.text)
207
+ link = entry.elements["link[@rel='SELF']"]
208
+ @href = URI(link.attributes["href"]) unless link.nil?
209
+ @etag = entry.elements["etag:etag"]&.text&.strip
210
+ content = entry.elements["content"]
211
+ @content_type = content.attributes["type"]
212
+ super(content.elements.first)
213
+ end
214
+
215
+ def to_s
216
+ str = super
217
+ str << " uuid: '#{uuid}'\n" if defined?(@uuid)
218
+ str << " published: '#{published}'\n" if defined?(@published)
219
+ str
220
+ end
221
+ end
222
+
223
+ # Error response from HMC
224
+ class HttpErrorResponse < AbstractRest
225
+ ATTRS = {
226
+ :status => "HTTPStatus",
227
+ :uri => "RequestURI",
228
+ :reason => "ReasonCode",
229
+ :message => "Message"
230
+ }.freeze
231
+ end
232
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IbmPowerHmc
4
+ # Performance and Capacity Monitoring preferences
5
+ class ManagementConsolePcmPreference < AbstractRest
6
+ ATTRS = {
7
+ :max_ltm => "MaximumManagedSystemsForLongTermMonitor",
8
+ :max_compute_ltm => "MaximumManagedSystemsForComputeLTM",
9
+ :max_aggregation => "MaximumManagedSystemsForAggregation",
10
+ :max_stm => "MaximumManagedSystemsForShortTermMonitor",
11
+ :max_em => "MaximumManagedSystemsForEnergyMonitor",
12
+ :aggregated_storage_duration => "AggregatedMetricsStorageDuration"
13
+ }.freeze
14
+
15
+ def managed_system_preferences
16
+ collection_of(nil, "ManagedSystemPcmPreference")
17
+ end
18
+ end
19
+
20
+ class ManagedSystemPcmPreference < AbstractNonRest
21
+ ATTRS = {
22
+ :id => "Metadata/Atom/AtomID",
23
+ :name => "SystemName",
24
+ :long_term_monitor => "LongTermMonitorEnabled",
25
+ :aggregation => "AggregationEnabled",
26
+ :short_term_monitor => "ShortTermMonitorEnabled",
27
+ :compute_ltm => "ComputeLTMEnabled",
28
+ :energy_monitor => "EnergyMonitorEnabled"
29
+ }.freeze
30
+ end
31
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IbmPowerHmc
4
+ # Serviceable Event
5
+ class ServiceableEvent < AbstractRest
6
+ ATTRS = {
7
+ :prob_uuid => "problemUuid",
8
+ :hostname => "reportingConsoleNode/hostName",
9
+ :number => "problemNumber",
10
+ :hw_record => "problemManagementHardwareRecord",
11
+ :description => "shortDescription",
12
+ :state => "problemState",
13
+ :approval_state => "ApprovalState",
14
+ :refcode => "referenceCode",
15
+ :refcode_ext => "referenceCodeExtension",
16
+ :refcode_sys => "systemReferenceCode",
17
+ :call_home => "callHomeEnabled",
18
+ :dup_count => "duplicateCount",
19
+ :severity => "eventSeverity",
20
+ :notif_type => "notificationType",
21
+ :notif_status => "notificationStatus",
22
+ :post_action => "postAction",
23
+ :symptom => "symptomString",
24
+ :lpar_id => "partitionId",
25
+ :lpar_name => "partitionName",
26
+ :lpar_hostname => "partitionHostName",
27
+ :lpar_ostype => "partitionOSType",
28
+ :syslog_id => "sysLogId",
29
+ :total_events => "totalEvents"
30
+ }.freeze
31
+
32
+ def reporting_mtms
33
+ mtms("reportingManagedSystemNode")
34
+ end
35
+
36
+ def failing_mtms
37
+ mtms("failingManagedSystemNode")
38
+ end
39
+
40
+ def time
41
+ timestamp("primaryTimestamp")
42
+ end
43
+
44
+ def created_time
45
+ timestamp("createdTimestamp")
46
+ end
47
+
48
+ def first_reported_time
49
+ timestamp("firstReportedTimestamp")
50
+ end
51
+
52
+ def last_reported_time
53
+ timestamp("lastReportedTimestamp")
54
+ end
55
+
56
+ def frus
57
+ collection_of("fieldReplaceableUnits", "FieldReplaceableUnit")
58
+ end
59
+
60
+ def ext_files
61
+ collection_of("extendedErrorData", "ExtendedFileData")
62
+ end
63
+
64
+ private
65
+
66
+ def mtms(prefix)
67
+ machtype = singleton("#{prefix}/managedTypeModelSerial/MachineType")
68
+ model = singleton("#{prefix}/managedTypeModelSerial/Model")
69
+ serial = singleton("#{prefix}/managedTypeModelSerial/SerialNumber")
70
+ "#{machtype}-#{model}*#{serial}"
71
+ end
72
+ end
73
+
74
+ class FieldReplaceableUnit < AbstractNonRest
75
+ ATTRS = {
76
+ :part_number => "partNumber",
77
+ :fru_class => "class",
78
+ :description => "fieldReplaceableUnitDescription",
79
+ :location => "locationCode",
80
+ :serial => "SerialNumber",
81
+ :ccin => "ccin"
82
+ }.freeze
83
+ end
84
+
85
+ class ExtendedFileData < AbstractNonRest
86
+ ATTRS = {
87
+ :filename => "fileName",
88
+ :description => "description",
89
+ :zipfilename => "zipFileName"
90
+ }.freeze
91
+ end
92
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IbmPowerHmc
4
+ class PartitionTemplateSummary < AbstractRest
5
+ ATTRS = {
6
+ :name => "partitionTemplateName"
7
+ }.freeze
8
+ end
9
+
10
+ class PartitionTemplate < AbstractRest
11
+ ATTRS = {
12
+ :name => "partitionTemplateName",
13
+ :description => "description",
14
+ :lpar_name => "logicalPartitionConfig/partitionName",
15
+ :lpar_type => "logicalPartitionConfig/partitionType",
16
+ :lpar_id => "logicalPartitionConfig/partitionId",
17
+ :os => "logicalPartitionConfig/osVersion",
18
+ :memory => "logicalPartitionConfig/memoryConfiguration/currMemory",
19
+ :dedicated => "logicalPartitionConfig/processorConfiguration/hasDedicatedProcessors",
20
+ :sharing_mode => "logicalPartitionConfig/processorConfiguration/sharingMode",
21
+ :vprocs => "logicalPartitionConfig/processorConfiguration/sharedProcessorConfiguration/desiredVirtualProcessors",
22
+ :proc_units => "logicalPartitionConfig/processorConfiguration/sharedProcessorConfiguration/desiredProcessingUnits",
23
+ :pool_id => "logicalPartitionConfig/processorConfiguration/sharedProcessorConfiguration/sharedProcessorPoolId",
24
+ :procs => "logicalPartitionConfig/processorConfiguration/dedicatedProcessorConfiguration/desiredProcessors"
25
+ }.freeze
26
+
27
+ def vscsi
28
+ REXML::XPath.match(xml, 'logicalPartitionConfig/virtualSCSIClientAdapters/VirtualSCSIClientAdapter').map do |adap|
29
+ {
30
+ :vios => adap.elements['connectingPartitionName']&.text,
31
+ :physvol => adap.elements['associatedPhysicalVolume/PhysicalVolume/name']&.text,
32
+ }
33
+ end
34
+ end
35
+
36
+ def vscsi=(list = [])
37
+ adaps = REXML::Element.new('virtualSCSIClientAdapters')
38
+ adaps.add_attribute('schemaVersion', 'V1_5_0')
39
+ list.each do |vscsi|
40
+ adaps.add_element('VirtualSCSIClientAdapter', {'schemaVersion' => 'V1_5_0'}).tap do |v|
41
+ v.add_element('associatedLogicalUnits', {'schemaVersion' => 'V1_5_0'})
42
+ v.add_element('associatedPhysicalVolume', {'schemaVersion' => 'V1_5_0'}).tap do |e|
43
+ e.add_element('PhysicalVolume', {'schemaVersion' => 'V1_5_0'}).add_element('name').text = vscsi[:physvol] if vscsi[:physvol]
44
+ end
45
+ v.add_element('connectingPartitionName').text = vscsi[:vios]
46
+ v.add_element('AssociatedTargetDevices', {'schemaVersion' => 'V1_5_0'})
47
+ v.add_element('associatedVirtualOpticalMedia', {'schemaVersion' => 'V1_5_0'})
48
+ end
49
+ end
50
+ if xml.elements['logicalPartitionConfig/virtualSCSIClientAdapters']
51
+ xml.elements['logicalPartitionConfig/virtualSCSIClientAdapters'] = adaps
52
+ else
53
+ xml.elements['logicalPartitionConfig'].add_element(adaps)
54
+ end
55
+ end
56
+
57
+ def vfc
58
+ REXML::XPath.match(xml, 'logicalPartitionConfig/virtualFibreChannelClientAdapters/VirtualFibreChannelClientAdapter').map do |adap|
59
+ {
60
+ :vios => adap.elements['connectingPartitionName']&.text,
61
+ :port => adap.elements['portName']&.text
62
+ }
63
+ end
64
+ end
65
+
66
+ def vfc=(list = [])
67
+ adaps = REXML::Element.new('virtualFibreChannelClientAdapters')
68
+ adaps.add_attribute('schemaVersion', 'V1_5_0')
69
+ list.each do |vfc|
70
+ adaps.add_element('VirtualFibreChannelClientAdapter', {'schemaVersion' => 'V1_5_0'}).tap do |v|
71
+ v.add_element('connectingPartitionName').text = vfc[:vios]
72
+ v.add_element('portName').text = vfc[:port]
73
+ end
74
+ end
75
+ if xml.elements['logicalPartitionConfig/virtualFibreChannelClientAdapters']
76
+ xml.elements['logicalPartitionConfig/virtualFibreChannelClientAdapters'] = adaps
77
+ else
78
+ xml.elements['logicalPartitionConfig'].add_element(adaps)
79
+ end
80
+ end
81
+
82
+ def vlans
83
+ REXML::XPath.match(xml, 'logicalPartitionConfig/clientNetworkAdapters/ClientNetworkAdapter/clientVirtualNetworks/ClientVirtualNetwork').map do |vlan|
84
+ {
85
+ :name => vlan.elements['name']&.text,
86
+ :vlan_id => vlan.elements['vlanId']&.text,
87
+ :switch => vlan.elements['associatedSwitchName']&.text
88
+ }
89
+ end
90
+ end
91
+
92
+ def vlans=(list = [])
93
+ adaps = REXML::Element.new('clientNetworkAdapters')
94
+ adaps.add_attribute('schemaVersion', 'V1_5_0')
95
+ list.each do |vlan|
96
+ adaps.add_element('ClientNetworkAdapter', {'schemaVersion' => 'V1_5_0'})
97
+ .add_element('clientVirtualNetworks', {'schemaVersion' => 'V1_5_0'})
98
+ .add_element('ClientVirtualNetwork', {'schemaVersion' => 'V1_5_0'})
99
+ .tap do |v|
100
+ v.add_element('name').text = vlan[:name]
101
+ v.add_element('vlanId').text = vlan[:vlan_id]
102
+ v.add_element('associatedSwitchName').text = vlan[:switch]
103
+ end
104
+ end
105
+ xml.elements['logicalPartitionConfig/clientNetworkAdapters'] = adaps
106
+ end
107
+ end
108
+ end