From ca58ad62714d50c89e6958af692f028e8cf80990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Ne=C4=8Das?= Date: Tue, 26 May 2015 17:12:11 +0200 Subject: [PATCH] Vsphere - customizing interfaces and disks when cloning from template After this patch, the clone_vm accepts interfaces and volumes options, taking interface resp. volume objects to modify the ones defined in the template. This allows to customize all the interfaces (not just changing the network_label on one of them), as well as increasing the size of the volumes and adding or removing them. --- .../requests/compute/list_vm_interfaces.rb | 4 +- lib/fog/vsphere/requests/compute/vm_clone.rb | 131 ++++++++++++++---- 2 files changed, 103 insertions(+), 32 deletions(-) diff --git a/lib/fog/vsphere/requests/compute/list_vm_interfaces.rb b/lib/fog/vsphere/requests/compute/list_vm_interfaces.rb index 420b51a0b1..3b1e45dd5e 100644 --- a/lib/fog/vsphere/requests/compute/list_vm_interfaces.rb +++ b/lib/fog/vsphere/requests/compute/list_vm_interfaces.rb @@ -28,8 +28,8 @@ class Real #macAddress: "00:50:56:a9:00:28", #unitNumber: 7, # - def list_vm_interfaces(vm_id) - get_vm_ref(vm_id).config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).map do |nic| + def list_vm_interfaces(vm_id, datacenter = nil) + get_vm_ref(vm_id, datacenter).config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).map do |nic| { :name => nic.deviceInfo.label, :mac => nic.macAddress, diff --git a/lib/fog/vsphere/requests/compute/vm_clone.rb b/lib/fog/vsphere/requests/compute/vm_clone.rb index 82adbc36d9..be8e37c1de 100644 --- a/lib/fog/vsphere/requests/compute/vm_clone.rb +++ b/lib/fog/vsphere/requests/compute/vm_clone.rb @@ -68,6 +68,14 @@ class Real # Default true # * 'time_zone'<~String> - *REQUIRED* Only valid linux options # are valid - example: 'America/Denver' + # * 'interfaces' <~Array> - interfaces object to apply to + # the template when cloning: overrides the + # network_label, network_adapter_device_key and nic_type attributes + # * 'volumes' <~Array> - volumes object to apply to + # the template when cloning: this allows to resize the + # existing disks as well as add or remove them. The + # resizing is applied only when the size is bigger then the + # in size in the template def vm_clone(options = {}) # Option handling options = vm_clone_check_options(options) @@ -113,38 +121,21 @@ def vm_clone(options = {}) datastore_obj ||= nil virtual_machine_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec() - # Options['network'] - # Build up the config spec - if ( options.key?('network_label') ) - config_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit') - # Get the portgroup and handle it from there. - network = get_raw_network(options['network_label'],options['datacenter']) - if ( network.kind_of? RbVmomi::VIM::DistributedVirtualPortgroup) - # Create the NIC backing for the distributed virtual portgroup - nic_backing_info = RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo( - :port => RbVmomi::VIM::DistributedVirtualSwitchPortConnection( - :portgroupKey => network.key, - :switchUuid => network.config.distributedVirtualSwitch.uuid - ) - ) - else - # Otherwise it's a non distributed port group - nic_backing_info = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(:deviceName => options['network_label']) + device_change = [] + # fully futured interfaces api: replace the current nics + # with the new based on the specification + if (options.key?('interfaces') ) + if options.key?('network_label') + raise ArgumentError, "interfaces option can't be specified together with network_label" end - connectable = RbVmomi::VIM::VirtualDeviceConnectInfo( - :allowGuestControl => true, - :connected => true, - :startConnected => true) - device = RbVmomi::VIM.public_send "#{options['nic_type']}", - :backing => nic_backing_info, - :deviceInfo => RbVmomi::VIM::Description(:label => "Network adapter 1", :summary => options['network_label']), - :key => options['network_adapter_device_key'], - :connectable => connectable - device_spec = RbVmomi::VIM::VirtualDeviceConfigSpec( - :operation => config_spec_operation, - :device => device) - virtual_machine_config_spec.deviceChange = [device_spec] + device_change.concat(modify_template_nics_specs(template_path, options['interfaces'], options['datacenter'])) + elsif options.key?('network_label') + device_change << modify_template_nics_simple_spec(options['network_label'], options['nic_type'], options['network_adapter_device_key'], options['datacenter']) end + if disks = options['volumes'] + device_change.concat(modify_template_volumes_specs(vm_mob_ref, options['volumes'])) + end + virtual_machine_config_spec.deviceChange = device_change if device_change.any? # Options['numCPUs'] or Options['memoryMB'] # Build up the specification for Hardware, for more details see ____________ # https://github.com/rlane/rbvmomi/blob/master/test/test_serialization.rb @@ -286,6 +277,86 @@ def vm_clone(options = {}) 'task_ref' => task._ref } end + + # Build up the network config spec for simple case: + # simple case: apply just the network_label, nic_type and network_adapter_device_key + def modify_template_nics_simple_spec(network_label, nic_type, network_adapter_device_key, datacenter) + config_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit') + # Get the portgroup and handle it from there. + network = get_raw_network(network_label, datacenter) + if ( network.kind_of? RbVmomi::VIM::DistributedVirtualPortgroup) + # Create the NIC backing for the distributed virtual portgroup + nic_backing_info = RbVmomi::VIM::VirtualEthernetCardDistributedVirtualPortBackingInfo( + :port => RbVmomi::VIM::DistributedVirtualSwitchPortConnection( + :portgroupKey => network.key, + :switchUuid => network.config.distributedVirtualSwitch.uuid + ) + ) + else + # Otherwise it's a non distributed port group + nic_backing_info = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(:deviceName => network_label) + end + connectable = RbVmomi::VIM::VirtualDeviceConnectInfo( + :allowGuestControl => true, + :connected => true, + :startConnected => true) + device = RbVmomi::VIM.public_send "#{nic_type}", + :backing => nic_backing_info, + :deviceInfo => RbVmomi::VIM::Description(:label => "Network adapter 1", :summary => network_label), + :key => network_adapter_device_key, + :connectable => connectable + device_spec = RbVmomi::VIM::VirtualDeviceConfigSpec( + :operation => config_spec_operation, + :device => device) + return device_spec + end + + + def modify_template_nics_specs(template_path, new_nics, datacenter) + #new_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('new') + #remove_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('remove') + + template_nics = list_vm_interfaces(template_path, datacenter).map do |old_attributes| + Fog::Compute::Vsphere::Interface.new(old_attributes) + end + specs = [] + + template_nics.each do |interface| + specs << create_interface(interface, interface.key, :remove) + end + + new_nics.each do |interface| + specs << create_interface(interface, 0, :add) + end + + return specs + end + + def modify_template_volumes_specs(vm_mob_ref, volumes) + template_volumes = vm_mob_ref.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk) + modified_volumes = volumes.take(template_volumes.size) + new_volumes = volumes.drop(template_volumes.size) + + specs = [] + template_volumes.zip(modified_volumes).each do |template_volume, new_volume| + if new_volume + # updated the attribtues on the existing volume + # it's not allowed to reduce the size of the volume when cloning + if new_volume.size > template_volume.capacityInKB + template_volume.capacityInKB = new_volume.size + end + template_volume.backing.diskMode = new_volume.mode + template_volume.backing.thinProvisioned = new_volume.thin + specs << { :operation => :edit, :device => template_volume } + else + specs << { :operation => :remove, + :fileOperation => :destroy, + :device => template_volume } + end + end + specs.concat(new_volumes.map { |volume| create_disk(volume, volumes.index(volume)) }) + return specs + end end class Mock