With this module, we able to use SDK for the oVirt engine API to manipulate resources in oVirt such as Data Centers, Clusters, Hosts, Storage Domains, VMs, etc.
At present, we haven't cover everything into our oVirt module, which only includes some necessary API implementation for virt-v2v requirement. However, it's very easy to extend new functions to support oVirt autotest in the future. Signed-off-by: Alex Jia <[email protected]> Signed-off-by: Wayne Sun <[email protected]> --- client/virt/ovirt.py | 646 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 646 insertions(+) create mode 100644 client/virt/ovirt.py diff --git a/client/virt/ovirt.py b/client/virt/ovirt.py new file mode 100644 index 0000000..bffb3a6 --- /dev/null +++ b/client/virt/ovirt.py @@ -0,0 +1,646 @@ +""" +oVirt SDK wrapper module. + +@copyright: 2008-2012 Red Hat Inc. +""" + + +import time, logging + +try: + from ovirtsdk.api import API + from ovirtsdk.xml import params as param +except ImportError: + logging.warning("ovirtsdk module doesn't eixst") + +import virt_vm + +_api = None +_connected = False + +def connect(params): + """ + Connect ovirt manager API. + """ + url = params.get('ovirt_url') + username = params.get('ovirt_user') + password = params.get('ovirt_password') + version = params.get('ovirt_version') + + if url is None or username is None or password is None: + logging.error('ovirt_[url|user|password] are necessary!!') + + if version is None: + version = param.Version(major='3', minor='0') + else: + version = param.Version(version) + + global _api, _connected + + try: + # Try to connect oVirt API if connection doesn't exist, + # otherwise, directly return existing API connection. + if not _connected: + _api = API(url, username, password) + _connected = True + return (_api, version) + else: + return (_api, version) + except Exception as e: + logging.error('Failed to connect: %s\n' % str(e)) + else: + logging.info('Succeed to connect oVirt/Rhevm manager\n') + +def disconnect(): + """ + Disconnect ovirt manager connection. + """ + global _api, _connected + + if _connected: + return _api.disconnect() + + +class VM(virt_vm.BaseVM): + """ + This class handles all basic VM operations for oVirt. + """ + + def __init__(self, name, params, root_dir, address_cache=None, state=None): + """ + Initialize the object and set a few attributes. + + @param name: The name of the object + @param params: A dict containing VM params + (see method make_qemu_command for a full description) + @param root_dir: Base directory for relative filenames + @param address_cache: A dict that maps MAC addresses to IP addresses + @param state: If provided, use this as self.__dict__ + """ + + if state: + self.__dict__ = state + else: + self.process = None + self.serial_console = None + self.redirs = {} + self.vnc_port = 5900 + self.vnclisten = "0.0.0.0" + self.pci_assignable = None + self.netdev_id = [] + self.device_id = [] + self.pci_devices = [] + self.uuid = None + self.only_pty = False + + self.spice_port = 8000 + self.name = name + self.params = params + self.root_dir = root_dir + self.address_cache = address_cache + self.vnclisten = "0.0.0.0" + self.driver_type = "virt_v2v" + + super(VM, self).__init__(name, params) + (self.api, self.version) = connect(self.params) + + def list(self): + """ + List all of VMs. + """ + vm_list = [] + try: + vms = self.api.vms.list(query='name=*') + for i in range(len(vms)): + vm_list.append(vms[i].name) + return vm_list + except Exception as e: + logging.error('Failed to get vms:\n%s' % str(e)) + + def state(self): + """ + Return VM state. + """ + try: + vm = self.api.vms.get(self.name) + return vm.status.state + except Exception as e: + logging.error('Failed to get %s status:\n%s' % (self.name, str(e))) + + def lookup_by_name(self): + """ + Lookup VM object according to VM name. + """ + try: + return self.api.vms.get(self.name) + except Exception as e: + logging.error('could not find vm: %s\n' % str(e)) + else: + logging.info('successs: can find vm %s' % self.name) + + def get_mac_address(self): + """ + Return MAC address of a VM. + """ + try: + vm = self.api.vms.get(self.name) + return vm.nics.get().get_mac().get_address() + except Exception as e: + logging.error('Failed to get %s status:\n%s' % (self.name, str(e))) + + def lookup_by_storagedomains(self, storage_name): + """ + Lookup VM object in storage domain according to VM name. + """ + try: + storage = self.api.storagedomains.get(storage_name) + return storage.vms.get(self.name) + except Exception as e: + logging.error('Failed to get %s from %s:\n%s' % (self.name, + storage_name, str(e))) + + def verify_alive(self): + """ + Make sure the VM is alive. + @raise VMDeadError: If the VM is dead. + """ + if not self.is_alive(): + raise virt_vm.VMDeadError("Domain %s is inactive" % self.name, + self.state(self.name)) + + def is_alive(self): + if not hasattr(self.api.vms, 'get'): + return False + + vm = self.api.vms.get(self.name) + if vm.status.state == 'up': + logging.info('The %s status is <Up>' % self.name) + return True + else: + logging.debug('The %s status is <not Up>' % self.name) + return False + + def is_dead(self): + if not hasattr(self.api.vms, 'get'): + return False + + vm = self.api.vms.get(self.name) + if vm.status.state == 'down': + logging.info('The %s status is <Down>' % self.name) + return True + else: + logging.debug('The %s status is <not Down>' % self.name) + return False + + def start(self): + try: + vm = self.api.vms.get(self.name) + if vm.status.state != 'up': + logging.info('Starting VM %s' % self.name) + vm.start() + logging.info('Waiting for VM to reach <Up> status ...') + while vm.status.state != 'up': + vm = self.api.vms.get(self.name) + time.sleep(1) + else: + logging.debug('VM already up') + except Exception as e: + logging.error('Failed to start VM:\n%s' % str(e)) + + + def suspend(self): + vm = self.api.vms.get(self.name) + while vm.status.state != 'suspended': + try: + logging.info('Suspend VM %s' % self.name) + vm.suspend() + logging.info('Waiting for VM to reach <Suspended> status ...') + while vm.status.state != 'suspended': + vm = self.api.vms.get(self.name) + time.sleep(1) + + except Exception as e: + if e.reason == 'Bad Request' \ + and 'asynchronous running tasks' in e.detail: + logging.warning('VM has asynchronous running tasks, trying again') + time.sleep(1) + else: + logging.error('Failed to suspend VM:\n%s' % str(e)) + break + + def resume(self): + try: + vm = self.api.vms.get(self.name) + if vm.status.state != 'up': + logging.info('Resume VM %s' % self.name) + vm.start() + logging.info('Waiting for VM to <Resume> status ...') + while vm.status.state != 'up': + vm = self.api.vms.get(self.name) + time.sleep(1) + else: + logging.debug('VM already up') + except Exception as e: + logging.error('Failed to resume VM:\n%s' % str(e)) + + + def shutdown(self): + try: + vm = self.api.vms.get(self.name) + if vm.status.state != 'down': + logging.info('Stop VM %s' % self.name) + vm.stop() + logging.info('Waiting for VM to reach <Down> status ...') + while vm.status.state != 'down': + vm = self.api.vms.get(self.name) + time.sleep(1) + else: + logging.debug('VM already down') + except Exception as e: + logging.error('Failed to Stop VM:\n%s' % str(e)) + + def delete(self): + try: + vm = self.api.vms.get(self.name) + if vm.status.state == 'down': + logging.info('Delete VM %s' % self.name) + vm.delete() + logging.info('Waiting for VM to be <Deleted> ...') + while self.name in [vm.name for vm in self.api.vms.list()]: + time.sleep(1) + logging.info('VM was removed successfully') + else: + logging.debug('VM already is down status') + except Exception as e: + logging.error('Failed to remove VM:\n%s' % str(e)) + + + def destroy(self): + if self.api.vms is None: + return + + self.shutdown() + + def delete_from_export_domain(self, export_name): + vm = self.api.storagedomains.get(export_name).vms.get(self.name) + print dir(vm) + try: + vm.delete() + except Exception as e: + logging.error('Failed to remove VM:\n%s' % str(e)) + + def get_address(self, index=0): + """ + Return the address of a NIC of the guest, in host space. + + If port redirection is used, return 'localhost' (the NIC has no IP + address of its own). Otherwise return the NIC's IP address. + + @param index: Index of the NIC whose address is requested. + @raise VMIPAddressMissingError: If no IP address is found for the the + NIC's MAC address + """ + nics = self.params.objects('nics') + nic_name = nics[index] + nic_params = self.params.object_params(nic_name) + if nic_params.get('nic_mode') == 'tap': + mac = self.get_mac_address() + # Get the IP address from the cache + ip = self.address_cache.get(mac) + if not ip: + raise virt_vm.VMIPAddressMissingError(mac) + return ip + else: + return 'localhost' + + def get_port(self, port, nic_index=0): + """ + Return the port in host space corresponding to port in guest space. + + @param port: Port number in host space. + @param nic_index: Index of the NIC. + @return: If port redirection is used, return the host port redirected + to guest port port. Otherwise return port. + @raise VMPortNotRedirectedError: If an unredirected port is requested + in user mode + """ + nic_name = self.params.objects('nics')[nic_index] + nic_params = self.params.object_params(nic_name) + if nic_params.get('nic_mode') == 'tap': + return port + else: + try: + return self.redirs[port] + except KeyError: + raise virt_vm.VMPortNotRedirectedError(port) + + def import_from_export_domain(self, export_name, storage_name, cluster_name): + """ + Import a VM from export domain to storage domain. + + @export_name: Export domain name. + @storage_name: Storage domain name. + @cluster_name: Cluster name. + """ + vm = self.api.storagedomains.get(export_name).vms.get(self.name) + storage_domains = self.api.storagedomains.get(storage_name) + clusters = self.api.clusters.get(cluster_name) + try: + logging.info('Import VM %s' % self.name) + vm.import_vm(param.Action(storage_domain=storage_domains, cluster=clusters)) + logging.info('Waiting for VM to reach <Down> status ...') + while self.api.vms.get(self.name).status.state != 'down': + time.sleep(1) + logging.info('VM was imported successfully') + except Exception as e: + logging.error('Failed to import VM:\n%s' % str(e)) + + def export_from_export_domain(self, export_name): + """ + Export a VM from storage domain to export domain. + + @export_name: Export domain name. + """ + vm = self.api.vms.get(self.name) + storage_domains = self.api.storagedomains.get(export_name) + try: + logging.info('Export VM %s' % self.name) + vm.export(param.Action(storage_domain=storage_domains)) + logging.info('Waiting for VM to reach <Down> status ...') + while vm.status.state != 'down': + time.sleep(1) + logging.info('VM was exported successfully') + except Exception as e: + logging.error('Failed to export VM:\n%s' % str(e)) + + def snapshot(self, snapshot_name='my_snapshot'): + snap_params = param.Snapshot(description=snapshot_name, + vm=self.api.vms.get(self.name)) + try: + logging.info('Creating a snapshot %s for VM %s' + % (snapshot_name, self.name)) + self.api.vms.get(self.name).snapshots.add(snap_params) + logging.info('Waiting for snapshot creation to finish ...') + while self.api.vms.get(self.name).status.state == 'image_locked': + time.sleep(1) + logging.info('Snapshot was created successfully') + except Exception as e: + logging.error('Failed to create a snapshot:\n%s' % str(e)) + + def create_template(self, cluster_name, template_name='my_template'): + template_params = param.Template(name=template_name, + vm=self.api.vms.get(self.name), + cluster=self.api.clusters.get(cluster_name)) + try: + logging.info('Creating a template %s from VM %s' + % (template_name, self.name)) + self.api.templates.add(template_params) + logging.info('Waiting for VM to reach <Down> status ...') + while self.api.vms.get(self.name).status.state != 'down': + time.sleep(1) + except Exception as e: + logging.error('Failed to create a template from VM:\n%s' % str(e)) + + def add_vm(self, memory, disk_size, cluster_name, storage_name, + nic_name='eth0', network_interface='virtio', + network_name='ovirtmgmt', disk_interface='virtio', + disk_format='raw', template_name='Blank'): + # network name is ovirtmgmt for ovirt, rhevm for rhel. + vm_params = param.VM(name=self.name, memory=memory, + cluster=self.api.clusters.get(cluster_name), + template=self.api.templates.get(template_name)) + + storage = self.api.storagedomains.get(storage_name) + + storage_params = param.StorageDomains(storage_domain=[storage]) + + nic_params = param.NIC(name=nic_name, + network=param.Network(name=network_name), + interface=network_interface) + + disk_params = param.Disk(storage_domains=storage_params, + size=disk_size, + type_='system', + status=None, + interface=disk_interface, + format=disk_format, + sparse=True, + bootable=True) + + try: + logging.info('Creating a VM %s' % self.name) + self.api.vms.add(vm_params) + + logging.info('NIC is added to VM %s' % self.name) + self.api.vms.get(self.name).nics.add(nic_params) + + self.api.vms.get(self.name).disks.add(disk_params) + + logging.info('Disk is added to VM %s' % self.name) + logging.info('Waiting for VM to reach <Down> status ...') + while self.api.vms.get(self.name).status.state != 'down': + time.sleep(1) + + except Exception as e: + logging.error('Failed to create VM with disk and NIC\n%s' % str(e)) + + def add_vm_from_template(self, cluster_name, template_name='Blank', new_name='my_new_vm'): + vm_params = param.VM(name=new_name, + cluster=self.api.clusters.get(cluster_name), + template=self.api.templates.get(template_name)) + try: + logging.info('Creating a VM %s from template %s' + % (new_name, template_name)) + self.api.vms.add(vm_params) + logging.info('Waiting for VM to reach <Down> status ...') + while self.api.vms.get(self.name).status.state != 'down': + time.sleep(1) + logging.info('VM was created from template successfully') + except Exception as e: + logging.error('Failed to create VM from template:\n%s' % str(e)) + + +class DataCenters(object): + """ + This class handles all basic datacenter operations. + """ + + def __init__(self, params): + self.params = params + (self.api, self.version) = connect(self.params) + + def list(self): + dc_list = [] + try: + logging.info('List Data centers') + dcs = self.api.datacenters.list(query='name=*') + for i in range(len(dcs)): + dc_list.append(dcs[i].name) + return dc_list + except Exception as e: + logging.error('Failed to get data centers:\n%s' % str(e)) + + def add_dc(self, dc_name, storage_type): + try: + logging.info('Creating a %s type datacenter %s' + % (storage_type, dc_name)) + if self.api.datacenters.add(param.DataCenter(name=dc_name, + storage_type=storage_type, + version=self.version)): + logging.info('Data center was created successfully') + except Exception as e: + logging.error('Failed to create data center:\n%s' % str(e)) + + +class Clusters(object): + """ + This class handles all basic cluster operations. + """ + + def __init__(self, params): + self.params = params + (self.api, self.version) = connect(self.params) + + def list(self): + cluster_list = [] + try: + logging.info('List clusters') + clusters = self.api.clusters.list(query='name=*') + for i in range(len(clusters)): + cluster_list.append(clusters[i].name) + return cluster_list + except Exception as e: + logging.error('Failed to get clusters:\n%s' % str(e)) + + def lookup_by_name(self, cluster_name): + try: + logging.info('Finding cluster %s' % cluster_name) + return self.api.clusters.get(cluster_name) + except Exception as e: + logging.error('Failed to find cluster: %s\n' % str(e)) + else: + logging.info('Can find cluster %s' % cluster_name) + + def add_cluster(self, cluster_name, dc_name, cpu_type='Intel Nehalem Family'): + dc = self.api.datacenters.get(dc_name) + try: + logging.info('Creating a cluster %s in datacenter %s' + % (cluster_name, dc_name)) + if self.api.clusters.add(param.Cluster(name=cluster_name, + cpu=param.CPU(id=cpu_type), + data_center=dc, + version=self.version)): + logging.info('Cluster was created successfully') + except Exception as e: + logging.error('Failed to create cluster:\n%s' % str(e)) + + +class Hosts(object): + """ + This class handles all basic host operations. + """ + + def __init__(self, params): + self.params = params + (self.api, self.version) = connect(self.params) + + def list(self): + host_list = [] + try: + logging.info('List hosts') + hosts = self.api.hosts.list(query='name=*') + for i in range(len(hosts)): + host_list.append(hosts[i].name) + return host_list + except Exception as e: + logging.error('Failed to get hosts:\n%s' % str(e)) + + def add_host(self, host_address, host_password, cluster_name, host_name='my_host'): + clusters = self.api.clusters.get(cluster_name) + host_params = param.Host(name=host_name, address=host_address, + cluster=clusters, root_password=host_password) + try: + logging.info('Registing a host %s into cluster %s' + % (host_name, cluster_name)) + if self.api.hosts.add(host_params): + logging.info('Waiting for host to reach the <Up> status ...') + while self.api.hosts.get(host_name).status.state != 'up': + time.sleep(1) + else: + logging.info('Host is up') + logging.info('Host was installed successfully') + except Exception as e: + logging.error('Failed to install host:\n%s' % str(e)) + + def get_address(self, host_name): + """ + Return host IP address. + """ + try: + logging.info('Get host %s IP' % host_name) + host = self.api.hosts.get(host_name) + return host.get_address() + except Exception as e: + logging.error('Failed to get host %s IP address:\n%s' % + (host_name, str(e))) + + +class StorageDomains(object): + """ + This class handles all basic storage domain operations. + """ + + def __init__(self, params): + self.params = params + (self.api, self.version) = connect(self.params) + + def list(self): + storage_list = [] + try: + logging.info('List storage domains') + storages = self.api.storagedomains.list() + for i in range(len(storages)): + storage_list.append(storages[i].name) + return storage_list + except Exception as e: + logging.error('Failed to get storage domains:\n%s' % str(e)) + + def lookup_by_name(self, storage_name): + try: + logging.info('Finding storage domain %s' % storage_name) + return self.api.storagedomains.get(storage_name) + except Exception as e: + logging.error('Failed to find storage domain: %s\n' % str(e)) + else: + logging.info('Can find storage domain %s' % storage_name) + + def attach_iso_export_domain_into_datacenter(self, iso_address, iso_path, + dc_name, host_name, + domain_type, type='nfs', + iso_name='my_iso'): + + dc = self.api.datacenters.get(dc_name) + host= self.api.hosts.get(host_name) + storage_params = param.Storage(type_=type, address=iso_address, path=iso_path) + + iso_params = param.StorageDomain(name=iso_name, + data_center=dc, + type_=domain_type, + host=host, + storage = storage_params) + + try: + logging.info('Create/import ISO storage domain %s' % iso_name) + if self.api.storagedomains.add(iso_params): + logging.info('%s domain was created/imported successfully' % domain_type) + + logging.info('Attach ISO storage domain %s' % iso_name) + if self.api.datacenters.get(dc_name).storagedomains.add(self.api.storagedomains.get(iso_name)): + logging.info('%s domain was attached successfully' % domain_type) + + logging.info('Activate ISO storage domain %s' % iso_name) + if self.api.datacenters.get(dc_name).storagedomains.get(iso_name).activate(): + logging.info('%s domain was activated successfully' % domain_type) + except Exception as e: + logging.error('Failed to add %s domain:\n%s' % (domain_type, str(e))) + -- 1.7.10.2 _______________________________________________ Autotest mailing list [email protected] http://test.kernel.org/cgi-bin/mailman/listinfo/autotest
