Class: Ironfan::Provider::Vsphere::Machine

Inherits:
IaasProvider::Machine show all
Defined in:
lib/ironfan/headers.rb,
lib/ironfan/provider/vsphere/machine.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from IaasProvider::Machine

cloud_init_user_data

Class Method Details

.create!(computer) ⇒ Object


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
# File 'lib/ironfan/provider/vsphere/machine.rb', line 163

def self.create!(computer)
  return if computer.machine? and computer.machine.created?
  Ironfan.step(computer.name,"creating cloud machine", :green)

  errors = lint(computer)
  if errors.present? then raise ArgumentError, "Failed validation: #{errors.inspect}" ; end

  # TODO: Pass launch_desc to a single function and let it do the rest... like fog does
  launch_desc = launch_description(computer)
  cpus          = launch_desc[:cpus]
  datacenter    = launch_desc[:datacenter]
  memory        = launch_desc[:memory]
  template      = launch_desc[:template]
  user_data     = launch_desc[:user_data]
  virtual_disks = launch_desc[:virtual_disks]
  network       = launch_desc[:network]
  ip_settings   = launch_desc[:ip_settings]

  datacenter    = Vsphere.find_dc(launch_desc[:datacenter])
  raise "Couldn't find #{launch_desc[:datacenter]} datacenter" unless datacenter
  cluster = Vsphere.find_in_folder(datacenter.hostFolder, RbVmomi::VIM::ClusterComputeResource, launch_desc[:cluster])
  raise "Couldn't find #{launch_desc[:cluster]} cluster in #{datacenter}" unless cluster
  datastore     = Vsphere.find_ds(datacenter, launch_desc[:datastore]) # FIXME: add in round robin choosing?
  raise "Couldn't find #{launch_desc[:datastore]} datastore in #{datacenter}" unless datastore
  src_folder    = datacenter.vmFolder
  src_vm        = Vsphere.find_in_folder(src_folder, RbVmomi::VIM::VirtualMachine, template)
  raise "Couldn't find template #{template} in #{datacenter}:#{cluster}" unless src_vm

  Ironfan.safely do
    clone_spec = generate_clone_spec(src_vm.config, datacenter, cpus, memory, datastore, virtual_disks, network, cluster)
    clone_spec.customization = ip_settings(ip_settings, computer.name) if ip_settings[:ip]

    vsphere_server = src_vm.CloneVM_Task(:folder => src_vm.parent, :name => computer.name, :spec => clone_spec)

    state = vsphere_server.info.state # RbVmomi wait_for_completion stops world... 
    while (state != 'error') and (state != 'success')
      sleep 2
      state = vsphere_server.info.state
    end

    # Will break if the VM isn't finished building.  
    new_vm = Vsphere.find_in_folder(src_folder, RbVmomi::VIM::VirtualMachine, computer.name)
    machine = Machine.new(:adaptee => new_vm)
    computer.machine = machine
    remember machine, :id => machine.name

    Ironfan.step(computer.name,"pushing keypair", :green)
    public_key = Vsphere::Keypair.public_key(computer)    # TODO - This probably belongs somewhere else.  This is how we'll enject the pubkey as well as any user information we may want
    # This data is not persistent across reboots...

    extraConfig = [{:key => 'guestinfo.pubkey', :value => public_key}, {:key => "guestinfo.user_data", :value => user_data}]
    machine.ReconfigVM_Task(:spec => RbVmomi::VIM::VirtualMachineConfigSpec(:extraConfig => extraConfig)).wait_for_completion

    # FIXME: This is extremelty fragile right now
    if ip_settings[:ip]
      Ironfan.step(computer.name,"waiting for ip customizations to complete", :green)
      while machine.guest.ipAddress != ip_settings[:ip]
        sleep 2
      end
    end

  end
end

.destroy!(computer) ⇒ Object


247
248
249
250
251
# File 'lib/ironfan/provider/vsphere/machine.rb', line 247

def self.destroy!(computer)
  return unless computer.machine?
  forget computer.machine.name
  computer.machine.destroy
end

.expected_ids(computer) ⇒ Object


13
# File 'lib/ironfan/provider/vsphere/machine.rb', line 13

def self.expected_ids(computer) [computer.server.full_name];   end

.generate_clone_spec(src_config, dc, cpus, memory, datastore, virtual_disks, network, cluster) ⇒ Object


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
# File 'lib/ironfan/provider/vsphere/machine.rb', line 107

def self.generate_clone_spec(src_config, dc, cpus, memory, datastore, virtual_disks, network, cluster)
  # TODO : A lot of this should be moved to utilities in providers/vsphere.rb
  rspec = Vsphere.get_rspec(dc, cluster)
  rspec.datastore = datastore

  clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => rspec, :template => false, :powerOn => true)
  clone_spec.config = RbVmomi::VIM.VirtualMachineConfigSpec(:deviceChange => Array.new, :extraConfig => nil)

  network = Vsphere.find_network(network, dc)
  card = src_config.hardware.device.find { |d| d.deviceInfo.label == "Network adapter 1" }
  begin
    switch_port = RbVmomi::VIM.DistributedVirtualSwitchPortConnection(
      :switchUuid => network.config.distributedVirtualSwitch.uuid,
      :portgroupKey => network.key
    )
    card.backing.port = switch_port
  rescue
    card.backing.deviceName = network.name
  end

 	  network_spec = RbVmomi::VIM.VirtualDeviceConfigSpec(:device => card, :operation => "edit")
  clone_spec.config.deviceChange.push network_spec

  virtual_disks.each_with_index do |vd, idx|
    size = vd[:size].to_i
    label = vd[:label].to_i
    key = 2001 + idx # key 2000 -> SCSI0, 2001 -> SCSI1...
    filename = vd[:datastore] || datastore.name

    disk = RbVmomi::VIM.VirtualDisk(
      :key => key,
      :capacityInKB => size * 1024 * 1024,
      :controllerKey => 1000, # SCSI controller
      :unitNumber => idx + 1,
      :backing => RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo(
        :fileName => "[#{filename}]",
        :diskMode => :persistent,
        :thinProvisioned => true,
        :datastore => Vsphere.find_ds(dc, vd[:datastore]) || datastore
      ),
      :deviceInfo => RbVmomi::VIM.Description(
        :label => label, 
        :summary => "%sGB" %[size]
      ),
    )  

    disk_spec = {:operation => :add, :fileOperation => :create, :device => disk }
    clone_spec.config.deviceChange.push disk_spec
  end
  
  clone_spec.config.numCPUs  = Integer(cpus)
  clone_spec.config.memoryMB = Integer(memory) * 1024
 
  clone_spec
end

.ip_settings(settings, hostname) ⇒ Object


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/ironfan/provider/vsphere/machine.rb', line 85

def self.ip_settings(settings, hostname)
  # FIXME: A lot of this is required if using a static IP
  # Heavily referenced Serengeti's FOG here 

  ip_settings = RbVmomi::VIM::CustomizationIPSettings.new(:ip => 
    RbVmomi::VIM::CustomizationFixedIp(:ipAddress => settings[:ip]), :gateway => settings[:gateway], :subnetMask => settings[:subnet]) if settings[:ip]
  ip_settings ||= RbVmomi::VIM::CustomizationIPSettings.new("ip" => RbVmomi::VIM::CustomizationDhcpIpGenerator.new())
  ip_settings.dnsDomain = settings[:domain]
  global_ip_settings = RbVmomi::VIM.CustomizationGlobalIPSettings
  global_ip_settings.dnsServerList = settings[:dnsServers]
  global_ip_settings.dnsSuffixList = [settings[:domain]]
  hostname = RbVmomi::VIM::CustomizationFixedName.new(:name => hostname)
  linux_prep = RbVmomi::VIM::CustomizationLinuxPrep.new( :domain => settings[:domain], :hostName => hostname )
  adapter_mapping = [RbVmomi::VIM::CustomizationAdapterMapping.new("adapter" => ip_settings)]
  spec = RbVmomi::VIM::CustomizationSpec.new( :identity => linux_prep,
                                              :globalIPSettings => global_ip_settings,
                                              :nicSettingMap => adapter_mapping )

  return spec

end

.launch_description(computer) ⇒ Object


253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/ironfan/provider/vsphere/machine.rb', line 253

def self.launch_description(computer)
  cloud = computer.server.cloud(:vsphere)
  user_data_hsh =               {
    :chef_server 	=>      Chef::Config[:chef_server_url],
    :client_key 	=>      computer.private_key,
    :cluster_name 	=>      computer.server.cluster_name,
    :facet_index 	=>      computer.server.index,
    :facet_name 	=>      computer.server.facet_name,
    :node_name	 	=>	computer.name,
    :organization 	=>	Chef::Config[:organization],
  }

  ip_settings = {
	    :dnsServers 	=> 	cloud.dns_servers,
	    :domain 		=>	cloud.domain,
    :gateway            =>      cloud.gateway,
    :ip 		=>	cloud.ip,
    :subnet		=> 	cloud.subnet,
  }

  description = {
    :cpus		=> 	cloud.cpus,
    :cluster		=>	cloud.cluster,
    :datacenter		=>	cloud.datacenter,
    :datastore		=>	cloud.datastore, 
    :memory		=>	cloud.memory,
    :network		=>	cloud.network, 
    :template		=>	cloud.template,
    :user_data		=>	JSON.pretty_generate(user_data_hsh),
    :virtual_disks	=>	cloud.virtual_disks,
    :ip_settings	=>	ip_settings
  }
  description
end

.lint(computer) ⇒ Object


289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/ironfan/provider/vsphere/machine.rb', line 289

def self.lint(computer)
  cloud = computer.server.cloud(:vsphere)
  info  = [computer.name, cloud.inspect]
  errors = {}
  server_errors = computer.server.lint
  errors["Unhappy Server"] = server_errors if server_errors.present?
  errors["Datacenter"] = info if cloud.datacenter.blank?
  errors["Template"] = info if cloud.template.blank?
  errors["Datastore"] = info if cloud.datastore.blank?
  errors['Missing client']      = info            unless computer.client?
  errors['Missing private_key'] = computer.client unless computer.private_key  #

  errors
end

.load!(cluster = nil) ⇒ Object

Discovery


230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/ironfan/provider/vsphere/machine.rb', line 230

def self.load!(cluster=nil)
  # FIXME:  Fix this one day when we have multiple "datacenters".
  cloud = cluster.servers.values[0].cloud(:vsphere)
  vm_folder = Vsphere.find_dc(cloud.datacenter).vmFolder
  Vsphere.find_all_in_folder(vm_folder, RbVmomi::VIM::VirtualMachine).each do |fs|
    machine = new(:adaptee => fs)
    if recall? machine.name
      machine.bogus <<                 :duplicate_machines
      recall(machine.name).bogus <<    :duplicate_machines
      remember machine, :append_id => "duplicate:#{machine.config.uuid}"
    else # never seen it
      remember machine
    end
    Chef::Log.debug("Loaded #{machine}")
  end
end

.multiple?Boolean

Returns:

  • (Boolean)

11
# File 'lib/ironfan/provider/vsphere/machine.rb', line 11

def self.multiple?()    false;  end

.resource_typeObject


12
# File 'lib/ironfan/provider/vsphere/machine.rb', line 12

def self.resource_type()        :machine;   end

.shared?Boolean

Returns:

  • (Boolean)

10
# File 'lib/ironfan/provider/vsphere/machine.rb', line 10

def self.shared?()      false;  end

.validate_resources!(computers) ⇒ Object


329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/ironfan/provider/vsphere/machine.rb', line 329

def self.validate_resources!(computers)
  recall.each_value do |machine|
    next unless machine.users.empty? and machine.name
    if machine.name.match("^#{computers.cluster.name}-")
      machine.bogus << :unexpected_machine
    end
    next unless machine.bogus?
    fake           = Ironfan::Broker::Computer.new
    fake[:machine] = machine
    fake.name      = machine.name
    machine.users << fake
    computers     << fake
  end
end

Instance Method Details

#created?Boolean

Returns:

  • (Boolean)

53
54
55
# File 'lib/ironfan/provider/vsphere/machine.rb', line 53

def created?
  ["poweredOn", "poweredOff"].include? adaptee.runtime.powerState
end

#destroyObject


47
48
49
50
51
# File 'lib/ironfan/provider/vsphere/machine.rb', line 47

def destroy
  adaptee.PowerOffVM_Task.wait_for_completion unless adaptee.runtime.powerState == "poweredOff"
  adaptee.Destroy_Task.wait_for_completion
  state = "destroyed"
end

#dns_nameObject


26
27
28
29
30
31
# File 'lib/ironfan/provider/vsphere/machine.rb', line 26

def dns_name
  host = adaptee.guest.hostName 
  domain = adaptee.guest.domainName
  return host unless domain
  return "%s.%s" %[host, domain]
end

#keypairObject


19
20
# File 'lib/ironfan/provider/vsphere/machine.rb', line 19

def keypair
end

#nameObject


15
16
17
# File 'lib/ironfan/provider/vsphere/machine.rb', line 15

def name
   return adaptee.config.name
end

#perform_after_launch_tasks?Boolean

Returns:

  • (Boolean)

81
82
83
# File 'lib/ironfan/provider/vsphere/machine.rb', line 81

def perform_after_launch_tasks?
  true
end

#private_ip_addressObject


38
39
40
# File 'lib/ironfan/provider/vsphere/machine.rb', line 38

def private_ip_address
  return adaptee.guest.ipAddress
end

#public_hostnameObject


42
43
44
45
# File 'lib/ironfan/provider/vsphere/machine.rb', line 42

def public_hostname  # FIXME

  return nil
end

#public_ip_addressObject


33
34
35
36
# File 'lib/ironfan/provider/vsphere/machine.rb', line 33

def public_ip_address  # FIXME

  return adaptee.guest.ipAddress
end

#running?Boolean

Returns:

  • (Boolean)

61
62
63
64
# File 'lib/ironfan/provider/vsphere/machine.rb', line 61

def running?
  adaptee.runtime.powerState == "poweredOn"
  state = "poweredOn"
end

#ssh_keyObject


325
326
327
# File 'lib/ironfan/provider/vsphere/machine.rb', line 325

def ssh_key
  keypair = cloud.keypair || computer.server.cluster_name
end

#startObject


71
72
73
# File 'lib/ironfan/provider/vsphere/machine.rb', line 71

def start
  adaptee.PowerOnVM_Task.wait_for_completion
end

#stopObject


75
76
77
78
79
# File 'lib/ironfan/provider/vsphere/machine.rb', line 75

def stop  # TODO: Gracefully shutdown guest using adatee.ShutdownGuest ? 
  # There is no "wait_for_completion" with this however...

  adaptee.PowerOffVM_Task.wait_for_completion
end

#stopped?Boolean

Returns:

  • (Boolean)

66
67
68
69
# File 'lib/ironfan/provider/vsphere/machine.rb', line 66

def stopped?
  adaptee.runtime.powerState == "poweredOff"
  state = "poweredOff"
end

#to_display(style, values = {}) ⇒ Object


304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/ironfan/provider/vsphere/machine.rb', line 304

def to_display(style,values={})
  # style == :minimal
  values["State"] =            runtime.powerState rescue "Terminated"
  values["MachineID"] =        config.uuid rescue ""#          values["Public IP"] =         public_ip_address

  values["Private IP"] =        guest.ipAddress rescue ""#          values["Created On"] =        created_at.to_date

  return values if style == :minimal

  # style == :default
#          values["Flavor"] =            flavor_id
#          values["AZ"] =                availability_zone
  return values if style == :default

  # style == :expanded
#          values["Image"] =             image_id
  values["Virtual Disks"] =      disks.map { |d| d.backing.fileName }.join(', ')#          values["SSH Key"] =           key_name

  values
end

#vpc_idObject


22
23
24
# File 'lib/ironfan/provider/vsphere/machine.rb', line 22

def vpc_id
  return true
end

#wait_forObject


57
58
59
# File 'lib/ironfan/provider/vsphere/machine.rb', line 57

def wait_for
  return true
end