Class: RSpecSystem::NodeSet::Vsphere
- Includes:
- Log
- Defined in:
- lib/rspec-system/node_set/vsphere.rb
Overview
A NodeSet implementation for VSphere
Constant Summary collapse
- ENV_TYPE =
'vsphere'
Instance Attribute Summary collapse
-
#vmconf ⇒ Object
readonly
Returns the value of attribute vmconf.
Attributes inherited from Base
#config, #custom_prefabs_path, #destroy, #nodes, #setname
NodeSet Methods collapse
-
#rcp(opts) ⇒ Boolean
Transfer files to a host in the NodeSet.
-
#run(opts) ⇒ Hash
Run a command on a host in the NodeSet.
-
#setup ⇒ void
Setup the NodeSet by starting all nodes.
-
#teardown ⇒ void
Shutdown the NodeSet by shutting down all nodes.
Instance Method Summary collapse
-
#initialize(setname, config, custom_prefabs_path, options) ⇒ Vsphere
constructor
Creates a new instance of RSpecSystem::NodeSet::Vsphere.
-
#load_fog_config(path = ENV['HOME'] + '/.fog') ⇒ Object
private
Retrieves fog configuration if it exists.
-
#with_connection(&block) ⇒ Object
private
This is a DSL based wrapper that provides connection and disconnection handling for the VSphere client API.
Methods included from Log
#bold, #color, #formatter, #log, #output
Methods inherited from Base
#default_node, #env_type, #randmac, #random_string, #ssh_exec!, #tmppath
Constructor Details
#initialize(setname, config, custom_prefabs_path, options) ⇒ Vsphere
Creates a new instance of RSpecSystem::NodeSet::Vsphere
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/rspec-system/node_set/vsphere.rb', line 23 def initialize(setname, config, custom_prefabs_path, ) super # Valid supported ENV variables = [:host, :user, :pass, :dest_dir, :template_dir, :rpool, :cluster, :ssh_keys, :datacenter, :node_timeout, :node_tries, :node_sleep, :ssh_timeout, :ssh_tries, :ssh_sleep, :connect_timeout, :connect_tries] # Devise defaults, use fog configuration from file system if it exists defaults = load_fog_config() defaults = defaults.merge({ :node_timeout => 1200, :node_tries => 10, :node_sleep => 30 + rand(60), :ssh_timeout => 60, :ssh_tries => 10, :ssh_sleep => 4, :connect_timeout => 60, :connect_tries => 10, }) # Traverse the ENV variables and load them into our config automatically @vmconf = defaults ENV.each do |k,v| next unless k =~/^RSPEC_VSPHERE_/ var = k.sub(/^RSPEC_VSPHERE_/, '').downcase.to_sym unless .include?(var) log.info("Ignoring unknown environment variable #{k}") next end @vmconf[var] = v end # Initialize node storage if not already RSpec.configuration.rspec_storage[:nodes] ||= {} end |
Instance Attribute Details
#vmconf ⇒ Object (readonly)
Returns the value of attribute vmconf.
15 16 17 |
# File 'lib/rspec-system/node_set/vsphere.rb', line 15 def vmconf @vmconf end |
Instance Method Details
#load_fog_config(path = ENV['HOME'] + '/.fog') ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Retrieves fog configuration if it exists
64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/rspec-system/node_set/vsphere.rb', line 64 def load_fog_config(path = ENV['HOME'] + '/.fog') creds = {} if File.exists?(path) fog = YAML.load_file(path) fog[:default] ||= {} creds = { :host => fog[:default][:vsphere_server], :user => fog[:default][:vsphere_username], :pass => fog[:default][:vsphere_password], } end return creds end |
#rcp(opts) ⇒ Boolean
This is damn ugly, because we ssh in as vagrant, we copy to a temp path then move it later. Its slow and brittle and we need a better solution. Its also very Linux-centrix in its use of temp dirs.
Transfer files to a host in the NodeSet.
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'lib/rspec-system/node_set/vsphere.rb', line 347 def rcp(opts) dest = opts[:d].name source = opts[:sp] dest_path = opts[:dp] # Do the copy and print out results for debugging ssh = RSpec.configuration.rspec_storage[:nodes][dest][:ssh] begin ssh.scp.upload! source.to_s, dest_path.to_s, :recursive => true rescue => e log.error("Error with scp of file #{source} to #{dest}:#{dest_path}") raise e end true end |
#run(opts) ⇒ Hash
Run a command on a host in the NodeSet.
332 333 334 335 336 337 338 |
# File 'lib/rspec-system/node_set/vsphere.rb', line 332 def run(opts) dest = opts[:n].name cmd = opts[:c] ssh = RSpec.configuration.rspec_storage[:nodes][dest][:ssh] ssh_exec!(ssh, cmd) end |
#setup ⇒ void
This method returns an undefined value.
Setup the NodeSet by starting all nodes.
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/rspec-system/node_set/vsphere.rb', line 128 def setup with_connection do |dc| # Traverse folders to find target folder for new vm's and template # folders. Automatically create the destination folder if it doesn't # exist. dest_folder = dc.vmFolder.traverse!(vmconf[:dest_dir], RbVmomi::VIM::Folder) raise "Destination folder #{vmconf[:dest_dir]} not found" if dest_folder.nil? template_folder = dc.vmFolder.traverse(vmconf[:template_dir], RbVmomi::VIM::Folder) raise "Template folder #{vmconf[:template_dir]} not found" if template_folder.nil? # Find resource pool and prepare clone spec for cloning further down. rp = dc.find_compute_resource(vmconf[:cluster]). resourcePool. traverse(vmconf[:rpool]) relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => rp) spec = RbVmomi::VIM.VirtualMachineCloneSpec( :location => relocateSpec, :powerOn => true, :template => false ) log.info "Launching VSphere instances one by one" nodes.each do |k,v| ##################### # Node launching step ##################### RSpec.configuration.rspec_storage[:nodes][k] ||= {} # Obtain the template name to use ps = v.provider_specifics['vsphere'] raise 'No provider specifics for this prefab' if ps.nil? template = ps['template'] raise "No template specified for this prefab" if template.nil? # Traverse to find template VM object vm = template_folder.find(template, RbVmomi::VIM::VirtualMachine) raise "Cannot template find template #{template} in folder #{vmconf[:template_dir]}" if vm.nil? # Create a random name for the new VM vm_name = "rspec-system-#{k}-#{random_string(10)}" RSpec.configuration.rspec_storage[:nodes][k][:vm] = vm_name log.info "Launching VSphere instance #{k} with template #{vmconf[:template_dir]}/#{template} as #{vmconf[:dest_dir]}/#{vm_name}" ipaddress = nil newvm = nil tries = 0 start_time = Time.now begin timeout(vmconf[:node_timeout]) do log.info "Cloning new VSphere vm #{vm_name} in folder #{vmconf[:dest_dir]}" vm.CloneVM_Task( :folder => dest_folder, :name => vm_name, :spec => spec ).wait_for_completion time1 = Time.now log.info "Cloning complete, took #{time1 - start_time} seconds" newvm = dest_folder.find(vm_name, RbVmomi::VIM::VirtualMachine) raise "Cannot find newly built virtual machine #{vm_name} in folder #{vmconf[:dest_dir]}" if newvm.nil? while(newvm.guest.guestState != 'running') do sleep 4 log.info "#{k}> Waiting for vm to run ..." end time2 = Time.now log.info "#{k}> Time in seconds for VM to run: #{time2 - time1}" while((ipaddress = newvm.guest.ipAddress) == nil) do sleep 4 log.info "#{k}> Waiting for ip address ..." end time3 = Time.now log.info "#{k}> Time in seconds waiting for IP: #{time3 - time2}" end RSpec.configuration.rspec_storage[:nodes][k][:ipaddress] = ipaddress rescue Timeout::Error, SystemCallError => e tries += 1 log.error("VM launch attempt #{tries} failed with: " + e.) if tries < vmconf[:node_tries] log.info("Destroying any VM's, sleeping then trying again ...") begin newvm.PowerOffVM_Task.wait_for_completion rescue RbVmomi::Fault => e log.error "Fault attempting to power off node #{k}, #{e.message}" ensure begin newvm.Destroy_Task.wait_for_completion rescue RbVmomi::Fault => e log.error "Fault attempting to destroy node #{k}, #{e.message}" end end sleep_time = vmconf[:node_sleep] log.info("Sleeping #{sleep_time} seconds before trying again ...") sleep sleep_time retry else log.error("Failed to create VM and already retried #{tries} times, throwing exception") raise e end end time2 = Time.now log.info "#{k}> Took #{time2 - start_time} seconds to boot instance" ##################### # SSH Step ##################### tries = 0 begin timeout(vmconf[:ssh_timeout]) do output << bold(color("localhost$", :green)) << " ssh #{k}\n" chan = Net::SSH.start(ipaddress, 'root', { :keys => vmconf[:ssh_keys].split(":"), }) RSpec.configuration.rspec_storage[:nodes][k][:ssh] = chan end rescue Timeout::Error, SystemCallError => e tries += 1 output << e. << "\n" if tries < vmconf[:ssh_tries] log.info("Sleeping for #{vmconf[:ssh_sleep]} seconds then trying again ...") sleep vmconf[:ssh_sleep] retry else log.error("Inability to connect to host, already tried #{tries} times, throwing exception") raise e end end time3 = Time.now log.info "#{k}> Took #{time3 - start_time} seconds for instance to be ready" ###################### # Do initial box setup ###################### hosts = "127.0.0.1 localhost localhost.localdomain\n\#{ipaddress} \#{k}\n EOS\n shell(:n => k, :c => \"echo '\#{hosts}' > /etc/hosts\")\n shell(:n => k, :c => \"hostname \#{k}\")\n end\n end\n\n nil\nend\n" |
#teardown ⇒ void
This method returns an undefined value.
Shutdown the NodeSet by shutting down all nodes.
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/rspec-system/node_set/vsphere.rb', line 279 def teardown with_connection do |dc| nodes.each do |k,v| storage = RSpec.configuration.rspec_storage[:nodes][k] if storage.nil? log.info "No entry for node #{k}, no teardown necessary" next end ssh = storage[:ssh] unless ssh.nil? or ssh.closed? ssh.close end if destroy log.info "Destroying instance #{k}" vm_name = storage[:vm] if vm_name == nil log.error "No vm object for #{k}" next end # Traverse folders to find target folder for new vm's vm_folder = dc.vmFolder.traverse(vmconf[:dest_dir], RbVmomi::VIM::Folder) raise "VirtualMachine folder #{vmconf[:dest_dir]} not found" if vm_folder.nil? vm = vm_folder.find(vm_name, RbVmomi::VIM::VirtualMachine) raise "VirtualMachine #{vm_name} not found in #{vmconf[:dest_dir]}" if vm.nil? begin vm.PowerOffVM_Task.wait_for_completion rescue RbVmomi::Fault => e log.error "Fault attempting to power off node #{k}, #{e.message}" ensure begin vm.Destroy_Task.wait_for_completion rescue RbVmomi::Fault => e log.error "Fault attempting to destroy node #{k}, #{e.message}" end end else next end end end nil end |
#with_connection(&block) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
This is a DSL based wrapper that provides connection and disconnection handling for the VSphere client API.
The connection handling automatically retries upon failure.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/rspec-system/node_set/vsphere.rb', line 85 def with_connection(&block) vim = nil dc = nil tries = 0 begin timeout(vmconf[:connect_timeout]) do vim = RbVmomi::VIM.connect( :host => vmconf[:host], :user => vmconf[:user], :password => vmconf[:pass], :ssl => true, :insecure => true ) end rescue => e tries += 1 log.error("Failure to connect (attempt #{tries})") if tries < vmconf[:connect_tries] log.info("Retry connection") retry end log.info("Failed to connect after #{tries} attempts, throwing exception") raise e end begin dc = vim.serviceInstance.find_datacenter(vmconf[:datacenter]) rescue => e log.error("Unable to retrieve datacenter #{vmconf[:datacenter]}") raise e end block.call(dc) vim.close end |