Class: Deltacloud::Drivers::Vsphere::VsphereDriver

Inherits:
BaseDriver
  • Object
show all
Includes:
Deltacloud::Drivers::VSphere::Helper, VSphere::FileManager
Defined in:
lib/deltacloud/drivers/vsphere/vsphere_driver.rb

Constant Summary

Constants inherited from BaseDriver

BaseDriver::MEMBER_SHOW_METHODS, BaseDriver::STATE_MACHINE_OPTS

Constants included from Deltacloud

API_VERSION, CIMI_API_VERSION

Instance Method Summary collapse

Methods included from Deltacloud::Drivers::VSphere::Helper

#extract_architecture, #find_datastore, #find_resource_pool, #find_vm, #list_datastores, #list_virtual_machines, #load_serialized_instance, #map_task_to_instance, #stored_tasks

Methods inherited from BaseDriver

#address, #api_provider, #blob, #bucket, #catched_exceptions_list, #configured_providers, constraints, define_hardware_profile, define_instance_states, driver_name, feature, features, #filter_hardware_profiles, #filter_on, #find_hardware_profile, #firewall, #hardware_profile, hardware_profiles, #has_capability?, #has_feature?, has_feature?, #image, #instance, #instance_actions_for, instance_state_machine, #instance_state_machine, #key, #name, #realm, #storage_snapshot, #storage_volume, #supported_collections, #valid_credentials?

Methods included from Deltacloud

[], config, configure, connect, database, default_frontend, drivers, enabled_frontends, frontend_required?, frontends, generate_routes, initialize_database, need_database?, new, require_frontend!

Methods included from CollectionMethods

#collection_exists?, #collection_names, #collections

Methods included from Exceptions

exception_from_status, exceptions, included, logger, #safely

Instance Method Details

#create_image(credentials, opts = {}) ⇒ Object



116
117
118
119
120
121
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 116

def create_image(credentials, opts={})
  safely do
    find_vm(credentials, opts[:id])[:instance].MarkAsTemplate
  end
  image(credentials, :id => opts[:id])
end

#create_instance(credentials, image_id, opts = {}) ⇒ Object



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
275
276
277
278
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
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 212

def create_instance(credentials, image_id, opts={})
  safely do
    if opts[:hwp_cpu]
      raise "Invalid CPU value. Must be in integer format" unless valid_cpu_value?(opts[:hwp_cpu])
    end
    vm = find_vm(credentials, image_id)
    raise "ERROR: Could not find the image in given datacenter" unless vm[:instance]
    # New instance need valid resource pool and datastore to be placed.
    # For this reason, realm_id **needs** to be set.
    if opts and opts[:realm_id]
      resourcePool = find_resource_pool(credentials, opts[:realm_id])
      datastore = find_datastore(credentials, opts[:realm_id])
    else
      resourcePool = find_resource_pool(credentials, vm[:datastore])
      datastore = find_datastore(credentials, vm[:datastore])
    end
    relocate = { :pool => resourcePool, :datastore => datastore }
    relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(relocate)
    # Set extra configuration for VM, like template_id
    raise "ERROR: Memory must be multiple of 4" unless valid_memory_value?(opts[:hwp_memory])
    machine_config = {
      :memoryMB => opts[:hwp_memory],
      :numCPUs => opts[:hwp_cpu],
      :extraConfig => [
        { :key => 'template_id', :value => image_id },
      ]
    }
    if (opts[:user_data] and not opts[:user_data].empty?) and (opts[:user_iso] and not opts[:user_iso].empty?)
      raise "ERROR: You cannot use user_data and user_iso features together"
    end
    # If user wants to inject data into instance he need to submit a Base64
    # encoded gzipped ISO image.
    # This image will be uplaoded to the Datastore given in 'realm_id'
    # parameter and them attached to instance.
    if opts[:user_data] and not opts[:user_data].empty?
      device = vm[:instance].config.hardware.device.select { |hw| hw.class == RbVmomi::VIM::VirtualCdrom }.first
      if device
        VSphere::FileManager::user_data!(datastore, opts[:user_data], "#{opts[:name]}.iso")
        machine_config[:extraConfig] << {
          :key => 'user_data_file', :value => "#{opts[:name]}.iso"
        }
        device.connectable.startConnected = true
        device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] "+
                                                                 "/#{VSphere::FileManager::DIRECTORY_PATH}/#{opts[:name]}.iso")
        machine_config.merge!({
          :deviceChange => [{
            :operation => :edit,
            :device => device
          }]
        })
      else
        raise "Failed to inject data to device because there is no CD-ROM drive defined in given template"
      end
    end
    if opts[:user_iso] and not opts[:user_iso].empty?
      device = vm[:instance].config.hardware.device.select { |hw| hw.class == RbVmomi::VIM::VirtualCdrom }.first
      if device
        VSphere::FileManager::store_iso!(datastore, opts[:user_iso], "#{opts[:name]}.iso")
        machine_config[:extraConfig] << {
          :key => 'user_iso_file', :value => "#{opts[:name]}.iso"
        }
        device.connectable.startConnected = true
        device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] "+
                                                                 "/#{VSphere::FileManager::DIRECTORY_PATH}/#{opts[:name]}.iso")
        machine_config.merge!({
          :deviceChange => [{
            :operation => :edit,
            :device => device
          }]
        })
      else
        raise "Failed to inject data to device because there is no CD-ROM drive defined in given template"
      end
    end
    spec = RbVmomi::VIM.VirtualMachineCloneSpec(
      :location => relocateSpec,
      :powerOn => true,
      :template => false,
      :config => RbVmomi::VIM.VirtualMachineConfigSpec(machine_config)
    )
    instance_profile = InstanceProfile::new('default', :hwp_memory => opts[:hwp_memory], :hwp_cpu => opts[:hwp_cpu])
    task = vm[:instance].CloneVM_Task(:folder => vm[:instance].parent, :name => opts[:name] || "inst#{Time.now.to_i}", :spec => spec)
    new_instance = Instance::new(
      :id => opts[:name],
      :name => opts[:name],
      :description => opts[:name],
      :owner_id => credentials.user,
      :image_id => opts[:image_id],
      :realm_id => opts[:realm_id] || vm[:datastore],
      :state => 'PENDING',
      :public_addresses => [],
      :private_addresses => [],
      :instance_profile => instance_profile,
      :actions => instance_actions_for( 'PENDING' ),
      :create_image => false
    )
    # This will 'serialize' instance to YAML file and map it to the task.
    # Ussualy it takes like 2-3 minutes (depending on storage size) to
    # complete instance cloning process.
    map_task_to_instance(datastore, task.info.key, new_instance)
  end
end

#destroy_instance(credentials, instance_id) ⇒ Object Also known as: destroy_image

Destroy an instance, given its id. Note that this will destroy all instance data.

If there is user-data dile asocciated with instance, remove this file as well.



335
336
337
338
339
340
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 335

def destroy_instance(credentials, instance_id)
  vm = find_vm(credentials, instance_id)
  user_file = vm[:instance].config[:extraConfig].select { |k| k.key == 'user_iso_file' }.first
  VSphere::FileManager::delete_iso!(vm[:instance].send(:datastore).first, user_file.value) if user_file
  vm[:instance].Destroy_Task.wait_for_completion
end

#hardware_profiles(credentials, opts = {}) ⇒ Object

There is just one hardware profile where memory is measured using maximum memory available on ESX for virtual machines and CPU using maximum free CPU cores in ESX.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 41

def hardware_profiles(credentials, opts={})
  vsphere = new_client(credentials)
  safely do
    service = vsphere.serviceInstance.content
    max_memory, max_cpu_cores = [], []
    #
    # Note: Memory is being hardcoded now to range 512MB to 2GB
    #       JIRA: DTACLOUD-123
    #
    service.rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
      # max_memory << dc.hostFolder.childEntity.first.summary.effectiveMemory
      max_cpu_cores << dc.hostFolder.childEntity.first.summary.numCpuCores
    end
    [Deltacloud::HardwareProfile::new('default') do
      cpu (1..max_cpu_cores.min)
      memory (512..2048)
      architecture ['x86_64', 'i386']
    end]
  end
end

#images(credentials, opts = {}) ⇒ Object

Images are virtual machines with ‘template’ flag set to be true. Thus we’re getting them using find_vm and list_virtual_machines



76
77
78
79
80
81
82
83
84
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
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 76

def images(credentials, opts={})
  img_arr = []
  profiles = hardware_profiles(credentials)
  # Skip traversing through all instances in all datacenters when ID
  # attribute is set
  safely do
    if opts[:id]
      template_vms = [ find_vm(credentials, opts[:id]) ].select { |vm| vm[:instance] }.compact
    else
      template_vms = list_virtual_machines(credentials).select { |vm| vm[:instance] && vm[:instance].summary.config[:template] }
    end
    img_arr = template_vms.collect do |image_hash|
      image = image_hash[:instance]
      config = image.summary.config
      # Preload all properties to save multiple SOAP calls to vSphere
      properties = {
        :name => config[:name],
        :full_name => config[:guestFullName]
      }
      image_state = convert_state(:image, image.summary.runtime[:powerState])
      # This will determine image architecture using image description.
      # Ussualy description include '64-bit' or '32-bit'. In case you used
      # some weird template/OS it will fallback to 64 bit
      image_architecture = extract_architecture(properties[:full_name]) || 'x86_64'
      Image.new(
        :id => properties[:name],
        :name => properties[:name],
        :architecture => image_architecture,
        :owner_id => credentials.user,
        :description => properties[:full_name],
        :state => image_state,
        :creation_time => image.storage[:timestamp],
        :hardware_profiles => profiles
      )
    end
  end
  img_arr = filter_on( img_arr, :architecture, opts )
  img_arr.sort_by{|e| [e.owner_id, e.name]}
end

#instances(credentials, opts = {}) ⇒ Object

List all running instances, across all datacenters. DeltaCloud API does not yet support filtering instances by realm.



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
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 141

def instances(credentials, opts={})
  inst_arr, machine_vms, pending_vms = [], [], []
  safely do
    # Using find_vm is a way faster than listing all virtual machines
    if opts[:id]
      list_vms = [ find_vm(credentials, opts[:id]) ].compact
    else
      list_vms = list_virtual_machines(credentials)
    end
    # Split machines to the 'real' one and PENDING one.
    machine_vms = list_vms.select { |vm| vm[:instance] && !vm[:instance].summary.config[:template] }
    pending_vms = list_vms.select { |vm| vm[:stored_instance] }.collect { |vm| vm[:stored_instance]}
  end
  safely do
    inst_arr = machine_vms.collect do |vm_hash|
      # Since all calls to vm are threaten as SOAP calls, reduce them using
      # local variable.
      vm, realm_id = vm_hash[:instance], vm_hash[:datastore]
      config = vm.summary.config
      next if not config
      # Template (image_id) is beeing stored as 'extraConfig' parameter in
      # instance.
      template_id = vm.config[:extraConfig].select { |k| k.key == 'template_id' }
      template_id = template_id.first.value unless template_id.empty?

      properties = {
        :memory => config[:memorySizeMB],
        :cpus => config[:numCpu],
        :storage => vm.summary.storage[:unshared],
        :name => config[:name],
        :full_name => config[:guestFullName],
      }
      instance_state = convert_state(:instance, vm.summary.runtime[:powerState])
      instance_profile = InstanceProfile::new('default',
                                              :hwp_cpu => properties[:cpus],
                                              :hwp_memory => properties[:memory],
                                              :hwp_storage => properties[:storage])
      # We're getting IP address from 'vmware guest tools'.
      # If guest tools are not installed, we return list of MAC addresses
      # assigned to this instance.
      public_addresses = vm.macs.values.collect { |mac_address| InstanceAddress.new(mac_address, :type => :mac) }
      if !vm.guest[:net].empty? and ip_address = vm.guest[:net].first[:ipAddress].first
        public_addresses += [InstanceAddress.new(ip_address)]
      end
      Instance.new(
        :id => properties[:name],
        :name => properties[:name],
        :owner_id => credentials.user,
        :image_id => template_id.empty? ? nil : template_id,
        :description => properties[:full_name],
        :realm_id => realm_id,
        :state => instance_state,
        :public_addresses => public_addresses,
        :private_addresses => [],
        :instance_profile => instance_profile,
        :actions => instance_actions_for( instance_state ),
        :launch_time => vm.runtime.props[:bootTime],
        :create_image => true
      )
    end
  end
  inst_arr.compact!
  # Append 'temporary' instances to real instances.
  # 'Temporary' or 'stored' instance are used to speed up instance creation
  # process by serializing instances to datastore and map instance to task.
  #
  inst_arr += pending_vms
  filter_on( inst_arr, :state, opts )
end

#realms(credentials, opts = {}) ⇒ Object

List all datacenters managed by the vSphere or vCenter entrypoint.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 124

def realms(credentials, opts={})
  vsphere = new_client(credentials)
  safely do
    if opts and opts[:id]
      datastore = find_datastore(credentials, opts[:id])
      [convert_realm(datastore)]
    else
      rootFolder = vsphere.serviceInstance.content.rootFolder
      rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
        dc.datastoreFolder.childEntity.collect { |ds| convert_realm(ds) }
      end.flatten
    end
  end
end

#reboot_instance(credentials, id) ⇒ Object

Reboot an instance, given its id.



316
317
318
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 316

def reboot_instance(credentials, id)
  find_vm(credentials, id)[:instance].RebootGuest
end

#start_instance(credentials, id) ⇒ Object

Start an instance, given its id.



321
322
323
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 321

def start_instance(credentials, id)
  find_vm(credentials, id)[:instance].PowerOnVM_Task
end

#stop_instance(credentials, id) ⇒ Object

Stop an instance, given its id.



326
327
328
# File 'lib/deltacloud/drivers/vsphere/vsphere_driver.rb', line 326

def stop_instance(credentials, id)
  find_vm(credentials, id)[:instance].PowerOffVM_Task
end