rbvppc 1.0.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.
@@ -0,0 +1,1140 @@
1
+ #
2
+ # Authors: Christopher M Wood (<woodc@us.ibm.com>)
3
+ # John F Hutchinson (<jfhutchi@us.ibm.com)
4
+ # © Copyright IBM Corporation 2015.
5
+ #
6
+ # LICENSE: MIT (http://opensource.org/licenses/MIT)
7
+ #
8
+ =begin
9
+ Assumptions:
10
+ -operations on LPARs will be done simultaneously to both their current profile and
11
+ the LPAR's hardware itself, removing the need to abstract data into both LPAR attributes
12
+ and attributes of that LPAR's profile.
13
+ Future features:
14
+ -May split lpar_profile into a subclass of LPAR in the future, to allow greater levels of
15
+ customization.
16
+ =end
17
+ require_relative 'hmc'
18
+ require_relative 'vscsi'
19
+ require_relative 'network'
20
+ require_relative 'vnic'
21
+
22
+ class Lpar
23
+
24
+ attr_accessor :min_proc_units, :max_proc_units, :desired_proc_units,
25
+ :min_memory, :max_memory, :desired_memory,
26
+ :min_vcpu, :max_vcpu, :desired_vcpu,
27
+ :hostname, :uncap_weight, :max_virtual_slots
28
+
29
+ attr_reader :hmc, :id, :name, :proc_mode, :sharing_mode, :frame,
30
+ :current_profile, :default_profile
31
+
32
+ #Class variable to hold all 'valid' attributes that can be set on an LPAR
33
+ @@valid_attributes = ["min_mem", "desired_mem", "max_mem", "min_num_huge_pages", "desired_num_huge_pages", "max_num_huge_pages",
34
+ "mem_mode", "hpt_ratio", "proc_mode", "min_proc_units", "desired_proc_units", "max_proc_units", "min_procs",
35
+ "desired_procs", "max_procs", "sharing_mode", "uncap_weight", "shared_proc_pool_id", "shared_proc_pool_name",
36
+ "io_slots", "lpar_io_pool_ids", "max_virtual_slots", "hca_adapters", "boot_mode", "conn_monitoring", "auto_start",
37
+ "power_ctrl_lpar_ids", "work_group_id", "redundant_err_path_reporting", "bsr_arrays", "lhea_logical_ports", "lhea_capabilities", "lpar_proc_compat_mode", "electronic_err_reporting"]
38
+ #Small hash to handle translating HMC labels to Lpar class attributes
39
+ @@attr_mapping = {"min_mem" => "min_memory",
40
+ "max_mem" => "max_memory",
41
+ "desired_mem" => "desired_memory",
42
+ "min_proc_units" => "min_proc_units",
43
+ "max_proc_units" => "max_proc_units",
44
+ "desired_proc_units" => "desired_proc_units",
45
+ "min_procs" => "min_vcpu",
46
+ "max_procs" => "max_vcpu",
47
+ "desired_procs" => "desired_vcpu"
48
+ }
49
+
50
+ def initialize(options_hash, disable_auto_reboot = false)
51
+
52
+ #Test for the explicitly required parameters
53
+ raise StandardError.new("An Lpar cannot be defined without a managing HMC") if options_hash[:hmc].nil? or !options_hash[:hmc].respond_to?(:execute_cmd)
54
+ raise StandardError.new("An Lpar cannot be defined without a name") if options_hash[:name].nil?
55
+ raise StandardError.new("An Lpar cannot be defined without specifying the frame it resides/will reside on") if options_hash[:frame].nil?
56
+ raise StandardError.new("An Lpar cannot be defined without specifying it's desired processing units") if options_hash[:des_proc].nil?
57
+ raise StandardError.new("An Lpar cannot be defined without specifying it's desired virtual CPUs") if options_hash[:des_vcpu].nil?
58
+ raise StandardError.new("An Lpar cannot be defined without specifying it's desired memory") if options_hash[:des_mem].nil?
59
+
60
+ #TODO: We should not really be storing the hostname (or vlan_id, or management_ip) as an attribute of LPAR, much less any network configuration stuff...
61
+ #Maybe we should consider a NetworkSettings class that is just an attribute of LPAR, which we leverage to find any network info we need here...?
62
+ raise StandardError.new("An Lpar cannot be defined without specifying it's FQDN") if options_hash[:hostname].nil? && options_hash[:name].nil?
63
+
64
+ #Parameters that are explicitly required to make an LPAR object
65
+ @hmc = options_hash[:hmc]
66
+ @desired_proc_units = options_hash[:des_proc].to_f
67
+ @desired_memory = options_hash[:des_mem].to_i
68
+ @desired_vcpu = options_hash[:des_vcpu].to_i
69
+ @frame = options_hash[:frame]
70
+ @name = options_hash[:name]
71
+
72
+ #Parameters that can be defaulted if they are not provided
73
+ !options_hash[:hostname].nil? ? @hostname = options_hash[:hostname] : @hostname = @name
74
+ !options_hash[:min_proc].nil? ? @min_proc_units = options_hash[:min_proc].to_f : @min_proc_units = @desired_proc_units
75
+ !options_hash[:max_proc].nil? ? @max_proc_units = options_hash[:max_proc].to_f : @max_proc_units = @desired_proc_units
76
+ !options_hash[:max_mem].nil? ? @max_memory = options_hash[:max_mem].to_i : @max_memory = @desired_memory
77
+ !options_hash[:min_mem].nil? ? @min_memory = options_hash[:min_mem].to_i : @min_memory = @desired_memory
78
+ !options_hash[:max_vcpu].nil? ? @max_vcpu = options_hash[:max_vcpu].to_i : @max_vcpu = @desired_vcpu
79
+ !options_hash[:min_vcpu].nil? ? @min_vcpu = options_hash[:min_vcpu].to_i : @min_vcpu = @desired_vcpu
80
+ !options_hash[:max_virt_slots].nil? ? @max_virtual_slots = options_hash[:max_virt_slots].to_i : @max_virtual_slots = 30
81
+ !options_hash[:current_profile].nil? ? @current_profile = options_hash[:current_profile] : @current_profile = @name + "_profile"
82
+ !options_hash[:default_profile].nil? ? @default_profile = options_hash[:default_profile] : @default_profile = @current_profile
83
+ !options_hash[:sharing_mode].nil? ? @sharing_mode = options_hash[:sharing_mode] : @sharing_mode = "cap"
84
+ @sharing_mode == "uncap" ? @uncap_weight = options_hash[:uncap_weight].to_i : @uncap_weight = nil
85
+ !options_hash[:proc_mode].nil? ? @proc_mode = options_hash[:proc_mode] : @proc_mode = "shared"
86
+
87
+ #Parameters that hold no value unless the LPAR already exists
88
+ #or create() is called
89
+ !options_hash[:id].nil? ? @id = options_hash[:id] : @id = nil
90
+ @disable_auto_reboot = disable_auto_reboot
91
+ #TODO: Implement the VIO pair as attributes of the LPAR???
92
+ end
93
+
94
+ #Create an LPAR
95
+ def create
96
+
97
+ #Stop the create from proceeding if this LPAR already exists
98
+ raise StandardError.new("This LPAR already exists on #{frame}, cannot create #{name}") if exists?
99
+
100
+ command = "mksyscfg -r lpar -m #{@frame} -i name=#{@name}, profile_name=#{@current_profile},boot_mode=norm," +
101
+ "auto_start=0,lpar_env=aixlinux,max_virtual_slots=#{@max_virtual_slots},desired_mem=#{@desired_memory}," +
102
+ "min_mem=#{@min_memory},max_mem=#{@max_memory},desired_procs=#{@desired_vcpu},min_procs=#{@min_vcpu}," +
103
+ "max_procs=#{@max_vcpu},proc_mode=#{@proc_mode},sharing_mode=#{@sharing_mode},desired_proc_units=#{@desired_proc_units}," +
104
+ "max_proc_units=#{@max_proc_units},min_proc_units=#{@min_proc_units}"
105
+ command += ",uncap_weight=#{@uncap_weight}" if !@uncap_weight.nil?
106
+
107
+ hmc.execute_cmd(command)
108
+ end
109
+
110
+ #Delete an LPAR
111
+ #Takes an optional array of Vio objects representing
112
+ #the VIO pair that serves storage to this LPAR.
113
+ def delete(vio_array = nil)
114
+ #Check that this LPAR exists before attempting to delete it.
115
+ #If the LPAR does not exist, simply output a warning stating such
116
+ if !exists?
117
+ warn "This LPAR (#{name}) does not currently exist on #{frame} to be deleted."
118
+ return
119
+ end
120
+
121
+ #Do a hard shutdown and then remove the LPAR definition
122
+ hard_shutdown unless not_activated?
123
+ sleep(10) until not_activated?
124
+
125
+ #If not passed, try to find the VIO servers by looking
126
+ #at this LPAR's vSCSI adapters
127
+ server_lpars = []
128
+ if vio_array.nil?
129
+ vscsis = get_vscsi_adapters
130
+ vscsis.each do |adapter|
131
+ server_lpars.push(adapter.remote_lpar_name)
132
+ end
133
+ server_lpars.uniq!
134
+
135
+ #Now if there are only two unique server LPARs
136
+ #that serve this LPAR, we have our VIO servers
137
+ if server_lpars.length == 2
138
+ vio_array = []
139
+ vio_array.push(Vio.new(hmc,frame,server_lpars[0]))
140
+ vio_array.push(Vio.new(hmc,frame,server_lpars[1]))
141
+ else
142
+ warn "Unable to determine this LPAR's VIO servers, proceeding without removing disks"
143
+ vio_array = nil
144
+ end
145
+ end
146
+
147
+ #Attempt to remove all of the LPAR's disks/vSCSIs before deleting
148
+ remove_storage(vio_array[0],vio_array[1]) if !vio_array.nil?
149
+
150
+ hmc.execute_cmd "rmsyscfg -r lpar -m #{frame} -n #{name}"
151
+ end
152
+
153
+ #Rename an LPAR
154
+ def rename(newname)
155
+ hmc.execute_cmd "chsyscfg -r lpar -m #{frame} -i \'name=#{name},new_name=#{newname}\'"
156
+ @name = newname
157
+ end
158
+
159
+ #Active an LPAR using a profile
160
+ def activate(profile_name = @current_profile)
161
+ hmc.execute_cmd "chsysstate -r lpar -m #{frame} -o on -n #{name} -f #{profile_name}"
162
+ @current_profile = profile_name if @current_profile != profile_name
163
+ end
164
+
165
+ #Hard shutdown LPAR
166
+ def hard_shutdown
167
+ hmc.execute_cmd "chsysstate -r lpar -m #{frame} -o shutdown --immed -n #{name}"
168
+ end
169
+
170
+ #Soft shutdown an LPAR
171
+ def soft_shutdown
172
+ hmc.execute_cmd "chsysstate -r lpar -m #{frame} -o shutdown -n #{name}"
173
+ end
174
+
175
+ #Get LPAR state
176
+ def check_state
177
+ hmc.execute_cmd("lssyscfg -r lpar -m #{frame} --filter lpar_names=#{name} -F state").chomp
178
+ end
179
+
180
+ #Returns true if this LPAR actually exists on it's frame
181
+ #Returns false if it doesn't
182
+ def exists?
183
+ #List all LPARs residing underneath this frame
184
+ result = hmc.execute_cmd("lssyscfg -r lpar -m #{frame} -F name").chomp
185
+ #See if any of their names match this Lpar's name
186
+ result.each_line do |line|
187
+ line.chomp!
188
+ if line == name
189
+ return true
190
+ end
191
+ end
192
+ #Return false if none the names listed match this Lpar's name
193
+ return false
194
+ end
195
+
196
+ #Returns true/false value depending on if the LPAR is running or not
197
+ #Since an LPAR can have states such as "Not Activated", "Open Firmware", "Shutting Down",
198
+ #this function only helps for when we are explicitly looking for an LPAR to be either "Running" or not.
199
+ def is_running?
200
+ return check_state == "Running"
201
+ end
202
+
203
+ #Similar to is_running? - only returns true if the LPAR's state is "Not Activated".
204
+ #Any other state is percieved as false
205
+ def not_activated?
206
+ return check_state == "Not Activated"
207
+ end
208
+
209
+ def get_info
210
+ info = hmc.execute_cmd "lssyscfg -r lpar -m \'#{frame}\' --filter lpar_names=\'#{name}\'"
211
+ return info.chomp
212
+ end
213
+
214
+ #Get the ID of an LPAR
215
+ def id
216
+ if @id.nil?
217
+ #Use an HMC command to pull the LPAR's ID if this is the first time that
218
+ #the LPAR's ID is being accessed
219
+ @id = hmc.execute_cmd("lssyscfg -r lpar -m #{frame} --filter lpar_names=#{name} -F lpar_id").chomp
220
+ end
221
+ return @id
222
+ end
223
+
224
+ #Get the Current Profile of an LPAR
225
+ def current_profile
226
+ if @current_profile.nil?
227
+ @current_profile = hmc.execute_cmd("lssyscfg -r lpar -m #{frame} --filter lpar_names=#{name} -F curr_profile").chomp
228
+ end
229
+ return @current_profile
230
+ end
231
+
232
+ #Get the Default Profile of an LPAR
233
+ def default_profile
234
+ if @default_profile.nil?
235
+ @default_profile = hmc.execute_cmd("lssyscfg -r lpar -m #{frame} --filter lpar_names=#{name} -F default_profile").chomp
236
+ end
237
+ return @default_profile
238
+ end
239
+
240
+ #Get the MAC address of an LPAR
241
+ def get_mac_address
242
+ result = hmc.execute_cmd("lshwres -r virtualio --rsubtype eth --level lpar -m #{frame} -F mac_addr --filter \"lpar_names=#{name}\" ")
243
+ return result.chomp
244
+ end
245
+
246
+ #Set an LPAR profile's attribute, specifying the units to set the attribute to and the HMC label for the attribute
247
+ def set_attr_profile(units,hmc_label)
248
+ cmd = "chsyscfg -m #{frame} -r prof -i \"name=#{current_profile}, lpar_name=#{name}, #{hmc_label}=#{units} \" "
249
+ hmc.execute_cmd(cmd)
250
+ end
251
+
252
+ #Function to use for all Min/Max attribute changing
253
+ def set_attr_and_reactivate(units,hmc_label)
254
+ #Change the profile attribute
255
+ set_attr_profile(units,hmc_label)
256
+ reactivate unless @disable_auto_reboot
257
+ end
258
+
259
+ # Shutdown and reactivate the LPAR so that the attribute changes take effect
260
+ def reactivate
261
+ #Shut down the LPAR
262
+ soft_shutdown unless not_activated?
263
+ #Wait until it's state is "Not Activated"
264
+ sleep(10) until not_activated?
265
+ #Reactivate the LPAR so that the attribute changes take effect
266
+ activate
267
+ end
268
+
269
+ #Bulk modifies an LPAR's resources based on the provided hash.
270
+ #The Hash is required to have it's keys represent labels for HMC attributes (ie, min_mem, max_mem, etc)
271
+ #while it's values are what the user requests those attributes be set to for this LPAR.
272
+ #The LPAR is then reactivated once all of the changes are made for them to take effect.
273
+ # The Class Instance variable @@valid_attributes is used to determine if a key in options is a valid
274
+ # attribute. If an attribute in options is deemed invalid, nothing is done with respect to that attribute.
275
+ def modify_resources(options, reboot = true)
276
+ options.each do |key,val|
277
+ if @@valid_attributes.include?(key)
278
+ #Check for min/max/desired in the key to determine if
279
+ #some bound needs to be checked first
280
+ verify_and_handle_attr_bounds(options,key,val)
281
+
282
+ set_attr_profile(val,key)
283
+
284
+ #Handle setting of any instance variables that should change
285
+ #due to this
286
+ map_key_to_attr(key, val)
287
+ end
288
+ end
289
+ reactivate if reboot
290
+ end
291
+
292
+ #####################################
293
+ # Processing Unit functions
294
+ #####################################
295
+
296
+ #Set the processing units for an LPAR
297
+ def desired_proc_units=(units)
298
+
299
+ raise StandardError.new("Processing unit value is lower than the Minimum Processing Units specified for this LPAR: #{min_proc_units}") if units < min_proc_units
300
+ raise StandardError.new("Processing unit value is higher than the Maximum Processing Units specified for this LPAR: #{max_proc_units}") if units > max_proc_units
301
+
302
+ #Validate that this value adheres to the vCPU:Proc_unit ratio of 10:1
303
+ raise StandardError.new("Desired processing unit value must be less than or equal to Desired vCPU value: #{desired_vcpu}") if units > desired_vcpu
304
+ raise StandardError.new("Desired processing unit value must be at least 1/10 the Desired vCPU value: #{desired_vcpu}") if desired_vcpu/units > 10
305
+
306
+ #Set processing units on the Profile
307
+ set_attr_profile(units,"desired_proc_units")
308
+ #Set processing units via DLPAR
309
+ set_proc_units_dlpar(units)
310
+
311
+ #After the Desired Proc units are set on the profile and hardware, set
312
+ #the private attribute
313
+ @desired_proc_units = units
314
+ end
315
+
316
+ #Set the max processing units for an LPAR
317
+ def max_proc_units=(units)
318
+ raise StandardError.new("Maximum processing unit value is lower than the Desired Processing Units specified for this LPAR") if units < desired_proc_units
319
+
320
+ #Validate that the value specified does not violate the 10:1 ratio requirement between max vCPU and max proc units.
321
+ raise StandardError.new("Maximum processing unit value must be less than or equal to Maximum vCPU value: #{max_vcpu}") if units > max_vcpu
322
+ raise StandardError.new("Maximum processing unit value must at least be 1/10 the Maximum vCPU value: #{max_vcpu}") if max_vcpu/units > 10
323
+
324
+ #Set the Max Proc Units on the LPAR profile
325
+ #and reactivate the LPAR
326
+ set_attr_and_reactivate(units,"max_proc_units")
327
+
328
+ #Set the private member
329
+ @max_proc_units = units
330
+ end
331
+
332
+ #Set the min processing units for an LPAR
333
+ def min_proc_units=(units)
334
+ raise StandardError.new("Minimum processing unit value is greater than the Desired Processing Units specified for this LPAR") if units > desired_proc_units
335
+
336
+ #Validate that this value adheres to the vCPU:Proc_unit ratio of 10:1
337
+ raise StandardError.new("Minimum processing unit value must be less than or equal to Minimum vCPU value: #{min_vcpu}") if units > min_vcpu
338
+ raise StandardError.new("Minimum processing unit value must be at least 1/10 the Minimum vCPU value: #{min_vcpu}") if min_vcpu/units > 10
339
+
340
+ #Set the Max Proc Units on the LPAR profile
341
+ #and reactivate the LPAR
342
+ set_attr_and_reactivate(units,"min_proc_units")
343
+
344
+ #Set the private member
345
+ @min_proc_units = units
346
+ end
347
+
348
+ #####################################
349
+ # Virtual CPU functions
350
+ #####################################
351
+
352
+ #Set the virtual CPUs for an LPAR
353
+ def desired_vcpu=(units)
354
+ raise StandardError.new("Virtual CPU value is lower than the Minimum Virtual CPU value specified for this LPAR: #{min_vcpu}") if units < min_vcpu
355
+ raise StandardError.new("Virtual CPU value is higher than the Maximum Virtual CPU value specified for this LPAR: #{max_vcpu}") if units > max_vcpu
356
+
357
+ #Validate that this value adheres to the vCPU:Proc_unit ratio of 10:1
358
+ raise StandardError.new("Desired vCPU value must be greater than or equal to Desired processing unit value: #{desired_proc_units}") if units < desired_proc_units
359
+ raise StandardError.new("Desired vCPU value must be at most 10 times as large as Desired processing unit value: #{desired_proc_units}") if units/desired_proc_units > 10
360
+
361
+ #Set processing units on the Profile
362
+ set_attr_profile(units,"desired_procs")
363
+ #Set processing units via DLPAR
364
+ set_vcpu_dlpar(units)
365
+
366
+ #After the Desired Proc units are set on the profile and hardware, set
367
+ #the private attribute
368
+ @desired_vcpu = units
369
+ end
370
+
371
+ #Set the minimum virtual CPUs for an LPAR
372
+ def min_vcpu=(units)
373
+ raise StandardError.new("Minimum vCPU value is higher than the Desired Virtual CPU specified for this LPAR: #{desired_vcpu}") if units > desired_vcpu
374
+
375
+ #Validate that this value adheres to the vCPU:Proc_unit ratio of 10:1
376
+ raise StandardError.new("Minimum vCPU value must be greater than or equal to Minimum processing unit value: #{min_proc_units}") if units < min_proc_units
377
+ raise StandardError.new("Minimum vCPU value must be at most 10 times as large as Minimum processing unit value: #{min_proc_units}") if units/min_proc_units > 10
378
+
379
+ #Set the Min vCPU on the LPAR profile
380
+ #and reactivate the LPAR
381
+ set_attr_and_reactivate(units,"min_procs")
382
+
383
+ #Set the private member
384
+ @min_vcpu = units
385
+ end
386
+
387
+ #Set the maximum virtual CPUs for an LPAR
388
+ def max_vcpu=(units)
389
+ raise StandardError.new("Maximum vCPU value is lower than the Desired Virtual CPU specified for this LPAR: #{desired_vcpu}") if units < desired_vcpu
390
+
391
+ #Validate that this value adheres to the vCPU:Proc_unit ratio of 10:1
392
+ raise StandardError.new("Maximum vCPU value must be greater than or equal to Maximum processing unit value: #{max_proc_units}") if units < max_proc_units
393
+ raise StandardError.new("Maximum vCPU value must be at most 10 times as large as Maximum processing unit value: #{max_proc_units}") if units/max_proc_units > 10
394
+
395
+ #Set the Max vCPU on the LPAR profile
396
+ #and reactivate the LPAR
397
+ set_attr_and_reactivate(units,"max_procs")
398
+
399
+ #Set the private member
400
+ @max_vcpu = units
401
+ end
402
+
403
+ ############################################
404
+ # Memory allocation/deallocation functions
405
+ ############################################
406
+
407
+ #Set the Memory allocated to an LPAR (in MB)
408
+ def desired_memory=(units)
409
+
410
+ raise StandardError.new("Memory value is lower than the Minimum Memory specified for this LPAR") if units < min_memory
411
+ raise StandardError.new("Memory value is higher than the Maximum Memory specified for this LPAR") if units > max_memory
412
+
413
+ #Set the desired memory of the LPAR in the profile
414
+ set_attr_profile(units,"desired_mem")
415
+
416
+ #Set the desired memory of the LPAR via DLPAR
417
+ set_memory_dlpar(units)
418
+
419
+ #After it has been set on the profile and the LPAR, set the attribute for the object
420
+ @desired_memory = units
421
+ end
422
+
423
+ #Set the minimum virtual CPUs for an LPAR
424
+ def min_memory=(units)
425
+ raise StandardError.new("Minimum Memory value is higher than the Desired Memory specified for this LPAR: #{desired_memory}") if units > desired_memory
426
+
427
+ #Set the Min Memory on the LPAR profile
428
+ #and reactivate
429
+ set_attr_and_reactivate(units,"min_mem")
430
+
431
+ #Set private member
432
+ @min_memory = units
433
+ end
434
+
435
+ #Set the maximum virtual CPUs for an LPAR
436
+ def max_memory=(units)
437
+ raise StandardError.new("Maximum Memory value is lower than the Desired Memory specified for this LPAR: #{desired_memory}") if units < desired_memory
438
+
439
+ #Set the Max vCPU on the LPAR profile
440
+ #and reactivate
441
+ set_attr_and_reactivate(units,"max_mem")
442
+
443
+ #Set private member
444
+ @max_memory = units
445
+ end
446
+
447
+ ####################################
448
+ # Misc LPAR Attribute functions
449
+ ####################################
450
+
451
+ #Set the Uncapped Processor Weight for this LPAR
452
+ #TODO: change set_attr_profile to set_attr_and_reactivate?
453
+ def uncap_weight=(units)
454
+ if sharing_mode == "uncap"
455
+ set_attr_profile(units,"uncap_weight")
456
+ #set_attr_and_reactivate(units,"uncap_weight")
457
+ @uncap_weight = units
458
+ else
459
+ #Warn user that the sharing mode doesn't permit modifying uncap_weight
460
+ warn "Cannot change uncap_weight on a capped LPAR"
461
+ return nil
462
+ end
463
+ end
464
+
465
+ #Set the Maximum number of virtual adapters for this LPAR
466
+ #TODO: change set_attr_profile to set_attr_and_reactivate?
467
+ def max_virtual_slots=(units)
468
+ if units < max_virtual_slots
469
+ #Test to make sure that any occupied slots are
470
+ #less than units
471
+ used_slots = get_used_virtual_slots
472
+ max = used_slots.sort[0]
473
+ raise StandardError.new("Cannot reduce the maximum number of virtual slots to #{units} because slot #{max} is currently in use") if units < max
474
+ end
475
+
476
+ set_attr_profile(units,"max_virtual_slots")
477
+ @max_virtual_slots = units
478
+ end
479
+
480
+ ####################################
481
+ # vSCSI Adapter functions
482
+ ####################################
483
+
484
+ #Returns array of output with vSCSI adapter information
485
+ #about the client LPAR
486
+ def get_vscsi_adapters
487
+
488
+ #Get vSCSI adapter info from this LPAR's profile
489
+ scsi_adapter_output = clean_vadapter_string(hmc.execute_cmd("lssyscfg -r prof -m #{frame} --filter 'lpar_names=#{name},profile_names=#{current_profile}' -F virtual_scsi_adapters").chomp)
490
+ vscsi_adapters = []
491
+ if scsi_adapter_output.include?(",")
492
+ scsi_adapters = scsi_adapter_output.split(/,/)
493
+ #split on /
494
+ #11/client/1/rslppc09a/17/0,12/client/2/rslppc09b/17/0
495
+ scsi_adapters.each do |scsi_adapter|
496
+ scsi_adapter = scsi_adapter.split("/")
497
+ vscsi = Vscsi.new(scsi_adapter[0],scsi_adapter[1],scsi_adapter[2],scsi_adapter[3],scsi_adapter[4],scsi_adapter[5])
498
+ vscsi_adapters.push(vscsi)
499
+ end
500
+ elsif !scsi_adapter_output.empty?
501
+ scsi_adapter = scsi_adapter_output
502
+ scsi_adapter = scsi_adapter.split("/")
503
+ vscsi = Vscsi.new(scsi_adapter[0],scsi_adapter[1],scsi_adapter[2],scsi_adapter[3],scsi_adapter[4],scsi_adapter[5])
504
+ vscsi_adapters.push(vscsi)
505
+ end
506
+ return vscsi_adapters
507
+ end
508
+
509
+ #Return array of used virtual adapter slots
510
+ #for an LPAR
511
+ def get_used_virtual_slots
512
+ #scsi_slot_output = execute_cmd "lshwres -r virtualio --rsubtype scsi -m #{frame} --level lpar --filter lpar_names=#{lpar} -F slot_num"
513
+ #eth_slot_output = execute_cmd "lshwres -r virtualio --rsubtype eth -m #{frame} --level lpar --filter lpar_names=#{lpar} -F slot_num"
514
+ #serial_slot_output = execute_cmd "lshwres -r virtualio --rsubtype serial -m #{frame} --level lpar --filter lpar_names=#{lpar} -F slot_num"
515
+ #lpar_prof = get_lpar_curr_profile(frame,lpar)
516
+
517
+ scsi_slot_output = clean_vadapter_string(hmc.execute_cmd "lssyscfg -r prof -m '#{frame}' --filter 'lpar_names=#{name},profile_names=#{current_profile}' -F virtual_scsi_adapters")
518
+ serial_slot_output = clean_vadapter_string(hmc.execute_cmd "lssyscfg -r prof -m '#{frame}' --filter 'lpar_names=#{name},profile_names=#{current_profile}' -F virtual_serial_adapters")
519
+ eth_slot_output = clean_vadapter_string(hmc.execute_cmd "lssyscfg -r prof -m '#{frame}' --filter 'lpar_names=#{name},profile_names=#{current_profile}' -F virtual_eth_adapters")
520
+ used_slots = []
521
+
522
+ if scsi_slot_output.include?(",")
523
+ scsi_slots = scsi_slot_output.split(/,/)
524
+ else
525
+ scsi_slots = [scsi_slot_output]
526
+ end
527
+
528
+ if serial_slot_output.include?(",")
529
+ serial_slots = serial_slot_output.split(/,/)
530
+ else
531
+ serial_slots = [serial_slot_output]
532
+ end
533
+
534
+ if eth_slot_output.include?(",")
535
+ eth_slots = eth_slot_output.split(/,/)
536
+ else
537
+ eth_slots = [eth_slot_output]
538
+ end
539
+
540
+ scsi_slots.each do |adapter_line|
541
+ if !adapter_line.empty?
542
+ parse_hash = parse_vscsi_syntax(adapter_line)
543
+ used_slots.push(parse_hash[:virtual_slot_num].to_i)
544
+ end
545
+ end
546
+
547
+ serial_slots.each do |adapter_line|
548
+ if !adapter_line.empty?
549
+ parse_hash = parse_vserial_syntax(adapter_line)
550
+ used_slots.push(parse_hash[:virtual_slot_num].to_i)
551
+ end
552
+ end
553
+
554
+ eth_slots.each do |adapter_line|
555
+ if !adapter_line.empty?
556
+ parse_hash = parse_vnic_syntax(adapter_line)
557
+ used_slots.push(parse_hash[:virtual_slot_num].to_i)
558
+ end
559
+ end
560
+
561
+ return used_slots
562
+ end
563
+
564
+ #Get next usable virtual slot on an LPAR
565
+ #Returns nil if no usable slots exist
566
+ def get_available_slot(type = nil)
567
+ max_slots = max_virtual_slots
568
+ used_slots = get_used_virtual_slots
569
+ lowest_slot=11
570
+ if !type.nil?
571
+ lowest_slot=2 if type == "eth"
572
+ end
573
+
574
+ lowest_slot.upto(max_slots) do |n|
575
+ if !used_slots.include?(n)
576
+ return n
577
+ end
578
+ end
579
+ return nil
580
+ end
581
+
582
+ #Remove vSCSI from LPAR
583
+ #Handles removing from the LPAR profiles as well as DLPAR
584
+ #Last parameter is optional and if it isn't specified
585
+ #then it looks for an adapter on lpar that is attached to server_lpar
586
+ #and removes that from the profile/hardware of both the client
587
+ #and server
588
+ #MAY BE DANGEROUS- if multiple vSCSI adapters on a single lpar pointing at the same VIO
589
+ def remove_vscsi(server_lpar,adapter_details=nil)
590
+ if adapter_details.nil?
591
+ adapters = get_vscsi_adapters
592
+ adapters.each do |adapter|
593
+ adapter_details = adapter if adapter.remote_lpar_name == server_lpar.name
594
+ end
595
+ end
596
+
597
+ #Remove this vSCSI from the lpar and server lpar profiles
598
+ remove_vscsi_from_profile(server_lpar,adapter_details)
599
+
600
+ #Remove this vSCSI from the actual hardware of lpar and server lpar
601
+ remove_vscsi_dlpar(server_lpar,adapter_details)
602
+
603
+ end
604
+
605
+ #Remove vSCSI from the LPAR profiles only
606
+ def remove_vscsi_from_profile(server_lpar,vscsi)
607
+ remote_lpar_profile = server_lpar.current_profile
608
+ client_lpar_id = id
609
+
610
+ client_slot = vscsi.virtual_slot_num
611
+ server_lpar_id = vscsi.remote_lpar_id
612
+ if server_lpar != vscsi.remote_lpar_name
613
+ #server_lpar and the LPAR cited in the
614
+ #vscsi hash aren't the same...
615
+ #error out or do something else here...?
616
+ end
617
+ server_slot = vscsi.remote_slot_num
618
+ is_req = vscsi.is_required
619
+
620
+ #Modify client LPAR's profile to no longer include the adapter
621
+ #whose details occupy the vscsi_hash
622
+ hmc.execute_cmd("chsyscfg -r prof -m #{frame} -i \"name=#{current_profile},lpar_name=#{name}," +
623
+ "virtual_scsi_adapters-=#{client_slot}/client/#{server_lpar_id}/#{server_lpar.name}/#{server_slot}/#{is_req}\" ")
624
+
625
+ #Modify the server LPAR's profile to no longer include the client
626
+ hmc.execute_cmd("chsyscfg -r prof -m #{server_lpar.frame} -i \"name=#{remote_lpar_profile},lpar_name=#{server_lpar.name}," +
627
+ "virtual_scsi_adapters-=#{server_slot}/server/#{client_lpar_id}/#{name}/#{client_slot}/#{is_req}\" ")
628
+
629
+ end
630
+
631
+ #Remove vSCSI from LPARs via DLPAR
632
+ def remove_vscsi_dlpar(server_lpar,vscsi)
633
+
634
+ client_slot = vscsi.virtual_slot_num
635
+ server_slot = vscsi.remote_slot_num
636
+
637
+ #If the client LPAR is running, we have to do DLPAR on it.
638
+ if is_running?
639
+ hmc.execute_cmd("chhwres -r virtualio -m #{frame} -p #{name} -o r --rsubtype scsi -s #{client_slot}")
640
+ #-a \"adapter_type=client,remote_lpar_name=#{server_lpar},remote_slot_num=#{server_slot}\" ")
641
+ end
642
+
643
+ #If the server LPAR is running, we have to do DLPAR on it.
644
+ if server_lpar.is_running?
645
+ hmc.execute_cmd("chhwres -r virtualio -m #{server_lpar.frame} -p #{server_lpar.name} -o r --rsubtype scsi -s #{server_slot}")
646
+ #-a \"adapter_type=server,remote_lpar_name=#{lpar},remote_slot_num=#{client_slot}\" ")
647
+ end
648
+ end
649
+
650
+ #Add vSCSI to LPAR
651
+ #Handles adding to profile and via DLPAR
652
+ def add_vscsi(server_lpar)
653
+ #Add vscsi to client and server LPAR profiles
654
+ #Save the adapter slots used
655
+ client_slot, server_slot = add_vscsi_to_profile(server_lpar)
656
+
657
+ #Run DLPAR commands against LPARs themselves (if necessary)
658
+ add_vscsi_dlpar(server_lpar, client_slot, server_slot)
659
+
660
+ return [client_slot, server_slot]
661
+ end
662
+
663
+ #Add vSCSI adapter to LPAR profile
664
+ def add_vscsi_to_profile(server_lpar)
665
+ virtual_slot_num = get_available_slot
666
+ remote_slot_num = server_lpar.get_available_slot
667
+ lpar_profile = current_profile
668
+ remote_lpar_profile = server_lpar.current_profile
669
+
670
+ raise StandardError.new("No available virtual adapter slots on client LPAR #{lpar}") if virtual_slot_num.nil?
671
+ raise StandardError.new("No available virtual adapter slots on server LPAR #{server_lpar}") if remote_slot_num.nil?
672
+
673
+ #Modify client LPAR's profile
674
+ hmc.execute_cmd("chsyscfg -r prof -m #{frame} -i \"name=#{lpar_profile},lpar_name=#{name},virtual_scsi_adapters+=#{virtual_slot_num}/client//#{server_lpar.name}/#{remote_slot_num}/0\" ")
675
+ #Modify server LPAR's profile
676
+ hmc.execute_cmd("chsyscfg -r prof -m #{server_lpar.frame} -i \"name=#{remote_lpar_profile},lpar_name=#{server_lpar.name},virtual_scsi_adapters+=#{remote_slot_num}/server//#{name}/#{virtual_slot_num}/0\" ")
677
+
678
+ #Return the client slot and server slot used in the LPAR profiles
679
+ return [virtual_slot_num, remote_slot_num]
680
+ end
681
+
682
+ #Add vSCSI adapter via DLPAR command
683
+ def add_vscsi_dlpar(server_lpar,client_slot_to_use = nil, server_slot_to_use = nil)
684
+ if client_slot_to_use.nil? or server_slot_to_use.nil?
685
+ client_slot_to_use = get_available_slot
686
+ server_slot_to_use = server_lpar.get_available_slot
687
+ end
688
+
689
+ #If the client LPAR is running, we have to do DLPAR on it.
690
+ if is_running?
691
+ hmc.execute_cmd("chhwres -r virtualio -m #{frame} -p #{name} -o a --rsubtype scsi -s #{client_slot_to_use} -a \"adapter_type=client,remote_lpar_name=#{server_lpar.name},remote_slot_num=#{server_slot_to_use}\" ")
692
+ end
693
+
694
+ #If the server LPAR is running, we have to do DLPAR on it.
695
+ if server_lpar.is_running?
696
+ hmc.execute_cmd("chhwres -r virtualio -m #{server_lpar.frame} -p #{server_lpar.name} -o a --rsubtype scsi -s #{server_slot_to_use} -a \"adapter_type=server,remote_lpar_name=#{name},remote_slot_num=#{client_slot_to_use}\" ")
697
+ end
698
+ #chhwres -r virtualio -m "FrameName" -p VioName -o a --rsubtype scsi -s 11 -a "adapter_type=server,remote_lpar_name=ClientLPAR,remote_slot_num=5"
699
+ end
700
+
701
+
702
+ #####################################
703
+ # Disk management functions
704
+ #####################################
705
+
706
+ #Adds storage to this LPAR using the provided Primary and Secondary VIOs
707
+ #and the amount of storage requested, in GB
708
+ def add_storage(primary_vio,secondary_vio,size_in_gb)
709
+ #Get info about all vSCSIs attached to this LPAR
710
+ attached_vscsis = get_vscsi_adapters
711
+
712
+ #Keep track of the number of vSCSI adapters that attach to the
713
+ #VIOs. This should be 2 after finding the vSCSI adapters that attach
714
+ #to each of the VIOs (one for each VIO). If it is more, then there must
715
+ #be more than one vSCSI attached to one or both of the VIOs.
716
+ adapter_count = 0
717
+ primary_vscsi = nil
718
+ secondary_vscsi = nil
719
+ #Find the vSCSI adapters for each VIO
720
+ attached_vscsis.each do |vscsi|
721
+ if vscsi.remote_lpar_name == primary_vio.name
722
+ primary_vscsi = vscsi
723
+ adapter_count += 1
724
+ end
725
+
726
+ if vscsi.remote_lpar_name == secondary_vio.name
727
+ secondary_vscsi = vscsi
728
+ adapter_count += 1
729
+ end
730
+ end
731
+
732
+ #If the adapter count is greater than 2, that means that at least one of the
733
+ #VIOs in this pair has more than one vSCSI that attaches to this LPAR.
734
+ #Fail out for now.
735
+ #TODO: Add better handling logic that can avoid this issue.
736
+ if adapter_count > 2
737
+ warn "This LPAR has multiple adapter connections to it's VIOs; unable to determine which adapter to attach new disks to..."
738
+ return nil
739
+ end
740
+
741
+ #If an adapter cannot not be found for either of the VIOs, fail out
742
+ #because a disk cannot be attached.
743
+ if primary_vscsi.nil? or secondary_vscsi.nil?
744
+ raise StandardError.new("Cannot attach storage to this LPAR. It does not have a vSCSI adapter defined to one of it's VIOs")
745
+ end
746
+
747
+ #Use the remote_slot_num attribute of the two vSCSIs that were found
748
+ #to find out the names of the vhosts they reference on each of the VIOs
749
+ primary_vhost = primary_vio.find_vhost_given_virtual_slot(primary_vscsi.remote_slot_num)
750
+ secondary_vhost = secondary_vio.find_vhost_given_virtual_slot(secondary_vscsi.remote_slot_num)
751
+
752
+ #Use Vio map_by_size function to map the appropriate disks to both VIOs
753
+ primary_vio.map_by_size(primary_vhost, secondary_vio, secondary_vhost, size_in_gb)
754
+ end
755
+
756
+ #Removes/deallocates all storage from this LPAR and unmaps all of these disks
757
+ #on the specified Primary and Secondary VIO servers. The disks attached to
758
+ #the LPAR are assumed to be supplied by the Primary and Secondary VIOs specified.
759
+ def remove_storage(primary_vio,secondary_vio)
760
+ #Deallocates all storage and vSCSI adapters from this LPAR
761
+ primary_vio.unmap_all_disks(secondary_vio, self)
762
+ end
763
+
764
+ #Removes/deallocates a disk from the LPAR specified by it's PVID.
765
+ #This disk is assumed to be supplied by the Primary and Secondary VIO specified
766
+ def remove_disk(primary_vio,secondary_vio,pvid)
767
+ #Use unmap_by_pvid Vio function to unmap a single disk from this LPAR.
768
+ primary_vio.unmap_by_pvid(secondary_vio, pvid)
769
+ end
770
+
771
+ #####################################
772
+ # vNIC functions
773
+ #####################################
774
+
775
+ #Gets a list of vNIC objects
776
+ def get_vnic_adapters
777
+ #Get vNIC adapter info from this LPARs profile
778
+ eth_adapter_output = clean_vadapter_string(hmc.execute_cmd("lssyscfg -r prof -m #{frame} --filter 'lpar_names=#{name},profile_names=#{current_profile}' -F virtual_eth_adapters").chomp)
779
+ eth_adapters = []
780
+ #If there are multiple vNICs,
781
+ #they must be split on ',' and handled
782
+ #individually
783
+ if eth_adapter_output.include?(",")
784
+ #TODO:
785
+ # => Test with vNICs that have Additional VLAN IDs.
786
+ # => If Additional VLAN IDs have commas too, re-evaluate the logic here.
787
+ adapters = eth_adapter_output.split(/,/)
788
+ adapters.each do |adapter|
789
+ split_adapter = adapter.split("/")
790
+ vnic = Vnic.new(split_adapter[0],split_adapter[1],split_adapter[2],split_adapter[3],split_adapter[4],split_adapter[5])
791
+ eth_adapters.push(vnic)
792
+ end
793
+ elsif !eth_adapter_output.empty?
794
+ #If there are no ','s assume the there is only one vNIC defined
795
+ split_adapter = eth_adapter_output.split("/")
796
+ vnic = Vnic.new(split_adapter[0],split_adapter[1],split_adapter[2],split_adapter[3],split_adapter[4],split_adapter[5])
797
+ eth_adapters.push(vnic)
798
+ end
799
+
800
+ return eth_adapters
801
+ end
802
+
803
+ #Create vNIC on LPAR
804
+ def create_vnic(vlan_id,addl_vlan_ids = "")
805
+ if validate_vlan_id(vlan_id)
806
+ #default value for is_trunk = 0
807
+ #default value for is_required = 1
808
+ slot_num = get_available_slot("eth")
809
+ create_vnic_profile(slot_num,vlan_id,addl_vlan_ids,0,1)
810
+
811
+ #TODO: Handle logic for dealing with an LPAR
812
+ #that isn't Not Activated, but also isn't
813
+ #Running
814
+ create_vnic_dlpar(slot_num,vlan_id)
815
+
816
+ #LPAR requires a power cycle in order to
817
+ #get a MAC address from this vNIC
818
+ if not_activated?
819
+ activate
820
+ sleep(10) until !not_activated?
821
+ soft_shutdown
822
+ end
823
+ else
824
+ raise StandardError.new("VLAN ID: #{vlan_id} not found on #{frame}")
825
+ end
826
+ end
827
+
828
+ #Create vNIC on LPAR profile
829
+ def create_vnic_profile(slot_number, vlan_id, addl_vlan_ids, is_trunk, is_required)
830
+ if validate_vlan_id(vlan_id)
831
+ ##chsyscfg -m Server-9117-MMA-SNxxxxx -r prof -i 'name=server_name,lpar_id=xx,"virtual_eth_adapters=596/1/596//0/1,506/1/506//0/1,"'
832
+ #slot_number/is_ieee/port_vlan_id/"additional_vlan_id,additional_vlan_id"/is_trunk(number=priority)/is_required
833
+ lpar_prof = current_profile
834
+
835
+ #Going to assume adapter will always be ieee
836
+ #For is Trunk how do we determine the number for priority? Do we just let the user pass it?
837
+ hmc.execute_cmd("chsyscfg -m #{frame} -r prof -i \'name=#{lpar_prof},lpar_name=#{name},"+
838
+ "\"virtual_eth_adapters+=#{slot_number}/1/#{vlan_id}/\"#{addl_vlan_ids}" +
839
+ "\"/#{is_trunk}/#{is_required} \"\'")
840
+ else
841
+ raise StandardError.new("VLAN ID: #{vlan_id} not found on #{frame}")
842
+ end
843
+ end
844
+
845
+ #Create vNIC on LPAR via DLPAR
846
+ #As writen today defaulting ieee_virtual_eth=1 sets us to Not IEEE 802.1Q compatible. To add compatability set value to 1
847
+ def create_vnic_dlpar(slot_number,vlan_id)
848
+ if is_running?
849
+ if validate_vlan_id(vlan_id)
850
+ hmc.execute_cmd("chhwres -r virtualio -m #{frame} -o a -p #{name} --rsubtype eth -s #{slot_number} -a \"ieee_virtual_eth=1,port_vlan_id=#{vlan_id}\"")
851
+ else
852
+ raise StandardError.new("VLAN ID: #{vlan_id} not found on #{frame}")
853
+ end
854
+ end
855
+ end
856
+
857
+ #Change vlan id of vnic
858
+ def modify_vnic!(slot_number, vlan_id, is_trunk, is_required)
859
+
860
+ if validate_vlan_id(vlan_id)
861
+ #Power down
862
+ soft_shutdown unless not_activated?
863
+ sleep 5 until not_activated?
864
+ hmc.execute_cmd("chsyscfg -r prof -m #{frame} -i \'name=#{current_profile},lpar_name=#{name},\"virtual_eth_adapters=#{slot_number}/1//#{vlan_id}//#{is_trunk}/#{is_required}\"\'")
865
+ activate
866
+ else
867
+ raise StandardError.new("VLAN ID: #{vlan_id} not found on #{frame}")
868
+ end
869
+ end
870
+
871
+ #list available vlans
872
+ def list_vlans
873
+ vlans = []
874
+ vlans = hmc.list_vlans_on_frame(frame)
875
+ return vlans
876
+ end
877
+
878
+ #validate vlan exists on frame
879
+ def validate_vlan_id(vlan_id)
880
+ vlans = []
881
+ vlans = hmc.list_vlans_on_frame(frame)
882
+ count = 0
883
+ vlans_length = vlans.length
884
+ vlans.each do |vlan|
885
+ if vlan_id == vlan
886
+ puts "VLAN ID is valid for #{frame}"
887
+ return true
888
+ break
889
+ end
890
+ count+1
891
+ end
892
+ if count == vlans_length
893
+ puts "VLAN ID not valid for #{frame}"
894
+ return false
895
+ end
896
+ end
897
+
898
+ #Removes a vNIC adapter on an LPAR based on the
899
+ #slot number that the vNIC occupies
900
+ #TODO: Overload parameters to allow a different way to remove vNICs ???
901
+ def remove_vnic(slot_number)
902
+ #Find the vNIC that is desired to be
903
+ #removed
904
+ vnics = get_vnic_adapters
905
+ vnic = nil
906
+ vnics.each do |adapter|
907
+ if adapter.virtual_slot_num == slot_number
908
+ vnic = adapter
909
+ end
910
+ end
911
+ #If no vNIC occupies the slot specified, error out
912
+ raise StandardError.new("vNIC adapter does not currently occupy slot #{slot_number}") if vnic.nil?
913
+ #Remove the vNIC from this LPAR's profile
914
+ remove_vnic_profile(vnic)
915
+ #Remove the vNIC from the LPAR hardware if the LPAR is currently activated
916
+ remove_vnic_dlpar(slot_number)
917
+ end
918
+
919
+ #Remove a vNIC on the LPAR's profile denoted by
920
+ #the virtual slot number occupied by the vNIC
921
+ def remove_vnic_profile(vnic)
922
+ hmc.execute_cmd("chsyscfg -m #{frame} -r prof -i 'name=#{current_profile},lpar_name=#{name},"+
923
+ "\"virtual_eth_adapters-=#{vnic.virtual_slot_num}/#{vnic.is_ieee}/#{vnic.vlan_id}/#{vnic.additional_vlan_ids}" +
924
+ "/#{vnic.is_trunk}/#{vnic.is_required}\"'")
925
+ end
926
+
927
+ #Remove a vNIC on the LPAR via DLPAR denoted by
928
+ #the virtual slot number occupied by the vNIC
929
+ def remove_vnic_dlpar(slot_number)
930
+ if is_running?
931
+ hmc.execute_cmd("chhwres -r virtualio -m #{frame} -o r -p #{name} --rsubtype eth -s #{slot_number}")
932
+ end
933
+ end
934
+
935
+
936
+ #####################################################
937
+ # Private Methods
938
+ #####################################################
939
+ private
940
+
941
+ #Private function that handles bulk setting of instance variables
942
+ #Used by modify_resources() to handle setting attribute values
943
+ #on the Lpar object after modifying the value on the HMC.
944
+ def map_key_to_attr(key, value)
945
+ if @@attr_mapping.has_key?(key)
946
+ attr_name = @@attr_mapping[key]
947
+ #Use Object function instance_variable_set to take
948
+ #a string that is the name of an instance variable
949
+ #and change it's value
950
+ instance_variable_set("@" + attr_name, value)
951
+ end
952
+ end
953
+
954
+ #Private function that is used to ensure that the LPAR attribute key
955
+ #is set to value, while adhereing to any min, max, or desired qualification.
956
+ #options_hash represents a collection of other LPAR attributes that also will be changed.
957
+ #So this hash is checked to see if it contains any bounds shifts that would allow key to
958
+ #also be changed. If none of the bounds related to the attribute key are cited in the hash,
959
+ #the assumption that the attribute bounds should be changed to accomodate this is made.
960
+ def verify_and_handle_attr_bounds(options_hash, key, value)
961
+ split_key = key.split('_')
962
+ #Save the qualifier for the attribute
963
+ #as well it's the base name
964
+ qualifier = split_key[0]
965
+ split_key.delete_at(0)
966
+ base_attr = split_key.join('_')
967
+ fix_bounds = false
968
+ if ["min","max","desired"].include?(qualifier)
969
+ other_bounds = ["min","max","desired"].select { |x| x!=qualifier }
970
+ #Since there will only ever be 2 more array elements in other_bounds at this point,
971
+ #assign them, find their labels, find their attribute names,
972
+ #find their current values, and continue with validation
973
+ other_bound_a = other_bounds[0]
974
+ other_bound_b = other_bounds[1]
975
+ bound_a_label = [other_bound_a,base_attr].join('_')
976
+ bound_b_label = [other_bound_b,base_attr].join('_')
977
+ bound_a_instance_var = @@attr_mapping[bound_a_label]
978
+ bound_b_instance_var = @@attr_mapping[bound_b_label]
979
+ bound_a = instance_variable_get("@" + bound_a_instance_var)
980
+ bound_b = instance_variable_get("@" + bound_b_instance_var)
981
+ #Find out if this attribute change doesn't satisfy the current bounds
982
+ this_attr_label = key
983
+ this_attr_value = value
984
+
985
+ bound_a_new_val = nil
986
+ bound_b_new_val = nil
987
+
988
+ #If this value does not satisfy the current bounds, take note and
989
+ #rectify it later
990
+ if !satisfies_bounds?(qualifier, this_attr_value, bound_a, bound_b)
991
+ #Make the new bounds values be what is in the options hash, unless
992
+ #it isn't specified, then just make it the same as what we're trying to change.
993
+ if options_hash.has_key?(bound_a_label)
994
+ bound_a_new_val = options_hash[bound_a_label]
995
+ else
996
+ bound_a_new_val = value
997
+ end
998
+
999
+ if options_hash.has_key?(bound_b_label)
1000
+ bound_b_new_val = options_hash[bound_b_label]
1001
+ else
1002
+ bound_b_new_val = value
1003
+ end
1004
+
1005
+ #Check if the bounds might be satisfied if *only one* of the bounds changed
1006
+ if satisfies_bounds?(qualifier, this_attr_value, bound_a_new_val, bound_b)
1007
+ bound_b_new_val = bound_b
1008
+ elsif satisfies_bounds?(qualifier, this_attr_value, bound_a, bound_b_new_val)
1009
+ bound_a_new_val = bound_a
1010
+ end
1011
+ end
1012
+
1013
+ #If this is a vCPU or a Proc Units change, we need to ensure that
1014
+ #the new change adheres to the fact that the ratio between vCPUs and
1015
+ #Proc Units needs to be 10:1
1016
+ if ["procs","proc_units"].include?(base_attr)
1017
+ #TODO: Add logic that handles ensuring this 10:1 ratio remains in
1018
+ #place after this change.
1019
+ end
1020
+
1021
+ if !bound_a_new_val.nil? and !bound_b_new_val.nil?
1022
+ #Based on how the other_bounds array is constructed earlier,
1023
+ #other_bound_a can either be "min" or "max", which helps determine the order
1024
+ #in which to set bound_a and bound_b. Also, if the new bound is less than or greater than
1025
+ #the old bound will further determine this order.
1026
+
1027
+ #If other_bound_a == 'min', and it's new value is less than the old one,
1028
+ #Change this one first, and then the second bound
1029
+ #Same for if bound_a is 'max' and it's new value is greater than the old
1030
+ if (other_bound_a == "min" and bound_a_new_val <= bound_a) or
1031
+ (other_bound_a == "max" and bound_a_new_val > bound_a)
1032
+ set_attr_profile(bound_a_new_val, bound_a_label)
1033
+ set_attr_profile(bound_b_new_val, bound_b_label)
1034
+ elsif (other_bound_a == "min" and bound_a_new_val > bound_a) or
1035
+ (other_bound_a == "max" and bound_a_new_val <= bound_a)
1036
+ set_attr_profile(bound_b_new_val, bound_b_label)
1037
+ set_attr_profile(bound_a_new_val, bound_a_label)
1038
+ end
1039
+ instance_variable_set("@" + bound_a_instance_var, bound_a_new_val)
1040
+ instance_variable_set("@" + bound_b_instance_var, bound_b_new_val)
1041
+ end
1042
+ end
1043
+ end
1044
+
1045
+ def satisfies_bounds?(op, val1, val2, val3)
1046
+ if op == "min"
1047
+ return (val1 <= val2 and val1 <= val3 and val2 >= val3)
1048
+ elsif op == "max"
1049
+ return (val1 >= val2 and val1 >= val3 and val2 <= val3)
1050
+ elsif op == "desired"
1051
+ return (val1 >= val2 and val1 <= val3 and val2 <= val3)
1052
+ end
1053
+ end
1054
+
1055
+ #Set the processing units for an LPAR via DLPAR
1056
+ def set_proc_units_dlpar(units)
1057
+ #This command adds or removes, doesn't 'set'
1058
+ #TODO: add logic to make it behave like a 'set' and not an add/remove
1059
+ units > desired_proc_units ? op="a" : op="r"
1060
+ difference = (units-desired_proc_units).abs
1061
+ if is_running?
1062
+ if proc_mode == "shared"
1063
+ hmc.execute_cmd("chhwres -r proc -m #{frame} -o #{op} -p #{name} --procunits #{difference} ")
1064
+ elsif proc_mode == "dedicated"
1065
+ #Apparently if the proccessor sharing mode is 'dedicated',
1066
+ #then you need to use the --procs flag when changing processor units
1067
+ hmc.execute_cmd("chhwres -r proc -m #{frame} -o #{op} -p #{name} --procs #{difference} ")
1068
+ end
1069
+ end
1070
+ end
1071
+
1072
+ #Set the desired number of virtual CPUs for an LPAR using DLPAR commands
1073
+ def set_vcpu_dlpar(units)
1074
+ #This command adds or removes, it doesn't 'set'
1075
+ units > desired_vcpu ? op="a" : op="r"
1076
+ difference = (units-desired_vcpu).abs
1077
+ if is_running?
1078
+ hmc.execute_cmd("chhwres -r proc -m #{frame} -o #{op} -p #{name} --procs #{difference}")
1079
+ end
1080
+ end
1081
+
1082
+ #Set the Memory allocated to an LPAR via DLPAR (in MB)
1083
+ def set_memory_dlpar(units)
1084
+
1085
+ units > desired_memory ? op="a" : op="r"
1086
+ difference = (units-desired_memory).abs
1087
+
1088
+ if is_running?
1089
+ hmc.execute_cmd("chhwres -r mem -m #{frame} -o #{op} -p #{name} -q #{difference}")
1090
+ # chhwres -r mem -m Managed-System -o a -p Lpar_name -q 1024
1091
+ end
1092
+ end
1093
+
1094
+ #####################################
1095
+ # Utility Functions
1096
+ #####################################
1097
+
1098
+ #Handle strings of multiple vadapters that could be surrounded by
1099
+ #stray '"' or evaluate to "none" if there are no vadapters
1100
+ def clean_vadapter_string(vadapter_string)
1101
+ vadapter_string = "" if vadapter_string.chomp == "none"
1102
+ vadapter_string = vadapter_string[1..-1] if vadapter_string.start_with?('"')
1103
+ vadapter_string = vadapter_string[0..-2] if vadapter_string.end_with?('"')
1104
+
1105
+ return vadapter_string
1106
+ end
1107
+
1108
+
1109
+ #Three functions used to parse out vSCSI, vSerial
1110
+ #and vNIC adapter syntax
1111
+
1112
+ def parse_vscsi_syntax(vscsi_string)
1113
+ return parse_slash_delim_string(vscsi_string,
1114
+ [:virtual_slot_num, :client_or_server, :remote_lpar_id, :remote_lpar_name, :remote_slot_num, :is_required]) if !vscsi_string.empty?
1115
+ end
1116
+
1117
+ def parse_vserial_syntax(vserial_string)
1118
+ return parse_slash_delim_string(vserial_string,
1119
+ [:virtual_slot_num, :client_or_server, :supports_hmc, :remote_lpar_id, :remote_lpar_name, :remote_slot_num, :is_required]) if !vserial_string.empty?
1120
+ end
1121
+
1122
+ def parse_vnic_syntax(vnic_string)
1123
+ return parse_slash_delim_string(vnic_string,
1124
+ [:virtual_slot_num, :is_ieee, :port_vlan_id, :additional_vlan_ids, :is_trunk, :is_required]) if !vnic_string.empty?
1125
+ end
1126
+
1127
+ #Parse the slash delimited string (used for vadapters) by
1128
+ #separating the elements in the string into a hash
1129
+ #with keys based on what is specified in field_specs
1130
+ def parse_slash_delim_string(slash_string, field_specs)
1131
+ # slash_string = "596/1/596//0/1"
1132
+ # field_specs = [:virtual_slot_num, :client_or_server, :remote_lpar_id...]
1133
+ values = slash_string.split(/\//)
1134
+ result = {}
1135
+ field_specs.each_index do |i|
1136
+ result[field_specs[i]] = values[i]
1137
+ end
1138
+ return result
1139
+ end
1140
+ end