This series does a bit of cleanup on the imagefactory VMware builder. In particular it moves it under builders, removes sys.exit from error paths, and does a minor curl cleanup.
Signed-off-by: Chris Lalancette clalance@redhat.com --- imgfac/VMWare.py | 257 ----------------------------- imgfac/builders/Fedora_vsphere_Builder.py | 2 +- imgfac/builders/VMWare.py | 257 +++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 258 deletions(-) delete mode 100755 imgfac/VMWare.py create mode 100644 imgfac/builders/VMWare.py
diff --git a/imgfac/VMWare.py b/imgfac/VMWare.py deleted file mode 100755 index 559a727..0000000 --- a/imgfac/VMWare.py +++ /dev/null @@ -1,257 +0,0 @@ -# -# Copyright 2010 Jonathan Kinred jonathan.kinred@gmail.com -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# he Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/. - -# This is initial and very rough support for VMWare machine imports -# The script below is based off of an example in Jonathan Kinred's -# psphere package, modified to do an import rather than creating a blank -# vm - -# TODO: List all the TODOs :-) -# There are a number of baked in assumptions here -# Some are valid (like controller type) and others probably need to be -# dynamic - -import sys -import os -import math -import pycurl -from psphere.vim25 import ObjectNotFoundError -from psphere.vim25 import HttpNfcLease -from psphere.vim25 import Vim -from psphere.soap import VimFault -from psphere.scripting import BaseScript -from pprint import pprint -from time import sleep -from time import time -import logging - -logging.getLogger('suds').setLevel(logging.INFO) - -class VMImport: - def __init__(self, url, username, password): - self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__)) - self.vim = Vim(url) - self.vim.login(username, password) - - def curl_progress(self, download_t, download_d, upload_t, upload_d): - curtime=time() - # TODO: Make poke frequency variable - # 5 seconds isn't too much and it makes the status bar in the vSphere GUI look nice :-) - if (curtime - self.time_at_last_poke) >= 5: - self.vim.invoke('HttpNfcLeaseProgress', _this=self.lease_mo_ref, percent = int(upload_d*100/upload_t)) - self.time_at_last_poke = time() - - def import_vm(self, datastore, network_name, name, disksize_kb, - memory, num_cpus, guest_id, host=None, imagefilename=None): - - nic = {'network_name': network_name, 'type': 'VirtualE1000'} - nics = [nic] - # If the host is not set, use the ComputeResource as the target - if not host: - target = self.vim.find_entity_view(view_type='ComputeResource') - target.update_view_data(['name', 'datastore', 'network', 'parent', - 'resourcePool']) - resource_pool = target.resourcePool - else: - target = self.vim.find_entity_view(view_type='HostSystem', - filter={'name': host}) - # Retrieve the properties we're going to use - target.update_view_data(['name', 'datastore', 'network', 'parent']) - host_cr = self.vim.get_view(mo_ref=target.parent, vim=self.vim) - host_cr.update_view_data(properties=['resourcePool']) - resource_pool = host_cr.resourcePool - - # A list of devices to be assigned to the VM - vm_devices = [] - - # Create a disk controller - controller = self.create_controller('VirtualLsiLogicController') - vm_devices.append(controller) - - # Find the given datastore and ensure it is suitable - if host: - ds_target = host_cr - else: - ds_target = target - - try: - ds = ds_target.find_datastore(name=datastore) - except ObjectNotFoundError, e: - self.log.error('Could not find datastore with name %s: %s' % (datastore, - e.error)) - sys.exit() - - ds.update_view_data(properties=['summary']) - # Ensure the datastore is accessible and has enough space - if (not ds.summary.accessible or - ds.summary.freeSpace < disksize_kb * 1024): - self.log.error('Datastore (%s) exists, but is not accessible or' - 'does not have sufficient free space.' % ds.summary.name) - sys.exit() - - disk = self.create_disk(datastore=ds, disksize_kb=disksize_kb) - vm_devices.append(disk) - - for nic in nics: - nic_spec = self.create_nic(target, nic) - if not nic_spec: - self.log.error('Could not create spec for NIC') - sys.exit() - - # Append the nic spec to the vm_devices list - vm_devices.append(nic_spec) - - vmfi = self.vim.create_object('VirtualMachineFileInfo') - vmfi.vmPathName = '[%s]' % ds.summary.name - vm_config_spec = self.vim.create_object('VirtualMachineConfigSpec') - vm_config_spec.name = name - vm_config_spec.memoryMB = memory - vm_config_spec.files = vmfi - vm_config_spec.annotation = 'Auto-provisioned by pSphere' - vm_config_spec.numCPUs = num_cpus - vm_config_spec.guestId = guest_id - vm_config_spec.deviceChange = vm_devices - - # Find the datacenter of the target - try: - dc = target.find_datacenter() - except ObjectNotFoundError, e: - self.log.error('Error while trying to find datacenter for %s: %s' % - (target.name, e.error)) - sys.exit() - - dc.update_view_data(properties=['vmFolder']) - - importspec = self.vim.create_object('VirtualMachineImportSpec') - - importspec.configSpec = vm_config_spec - importspec.resPoolEntity = None - - lease_mo_ref = self.vim.invoke('ImportVApp', _this=resource_pool, spec = importspec, folder = dc.vmFolder) - - lease = HttpNfcLease(mo_ref=lease_mo_ref, vim=self.vim) - - self.lease_mo_ref = lease_mo_ref - - for i in range(1000): - lease.update_view_data() - if lease.state == "ready": - break - sleep(5) - - url = lease.info.deviceUrl[0]['url'] - - self.lease_timeout = lease.info.leaseTimeout - self.time_at_last_poke = time() - - image_file = open(imagefilename) - - # Upload the image itself - image_size = os.path.getsize(imagefilename) - curl = pycurl.Curl() - curl.setopt(pycurl.URL, str(url)) - curl.setopt(pycurl.SSL_VERIFYPEER, 0) - curl.setopt(pycurl.POST, 1) - curl.setopt(pycurl.POSTFIELDSIZE, image_size) - curl.setopt(pycurl.READFUNCTION, image_file.read) - curl.setopt(pycurl.HTTPHEADER, ["User-Agent: Load Tool (PyCURL Load Tool)", "Content-Type: application/octet-stream"]) - curl.setopt(pycurl.NOPROGRESS, 0) - curl.setopt(pycurl.PROGRESSFUNCTION, self.curl_progress) - curl.perform() - curl.close() - - image_file.close() - - self.vim.invoke('HttpNfcLeaseComplete', _this=lease.mo_ref) - - self.vim.logout() - - def create_nic(self, target, nic): - """Return a NIC spec""" - # Get all the networks associated with the HostSystem/ComputeResource - networks = self.vim.get_views(mo_refs=target.network, properties=['name']) - - # Iterate through the networks and look for one matching - # the requested name - for network in networks: - if network.name == nic['network_name']: - # Success! Create a nic attached to this network - backing = (self.vim. - create_object('VirtualEthernetCardNetworkBackingInfo')) - backing.deviceName = nic['network_name'] - backing.network = network.mo_ref - - connect_info = (self.vim. - create_object('VirtualDeviceConnectInfo')) - connect_info.allowGuestControl = True - connect_info.connected = False - connect_info.startConnected = True - - new_nic = self.vim.create_object(nic['type']) - new_nic.backing = backing - new_nic.key = 2 - # TODO: Work out a way to automatically increment this - new_nic.unitNumber = 1 - new_nic.addressType = 'generated' - new_nic.connectable = connect_info - - nic_spec = self.vim.create_object('VirtualDeviceConfigSpec') - nic_spec.device = new_nic - nic_spec.fileOperation = None - operation = (self.vim. - create_object('VirtualDeviceConfigSpecOperation')) - nic_spec.operation = (operation.add) - - return nic_spec - - def create_controller(self, controller_type): - controller = self.vim.create_object(controller_type) - controller.key = 0 - controller.device = [0] - controller.busNumber = 0, - controller.sharedBus = (self.vim. - create_object('VirtualSCSISharing').noSharing) - - spec = self.vim.create_object('VirtualDeviceConfigSpec') - spec.device = controller - spec.fileOperation = None - spec.operation = (self.vim. - create_object('VirtualDeviceConfigSpecOperation').add) - - return spec - - def create_disk(self, datastore, disksize_kb): - backing = (self.vim.create_object('VirtualDiskFlatVer2BackingInfo')) - backing.datastore = None - backing.diskMode = 'persistent' - backing.fileName = '[%s]' % datastore.summary.name - backing.thinProvisioned = False - - disk = self.vim.create_object('VirtualDisk') - disk.backing = backing - disk.controllerKey = 0 - disk.key = 0 - disk.unitNumber = 0 - disk.capacityInKB = disksize_kb - - disk_spec = self.vim.create_object('VirtualDeviceConfigSpec') - disk_spec.device = disk - file_op = self.vim.create_object('VirtualDeviceConfigSpecFileOperation') - disk_spec.fileOperation = file_op.create - operation = self.vim.create_object('VirtualDeviceConfigSpecOperation') - disk_spec.operation = operation.add - - return disk_spec diff --git a/imgfac/builders/Fedora_vsphere_Builder.py b/imgfac/builders/Fedora_vsphere_Builder.py index e9b4b85..07fd51b 100644 --- a/imgfac/builders/Fedora_vsphere_Builder.py +++ b/imgfac/builders/Fedora_vsphere_Builder.py @@ -25,7 +25,7 @@ import json import ConfigParser from imgfac.ApplicationConfiguration import ApplicationConfiguration from imgfac.ImageFactoryException import ImageFactoryException -from imgfac.VMWare import VMImport +from VMWare import VMImport from IBuilder import IBuilder from BaseBuilder import BaseBuilder from VMDKstream import convert_to_stream diff --git a/imgfac/builders/VMWare.py b/imgfac/builders/VMWare.py new file mode 100644 index 0000000..559a727 --- /dev/null +++ b/imgfac/builders/VMWare.py @@ -0,0 +1,257 @@ +# +# Copyright 2010 Jonathan Kinred jonathan.kinred@gmail.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# he Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/. + +# This is initial and very rough support for VMWare machine imports +# The script below is based off of an example in Jonathan Kinred's +# psphere package, modified to do an import rather than creating a blank +# vm + +# TODO: List all the TODOs :-) +# There are a number of baked in assumptions here +# Some are valid (like controller type) and others probably need to be +# dynamic + +import sys +import os +import math +import pycurl +from psphere.vim25 import ObjectNotFoundError +from psphere.vim25 import HttpNfcLease +from psphere.vim25 import Vim +from psphere.soap import VimFault +from psphere.scripting import BaseScript +from pprint import pprint +from time import sleep +from time import time +import logging + +logging.getLogger('suds').setLevel(logging.INFO) + +class VMImport: + def __init__(self, url, username, password): + self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__)) + self.vim = Vim(url) + self.vim.login(username, password) + + def curl_progress(self, download_t, download_d, upload_t, upload_d): + curtime=time() + # TODO: Make poke frequency variable + # 5 seconds isn't too much and it makes the status bar in the vSphere GUI look nice :-) + if (curtime - self.time_at_last_poke) >= 5: + self.vim.invoke('HttpNfcLeaseProgress', _this=self.lease_mo_ref, percent = int(upload_d*100/upload_t)) + self.time_at_last_poke = time() + + def import_vm(self, datastore, network_name, name, disksize_kb, + memory, num_cpus, guest_id, host=None, imagefilename=None): + + nic = {'network_name': network_name, 'type': 'VirtualE1000'} + nics = [nic] + # If the host is not set, use the ComputeResource as the target + if not host: + target = self.vim.find_entity_view(view_type='ComputeResource') + target.update_view_data(['name', 'datastore', 'network', 'parent', + 'resourcePool']) + resource_pool = target.resourcePool + else: + target = self.vim.find_entity_view(view_type='HostSystem', + filter={'name': host}) + # Retrieve the properties we're going to use + target.update_view_data(['name', 'datastore', 'network', 'parent']) + host_cr = self.vim.get_view(mo_ref=target.parent, vim=self.vim) + host_cr.update_view_data(properties=['resourcePool']) + resource_pool = host_cr.resourcePool + + # A list of devices to be assigned to the VM + vm_devices = [] + + # Create a disk controller + controller = self.create_controller('VirtualLsiLogicController') + vm_devices.append(controller) + + # Find the given datastore and ensure it is suitable + if host: + ds_target = host_cr + else: + ds_target = target + + try: + ds = ds_target.find_datastore(name=datastore) + except ObjectNotFoundError, e: + self.log.error('Could not find datastore with name %s: %s' % (datastore, + e.error)) + sys.exit() + + ds.update_view_data(properties=['summary']) + # Ensure the datastore is accessible and has enough space + if (not ds.summary.accessible or + ds.summary.freeSpace < disksize_kb * 1024): + self.log.error('Datastore (%s) exists, but is not accessible or' + 'does not have sufficient free space.' % ds.summary.name) + sys.exit() + + disk = self.create_disk(datastore=ds, disksize_kb=disksize_kb) + vm_devices.append(disk) + + for nic in nics: + nic_spec = self.create_nic(target, nic) + if not nic_spec: + self.log.error('Could not create spec for NIC') + sys.exit() + + # Append the nic spec to the vm_devices list + vm_devices.append(nic_spec) + + vmfi = self.vim.create_object('VirtualMachineFileInfo') + vmfi.vmPathName = '[%s]' % ds.summary.name + vm_config_spec = self.vim.create_object('VirtualMachineConfigSpec') + vm_config_spec.name = name + vm_config_spec.memoryMB = memory + vm_config_spec.files = vmfi + vm_config_spec.annotation = 'Auto-provisioned by pSphere' + vm_config_spec.numCPUs = num_cpus + vm_config_spec.guestId = guest_id + vm_config_spec.deviceChange = vm_devices + + # Find the datacenter of the target + try: + dc = target.find_datacenter() + except ObjectNotFoundError, e: + self.log.error('Error while trying to find datacenter for %s: %s' % + (target.name, e.error)) + sys.exit() + + dc.update_view_data(properties=['vmFolder']) + + importspec = self.vim.create_object('VirtualMachineImportSpec') + + importspec.configSpec = vm_config_spec + importspec.resPoolEntity = None + + lease_mo_ref = self.vim.invoke('ImportVApp', _this=resource_pool, spec = importspec, folder = dc.vmFolder) + + lease = HttpNfcLease(mo_ref=lease_mo_ref, vim=self.vim) + + self.lease_mo_ref = lease_mo_ref + + for i in range(1000): + lease.update_view_data() + if lease.state == "ready": + break + sleep(5) + + url = lease.info.deviceUrl[0]['url'] + + self.lease_timeout = lease.info.leaseTimeout + self.time_at_last_poke = time() + + image_file = open(imagefilename) + + # Upload the image itself + image_size = os.path.getsize(imagefilename) + curl = pycurl.Curl() + curl.setopt(pycurl.URL, str(url)) + curl.setopt(pycurl.SSL_VERIFYPEER, 0) + curl.setopt(pycurl.POST, 1) + curl.setopt(pycurl.POSTFIELDSIZE, image_size) + curl.setopt(pycurl.READFUNCTION, image_file.read) + curl.setopt(pycurl.HTTPHEADER, ["User-Agent: Load Tool (PyCURL Load Tool)", "Content-Type: application/octet-stream"]) + curl.setopt(pycurl.NOPROGRESS, 0) + curl.setopt(pycurl.PROGRESSFUNCTION, self.curl_progress) + curl.perform() + curl.close() + + image_file.close() + + self.vim.invoke('HttpNfcLeaseComplete', _this=lease.mo_ref) + + self.vim.logout() + + def create_nic(self, target, nic): + """Return a NIC spec""" + # Get all the networks associated with the HostSystem/ComputeResource + networks = self.vim.get_views(mo_refs=target.network, properties=['name']) + + # Iterate through the networks and look for one matching + # the requested name + for network in networks: + if network.name == nic['network_name']: + # Success! Create a nic attached to this network + backing = (self.vim. + create_object('VirtualEthernetCardNetworkBackingInfo')) + backing.deviceName = nic['network_name'] + backing.network = network.mo_ref + + connect_info = (self.vim. + create_object('VirtualDeviceConnectInfo')) + connect_info.allowGuestControl = True + connect_info.connected = False + connect_info.startConnected = True + + new_nic = self.vim.create_object(nic['type']) + new_nic.backing = backing + new_nic.key = 2 + # TODO: Work out a way to automatically increment this + new_nic.unitNumber = 1 + new_nic.addressType = 'generated' + new_nic.connectable = connect_info + + nic_spec = self.vim.create_object('VirtualDeviceConfigSpec') + nic_spec.device = new_nic + nic_spec.fileOperation = None + operation = (self.vim. + create_object('VirtualDeviceConfigSpecOperation')) + nic_spec.operation = (operation.add) + + return nic_spec + + def create_controller(self, controller_type): + controller = self.vim.create_object(controller_type) + controller.key = 0 + controller.device = [0] + controller.busNumber = 0, + controller.sharedBus = (self.vim. + create_object('VirtualSCSISharing').noSharing) + + spec = self.vim.create_object('VirtualDeviceConfigSpec') + spec.device = controller + spec.fileOperation = None + spec.operation = (self.vim. + create_object('VirtualDeviceConfigSpecOperation').add) + + return spec + + def create_disk(self, datastore, disksize_kb): + backing = (self.vim.create_object('VirtualDiskFlatVer2BackingInfo')) + backing.datastore = None + backing.diskMode = 'persistent' + backing.fileName = '[%s]' % datastore.summary.name + backing.thinProvisioned = False + + disk = self.vim.create_object('VirtualDisk') + disk.backing = backing + disk.controllerKey = 0 + disk.key = 0 + disk.unitNumber = 0 + disk.capacityInKB = disksize_kb + + disk_spec = self.vim.create_object('VirtualDeviceConfigSpec') + disk_spec.device = disk + file_op = self.vim.create_object('VirtualDeviceConfigSpecFileOperation') + disk_spec.fileOperation = file_op.create + operation = self.vim.create_object('VirtualDeviceConfigSpecOperation') + disk_spec.operation = operation.add + + return disk_spec
We definitely don't want to sys.exit on an error from the VMware stuff; instead, raise an error and let higher levels deal with the fallout.
Signed-off-by: Chris Lalancette clalance@redhat.com --- imgfac/builders/VMWare.py | 11 ++++++----- 1 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/imgfac/builders/VMWare.py b/imgfac/builders/VMWare.py index 559a727..319c96f 100644 --- a/imgfac/builders/VMWare.py +++ b/imgfac/builders/VMWare.py @@ -36,6 +36,7 @@ from psphere.scripting import BaseScript from pprint import pprint from time import sleep from time import time +from imgfac.ImageFactoryException import ImageFactoryException import logging
logging.getLogger('suds').setLevel(logging.INFO) @@ -91,8 +92,8 @@ class VMImport: ds = ds_target.find_datastore(name=datastore) except ObjectNotFoundError, e: self.log.error('Could not find datastore with name %s: %s' % (datastore, - e.error)) - sys.exit() + e.error)) + raise
ds.update_view_data(properties=['summary']) # Ensure the datastore is accessible and has enough space @@ -100,7 +101,7 @@ class VMImport: ds.summary.freeSpace < disksize_kb * 1024): self.log.error('Datastore (%s) exists, but is not accessible or' 'does not have sufficient free space.' % ds.summary.name) - sys.exit() + raise ImageFactoryException('Datastore (%s) exists, but is not accessible or does not have sufficient free space.' % ds.summary.name)
disk = self.create_disk(datastore=ds, disksize_kb=disksize_kb) vm_devices.append(disk) @@ -109,7 +110,7 @@ class VMImport: nic_spec = self.create_nic(target, nic) if not nic_spec: self.log.error('Could not create spec for NIC') - sys.exit() + raise ImageFactoryException("Could not create spec for NIC")
# Append the nic spec to the vm_devices list vm_devices.append(nic_spec) @@ -131,7 +132,7 @@ class VMImport: except ObjectNotFoundError, e: self.log.error('Error while trying to find datacenter for %s: %s' % (target.name, e.error)) - sys.exit() + raise
dc.update_view_data(properties=['vmFolder'])
Signed-off-by: Chris Lalancette clalance@redhat.com --- imgfac/builders/VMWare.py | 22 +++++++++++++--------- 1 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/imgfac/builders/VMWare.py b/imgfac/builders/VMWare.py index 319c96f..e950c5f 100644 --- a/imgfac/builders/VMWare.py +++ b/imgfac/builders/VMWare.py @@ -47,14 +47,6 @@ class VMImport: self.vim = Vim(url) self.vim.login(username, password)
- def curl_progress(self, download_t, download_d, upload_t, upload_d): - curtime=time() - # TODO: Make poke frequency variable - # 5 seconds isn't too much and it makes the status bar in the vSphere GUI look nice :-) - if (curtime - self.time_at_last_poke) >= 5: - self.vim.invoke('HttpNfcLeaseProgress', _this=self.lease_mo_ref, percent = int(upload_d*100/upload_t)) - self.time_at_last_poke = time() - def import_vm(self, datastore, network_name, name, disksize_kb, memory, num_cpus, guest_id, host=None, imagefilename=None):
@@ -157,6 +149,16 @@ class VMImport:
self.lease_timeout = lease.info.leaseTimeout self.time_at_last_poke = time() + def curl_progress(download_t, download_d, upload_t, upload_d): + curtime = time() + # TODO: Make poke frequency variable + # 5 seconds isn't too much and it makes the status bar in the + # vSphere GUI look nice :-) + if (curtime - self.time_at_last_poke) >= 5: + self.vim.invoke('HttpNfcLeaseProgress', + _this = self.lease_mo_ref, + percent = int(upload_d * 100 / upload_t)) + self.time_at_last_poke = time()
image_file = open(imagefilename)
@@ -168,7 +170,9 @@ class VMImport: curl.setopt(pycurl.POST, 1) curl.setopt(pycurl.POSTFIELDSIZE, image_size) curl.setopt(pycurl.READFUNCTION, image_file.read) - curl.setopt(pycurl.HTTPHEADER, ["User-Agent: Load Tool (PyCURL Load Tool)", "Content-Type: application/octet-stream"]) + curl.setopt(pycurl.HTTPHEADER, + ["User-Agent: Load Tool (PyCURL Load Tool)", + "Content-Type: application/octet-stream"]) curl.setopt(pycurl.NOPROGRESS, 0) curl.setopt(pycurl.PROGRESSFUNCTION, self.curl_progress) curl.perform()
aeolus-devel@lists.fedorahosted.org