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.
data/lib/rbvppc/lun.rb ADDED
@@ -0,0 +1,23 @@
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
+ class Lun
9
+ attr_reader :name, :pvid, :size_in_mb
10
+ def initialize(name,pvid, size_in_mb)
11
+ @name = name
12
+ #@serial_number = serial_number
13
+ @pvid = pvid
14
+ @size_in_mb = size_in_mb.to_i
15
+ end
16
+
17
+
18
+ #Override == to test equality of two disk's PVIDs??
19
+ def ==(other_lun)
20
+ return self.pvid == other_lun.pvid
21
+ end
22
+
23
+ end
@@ -0,0 +1,54 @@
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
+ class Network
9
+ attr_accessor :ip_address, :is_primary, :subnet_mask, :gateway, :dns, :given_name, :vlan_id
10
+
11
+ def initialize(options_hash)
12
+ #Test for the explicitly required parameters
13
+ raise StandardError.new("A Network cannot be defined without a IP Address") if options_hash[:ip_address].nil?
14
+ raise StandardError.new("A Network cannot be defined without a Subnet Mask") if options_hash[:subnet_mask].nil?
15
+ raise StandardError.new("A Network cannot be defined without specifying if it is the primary network or not") if options_hash[:is_primary].nil? or (options_hash[:is_primary] != "false" and options_hash[:is_primary] != "true")
16
+ raise StandardError.new("A Network cannot be defined without specifying a VLAN ID") if options_hash[:vlan_id].nil?
17
+
18
+ #Test for optional parameters
19
+ warn ("Warning: Gateway not defined") if options_hash[:gateway].nil?
20
+ warn ("Warning: DNS not defined") if options_hash[:dns].nil?
21
+ warn ("Warning: Given Name not defined") if options_hash[:given_name].nil?
22
+
23
+ #Parameters
24
+ @ip_address = options_hash[:ip_address]
25
+ @is_primary = options_hash[:is_primary]
26
+ @subnet_mask = options_hash[:subnet_mask]
27
+ @gateway = options_hash[:gateway]
28
+ @dns = options_hash[:dns]
29
+ @given_name = options_hash[:given_name]
30
+ @vlan_id = options_hash[:vlan_id]
31
+ end
32
+
33
+ def update_dns(new_dns,old_dns=nil)
34
+ if old_dns.nil?
35
+ if new_dns.is_array?
36
+ @dns = new_dns
37
+ else
38
+ @dns = [new_dns]
39
+ end
40
+ else
41
+ #find index of old_entry in @dns
42
+ i = @dns.index(old_dns)
43
+ @dns[i] = new_entry
44
+ end
45
+ end
46
+
47
+ def update_gateway(new_gateway)
48
+ @gateway = new_gateway
49
+ end
50
+
51
+ def update_given_name(new_name)
52
+ @vlan_id = new_name
53
+ end
54
+ end
data/lib/rbvppc/nim.rb ADDED
@@ -0,0 +1,442 @@
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
+
10
+ Assumptions:
11
+ -We will adopt the following naming convention to find/use/manage bosinst_data (BID) for a client LPAR:
12
+ LPAR Name = "lpar123" => BID Name = "lpar123_bid"
13
+
14
+ -All BID objects are assumed to be stored in /darwin of the NIM (for now)
15
+
16
+ TO-DO:
17
+ -Some functions that uses client_lpar as an input contains commented out versions of lines
18
+ citing client_lpar.name instead of just client_lpar. Use these lines when we convert the functions
19
+ to use LPAR obects and not just pass names.
20
+ =end
21
+
22
+ require_relative 'connectable_server'
23
+
24
+ class Nim < ConnectableServer
25
+
26
+
27
+ #Execute commands on NIM, outputting the full command
28
+ #with puts first.
29
+ def execute_cmd(command)
30
+ puts "#{command}" if debug
31
+ super "#{command}"
32
+ end
33
+
34
+ #list all defined objects of a specific type
35
+ #acceptable types are (standalone,ent,lpp_source,mksysb,spot,fb_script,script,bosinst_data,ent)
36
+ def list_objtype(type)
37
+ case type
38
+ when "standalone","ent","lpp_source","mksysb","spot","fb_script","script","bosinst_data"
39
+ output = execute_cmd "lsnim -t #{type}"
40
+ else
41
+ raise StandardError.new("Unknown type of NIM Object passed")
42
+ end
43
+ objects = []
44
+ output.each_line do |line|
45
+ line.chomp!
46
+ columns = line.split(/[[:blank:]]+/)
47
+ objects.push(columns[0]) if !columns[0].empty?
48
+ end
49
+
50
+ return objects
51
+ end
52
+
53
+ #Is the NIM Client defined?
54
+ def client_defined?(client_lpar)
55
+ #lsnim -Z lpar_name_here 2>/dev/null | awk '(NR==2) {print $1}' | awk -F: '{print $2}'
56
+ #result = execute_cmd "lsnim -Z #{client_lpar} 2>/dev/null | " +
57
+ result = execute_cmd "lsnim -Z #{client_lpar.name} 2>/dev/null | " +
58
+ "awk '(NR==2) {print $1}' | awk -F: '{print $2}'"
59
+ return result.chomp == "machines"
60
+ end
61
+
62
+ #Define the NIM Client
63
+ def define_client(client_lpar)
64
+ if client_defined?(client_lpar)
65
+ remove_client(client_lpar)
66
+ end
67
+ execute_cmd %Q{nim -o define -t standalone -a if1="find_net #{client_lpar.hostname} #{client_lpar.get_mac_address}" -a cable_type1="N/A" -a platform=chrp -a comments="Built by Darwin" -a net_settings1="auto auto" -a connect="shell" -a netboot_kernel=#{master_netboot_kernel} #{client_lpar.name}}
68
+ #execute_cmd %Q{nim -o define -t standalone -a if1="find_net #{hostname} #{mac}" -a cable_type1="N/A" -a platform=chrp -a comments="Built by Darwin" -a net_settings1="auto auto" -a connect="shell" -a netboot_kernel=#{master_netboot_kernel} #{client_lpar}}
69
+ end
70
+
71
+ #Pull the netboot_kernel attribute from NIM master object
72
+ def master_netboot_kernel
73
+ result = execute_cmd "lsnim -l master | awk '{if ($1 ~ /netboot_kernel/) print $3}'"
74
+ return result.chomp
75
+ end
76
+
77
+ #Returns the IP address of the NIM master as a String
78
+ def get_master_ip
79
+ niminfo_line = execute_cmd("cat /etc/niminfo | grep NIM_MASTER_HOSTNAME")
80
+ hostname = niminfo_line.split("=")[1].chomp
81
+
82
+ ipaddr_line = execute_cmd("host #{hostname}")
83
+ ip = ipaddr_line.split(" is ")[1].chomp
84
+
85
+ if ip.empty?
86
+ raise StandardError.new("Unable to determine the NIM Master's IP Address")
87
+ end
88
+
89
+ return ip
90
+ end
91
+
92
+ #Reset a NIM client
93
+ def reset_client(client_lpar)
94
+ execute_cmd "nim -F -o reset #{client_lpar.name}"
95
+ #execute_cmd "nim -F -o reset #{client_lpar}"
96
+ end
97
+
98
+ #Deallocates any/all NIM resources from the client manchine
99
+ def deallocate_resources(client_lpar)
100
+ execute_cmd "nim -o deallocate -a subclass=all #{client_lpar.name}"
101
+ end
102
+
103
+ #Remove a NIM client
104
+ def remove_client(client_lpar)
105
+ deallocate_resources(client_lpar)
106
+ execute_cmd "nim -F -o remove #{client_lpar.name}"
107
+ end
108
+
109
+ #Check the install status of a NIM client
110
+ #Returns current Cstate attribute of NIM client
111
+ def check_install_status(client_lpar)
112
+ #lsnim -Z -a Cstate -a Mstate -a Cstate_result -a info nim_client_name
113
+ result = execute_cmd "lsnim -Z -a Cstate -a Mstate -a Cstate_result -a info #{client_lpar.name}"
114
+ #result = execute_cmd "lsnim -Z -a Cstate -a Mstate -a Cstate_result -a info #{client_lpar}"
115
+ result.each_line do |line|
116
+ line.match(/#{client_lpar.name}/) do |m|
117
+ #line.match(/#{client_lpar}/) do |m|
118
+ #Cstate is the 2nd column of the : delimited output
119
+ cstate = line.split(/:/)[1]
120
+ return cstate
121
+ end
122
+ end
123
+ return nil
124
+ end
125
+
126
+ #Return an array of names of mksysbs that exist on this NIM
127
+ def list_images
128
+ list_objtype("mksysb")
129
+ end
130
+
131
+ #Capture a mksysb image from a NIM client
132
+ def capture_image(source_lpar,mksysb_name,path)
133
+ #Pull mksysb from a NIM client, give it a name and place it in some location on the NIM
134
+ execute_cmd "nim -o define -t mksysb -F -a server=master -a location=#{path} -a source=#{source_lpar.name} -a mk_image=yes -a mksysb_flags=XAe #{mksysb_name}"
135
+
136
+ #Create SPOT resource from this mksysb, giving it a name and a location on the NIM to store it
137
+ extract_spot(mksysb_name)
138
+ end
139
+
140
+ #Add a mksysb image to this NIM based on the name given and
141
+ #the local file path of the mksysb file on the NIM.
142
+ #Returns the name of the image that is created.
143
+ def add_image(file_path,mksysb_name)
144
+ #Check to make sure a mksysb with this name doesn't already exist
145
+ images = list_images
146
+ if images.include?(mksysb_name)
147
+ raise StandardError.new("A mksysb with the specified name #{mksysb_name} already exists, please specify another name")
148
+ end
149
+
150
+ #Add image to the NIM
151
+ execute_cmd "nim -o define -t mksysb -F -a server=master -a location=#{file_path} -a mksysb_flags=XAe #{mksysb_name}"
152
+
153
+ #Extract a SPOT from this mksysb
154
+ extract_spot(mksysb_name)
155
+
156
+ return mksysb_name
157
+ end
158
+
159
+ #Removes a mksysb from the NIM that identifies with the name specified.
160
+ #Attempts to remove the SPOT that was extracted from this mksysb first.
161
+ def remove_image(mksysb_name)
162
+ #Find if this mksysb actually exists on the NIM
163
+ images = list_images
164
+ if !images.include?(mksysb_name)
165
+ warn "#{mksysb_name} does not exist on this NIM."
166
+ return
167
+ end
168
+
169
+ #Find and remove the SPOT for this mksysb
170
+ spot_name = get_spot(mksysb_name)
171
+ if !spot_name.nil?
172
+ remove_spot(spot_name)
173
+ end
174
+
175
+ #Remove the mksysb from the NIM (along with it's mksysb file)
176
+ execute_cmd("nim -o remove -a rm_image=yes #{mksysb_name}")
177
+ end
178
+
179
+ #Deploy mksysb image to NIM client
180
+ def deploy_image(client_lpar, mksysb_name, firstboot_script = nil, lpp_source = nil)
181
+
182
+ bosinst_data_obj = client_lpar.name+"_bid"
183
+
184
+ #Create a NIM Client and a bosinst_data object if they don't
185
+ #already exist for this LPAR.
186
+ define_client(client_lpar) if !client_defined?(client_lpar)
187
+ create_bid(client_lpar) if !bid_exists?(client_lpar)
188
+
189
+ if !lpp_source.nil?
190
+ #TODO: Do something different if an lpp_source is specified...
191
+ end
192
+
193
+ #Get the SPOT to use for this image deployment
194
+ spot_name = get_spot(mksysb_name)
195
+ if spot_name.nil?
196
+ #Extract the spot from this mksysb and use it
197
+ spot_name = extract_spot(mksysb_name)
198
+ end
199
+
200
+ command = "nim -o bos_inst -a source=mksysb -a mksysb=#{mksysb_name} -a bosinst_data=#{bosinst_data_obj} -a no_nim_client=no " +
201
+ "-a accept_licenses=yes -a boot_client=no"
202
+ command += " -a spot=#{spot_name}" if !spot_name.nil?
203
+ command += " -a fb_script=#{firstboot_script}" if !firstboot_script.nil?
204
+ command += " -a lpp_source=#{lpp_source}" if !lpp_source.nil?
205
+ command += " #{client_lpar.name}"
206
+ #NIM command to start a remote mksysb install on NIM client
207
+ execute_cmd(command)
208
+
209
+ #Then, in order to actually start the install the HMC needs to netboot the LPAR
210
+ #Should that be called from here or just utilized separately from the HMC object?
211
+ #Maybe yeild to a block that should call the HMC LPAR netboot?
212
+ #Then upon returning to this function, we poll the NIM client for Cstate statuses
213
+ #until the build is finished?
214
+ network_name = get_lpar_network_name(client_lpar)
215
+ gateway = get_network_gateway(network_name)
216
+ subnetmask = get_network_subnetmask(network_name)
217
+ nim_ip = get_master_ip
218
+
219
+ yield(nim_ip,gateway,subnetmask)
220
+
221
+ #Implemented nicer looking progress message for BOS installs
222
+ print "Waiting for BOS install for #{client_lpar.name} to finish..."
223
+ print "." until bos_install_finished?(client_lpar)
224
+ puts "done"
225
+ end
226
+
227
+ #Used in populating the BOS Install status message
228
+ #Returns true of the specified lpar is done with is BOS build.
229
+ #Sleeps for 15 seconds and returns false if otherwise.
230
+ def bos_install_finished?(lpar)
231
+ if check_install_status(lpar).match(/ready for a NIM operation/i)
232
+ return true
233
+ else
234
+ sleep 15
235
+ return false
236
+ end
237
+ end
238
+
239
+ #Returns the filesystem location of the mksysb with the specified name
240
+ def get_mksysb_location(mksysb_name)
241
+ execute_cmd("lsnim -l #{mksysb_name} | awk '{if ($1 ~ /location/) print $3}'").chomp
242
+ end
243
+
244
+
245
+ #Returns the name of the SPOT extracted from the supplied mksysb
246
+ def get_spot(mksysb_name)
247
+ spot = execute_cmd("lsnim -l #{mksysb_name} | awk '{if ($1 ~ /extracted_spot/) print $3}'").chomp
248
+ if spot.empty?
249
+ return nil
250
+ else
251
+ return spot
252
+ end
253
+ end
254
+
255
+ #Extracts a SPOT from the mksysb image name specified.
256
+ #places the SPOT in a directory location adjacent to
257
+ #where the mksysb resides
258
+ #If a spot already exists for this mksysb, it's name is
259
+ #simply returned.
260
+ def extract_spot(mksysb_name)
261
+ #Find out if this mksysb exists on the NIM
262
+ if !list_objtype("mksysb").include?(mksysb_name)
263
+ #Mksysb not found - error out?
264
+ end
265
+
266
+ spot_name = mksysb_name+"_spot"
267
+ #Find out if a SPOT already exists for this mksysb
268
+ #if so, just return that name.
269
+ temp_name = get_spot(mksysb_name)
270
+ if !temp_name.nil?
271
+ return temp_name
272
+ end
273
+
274
+ #Get the location of this mksysb
275
+ mksysb_loc = get_mksysb_location(mksysb_name)
276
+
277
+ #Make sure the mksysb location is non-null
278
+ raise StandardError.new("Cannot locate where the image #{mksysb_name} exists on this NIM") if mksysb_loc.nil?
279
+
280
+ #Split the mksysb location on '/', pop the mksysb name and directory it resides
281
+ #in off of the array and push "spot" and the spot name onto the array to end up placing
282
+ #the SPOT in ../spot/spot_name
283
+ split_mksysb_path = mksysb_loc.split("/")
284
+ split_mksysb_path.pop
285
+ split_mksysb_path.pop
286
+ split_mksysb_path.push("spot")
287
+ split_mksysb_path.push(mksysb_name+"_spot")
288
+ spot_path = split_mksysb_path.join("/")
289
+
290
+ #Make a SPOT from this mksysb with the name <mksysb_name>_spot
291
+ execute_cmd("nim -o define -t spot -a server=master -a source=#{mksysb_name} -a location=#{spot_path} -a auto_expand=yes #{spot_name}")
292
+
293
+ #Return the name of the SPOT.
294
+ return spot_name
295
+ end
296
+
297
+ #Removes a SPOT object from a NIM based on the name
298
+ def remove_spot(spot_name)
299
+ execute_cmd("nim -Fo remove #{spot_name}")
300
+ end
301
+
302
+ #Creates a bosinst_data object for the client_lpar specified
303
+ def create_bid(client_lpar)
304
+ if bid_exists?(client_lpar)
305
+ #Force remove the BID and then continue to create a new one.
306
+ remove_bid(client_lpar)
307
+ end
308
+
309
+ #Use heredoc to populate the bosinst_data file in a multiline string
310
+ bid_contents = <<-EOS
311
+ # bosinst_data file created for #{client_lpar.name}
312
+
313
+ CONSOLE = Default
314
+ RECOVER_DEVICES = no
315
+ INSTALL_METHOD = overwrite
316
+ PROMPT = no
317
+ EXISTING_SYSTEM_OVERWRITE = any
318
+ ACCEPT_LICENSES = yes
319
+
320
+ locale:
321
+ BOSINST_LANG = en_US
322
+ CULTURAL_CONVENTION = en_US
323
+ MESSAGES = en_US
324
+ KEYBOARD = en_US
325
+ EOS
326
+
327
+ #Create bid contents file on NIM
328
+ execute_cmd "mkdir -p /darwin; echo '#{bid_contents}' > /darwin/#{client_lpar.name}_bid"
329
+
330
+
331
+ #Define the BID object
332
+ execute_cmd "nim -o define -t bosinst_data -a location=/darwin/#{client_lpar.name}_bid -a server=master #{client_lpar.name}_bid"
333
+
334
+ #Return the name of the BID created
335
+ return "#{client_lpar.name}_bid"
336
+ end
337
+
338
+ #Remove the NIM BID object for a client LPAR
339
+ def remove_bid(client_lpar)
340
+ execute_cmd "nim -F -o remove #{client_lpar.name}_bid"
341
+ #execute_cmd "nim -F -o remove #{client_lpar}_bid"
342
+ end
343
+
344
+ #Checks if BID object exists on NIM for the Client LPAR
345
+ def bid_exists?(client_lpar)
346
+ defined_bids = list_objtype("bosinst_data")
347
+ defined_bids.each do |obj_name|
348
+ #Iterate through array elements returned by list_objtype
349
+ #and check if any of them line up with the BID name for our LPAR
350
+ if (obj_name == "#{client_lpar.name}_bid")
351
+ #if (obj_name == "#{client_lpar}_bid")
352
+ return true
353
+ end
354
+ end
355
+ return false
356
+ end
357
+
358
+ #Find NIM interface settings for client LPAR
359
+ def get_lpar_network_name(client_lpar)
360
+ output = execute_cmd "lsnim -Z -a if1 #{client_lpar.name}"
361
+ nim_network=""
362
+ output.each_line do |line|
363
+ line.chomp!
364
+ if line.match(/^#{client_lpar.name}/)
365
+ network_args = line.split(/:/)
366
+ nim_network = network_args[1]
367
+ end
368
+ end
369
+ return nim_network
370
+ end
371
+
372
+ #Find Gateway IP for NIM network
373
+ def get_network_gateway(network_name)
374
+ output = execute_cmd "lsnim -Z -a net_addr -a snm -a routing #{network_name}"
375
+ output.each_line do |line|
376
+ line.chomp!
377
+ if line.match(/^#{network_name}/)
378
+ network_fields = line.split(/:/)
379
+ return network_fields[-1]
380
+ end
381
+ end
382
+ end
383
+
384
+ #Find Subnet mask for NIM network
385
+ def get_network_subnetmask(network_name)
386
+ output = execute_cmd "lsnim -Z -a net_addr -a snm -a routing #{network_name}"
387
+ output.each_line do |line|
388
+ line.chomp!
389
+ if line.match(/^#{network_name}/)
390
+ network_fields = line.split(/:/)
391
+ return network_fields[2]
392
+ end
393
+ end
394
+ end
395
+
396
+ #Add a NIM network object using the given name, network address,
397
+ #subnet mask, gateway
398
+ def add_network(network_name,network_addr,snm,gw)
399
+ #Ensure that network with this address doesn't already exist
400
+ raise StandardError.new("Network #{network_name} already exists on this NIM") if network_exists?(network_name,network_addr)
401
+
402
+ #Execute NIM command to create the network
403
+ #It is assumed that the network addess and the gateway are the same
404
+ execute_cmd("nim -o define -t ent -a net_addr=#{network_addr} -a snm=#{snm} #{network_name}")
405
+
406
+ #Add default route to the specified gateway
407
+ add_default_route(network_name,gw)
408
+ end
409
+
410
+ #Remove a NIM network given it's name and/or it's
411
+ #network address
412
+ def remove_network(network_name,network_addr=nil)
413
+ #Ensure that the network to remove is actually defined currently
414
+ raise StandardError.new("Network #{network_name} does not exist on this NIM to be removed") if !network_exists?(network_name,network_addr)
415
+
416
+ #Run command that removes this network from the NIM
417
+ execute_cmd("nim -Fo remove #{network_name}")
418
+ end
419
+
420
+ #Returns true if a network object exists on the NIM
421
+ #with either the specified name or network address.
422
+ #Returns false otherwise.
423
+ def network_exists?(network_name,network_addr=nil)
424
+ network_names = list_objtype("ent")
425
+ if network_names.include?(network_name)
426
+ return true
427
+ end
428
+ network_names.each do |net_name|
429
+ address = execute_cmd("lsnim -l #{net_name} | awk '{if ($1 ~ /net_addr/) print $3}'").chomp
430
+ if address == network_addr
431
+ return true
432
+ end
433
+ end
434
+
435
+ return false
436
+ end
437
+
438
+ def add_default_route(network_name,gateway)
439
+ #TODO: Add more robust creation/management of NIM network routes, if necessary
440
+ execute_cmd("nim -o change -a routing1='default #{gateway}' #{network_name}")
441
+ end
442
+ end
@@ -0,0 +1,10 @@
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
+ module Rbvppc
9
+ VERSION = "1.0.1"
10
+ end