From b578047ec26ac7d0ad26ccaab8b596ba5373b278 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Fri, 8 Oct 2010 17:57:49 -0700 Subject: hyper-v driver created --- nova/virt/hyperv.py | 387 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 nova/virt/hyperv.py (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py new file mode 100644 index 000000000..91b86e265 --- /dev/null +++ b/nova/virt/hyperv.py @@ -0,0 +1,387 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2010 Cloud.com, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +A connection to Hyper-V . + +""" + +import os +import logging +import wmi +import time + +from twisted.internet import defer + +from nova import flags +from nova.auth.manager import AuthManager +from nova.compute import power_state +from nova.virt import images + + +FLAGS = flags.FLAGS + + +HYPERV_POWER_STATE = { + 3 : power_state.SHUTDOWN, + 2 : power_state.RUNNING, + 32768 : power_state.PAUSED, + 32768: power_state.PAUSED, # TODO + 3 : power_state.CRASHED +} + +REQ_POWER_STATE = { + 'Enabled' : 2, + 'Disabled': 3, + 'Reboot' : 10, + 'Reset' : 11, + 'Paused' : 32768, + 'Suspended': 32769 +} + + +def get_connection(_): + return HyperVConnection() + + +class HyperVConnection(object): + def __init__(self): + self._conn = wmi.WMI(moniker = '//./root/virtualization') + self._cim_conn = wmi.WMI(moniker = '//./root/cimv2') + + def list_instances(self): + vms = [v.ElementName \ + for v in self._conn.Msvm_ComputerSystem(['ElementName'])] + return vms + + @defer.inlineCallbacks + def spawn(self, instance): + vm = yield self._lookup(instance.name) + if vm is not None: + raise Exception('Attempted to create non-unique name %s' % + instance.name) + + user = AuthManager().get_user(instance['user_id']) + project = AuthManager().get_project(instance['project_id']) + vhdfile = os.path.join(FLAGS.instances_path, instance['str_id'])+".vhd" + yield images.fetch(instance['image_id'], vhdfile, user, project) + + try: + yield self._create_vm(instance) + + yield self._create_disk(instance['name'], vhdfile) + yield self._create_nic(instance['name'], instance['mac_address']) + + logging.debug ('Starting VM %s ', instance.name) + yield self._set_vm_state(instance['name'], 'Enabled') + logging.info('Started VM %s ', instance.name) + except Exception as exn: + logging.error('spawn vm failed: %s', exn) + self.destroy(instance) + + def _create_vm(self, instance): + """Create a VM record. """ + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + + vs_gs_data = self._conn.Msvm_VirtualSystemGlobalSettingData.new() + vs_gs_data.ElementName = instance['name'] + (job, ret_val) = vs_man_svc.DefineVirtualSystem( + [], None, vs_gs_data.GetText_(1))[1:] + if (ret_val == 4096 ): #WMI job started + success = self._check_job_status(job) + else: + success = (ret_val == 0) + + if not success: + raise Exception('Failed to create VM %s', instance.name) + + logging.debug('Created VM %s...', instance.name) + vm = self._conn.Msvm_ComputerSystem (ElementName=instance.name)[0] + + vmsettings = vm.associators(wmi_result_class= + 'Msvm_VirtualSystemSettingData') + vmsetting = [s for s in vmsettings + if s.SettingType == 3][0] #avoid snapshots + memsetting = vmsetting.associators(wmi_result_class= + 'Msvm_MemorySettingData')[0] + #No Dynamic Memory + mem = long(str(instance['memory_mb'])) + memsetting.VirtualQuantity = mem + memsetting.Reservation = mem + memsetting.Limit = mem + + (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources( + vm.path_(), [memsetting.GetText_(1)]) + + logging.debug('Set memory for vm %s...', instance.name) + procsetting = vmsetting.associators(wmi_result_class= + 'Msvm_ProcessorSettingData')[0] + vcpus = long(str(instance['vcpus'])) + #vcpus = 1 + procsetting.VirtualQuantity = vcpus + procsetting.Reservation = vcpus + procsetting.Limit = vcpus + + (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources( + vm.path_(), [procsetting.GetText_(1)]) + + logging.debug('Set vcpus for vm %s...', instance.name) + + + def _create_disk(self, vm_name, vhdfile): + """Create a disk and attach it to the vm""" + logging.debug("Creating disk for %s by attaching disk file %s", \ + vm_name, vhdfile) + vms = self._conn.MSVM_ComputerSystem (ElementName=vm_name) + vm = vms[0] + vmsettings = vm.associators( + wmi_result_class='Msvm_VirtualSystemSettingData') + rasds = vmsettings[0].associators( + wmi_result_class='MSVM_ResourceAllocationSettingData') + ctrller = [r for r in rasds + if r.ResourceSubType == 'Microsoft Emulated IDE Controller'\ + and r.Address == "0" ] + diskdflt = self._conn.query( + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType LIKE 'Microsoft Synthetic Disk Drive'\ + AND InstanceID LIKE '%Default%'")[0] + diskdrive = self._clone_wmi_obj( + 'Msvm_ResourceAllocationSettingData', diskdflt) + diskdrive.Parent = ctrller[0].path_() + diskdrive.Address = 0 + new_resources = self._add_virt_resource(diskdrive, vm) + + if new_resources is None: + raise Exception('Failed to add diskdrive to VM %s', vm_name) + + diskdrive_path = new_resources[0] + logging.debug("New disk drive path is " + diskdrive_path) + vhddefault = self._conn.query( + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType LIKE 'Microsoft Virtual Hard Disk' AND \ + InstanceID LIKE '%Default%' ")[0] + + vhddisk = self._clone_wmi_obj( + 'Msvm_ResourceAllocationSettingData', vhddefault) + vhddisk.Parent = diskdrive_path + vhddisk.Connection = [vhdfile] + + new_resources = self._add_virt_resource(vhddisk, vm) + if new_resources is None: + raise Exception('Failed to add vhd file to VM %s', vm_name) + logging.info("Created disk for %s ", vm_name) + + + def _create_nic(self, vm_name, mac): + """Create a (emulated) nic and attach it to the vm""" + logging.debug("Creating nic for %s ", vm_name) + vms = self._conn.Msvm_ComputerSystem (ElementName=vm_name) + extswitch = self._find_external_network() + vm = vms[0] + switch_svc = self._conn.Msvm_VirtualSwitchManagementService ()[0] + #use Msvm_SyntheticEthernetPortSettingData for Windows VMs or Linux with + #Linux Integration Components installed + emulatednics_data = self._conn.Msvm_EmulatedEthernetPortSettingData() + default_nic_data = [n for n in emulatednics_data + if n.InstanceID.rfind('Default') >0 ] + new_nic_data = self._clone_wmi_obj( + 'Msvm_EmulatedEthernetPortSettingData', + default_nic_data[0]) + + (created_sw, ret_val) = switch_svc.CreateSwitchPort(vm_name, vm_name, + "", extswitch.path_()) + if (ret_val != 0): + logging.debug("Failed to create a new port on the external network") + return + logging.debug("Created switch port %s on switch %s", + vm_name, extswitch.path_()) + new_nic_data.Connection = [created_sw] + new_nic_data.ElementName = vm_name + ' nic' + new_nic_data.Address = ''.join(mac.split(':')) + new_nic_data.StaticMacAddress = 'TRUE' + new_resources = self._add_virt_resource(new_nic_data, vm) + if new_resources is None: + raise Exception('Failed to add nic to VM %s', vm_name) + logging.info("Created nic for %s ", vm_name) + + + def _add_virt_resource(self, res_setting_data, target_vm): + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + (job, new_resources, return_val) = vs_man_svc.\ + AddVirtualSystemResources([res_setting_data.GetText_(1)], + target_vm.path_()) + success = True + if (return_val == 4096 ): #WMI job started + success = self._check_job_status(job) + else: + success = (return_val == 0) + if success: + return new_resources + else: + return None + + #TODO: use the reactor to poll instead of sleep + def _check_job_status(self, jobpath): + inst_id = jobpath.split(':')[1].split('=')[1].strip('\"') + jobs = self._conn.Msvm_ConcreteJob(InstanceID=inst_id) + if (len(jobs) == 0): + return False + job = jobs[0] + while job.JobState == 4: #job started + time.sleep(0.1) + job = self._conn.Msvm_ConcreteJob(InstanceID=inst_id)[0] + + if (job.JobState != 7): #job success + logging.debug("WMI job failed: " + job.ErrorSummaryDescription) + return False + + logging.debug("WMI job succeeded: " + job.Description + ",Elapsed = " \ + + job.ElapsedTime) + + return True + + + + def _find_external_network(self): + bound = self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE') + if (len(bound) == 0): + return None + + return self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')[0]\ + .associators(wmi_result_class='Msvm_SwitchLANEndpoint')[0]\ + .associators(wmi_result_class='Msvm_SwitchPort')[0]\ + .associators(wmi_result_class='Msvm_VirtualSwitch')[0] + + def _clone_wmi_obj(self, wmi_class, wmi_obj): + cl = self._conn.__getattr__(wmi_class) + newinst = cl.new() + for prop in wmi_obj._properties: + newinst.Properties_.Item(prop).Value =\ + wmi_obj.Properties_.Item(prop).Value + return newinst + + + @defer.inlineCallbacks + def reboot(self, instance): + vm = yield self._lookup(instance.name) + if vm is None: + raise Exception('instance not present %s' % instance.name) + self._set_vm_state(instance.name, 'Reboot') + + + @defer.inlineCallbacks + def destroy(self, instance): + logging.debug("Got request to destroy vm %s", instance.name) + vm = yield self._lookup(instance.name) + if vm is None: + defer.returnValue(None) + vm = self._conn.Msvm_ComputerSystem (ElementName=instance.name)[0] + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + self._set_vm_state(instance.name, 'Disabled') + vmsettings = vm.associators(wmi_result_class= + 'Msvm_VirtualSystemSettingData') + rasds = vmsettings[0].associators(wmi_result_class= + 'MSVM_ResourceAllocationSettingData') + disks = [r for r in rasds \ + if r.ResourceSubType == 'Microsoft Virtual Hard Disk' ] + diskfiles = [] + for disk in disks: + diskfiles.extend([c for c in disk.Connection]) + + (job, ret_val) = vs_man_svc.DestroyVirtualSystem(vm.path_()) + if (ret_val == 4096 ): #WMI job started + success = self._check_job_status(job) + elif (ret_val == 0): + success = True + if not success: + raise Exception('Failed to destroy vm %s' % instance.name) + for disk in diskfiles: + vhdfile = self._cim_conn.CIM_DataFile(Name=disk) + for vf in vhdfile: + vf.Delete() + logging.debug("Deleted disk %s vm %s", vhdfile, instance.name) + + + + def get_info(self, instance_id): + vm = self._lookup(instance_id) + if vm is None: + raise Exception('instance not present %s' % instance_id) + vm = self._conn.Msvm_ComputerSystem(ElementName=instance_id)[0] + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + vmsettings = vm.associators(wmi_result_class= + 'Msvm_VirtualSystemSettingData') + settings_paths = [ v.path_() for v in vmsettings] + summary_info = vs_man_svc.GetSummaryInformation( + [4,100,103,105], settings_paths)[1] + info = summary_info[0] + logging.debug("Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, \ + cpu_time=%s", instance_id, + str(HYPERV_POWER_STATE[info.EnabledState]), + str(info.MemoryUsage), + str(info.NumberOfProcessors), + str(info.UpTime)) + + return {'state': HYPERV_POWER_STATE[info.EnabledState], + 'max_mem': info.MemoryUsage, + 'mem': info.MemoryUsage, + 'num_cpu': info.NumberOfProcessors, + 'cpu_time': info.UpTime} + + + def _lookup(self, i): + vms = self._conn.Msvm_ComputerSystem (ElementName=i) + n = len(vms) + if n == 0: + return None + elif n > 1: + raise Exception('duplicate name found: %s' % i) + else: + return vms[0].ElementName + + def _set_vm_state(self, vm_name, req_state): + vms = self._conn.Msvm_ComputerSystem (ElementName=vm_name) + if len(vms) == 0: + return False + status = vms[0].RequestStateChange(REQ_POWER_STATE[req_state]) + job = status[0] + return_val = status[1] + if (return_val == 4096 ): #WMI job started + success = self._check_job_status(job) + elif (return_val == 0): + success = True + if success: + logging.info("Successfully changed vm state of %s to %s", + vm_name, req_state) + return True + else: + logging.debug("Failed to change vm state of %s to %s", + vm_name, req_state) + return False + + + def attach_volume(self, instance_name, device_path, mountpoint): + vm = self._lookup(instance_name) + if vm is None: + raise Exception('Attempted to attach volume to nonexistent %s vm' % + instance_name) + + def detach_volume(self, instance_name, mountpoint): + vm = self._lookup(instance_name) + if vm is None: + raise Exception('Attempted to detach volume from nonexistent %s ' % + instance_name) + -- cgit From 85c890e91f493f254801edd5e5aed115d8d9c4a6 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Fri, 8 Oct 2010 17:58:01 -0700 Subject: Register the Hyper-V module into the list of virt modules --- nova/virt/connection.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/connection.py b/nova/virt/connection.py index 34e37adf7..0f736ce39 100644 --- a/nova/virt/connection.py +++ b/nova/virt/connection.py @@ -26,6 +26,7 @@ from nova import flags from nova.virt import fake from nova.virt import libvirt_conn from nova.virt import xenapi +from nova.virt import hyperv FLAGS = flags.FLAGS @@ -49,6 +50,8 @@ def get_connection(read_only=False): conn = libvirt_conn.get_connection(read_only) elif t == 'xenapi': conn = xenapi.get_connection(read_only) + elif t == 'hyperv': + conn = hyperv.get_connection(read_only) else: raise Exception('Unknown connection type "%s"' % t) -- cgit From 6669b46ca91f462c96b033c6e04618c06fecb31f Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Fri, 8 Oct 2010 17:58:53 -0700 Subject: curl not available on Windows for s3 download. also os-agnostic local copy --- nova/virt/images.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index dc50764d9..90071107b 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -24,13 +24,15 @@ Handling of VM disk images. import os.path import time import urlparse +import shutil from nova import flags -from nova import process from nova.auth import manager from nova.auth import signer -from nova.objectstore import image +import logging +import urllib2 +import os FLAGS = flags.FLAGS flags.DEFINE_bool('use_s3', True, @@ -47,6 +49,7 @@ def fetch(image, path, user, project): def _fetch_s3_image(image, path, user, project): url = image_url(image) + logging.debug("About to retrieve %s and place it in %s", url, path) # This should probably move somewhere else, like e.g. a download_as # method on User objects and at the same time get rewritten to use @@ -61,17 +64,32 @@ def _fetch_s3_image(image, path, user, project): url_path) headers['Authorization'] = 'AWS %s:%s' % (access, signature) - cmd = ['/usr/bin/curl', '--fail', '--silent', url] - for (k,v) in headers.iteritems(): - cmd += ['-H', '%s: %s' % (k,v)] + def urlretrieve(urlfile, fpath): + chunk = 1*1024*1024 + f = open(fpath, "wb") + while 1: + data = urlfile.read(chunk) + if not data: + break + f.write(data) - cmd += ['-o', path] - return process.SharedPool().execute(executable=cmd[0], args=cmd[1:]) + request = urllib2.Request(url) + for (k, v) in headers.iteritems(): + request.add_header(k, v) + + urlopened = urllib2.urlopen(request) + + urlretrieve(urlopened, path) + + logging.debug("Finished retreving %s -- placed in %s", url, path) + + return def _fetch_local_image(image, path, user, project): - source = _image_path('%s/image' % image) - return process.simple_execute('cp %s %s' % (source, path)) + source = _image_path(os.path.join(image,'image')) + logging.debug("About to copy %s to %s", source, path) + return shutil.copy(source, path) def _image_path(path): -- cgit From 20aab4195baac543d638cf9c3a1484f8f9fb3d80 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 12 Oct 2010 15:04:39 -0700 Subject: Add design doc, docstrings, document hyper-v wmi, python wmi usage. Adhere to pep-8 more closely --- nova/virt/hyperv.py | 148 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 107 insertions(+), 41 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 91b86e265..388e833b2 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -16,16 +16,58 @@ """ A connection to Hyper-V . +Uses Windows Management Instrumentation (WMI) calls to interact with Hyper-V +Hyper-V WMI usage: + http://msdn.microsoft.com/en-us/library/cc723875%28v=VS.85%29.aspx +The Hyper-V object model briefly: + The physical computer and its hosted virtual machines are each represented + by the Msvm_ComputerSystem class. + + Each virtual machine is associated with a + Msvm_VirtualSystemGlobalSettingData (vs_gs_data) instance and one or more + Msvm_VirtualSystemSettingData (vmsetting) instances. For each vmsetting + there is a series of Msvm_ResourceAllocationSettingData (rasd) objects. + The rasd objects describe the settings for each device in a virtual machine. + Together, the vs_gs_data, vmsettings and rasds describe the configuration + of the virtual machine. + + Creating new resources such as disks and nics involves cloning a default + rasd object and appropriately modifying the clone and calling the + AddVirtualSystemResources WMI method + Changing resources such as memory uses the ModifyVirtualSystemResources + WMI method + +Using the Python WMI library: + Tutorial: + http://timgolden.me.uk/python/wmi/tutorial.html + Hyper-V WMI objects can be retrieved simply by using the class name + of the WMI object and optionally specifying a column to filter the + result set. More complex filters can be formed using WQL (sql-like) + queries. + The parameters and return tuples of WMI method calls can gleaned by + examining the doc string. For example: + >>> vs_man_svc.ModifyVirtualSystemResources.__doc__ + ModifyVirtualSystemResources (ComputerSystem, ResourceSettingData[]) + => (Job, ReturnValue)' + When passing setting data (ResourceSettingData) to the WMI method, + an XML representation of the data is passed in using the GetText_(1) method. + Available methods on a service can be determined using method.keys(): + >>> vs_man_svc.methods.keys() + vmsettings and rasds for a vm can be retrieved using the 'associators' + method with the appropriate return class. + Long running WMI commands generally return a Job (an instance of + Msvm_ConcreteJob) whose state can be polled to determine when it finishes """ import os import logging -import wmi import time from twisted.internet import defer +import wmi +from nova import exception from nova import flags from nova.auth.manager import AuthManager from nova.compute import power_state @@ -39,10 +81,9 @@ HYPERV_POWER_STATE = { 3 : power_state.SHUTDOWN, 2 : power_state.RUNNING, 32768 : power_state.PAUSED, - 32768: power_state.PAUSED, # TODO - 3 : power_state.CRASHED } + REQ_POWER_STATE = { 'Enabled' : 2, 'Disabled': 3, @@ -53,6 +94,11 @@ REQ_POWER_STATE = { } +WMI_JOB_STATUS_STARTED = 4096 +WMI_JOB_STATE_RUNNING = 4 +WMI_JOB_STATE_COMPLETED = 7 + + def get_connection(_): return HyperVConnection() @@ -63,19 +109,22 @@ class HyperVConnection(object): self._cim_conn = wmi.WMI(moniker = '//./root/cimv2') def list_instances(self): + """ Return the names of all the instances known to Hyper-V. """ vms = [v.ElementName \ for v in self._conn.Msvm_ComputerSystem(['ElementName'])] return vms @defer.inlineCallbacks def spawn(self, instance): + """ Create a new VM and start it.""" vm = yield self._lookup(instance.name) if vm is not None: - raise Exception('Attempted to create non-unique name %s' % + raise exception.Duplicate('Attempted to create duplicate name %s' % instance.name) user = AuthManager().get_user(instance['user_id']) project = AuthManager().get_project(instance['project_id']) + #Fetch the file, assume it is a VHD file. vhdfile = os.path.join(FLAGS.instances_path, instance['str_id'])+".vhd" yield images.fetch(instance['image_id'], vhdfile, user, project) @@ -93,14 +142,14 @@ class HyperVConnection(object): self.destroy(instance) def _create_vm(self, instance): - """Create a VM record. """ + """Create a VM but don't start it. """ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] vs_gs_data = self._conn.Msvm_VirtualSystemGlobalSettingData.new() vs_gs_data.ElementName = instance['name'] (job, ret_val) = vs_man_svc.DefineVirtualSystem( [], None, vs_gs_data.GetText_(1))[1:] - if (ret_val == 4096 ): #WMI job started + if ret_val == WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) else: success = (ret_val == 0) @@ -117,7 +166,7 @@ class HyperVConnection(object): if s.SettingType == 3][0] #avoid snapshots memsetting = vmsetting.associators(wmi_result_class= 'Msvm_MemorySettingData')[0] - #No Dynamic Memory + #No Dynamic Memory, so reservation, limit and quantity are identical. mem = long(str(instance['memory_mb'])) memsetting.VirtualQuantity = mem memsetting.Reservation = mem @@ -129,8 +178,7 @@ class HyperVConnection(object): logging.debug('Set memory for vm %s...', instance.name) procsetting = vmsetting.associators(wmi_result_class= 'Msvm_ProcessorSettingData')[0] - vcpus = long(str(instance['vcpus'])) - #vcpus = 1 + vcpus = long(instance['vcpus']) procsetting.VirtualQuantity = vcpus procsetting.Reservation = vcpus procsetting.Limit = vcpus @@ -140,11 +188,11 @@ class HyperVConnection(object): logging.debug('Set vcpus for vm %s...', instance.name) - def _create_disk(self, vm_name, vhdfile): """Create a disk and attach it to the vm""" logging.debug("Creating disk for %s by attaching disk file %s", \ vm_name, vhdfile) + #Find the IDE controller for the vm. vms = self._conn.MSVM_ComputerSystem (ElementName=vm_name) vm = vms[0] vmsettings = vm.associators( @@ -154,14 +202,17 @@ class HyperVConnection(object): ctrller = [r for r in rasds if r.ResourceSubType == 'Microsoft Emulated IDE Controller'\ and r.Address == "0" ] + #Find the default disk drive object for the vm and clone it. diskdflt = self._conn.query( "SELECT * FROM Msvm_ResourceAllocationSettingData \ WHERE ResourceSubType LIKE 'Microsoft Synthetic Disk Drive'\ AND InstanceID LIKE '%Default%'")[0] diskdrive = self._clone_wmi_obj( 'Msvm_ResourceAllocationSettingData', diskdflt) - diskdrive.Parent = ctrller[0].path_() + #Set the IDE ctrller as parent. + diskdrive.Parent = ctrller[0].path_() diskdrive.Address = 0 + #Add the cloned disk drive object to the vm. new_resources = self._add_virt_resource(diskdrive, vm) if new_resources is None: @@ -169,31 +220,36 @@ class HyperVConnection(object): diskdrive_path = new_resources[0] logging.debug("New disk drive path is " + diskdrive_path) + #Find the default VHD disk object. vhddefault = self._conn.query( "SELECT * FROM Msvm_ResourceAllocationSettingData \ WHERE ResourceSubType LIKE 'Microsoft Virtual Hard Disk' AND \ InstanceID LIKE '%Default%' ")[0] + #Clone the default and point it to the image file. vhddisk = self._clone_wmi_obj( 'Msvm_ResourceAllocationSettingData', vhddefault) - vhddisk.Parent = diskdrive_path + #Set the new drive as the parent. + vhddisk.Parent = diskdrive_path vhddisk.Connection = [vhdfile] + #Add the new vhd object as a virtual hard disk to the vm. new_resources = self._add_virt_resource(vhddisk, vm) if new_resources is None: raise Exception('Failed to add vhd file to VM %s', vm_name) logging.info("Created disk for %s ", vm_name) - def _create_nic(self, vm_name, mac): """Create a (emulated) nic and attach it to the vm""" logging.debug("Creating nic for %s ", vm_name) + #Find the vswitch that is connected to the physical nic. vms = self._conn.Msvm_ComputerSystem (ElementName=vm_name) extswitch = self._find_external_network() vm = vms[0] switch_svc = self._conn.Msvm_VirtualSwitchManagementService ()[0] - #use Msvm_SyntheticEthernetPortSettingData for Windows VMs or Linux with - #Linux Integration Components installed + #Find the default nic and clone it to create a new nic for the vm. + #Use Msvm_SyntheticEthernetPortSettingData for Windows VMs or Linux with + #Linux Integration Components installed. emulatednics_data = self._conn.Msvm_EmulatedEthernetPortSettingData() default_nic_data = [n for n in emulatednics_data if n.InstanceID.rfind('Default') >0 ] @@ -201,30 +257,33 @@ class HyperVConnection(object): 'Msvm_EmulatedEthernetPortSettingData', default_nic_data[0]) + #Create a port on the vswitch. (created_sw, ret_val) = switch_svc.CreateSwitchPort(vm_name, vm_name, "", extswitch.path_()) - if (ret_val != 0): + if ret_val != 0: logging.debug("Failed to create a new port on the external network") return logging.debug("Created switch port %s on switch %s", vm_name, extswitch.path_()) + #Connect the new nic to the new port. new_nic_data.Connection = [created_sw] new_nic_data.ElementName = vm_name + ' nic' new_nic_data.Address = ''.join(mac.split(':')) new_nic_data.StaticMacAddress = 'TRUE' + #Add the new nic to the vm. new_resources = self._add_virt_resource(new_nic_data, vm) if new_resources is None: raise Exception('Failed to add nic to VM %s', vm_name) logging.info("Created nic for %s ", vm_name) - def _add_virt_resource(self, res_setting_data, target_vm): + """Add a new resource (disk/nic) to the VM""" vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] (job, new_resources, return_val) = vs_man_svc.\ AddVirtualSystemResources([res_setting_data.GetText_(1)], target_vm.path_()) success = True - if (return_val == 4096 ): #WMI job started + if return_val == WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) else: success = (return_val == 0) @@ -235,16 +294,17 @@ class HyperVConnection(object): #TODO: use the reactor to poll instead of sleep def _check_job_status(self, jobpath): + """Poll WMI job state for completion""" inst_id = jobpath.split(':')[1].split('=')[1].strip('\"') jobs = self._conn.Msvm_ConcreteJob(InstanceID=inst_id) - if (len(jobs) == 0): + if len(jobs) == 0: return False job = jobs[0] - while job.JobState == 4: #job started + while job.JobState == WMI_JOB_STATE_RUNNING: time.sleep(0.1) job = self._conn.Msvm_ConcreteJob(InstanceID=inst_id)[0] - if (job.JobState != 7): #job success + if job.JobState != WMI_JOB_STATE_COMPLETED: logging.debug("WMI job failed: " + job.ErrorSummaryDescription) return False @@ -253,11 +313,13 @@ class HyperVConnection(object): return True - - def _find_external_network(self): + """Find the vswitch that is connected to the physical nic. + Assumes only one physical nic on the host + """ + #If there are no physical nics connected to networks, return. bound = self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE') - if (len(bound) == 0): + if len(bound) == 0: return None return self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')[0]\ @@ -266,30 +328,33 @@ class HyperVConnection(object): .associators(wmi_result_class='Msvm_VirtualSwitch')[0] def _clone_wmi_obj(self, wmi_class, wmi_obj): - cl = self._conn.__getattr__(wmi_class) - newinst = cl.new() + """Clone a WMI object""" + cl = self._conn.__getattr__(wmi_class) #get the class + newinst = cl.new() + #Copy the properties from the original. for prop in wmi_obj._properties: newinst.Properties_.Item(prop).Value =\ wmi_obj.Properties_.Item(prop).Value return newinst - @defer.inlineCallbacks def reboot(self, instance): + """Reboot the specified instance.""" vm = yield self._lookup(instance.name) if vm is None: - raise Exception('instance not present %s' % instance.name) + raise exception.NotFound('instance not present %s' % instance.name) self._set_vm_state(instance.name, 'Reboot') - @defer.inlineCallbacks def destroy(self, instance): + """Destroy the VM. Also destroy the associated VHD disk files""" logging.debug("Got request to destroy vm %s", instance.name) vm = yield self._lookup(instance.name) if vm is None: defer.returnValue(None) vm = self._conn.Msvm_ComputerSystem (ElementName=instance.name)[0] vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + #Stop the VM first. self._set_vm_state(instance.name, 'Disabled') vmsettings = vm.associators(wmi_result_class= 'Msvm_VirtualSystemSettingData') @@ -298,33 +363,35 @@ class HyperVConnection(object): disks = [r for r in rasds \ if r.ResourceSubType == 'Microsoft Virtual Hard Disk' ] diskfiles = [] + #Collect disk file information before destroying the VM. for disk in disks: diskfiles.extend([c for c in disk.Connection]) - + #Nuke the VM. Does not destroy disks. (job, ret_val) = vs_man_svc.DestroyVirtualSystem(vm.path_()) - if (ret_val == 4096 ): #WMI job started + if ret_val == WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) - elif (ret_val == 0): + elif ret_val == 0: success = True if not success: raise Exception('Failed to destroy vm %s' % instance.name) + #Delete associated vhd disk files. for disk in diskfiles: vhdfile = self._cim_conn.CIM_DataFile(Name=disk) for vf in vhdfile: vf.Delete() logging.debug("Deleted disk %s vm %s", vhdfile, instance.name) - - def get_info(self, instance_id): + """Get information about the VM""" vm = self._lookup(instance_id) if vm is None: - raise Exception('instance not present %s' % instance_id) + raise exception.NotFound('instance not present %s' % instance_id) vm = self._conn.Msvm_ComputerSystem(ElementName=instance_id)[0] vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] vmsettings = vm.associators(wmi_result_class= 'Msvm_VirtualSystemSettingData') settings_paths = [ v.path_() for v in vmsettings] + #See http://msdn.microsoft.com/en-us/library/cc160706%28VS.85%29.aspx summary_info = vs_man_svc.GetSummaryInformation( [4,100,103,105], settings_paths)[1] info = summary_info[0] @@ -341,7 +408,6 @@ class HyperVConnection(object): 'num_cpu': info.NumberOfProcessors, 'cpu_time': info.UpTime} - def _lookup(self, i): vms = self._conn.Msvm_ComputerSystem (ElementName=i) n = len(vms) @@ -353,15 +419,16 @@ class HyperVConnection(object): return vms[0].ElementName def _set_vm_state(self, vm_name, req_state): + """Set the desired state of the VM""" vms = self._conn.Msvm_ComputerSystem (ElementName=vm_name) if len(vms) == 0: return False status = vms[0].RequestStateChange(REQ_POWER_STATE[req_state]) job = status[0] return_val = status[1] - if (return_val == 4096 ): #WMI job started + if return_val == WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) - elif (return_val == 0): + elif return_val == 0: success = True if success: logging.info("Successfully changed vm state of %s to %s", @@ -372,16 +439,15 @@ class HyperVConnection(object): vm_name, req_state) return False - def attach_volume(self, instance_name, device_path, mountpoint): vm = self._lookup(instance_name) if vm is None: - raise Exception('Attempted to attach volume to nonexistent %s vm' % + raise exception.NotFound('Cannot attach volume to missing %s vm' % instance_name) def detach_volume(self, instance_name, mountpoint): vm = self._lookup(instance_name) if vm is None: - raise Exception('Attempted to detach volume from nonexistent %s ' % + raise exception.NotFound('Cannot detach volume from missing %s ' % instance_name) -- cgit From f224c0ed419f885aa85065d1a27623b22721d34c Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 12 Oct 2010 23:45:30 -0700 Subject: Fix typo, fix import --- nova/virt/images.py | 54 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 22 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index 90071107b..dad285fe0 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -27,12 +27,14 @@ import urlparse import shutil from nova import flags +from nova import process from nova.auth import manager from nova.auth import signer import logging import urllib2 import os +import sys FLAGS = flags.FLAGS flags.DEFINE_bool('use_s3', True, @@ -47,6 +49,24 @@ def fetch(image, path, user, project): return f(image, path, user, project) +def _fetch_image_no_curl(url, path, headers): + request = urllib2.Request(url) + for (k, v) in headers.iteritems(): + request.add_header(k, v) + + def urlretrieve(urlfile, fpath): + chunk = 1*1024*1024 + f = open(fpath, "wb") + while 1: + data = urlfile.read(chunk) + if not data: + break + f.write(data) + + urlopened = urllib2.urlopen(request) + urlretrieve(urlopened, path) + logging.debug("Finished retreving %s -- placed in %s", url, path) + def _fetch_s3_image(image, path, user, project): url = image_url(image) logging.debug("About to retrieve %s and place it in %s", url, path) @@ -64,32 +84,22 @@ def _fetch_s3_image(image, path, user, project): url_path) headers['Authorization'] = 'AWS %s:%s' % (access, signature) - def urlretrieve(urlfile, fpath): - chunk = 1*1024*1024 - f = open(fpath, "wb") - while 1: - data = urlfile.read(chunk) - if not data: - break - f.write(data) - - request = urllib2.Request(url) - for (k, v) in headers.iteritems(): - request.add_header(k, v) - - urlopened = urllib2.urlopen(request) - - urlretrieve(urlopened, path) - - logging.debug("Finished retreving %s -- placed in %s", url, path) - - return - + if sys.platform.startswith('win'): + return _fetch_image_no_curl(url, path, headers) + else: + cmd = ['/usr/bin/curl', '--fail', '--silent', url] + for (k,v) in headers.iteritems(): + cmd += ['-H', '%s: %s' % (k,v)] + cmd += ['-o', path] + return process.SharedPool().execute(executable=cmd[0], args=cmd[1:]) def _fetch_local_image(image, path, user, project): source = _image_path(os.path.join(image,'image')) logging.debug("About to copy %s to %s", source, path) - return shutil.copy(source, path) + if sys.platform.startswith('win'): + return shutil.copy(source, path) + else: + return process.simple_execute('cp %s %s' % (source, path)) def _image_path(path): -- cgit From 01ad0a05c4f93bb5e95a1c781d492374739dce2c Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 12 Oct 2010 23:53:31 -0700 Subject: Remove extraneous newlines --- nova/virt/images.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index dad285fe0..75e6f783e 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -48,7 +48,6 @@ def fetch(image, path, user, project): f = _fetch_local_image return f(image, path, user, project) - def _fetch_image_no_curl(url, path, headers): request = urllib2.Request(url) for (k, v) in headers.iteritems(): @@ -101,11 +100,9 @@ def _fetch_local_image(image, path, user, project): else: return process.simple_execute('cp %s %s' % (source, path)) - def _image_path(path): return os.path.join(FLAGS.images_path, path) - def image_url(image): return "http://%s:%s/_images/%s/image" % (FLAGS.s3_host, FLAGS.s3_port, image) -- cgit From b28c43c1f66cc111e34e9bbc45a78ff7aa60fd29 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Wed, 13 Oct 2010 00:06:29 -0700 Subject: Newlines again, reorder imports --- nova/virt/images.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index 75e6f783e..a68d856a1 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -21,20 +21,20 @@ Handling of VM disk images. """ +import logging +import os import os.path +import shutil +import sys import time +import urllib2 import urlparse -import shutil from nova import flags from nova import process from nova.auth import manager from nova.auth import signer -import logging -import urllib2 -import os -import sys FLAGS = flags.FLAGS flags.DEFINE_bool('use_s3', True, @@ -48,6 +48,7 @@ def fetch(image, path, user, project): f = _fetch_local_image return f(image, path, user, project) + def _fetch_image_no_curl(url, path, headers): request = urllib2.Request(url) for (k, v) in headers.iteritems(): @@ -103,6 +104,7 @@ def _fetch_local_image(image, path, user, project): def _image_path(path): return os.path.join(FLAGS.images_path, path) + def image_url(image): return "http://%s:%s/_images/%s/image" % (FLAGS.s3_host, FLAGS.s3_port, image) -- cgit From 9caed7b34d9b953bb8ecd306509443d076d1e4fe Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Wed, 13 Oct 2010 23:21:22 -0700 Subject: review comments --- nova/virt/hyperv.py | 248 +++++++++++++++++++++++++++++----------------------- nova/virt/images.py | 11 ++- 2 files changed, 146 insertions(+), 113 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 388e833b2..7451cac97 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -20,21 +20,21 @@ Uses Windows Management Instrumentation (WMI) calls to interact with Hyper-V Hyper-V WMI usage: http://msdn.microsoft.com/en-us/library/cc723875%28v=VS.85%29.aspx The Hyper-V object model briefly: - The physical computer and its hosted virtual machines are each represented - by the Msvm_ComputerSystem class. - - Each virtual machine is associated with a - Msvm_VirtualSystemGlobalSettingData (vs_gs_data) instance and one or more - Msvm_VirtualSystemSettingData (vmsetting) instances. For each vmsetting - there is a series of Msvm_ResourceAllocationSettingData (rasd) objects. - The rasd objects describe the settings for each device in a virtual machine. - Together, the vs_gs_data, vmsettings and rasds describe the configuration + The physical computer and its hosted virtual machines are each represented + by the Msvm_ComputerSystem class. + + Each virtual machine is associated with a + Msvm_VirtualSystemGlobalSettingData (vs_gs_data) instance and one or more + Msvm_VirtualSystemSettingData (vmsetting) instances. For each vmsetting + there is a series of Msvm_ResourceAllocationSettingData (rasd) objects. + The rasd objects describe the settings for each device in a VM. + Together, the vs_gs_data, vmsettings and rasds describe the configuration of the virtual machine. - Creating new resources such as disks and nics involves cloning a default - rasd object and appropriately modifying the clone and calling the + Creating new resources such as disks and nics involves cloning a default + rasd object and appropriately modifying the clone and calling the AddVirtualSystemResources WMI method - Changing resources such as memory uses the ModifyVirtualSystemResources + Changing resources such as memory uses the ModifyVirtualSystemResources WMI method Using the Python WMI library: @@ -47,10 +47,10 @@ Using the Python WMI library: The parameters and return tuples of WMI method calls can gleaned by examining the doc string. For example: >>> vs_man_svc.ModifyVirtualSystemResources.__doc__ - ModifyVirtualSystemResources (ComputerSystem, ResourceSettingData[]) + ModifyVirtualSystemResources (ComputerSystem, ResourceSettingData[]) => (Job, ReturnValue)' - When passing setting data (ResourceSettingData) to the WMI method, - an XML representation of the data is passed in using the GetText_(1) method. + When passing setting data (ResourceSettingData) to the WMI method, + an XML representation of the data is passed in using GetText_(1). Available methods on a service can be determined using method.keys(): >>> vs_man_svc.methods.keys() vmsettings and rasds for a vm can be retrieved using the 'associators' @@ -65,22 +65,23 @@ import logging import time from twisted.internet import defer -import wmi from nova import exception from nova import flags -from nova.auth.manager import AuthManager +from nova.auth import manager from nova.compute import power_state from nova.virt import images +wmi = None + FLAGS = flags.FLAGS HYPERV_POWER_STATE = { - 3 : power_state.SHUTDOWN, - 2 : power_state.RUNNING, - 32768 : power_state.PAUSED, + 3 : power_state.SHUTDOWN, + 2 : power_state.RUNNING, + 32768 : power_state.PAUSED, } @@ -98,15 +99,42 @@ WMI_JOB_STATUS_STARTED = 4096 WMI_JOB_STATE_RUNNING = 4 WMI_JOB_STATE_COMPLETED = 7 +##### Exceptions + + +class HyperVError(Exception): + """Base Exception class for all hyper-v errors.""" + def __init__(self, *args): + Exception.__init__(self, *args) + + +class VmResourceAllocationError(HyperVError): + """Raised when Hyper-V is unable to create or add a resource to + a VM + """ + def __init__(self, *args): + HyperVError.__init__(self, *args) + + +class VmOperationError(HyperVError): + """Raised when Hyper-V is unable to change the state of + a VM (start/stop/reboot/destroy) + """ + def __init__(self, *args): + HyperVError.__init__(self, *args) + def get_connection(_): + global wmi + if wmi is None: + wmi = __import__('wmi') return HyperVConnection() class HyperVConnection(object): def __init__(self): - self._conn = wmi.WMI(moniker = '//./root/virtualization') - self._cim_conn = wmi.WMI(moniker = '//./root/cimv2') + self._conn = wmi.WMI(moniker='//./root/virtualization') + self._cim_conn = wmi.WMI(moniker='//./root/cimv2') def list_instances(self): """ Return the names of all the instances known to Hyper-V. """ @@ -121,20 +149,22 @@ class HyperVConnection(object): if vm is not None: raise exception.Duplicate('Attempted to create duplicate name %s' % instance.name) - - user = AuthManager().get_user(instance['user_id']) - project = AuthManager().get_project(instance['project_id']) + + user = manager.AuthManager().get_user(instance['user_id']) + project = manager.AuthManager().get_project(instance['project_id']) #Fetch the file, assume it is a VHD file. - vhdfile = os.path.join(FLAGS.instances_path, instance['str_id'])+".vhd" + base_vhd_filename = os.path.join(FLAGS.instances_path, + instance['str_id']) + vhdfile = "%s.vhd" % (base_vhd_filename) yield images.fetch(instance['image_id'], vhdfile, user, project) - + try: yield self._create_vm(instance) yield self._create_disk(instance['name'], vhdfile) yield self._create_nic(instance['name'], instance['mac_address']) - - logging.debug ('Starting VM %s ', instance.name) + + logging.debug('Starting VM %s ', instance.name) yield self._set_vm_state(instance['name'], 'Enabled') logging.info('Started VM %s ', instance.name) except Exception as exn: @@ -147,23 +177,24 @@ class HyperVConnection(object): vs_gs_data = self._conn.Msvm_VirtualSystemGlobalSettingData.new() vs_gs_data.ElementName = instance['name'] - (job, ret_val) = vs_man_svc.DefineVirtualSystem( + (job, ret_val) = vs_man_svc.DefineVirtualSystem( [], None, vs_gs_data.GetText_(1))[1:] - if ret_val == WMI_JOB_STATUS_STARTED: + if ret_val == WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) else: success = (ret_val == 0) - + if not success: - raise Exception('Failed to create VM %s', instance.name) - + raise VmResourceAllocationException('Failed to create VM %s', + instance.name) + logging.debug('Created VM %s...', instance.name) - vm = self._conn.Msvm_ComputerSystem (ElementName=instance.name)[0] + vm = self._conn.Msvm_ComputerSystem(ElementName=instance.name)[0] - vmsettings = vm.associators(wmi_result_class= - 'Msvm_VirtualSystemSettingData') + vmsettings = vm.associators( + wmi_result_class='Msvm_VirtualSystemSettingData') vmsetting = [s for s in vmsettings - if s.SettingType == 3][0] #avoid snapshots + if s.SettingType == 3][0] # avoid snapshots memsetting = vmsetting.associators(wmi_result_class= 'Msvm_MemorySettingData')[0] #No Dynamic Memory, so reservation, limit and quantity are identical. @@ -174,7 +205,6 @@ class HyperVConnection(object): (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources( vm.path_(), [memsetting.GetText_(1)]) - logging.debug('Set memory for vm %s...', instance.name) procsetting = vmsetting.associators(wmi_result_class= 'Msvm_ProcessorSettingData')[0] @@ -185,41 +215,39 @@ class HyperVConnection(object): (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources( vm.path_(), [procsetting.GetText_(1)]) - logging.debug('Set vcpus for vm %s...', instance.name) - + def _create_disk(self, vm_name, vhdfile): """Create a disk and attach it to the vm""" - logging.debug("Creating disk for %s by attaching disk file %s", \ + logging.debug("Creating disk for %s by attaching disk file %s", vm_name, vhdfile) #Find the IDE controller for the vm. - vms = self._conn.MSVM_ComputerSystem (ElementName=vm_name) + vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name) vm = vms[0] vmsettings = vm.associators( wmi_result_class='Msvm_VirtualSystemSettingData') rasds = vmsettings[0].associators( wmi_result_class='MSVM_ResourceAllocationSettingData') ctrller = [r for r in rasds - if r.ResourceSubType == 'Microsoft Emulated IDE Controller'\ - and r.Address == "0" ] + if r.ResourceSubType == 'Microsoft Emulated IDE Controller'\ + and r.Address == "0"] #Find the default disk drive object for the vm and clone it. diskdflt = self._conn.query( - "SELECT * FROM Msvm_ResourceAllocationSettingData \ - WHERE ResourceSubType LIKE 'Microsoft Synthetic Disk Drive'\ - AND InstanceID LIKE '%Default%'")[0] + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType LIKE 'Microsoft Synthetic Disk Drive'\ + AND InstanceID LIKE '%Default%'")[0] diskdrive = self._clone_wmi_obj( 'Msvm_ResourceAllocationSettingData', diskdflt) #Set the IDE ctrller as parent. - diskdrive.Parent = ctrller[0].path_() + diskdrive.Parent = ctrller[0].path_() diskdrive.Address = 0 #Add the cloned disk drive object to the vm. new_resources = self._add_virt_resource(diskdrive, vm) - if new_resources is None: - raise Exception('Failed to add diskdrive to VM %s', vm_name) - + raise VmResourceAllocationError('Failed to add diskdrive to VM %s', + vm_name) diskdrive_path = new_resources[0] - logging.debug("New disk drive path is " + diskdrive_path) + logging.debug("New disk drive path is %s", diskdrive_path) #Find the default VHD disk object. vhddefault = self._conn.query( "SELECT * FROM Msvm_ResourceAllocationSettingData \ @@ -230,64 +258,66 @@ class HyperVConnection(object): vhddisk = self._clone_wmi_obj( 'Msvm_ResourceAllocationSettingData', vhddefault) #Set the new drive as the parent. - vhddisk.Parent = diskdrive_path + vhddisk.Parent = diskdrive_path vhddisk.Connection = [vhdfile] #Add the new vhd object as a virtual hard disk to the vm. new_resources = self._add_virt_resource(vhddisk, vm) if new_resources is None: - raise Exception('Failed to add vhd file to VM %s', vm_name) + raise VmResourceAllocationError('Failed to add vhd file to VM %s', + vm_name) logging.info("Created disk for %s ", vm_name) - + def _create_nic(self, vm_name, mac): """Create a (emulated) nic and attach it to the vm""" logging.debug("Creating nic for %s ", vm_name) #Find the vswitch that is connected to the physical nic. - vms = self._conn.Msvm_ComputerSystem (ElementName=vm_name) + vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name) extswitch = self._find_external_network() vm = vms[0] - switch_svc = self._conn.Msvm_VirtualSwitchManagementService ()[0] + switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0] #Find the default nic and clone it to create a new nic for the vm. - #Use Msvm_SyntheticEthernetPortSettingData for Windows VMs or Linux with + #Use Msvm_SyntheticEthernetPortSettingData for Windows or Linux with #Linux Integration Components installed. emulatednics_data = self._conn.Msvm_EmulatedEthernetPortSettingData() default_nic_data = [n for n in emulatednics_data - if n.InstanceID.rfind('Default') >0 ] + if n.InstanceID.rfind('Default') > 0] new_nic_data = self._clone_wmi_obj( 'Msvm_EmulatedEthernetPortSettingData', default_nic_data[0]) - #Create a port on the vswitch. - (created_sw, ret_val) = switch_svc.CreateSwitchPort(vm_name, vm_name, + (new_port, ret_val) = switch_svc.CreateSwitchPort(vm_name, vm_name, "", extswitch.path_()) if ret_val != 0: - logging.debug("Failed to create a new port on the external network") - return + logging.error("Failed creating a new port on the external vswitch") + raise VmResourceAllocationError('Failed creating port for %s', + vm_name) logging.debug("Created switch port %s on switch %s", vm_name, extswitch.path_()) #Connect the new nic to the new port. - new_nic_data.Connection = [created_sw] + new_nic_data.Connection = [new_port] new_nic_data.ElementName = vm_name + ' nic' new_nic_data.Address = ''.join(mac.split(':')) new_nic_data.StaticMacAddress = 'TRUE' #Add the new nic to the vm. new_resources = self._add_virt_resource(new_nic_data, vm) if new_resources is None: - raise Exception('Failed to add nic to VM %s', vm_name) + raise VmResourceAllocationError('Failed to add nic to VM %s', + vm_name) logging.info("Created nic for %s ", vm_name) def _add_virt_resource(self, res_setting_data, target_vm): """Add a new resource (disk/nic) to the VM""" vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] - (job, new_resources, return_val) = vs_man_svc.\ + (job, new_resources, ret_val) = vs_man_svc.\ AddVirtualSystemResources([res_setting_data.GetText_(1)], target_vm.path_()) success = True - if return_val == WMI_JOB_STATUS_STARTED: + if ret_val == WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) else: - success = (return_val == 0) - if success: + success = (ret_val == 0) + if success: return new_resources else: return None @@ -295,24 +325,23 @@ class HyperVConnection(object): #TODO: use the reactor to poll instead of sleep def _check_job_status(self, jobpath): """Poll WMI job state for completion""" + #Jobs have a path of the form: + #\\WIN-P5IG7367DAG\root\virtualization:Msvm_ConcreteJob.InstanceID="8A496B9C-AF4D-4E98-BD3C-1128CD85320D" inst_id = jobpath.split(':')[1].split('=')[1].strip('\"') jobs = self._conn.Msvm_ConcreteJob(InstanceID=inst_id) if len(jobs) == 0: return False job = jobs[0] - while job.JobState == WMI_JOB_STATE_RUNNING: + while job.JobState == WMI_JOB_STATE_RUNNING: time.sleep(0.1) job = self._conn.Msvm_ConcreteJob(InstanceID=inst_id)[0] - - if job.JobState != WMI_JOB_STATE_COMPLETED: - logging.debug("WMI job failed: " + job.ErrorSummaryDescription) + if job.JobState != WMI_JOB_STATE_COMPLETED: + logging.debug("WMI job failed: %s", job.ErrorSummaryDescription) return False - - logging.debug("WMI job succeeded: " + job.Description + ",Elapsed = " \ - + job.ElapsedTime) - + logging.debug("WMI job succeeded: %s, Elapsed=%s ", job.Description, + job.ElapsedTime) return True - + def _find_external_network(self): """Find the vswitch that is connected to the physical nic. Assumes only one physical nic on the host @@ -321,16 +350,15 @@ class HyperVConnection(object): bound = self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE') if len(bound) == 0: return None - return self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')[0]\ .associators(wmi_result_class='Msvm_SwitchLANEndpoint')[0]\ .associators(wmi_result_class='Msvm_SwitchPort')[0]\ - .associators(wmi_result_class='Msvm_VirtualSwitch')[0] + .associators(wmi_result_class='Msvm_VirtualSwitch')[0] def _clone_wmi_obj(self, wmi_class, wmi_obj): """Clone a WMI object""" - cl = self._conn.__getattr__(wmi_class) #get the class - newinst = cl.new() + cl = self._conn.__getattr__(wmi_class) # get the class + newinst = cl.new() #Copy the properties from the original. for prop in wmi_obj._properties: newinst.Properties_.Item(prop).Value =\ @@ -343,8 +371,8 @@ class HyperVConnection(object): vm = yield self._lookup(instance.name) if vm is None: raise exception.NotFound('instance not present %s' % instance.name) - self._set_vm_state(instance.name, 'Reboot') - + self._set_vm_state(instance.name, 'Reboot') + @defer.inlineCallbacks def destroy(self, instance): """Destroy the VM. Also destroy the associated VHD disk files""" @@ -352,7 +380,7 @@ class HyperVConnection(object): vm = yield self._lookup(instance.name) if vm is None: defer.returnValue(None) - vm = self._conn.Msvm_ComputerSystem (ElementName=instance.name)[0] + vm = self._conn.Msvm_ComputerSystem(ElementName=instance.name)[0] vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] #Stop the VM first. self._set_vm_state(instance.name, 'Disabled') @@ -361,26 +389,26 @@ class HyperVConnection(object): rasds = vmsettings[0].associators(wmi_result_class= 'MSVM_ResourceAllocationSettingData') disks = [r for r in rasds \ - if r.ResourceSubType == 'Microsoft Virtual Hard Disk' ] + if r.ResourceSubType == 'Microsoft Virtual Hard Disk'] diskfiles = [] #Collect disk file information before destroying the VM. for disk in disks: diskfiles.extend([c for c in disk.Connection]) #Nuke the VM. Does not destroy disks. (job, ret_val) = vs_man_svc.DestroyVirtualSystem(vm.path_()) - if ret_val == WMI_JOB_STATUS_STARTED: + if ret_val == WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) elif ret_val == 0: success = True if not success: - raise Exception('Failed to destroy vm %s' % instance.name) + raise VmOperationError('Failed to destroy vm %s' % instance.name) #Delete associated vhd disk files. for disk in diskfiles: vhdfile = self._cim_conn.CIM_DataFile(Name=disk) for vf in vhdfile: vf.Delete() logging.debug("Deleted disk %s vm %s", vhdfile, instance.name) - + def get_info(self, instance_id): """Get information about the VM""" vm = self._lookup(instance_id) @@ -390,10 +418,10 @@ class HyperVConnection(object): vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] vmsettings = vm.associators(wmi_result_class= 'Msvm_VirtualSystemSettingData') - settings_paths = [ v.path_() for v in vmsettings] + settings_paths = [v.path_() for v in vmsettings] #See http://msdn.microsoft.com/en-us/library/cc160706%28VS.85%29.aspx summary_info = vs_man_svc.GetSummaryInformation( - [4,100,103,105], settings_paths)[1] + [4, 100, 103, 105], settings_paths)[1] info = summary_info[0] logging.debug("Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, \ cpu_time=%s", instance_id, @@ -401,16 +429,16 @@ class HyperVConnection(object): str(info.MemoryUsage), str(info.NumberOfProcessors), str(info.UpTime)) - + return {'state': HYPERV_POWER_STATE[info.EnabledState], 'max_mem': info.MemoryUsage, 'mem': info.MemoryUsage, 'num_cpu': info.NumberOfProcessors, 'cpu_time': info.UpTime} - + def _lookup(self, i): - vms = self._conn.Msvm_ComputerSystem (ElementName=i) - n = len(vms) + vms = self._conn.Msvm_ComputerSystem(ElementName=i) + n = len(vms) if n == 0: return None elif n > 1: @@ -420,34 +448,36 @@ class HyperVConnection(object): def _set_vm_state(self, vm_name, req_state): """Set the desired state of the VM""" - vms = self._conn.Msvm_ComputerSystem (ElementName=vm_name) + vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name) if len(vms) == 0: return False - status = vms[0].RequestStateChange(REQ_POWER_STATE[req_state]) - job = status[0] - return_val = status[1] - if return_val == WMI_JOB_STATUS_STARTED: + (job, ret_val) = vms[0].RequestStateChange(REQ_POWER_STATE[req_state]) + success = False + if ret_val == WMI_JOB_STATUS_STARTED: success = self._check_job_status(job) - elif return_val == 0: + elif ret_val == 0: + success = True + elif ret_val == 32775: + #Invalid state for current operation. Typically means it is + #already in the state requested success = True if success: logging.info("Successfully changed vm state of %s to %s", vm_name, req_state) - return True else: - logging.debug("Failed to change vm state of %s to %s", + logging.error("Failed to change vm state of %s to %s", vm_name, req_state) - return False - + raise VmOperationError("Failed to change vm state of %s to %s", + vm_name, req_state) + def attach_volume(self, instance_name, device_path, mountpoint): - vm = self._lookup(instance_name) + vm = self._lookup(instance_name) if vm is None: raise exception.NotFound('Cannot attach volume to missing %s vm' % instance_name) def detach_volume(self, instance_name, mountpoint): - vm = self._lookup(instance_name) + vm = self._lookup(instance_name) if vm is None: raise exception.NotFound('Cannot detach volume from missing %s ' % instance_name) - diff --git a/nova/virt/images.py b/nova/virt/images.py index a68d856a1..6ef652e4a 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -22,7 +22,6 @@ Handling of VM disk images. """ import logging -import os import os.path import shutil import sys @@ -34,6 +33,7 @@ from nova import flags from nova import process from nova.auth import manager from nova.auth import signer +from nova.objectstore import image FLAGS = flags.FLAGS @@ -55,7 +55,7 @@ def _fetch_image_no_curl(url, path, headers): request.add_header(k, v) def urlretrieve(urlfile, fpath): - chunk = 1*1024*1024 + chunk = 1 * 1024 * 1024 f = open(fpath, "wb") while 1: data = urlfile.read(chunk) @@ -66,7 +66,8 @@ def _fetch_image_no_curl(url, path, headers): urlopened = urllib2.urlopen(request) urlretrieve(urlopened, path) logging.debug("Finished retreving %s -- placed in %s", url, path) - + + def _fetch_s3_image(image, path, user, project): url = image_url(image) logging.debug("About to retrieve %s and place it in %s", url, path) @@ -93,14 +94,16 @@ def _fetch_s3_image(image, path, user, project): cmd += ['-o', path] return process.SharedPool().execute(executable=cmd[0], args=cmd[1:]) + def _fetch_local_image(image, path, user, project): - source = _image_path(os.path.join(image,'image')) + source = _image_path(os.path.join(image, 'image')) logging.debug("About to copy %s to %s", source, path) if sys.platform.startswith('win'): return shutil.copy(source, path) else: return process.simple_execute('cp %s %s' % (source, path)) + def _image_path(path): return os.path.join(FLAGS.images_path, path) -- cgit From 5a34f93790cf6fb98e9474797f5be3f231a4a6a4 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Thu, 14 Oct 2010 11:27:42 -0700 Subject: fix indent --- nova/virt/images.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index 6ef652e4a..b7daeb064 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -91,8 +91,9 @@ def _fetch_s3_image(image, path, user, project): cmd = ['/usr/bin/curl', '--fail', '--silent', url] for (k,v) in headers.iteritems(): cmd += ['-H', '%s: %s' % (k,v)] - cmd += ['-o', path] - return process.SharedPool().execute(executable=cmd[0], args=cmd[1:]) + + cmd += ['-o', path] + return process.SharedPool().execute(executable=cmd[0], args=cmd[1:]) def _fetch_local_image(image, path, user, project): -- cgit From b328bac09fee6ff2de6e8326e655ee648bda5e2d Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Thu, 14 Oct 2010 11:56:25 -0700 Subject: revert to generic exceptions --- nova/virt/hyperv.py | 38 +++++++------------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 7451cac97..968889116 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -99,30 +99,6 @@ WMI_JOB_STATUS_STARTED = 4096 WMI_JOB_STATE_RUNNING = 4 WMI_JOB_STATE_COMPLETED = 7 -##### Exceptions - - -class HyperVError(Exception): - """Base Exception class for all hyper-v errors.""" - def __init__(self, *args): - Exception.__init__(self, *args) - - -class VmResourceAllocationError(HyperVError): - """Raised when Hyper-V is unable to create or add a resource to - a VM - """ - def __init__(self, *args): - HyperVError.__init__(self, *args) - - -class VmOperationError(HyperVError): - """Raised when Hyper-V is unable to change the state of - a VM (start/stop/reboot/destroy) - """ - def __init__(self, *args): - HyperVError.__init__(self, *args) - def get_connection(_): global wmi @@ -244,7 +220,7 @@ class HyperVConnection(object): #Add the cloned disk drive object to the vm. new_resources = self._add_virt_resource(diskdrive, vm) if new_resources is None: - raise VmResourceAllocationError('Failed to add diskdrive to VM %s', + raise Exception('Failed to add diskdrive to VM %s', vm_name) diskdrive_path = new_resources[0] logging.debug("New disk drive path is %s", diskdrive_path) @@ -264,7 +240,7 @@ class HyperVConnection(object): #Add the new vhd object as a virtual hard disk to the vm. new_resources = self._add_virt_resource(vhddisk, vm) if new_resources is None: - raise VmResourceAllocationError('Failed to add vhd file to VM %s', + raise Exception('Failed to add vhd file to VM %s', vm_name) logging.info("Created disk for %s ", vm_name) @@ -290,7 +266,7 @@ class HyperVConnection(object): "", extswitch.path_()) if ret_val != 0: logging.error("Failed creating a new port on the external vswitch") - raise VmResourceAllocationError('Failed creating port for %s', + raise Exception('Failed creating port for %s', vm_name) logging.debug("Created switch port %s on switch %s", vm_name, extswitch.path_()) @@ -302,7 +278,7 @@ class HyperVConnection(object): #Add the new nic to the vm. new_resources = self._add_virt_resource(new_nic_data, vm) if new_resources is None: - raise VmResourceAllocationError('Failed to add nic to VM %s', + raise Exception('Failed to add nic to VM %s', vm_name) logging.info("Created nic for %s ", vm_name) @@ -327,7 +303,7 @@ class HyperVConnection(object): """Poll WMI job state for completion""" #Jobs have a path of the form: #\\WIN-P5IG7367DAG\root\virtualization:Msvm_ConcreteJob.InstanceID="8A496B9C-AF4D-4E98-BD3C-1128CD85320D" - inst_id = jobpath.split(':')[1].split('=')[1].strip('\"') + inst_id = jobpath.split('=')[1].strip('"') jobs = self._conn.Msvm_ConcreteJob(InstanceID=inst_id) if len(jobs) == 0: return False @@ -401,7 +377,7 @@ class HyperVConnection(object): elif ret_val == 0: success = True if not success: - raise VmOperationError('Failed to destroy vm %s' % instance.name) + raise Exception('Failed to destroy vm %s' % instance.name) #Delete associated vhd disk files. for disk in diskfiles: vhdfile = self._cim_conn.CIM_DataFile(Name=disk) @@ -467,7 +443,7 @@ class HyperVConnection(object): else: logging.error("Failed to change vm state of %s to %s", vm_name, req_state) - raise VmOperationError("Failed to change vm state of %s to %s", + raise Exception("Failed to change vm state of %s to %s", vm_name, req_state) def attach_volume(self, instance_name, device_path, mountpoint): -- cgit From a58648f0ce5472e0b671d1b043fc4e0afd01658c Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Thu, 14 Oct 2010 13:37:49 -0700 Subject: remove nonexistent exception --- nova/virt/hyperv.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 968889116..9adb2f00a 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -161,8 +161,7 @@ class HyperVConnection(object): success = (ret_val == 0) if not success: - raise VmResourceAllocationException('Failed to create VM %s', - instance.name) + raise Exception('Failed to create VM %s', instance.name) logging.debug('Created VM %s...', instance.name) vm = self._conn.Msvm_ComputerSystem(ElementName=instance.name)[0] -- cgit From 82ccd2b656a364251aeecbf4c31cd062af6513f0 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 14 Dec 2010 16:48:44 -0800 Subject: remove some logging --- nova/virt/images.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index b322f3eb2..69838ac5b 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -70,7 +70,6 @@ def _fetch_image_no_curl(url, path, headers): def _fetch_s3_image(image, path, user, project): url = image_url(image) - logging.debug("About to retrieve %s and place it in %s", url, path) # This should probably move somewhere else, like e.g. a download_as # method on User objects and at the same time get rewritten to use @@ -98,7 +97,6 @@ def _fetch_s3_image(image, path, user, project): def _fetch_local_image(image, path, user, project): source = _image_path(os.path.join(image, 'image')) - logging.debug("About to copy %s to %s", source, path) if sys.platform.startswith('win'): return shutil.copy(source, path) else: -- cgit From 66f8e28fb4f4a898803ac6a38974a9fa804612d0 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 30 Dec 2010 18:12:57 -0600 Subject: completed the basic xenstore read/write/delete functionality --- nova/virt/xenapi/vmops.py | 222 +++++++++++++++++++++++++++++++++++++++++----- nova/virt/xenapi_conn.py | 28 +++--- 2 files changed, 220 insertions(+), 30 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 0517209e2..5c9455db3 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2010 Citrix Systems, Inc. +# Copyright 2010 OpenStack LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -18,6 +19,7 @@ Management class for VM-related functions (spawn, reboot, etc). """ +import json import logging from nova import db @@ -36,7 +38,6 @@ class VMOps(object): """ Management class for VM-related tasks """ - def __init__(self, session): self.XenAPI = session.get_imported_xenapi() self._session = session @@ -120,6 +121,20 @@ class VMOps(object): timer.f = _wait_for_boot return timer.start(interval=0.5, now=True) + def _get_vm_opaque_ref(self, instance_or_vm): + """Refactored out the common code of many methods that receive either + a vm name or a vm instance, and want a vm instance in return. + """ + try: + instance_name = instance_or_vm.name + vm = VMHelper.lookup(self._session, instance_name) + except AttributeError: + # A vm opaque ref was passed + vm = instance_or_vm + if vm is None: + raise Exception(_('Instance not present %s') % instance_name) + return vm + def snapshot(self, instance, name): """ Create snapshot from a running VM instance @@ -168,11 +183,7 @@ class VMOps(object): def reboot(self, instance): """Reboot VM instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: - raise exception.NotFound(_('instance not' - ' found %s') % instance_name) + vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.clean_reboot', vm) self._session.wait_for_task(instance.id, task) @@ -215,27 +226,19 @@ class VMOps(object): ret = None try: ret = self._session.wait_for_task(instance_id, task) - except XenAPI.Failure, exc: + except self.XenAPI.Failure, exc: logging.warn(exc) callback(ret) def pause(self, instance, callback): """Pause VM instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: - raise exception.NotFound(_('Instance not' - ' found %s') % instance_name) + vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.pause', vm) self._wait_with_callback(instance.id, task, callback) def unpause(self, instance, callback): """Unpause VM instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: - raise exception.NotFound(_('Instance not' - ' found %s') % instance_name) + vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.unpause', vm) self._wait_with_callback(instance.id, task, callback) @@ -270,9 +273,7 @@ class VMOps(object): def get_diagnostics(self, instance_id): """Return data about VM diagnostics""" - vm = VMHelper.lookup(self._session, instance_id) - if vm is None: - raise exception.NotFound(_("Instance not found %s") % instance_id) + vm = self._get_vm_opaque_ref(instance) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_diagnostics(self._session, rec) @@ -280,3 +281,184 @@ class VMOps(object): """Return snapshot of console""" # TODO: implement this to fix pylint! return 'FAKE CONSOLE OUTPUT of instance' + + def list_from_xenstore(self, vm, path): + """Runs the xenstore-ls command to get a listing of all records + from 'path' downward. Returns a dict with the sub-paths as keys, + and the value stored in those paths as values. If nothing is + found at that path, returns None. + """ + ret = self._make_xenstore_call('list_records', vm, path) + try: + return json.loads(ret) + except ValueError: + # Not a valid JSON value + return ret + + def read_from_xenstore(self, vm, path): + """Returns the value stored in the xenstore record for the given VM + at the specified location. A XenAPIPlugin.PluginError will be raised + if any error is encountered in the read process. + """ + try: + ret = self._make_xenstore_call('read_record', vm, path, + {'ignore_missing_path': 'True'}) + except self.XenAPI.Failure, e: + print "XENERR", e + return None + except StandardError, e: + print "ERR", type(e), e, e.msg + return None + try: + return json.loads(ret) + except ValueError: + # Not a JSON object + if ret == "None": + # Can't marshall None over RPC calls. + return None + return ret + + def write_to_xenstore(self, vm, path, value): + """Writes the passed value to the xenstore record for the given VM + at the specified location. A XenAPIPlugin.PluginError will be raised + if any error is encountered in the write process. + """ + return self._make_xenstore_call('write_record', vm, path, {'value': json.dumps(value)}) + + def clear_xenstore(self, vm, path): + """Deletes the VM's xenstore record for the specified path. + If there is no such record, the request is ignored. + """ + self._make_xenstore_call('delete_record', vm, path) + + def _make_xenstore_call(self, method, vm, path, addl_args={}): + """Handles calls to the xenstore xenapi plugin.""" + return self._make_plugin_call('xenstore.py', method=method, vm=vm, path=path, + addl_args=addl_args) + + def _make_plugin_call(self, plugin, method, vm, path, addl_args={}): + """Abstracts out the process of calling a method of a xenapi plugin. + Any errors raised by the plugin will in turn raise a RuntimeError here. + """ + vm = self._get_vm_opaque_ref(vm) + rec = self._session.get_xenapi().VM.get_record(vm) + args = {'dom_id': rec['domid'], 'path': path} + args.update(addl_args) + # If the 'testing_mode' attribute is set, add that to the args. + if getattr(self, 'testing_mode', False): + args['testing_mode'] = 'true' + try: + task = self._session.async_call_plugin(plugin, method, args) + ret = self._session.wait_for_task(0, task) + except self.XenAPI.Failure, e: + raise RuntimeError("%s" % e.details[-1]) + return ret + + def add_to_xenstore(self, vm, path, key, value): + """Adds the passed key/value pair to the xenstore record for + the given VM at the specified location. A XenAPIPlugin.PluginError + will be raised if any error is encountered in the write process. + """ + current = self.read_from_xenstore(vm, path) + if not current: + # Nothing at that location + current = {key: value} + else: + current[key] = value + self.write_to_xenstore(vm, path, current) + + def remove_from_xenstore(self, vm, path, key_or_keys): + """Takes either a single key or a list of keys and removes + them from the xenstoreirecord data for the given VM. + If the key doesn't exist, the request is ignored. + """ + current = self.list_from_xenstore(vm, path) + if not current: + return + if isinstance(key_or_keys, basestring): + keys = [key_or_keys] + else: + keys = key_or_keys + keys.sort(lambda x,y: cmp(y.count('/'), x.count('/'))) + for key in keys: + if path: + keypath = "%s/%s" % (path, key) + else: + keypath = key + self._make_xenstore_call('delete_record', vm, keypath) + + + ######################################################################## + ###### The following methods interact with the xenstore parameter + ###### record, not the live xenstore. They were created before I + ###### knew the difference, and are left in here in case they prove + ###### to be useful. They all have '_param' added to their method + ###### names to distinguish them. (dabo) + ######################################################################## + def read_partial_from_param_xenstore(self, instance_or_vm, key_prefix): + """Returns a dict of all the keys in the xenstore parameter record + for the given instance that begin with the key_prefix. + """ + data = self.read_from_param_xenstore(instance_or_vm) + badkeys = [k for k in data.keys() + if not k.startswith(key_prefix)] + for badkey in badkeys: + del data[badkey] + return data + + def read_from_param_xenstore(self, instance_or_vm, keys=None): + """Returns the xenstore parameter record data for the specified VM instance + as a dict. Accepts an optional key or list of keys; if a value for 'keys' + is passed, the returned dict is filtered to only return the values + for those keys. + """ + vm = self._get_vm_opaque_ref(instance_or_vm) + data = self._session.call_xenapi_request('VM.get_xenstore_data', (vm, )) + ret = {} + if keys is None: + keys = data.keys() + elif isinstance(keys, basestring): + keys = [keys] + for key in keys: + raw = data.get(key) + if raw: + ret[key] = json.loads(raw) + else: + ret[key] = raw + return ret + + def add_to_param_xenstore(self, instance_or_vm, key, val): + """Takes a key/value pair and adds it to the xenstore parameter + record for the given vm instance. If the key exists in xenstore, + it is overwritten""" + vm = self._get_vm_opaque_ref(instance_or_vm) + self.remove_from_param_xenstore(instance_or_vm, key) + jsonval = json.dumps(val) + self._session.call_xenapi_request('VM.add_to_xenstore_data', + (vm, key, jsonval)) + + def write_to_param_xenstore(self, instance_or_vm, mapping): + """Takes a dict and writes each key/value pair to the xenstore + parameter record for the given vm instance. Any existing data for + those keys is overwritten. + """ + for k, v in mapping.iteritems(): + self.add_to_param_xenstore(instance_or_vm, k, v) + + def remove_from_param_xenstore(self, instance_or_vm, key_or_keys): + """Takes either a single key or a list of keys and removes + them from the xenstore parameter record data for the given VM. + If the key doesn't exist, the request is ignored. + """ + vm = self._get_vm_opaque_ref(instance_or_vm) + if isinstance(key_or_keys, basestring): + keys = [key_or_keys] + else: + keys = key_or_keys + for key in keys: + self._session.call_xenapi_request('VM.remove_from_xenstore_data', (vm, key)) + + def clear_param_xenstore(self, instance_or_vm): + """Removes all data from the xenstore parameter record for this VM.""" + self.write_to_param_xenstore(instance_or_vm, {}) + ######################################################################## diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index d12e78c67..863461ee7 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright (c) 2010 Citrix Systems, Inc. +# Copyright 2010 OpenStack LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -19,15 +20,15 @@ A connection to XenServer or Xen Cloud Platform. The concurrency model for this class is as follows: -All XenAPI calls are on a thread (using t.i.t.deferToThread, via the decorator -deferredToThread). They are remote calls, and so may hang for the usual -reasons. They should not be allowed to block the reactor thread. +All XenAPI calls are on a green thread (using eventlet's "tpool" +thread pool). They are remote calls, and so may hang for the usual +reasons. All long-running XenAPI calls (VM.start, VM.reboot, etc) are called async -(using XenAPI.VM.async_start etc). These return a task, which can then be -polled for completion. Polling is handled using reactor.callLater. +(using XenAPI.VM.async_start etc). These return a task, which can then be +polled for completion. -This combination of techniques means that we don't block the reactor thread at +This combination of techniques means that we don't block the main thread at all, and at the same time we don't hold lots of threads waiting for long-running operations. @@ -81,7 +82,7 @@ flags.DEFINE_string('xenapi_connection_password', flags.DEFINE_float('xenapi_task_poll_interval', 0.5, 'The interval used for polling of remote tasks ' - '(Async.VM.start, etc). Used only if ' + '(Async.VM.start, etc). Used only if ' 'connection_type=xenapi.') flags.DEFINE_float('xenapi_vhd_coalesce_poll_interval', 5.0, @@ -213,6 +214,14 @@ class XenAPISession(object): f = f.__getattr__(m) return tpool.execute(f, *args) + def call_xenapi_request(self, method, *args): + """Some interactions with dom0, such as interacting with xenstore's + param record, require using the xenapi_request method of the session + object. This wraps that call on a background thread. + """ + f = self._session.xenapi_request + return tpool.execute(f, method, *args) + def async_call_plugin(self, plugin, fn, args): """Call Async.host.call_plugin on a background thread.""" return tpool.execute(self._unwrap_plugin_exceptions, @@ -222,7 +231,6 @@ class XenAPISession(object): def wait_for_task(self, id, task): """Return the result of the given task. The task is polled until it completes.""" - done = event.Event() loop = utils.LoopingCall(self._poll_task, id, task, done) loop.start(FLAGS.xenapi_task_poll_interval, now=True) @@ -235,7 +243,7 @@ class XenAPISession(object): return self.XenAPI.Session(url) def _poll_task(self, id, task, done): - """Poll the given XenAPI task, and fire the given Deferred if we + """Poll the given XenAPI task, and fire the given action if we get a result.""" try: name = self._session.xenapi.task.get_name_label(task) @@ -290,7 +298,7 @@ class XenAPISession(object): def _parse_xmlrpc_value(val): - """Parse the given value as if it were an XML-RPC value. This is + """Parse the given value as if it were an XML-RPC value. This is sometimes used as the format for the task.result field.""" if not val: return val -- cgit From b5f5ec40bbc6b898ac73444e9a0f0372c92fc12a Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Thu, 30 Dec 2010 18:25:30 -0600 Subject: removed some debugging code left in previous push. --- nova/virt/xenapi/vmops.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 5c9455db3..c46a881a8 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -304,10 +304,6 @@ class VMOps(object): ret = self._make_xenstore_call('read_record', vm, path, {'ignore_missing_path': 'True'}) except self.XenAPI.Failure, e: - print "XENERR", e - return None - except StandardError, e: - print "ERR", type(e), e, e.msg return None try: return json.loads(ret) -- cgit From f0e4bed6f4bf4ab3835ecd3e54eb9d7ac21dd5f1 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Fri, 31 Dec 2010 07:04:40 -0600 Subject: fixed pep8 issues --- nova/virt/xenapi/vmops.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c46a881a8..20f5e0215 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -285,7 +285,7 @@ class VMOps(object): def list_from_xenstore(self, vm, path): """Runs the xenstore-ls command to get a listing of all records from 'path' downward. Returns a dict with the sub-paths as keys, - and the value stored in those paths as values. If nothing is + and the value stored in those paths as values. If nothing is found at that path, returns None. """ ret = self._make_xenstore_call('list_records', vm, path) @@ -319,7 +319,8 @@ class VMOps(object): at the specified location. A XenAPIPlugin.PluginError will be raised if any error is encountered in the write process. """ - return self._make_xenstore_call('write_record', vm, path, {'value': json.dumps(value)}) + return self._make_xenstore_call('write_record', vm, path, + {'value': json.dumps(value)}) def clear_xenstore(self, vm, path): """Deletes the VM's xenstore record for the specified path. @@ -329,8 +330,8 @@ class VMOps(object): def _make_xenstore_call(self, method, vm, path, addl_args={}): """Handles calls to the xenstore xenapi plugin.""" - return self._make_plugin_call('xenstore.py', method=method, vm=vm, path=path, - addl_args=addl_args) + return self._make_plugin_call('xenstore.py', method=method, vm=vm, + path=path, addl_args=addl_args) def _make_plugin_call(self, plugin, method, vm, path, addl_args={}): """Abstracts out the process of calling a method of a xenapi plugin. @@ -375,7 +376,7 @@ class VMOps(object): keys = [key_or_keys] else: keys = key_or_keys - keys.sort(lambda x,y: cmp(y.count('/'), x.count('/'))) + keys.sort(lambda x, y: cmp(y.count('/'), x.count('/'))) for key in keys: if path: keypath = "%s/%s" % (path, key) @@ -383,9 +384,8 @@ class VMOps(object): keypath = key self._make_xenstore_call('delete_record', vm, keypath) - ######################################################################## - ###### The following methods interact with the xenstore parameter + ###### The following methods interact with the xenstore parameter ###### record, not the live xenstore. They were created before I ###### knew the difference, and are left in here in case they prove ###### to be useful. They all have '_param' added to their method @@ -403,13 +403,14 @@ class VMOps(object): return data def read_from_param_xenstore(self, instance_or_vm, keys=None): - """Returns the xenstore parameter record data for the specified VM instance - as a dict. Accepts an optional key or list of keys; if a value for 'keys' - is passed, the returned dict is filtered to only return the values - for those keys. + """Returns the xenstore parameter record data for the specified VM + instance as a dict. Accepts an optional key or list of keys; if a + value for 'keys' is passed, the returned dict is filtered to only + return the values for those keys. """ vm = self._get_vm_opaque_ref(instance_or_vm) - data = self._session.call_xenapi_request('VM.get_xenstore_data', (vm, )) + data = self._session.call_xenapi_request('VM.get_xenstore_data', + (vm, )) ret = {} if keys is None: keys = data.keys() @@ -452,7 +453,8 @@ class VMOps(object): else: keys = key_or_keys for key in keys: - self._session.call_xenapi_request('VM.remove_from_xenstore_data', (vm, key)) + self._session.call_xenapi_request('VM.remove_from_xenstore_data', + (vm, key)) def clear_param_xenstore(self, instance_or_vm): """Removes all data from the xenstore parameter record for this VM.""" -- cgit From 97cfb850033597eebe6be88266cd0e1f457ec9bc Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Mon, 3 Jan 2011 11:37:07 -0800 Subject: Merge from trunk: process replaced with util --- nova/virt/images.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index 69838ac5b..0ec578b06 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -30,7 +30,7 @@ import urllib2 import urlparse from nova import flags -from nova import process +from nova import utils from nova.auth import manager from nova.auth import signer from nova.objectstore import image @@ -60,9 +60,7 @@ def _fetch_image_no_curl(url, path, headers): while 1: data = urlfile.read(chunk) if not data: - break - f.write(data) - + break f.write(data) urlopened = urllib2.urlopen(request) urlretrieve(urlopened, path) logging.debug("Finished retreving %s -- placed in %s", url, path) @@ -73,7 +71,7 @@ def _fetch_s3_image(image, path, user, project): # This should probably move somewhere else, like e.g. a download_as # method on User objects and at the same time get rewritten to use - # twisted web client. + # a web client. headers = {} headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) @@ -90,9 +88,9 @@ def _fetch_s3_image(image, path, user, project): cmd = ['/usr/bin/curl', '--fail', '--silent', url] for (k, v) in headers.iteritems(): cmd += ['-H', '%s: %s' % (k, v)] - - cmd += ['-o', path] - return process.SharedPool().execute(executable=cmd[0], args=cmd[1:]) + cmd += ['-o', path] + cmd_out = ' '.join(cmd) + return utils.execute(cmd_out) def _fetch_local_image(image, path, user, project): @@ -100,7 +98,7 @@ def _fetch_local_image(image, path, user, project): if sys.platform.startswith('win'): return shutil.copy(source, path) else: - return process.simple_execute('cp %s %s' % (source, path)) + return utils.execute('cp %s %s' % (source, path)) def _image_path(path): -- cgit From 8bbcadafbc25c7ab478d6143293232f2cea24411 Mon Sep 17 00:00:00 2001 From: Eric Day Date: Tue, 4 Jan 2011 09:44:15 -0800 Subject: Removed UUID keys for instance and volume. --- nova/virt/xenapi/volume_utils.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index 1ca813bcf..0ad6fd450 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -202,6 +202,9 @@ class VolumeHelper(HelperBase): def _get_volume_id(path): """Retrieve the volume id from device_path""" + # If we have the ID and not a path, just return it. + if isinstance(path, int): + return path # n must contain at least the volume_id # /vol- is for remote volumes # -vol- is for local volumes -- cgit From 2899896d1c7742ad59e2da2d2369bc2ff9526fed Mon Sep 17 00:00:00 2001 From: Eric Day Date: Tue, 4 Jan 2011 12:27:50 -0800 Subject: Renamed argument to represent possible types in volume_utils. --- nova/virt/xenapi/volume_utils.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index 0ad6fd450..4bbc41b03 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -200,18 +200,19 @@ class VolumeHelper(HelperBase): return -1 -def _get_volume_id(path): +def _get_volume_id(path_or_id): """Retrieve the volume id from device_path""" # If we have the ID and not a path, just return it. - if isinstance(path, int): - return path + if isinstance(path_or_id, int): + return path_or_id # n must contain at least the volume_id # /vol- is for remote volumes # -vol- is for local volumes # see compute/manager->setup_compute_volume - volume_id = path[path.find('/vol-') + 1:] - if volume_id == path: - volume_id = path[path.find('-vol-') + 1:].replace('--', '-') + volume_id = path_or_id[path_or_id.find('/vol-') + 1:] + if volume_id == path_or_id: + volume_id = path_or_id[path_or_id.find('-vol-') + 1:] + volume_id = volume_id.replace('--', '-') return volume_id -- cgit From 02c86d1e1146c1162a36620560eb8116ce8d47f1 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Tue, 4 Jan 2011 15:20:10 -0600 Subject: Made the plugin output fully json-ified, so I could remove the exception handlers in vmops.py. Cleaned up some pep8 issues that weren't caught in earlier runs. --- nova/virt/xenapi/vmops.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index d539f9416..b6d620782 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -289,11 +289,7 @@ class VMOps(object): found at that path, returns None. """ ret = self._make_xenstore_call('list_records', vm, path) - try: - return json.loads(ret) - except ValueError: - # Not a valid JSON value - return ret + return json.loads(ret) def read_from_xenstore(self, vm, path): """Returns the value stored in the xenstore record for the given VM @@ -305,14 +301,11 @@ class VMOps(object): {'ignore_missing_path': 'True'}) except self.XenAPI.Failure, e: return None - try: - return json.loads(ret) - except ValueError: - # Not a JSON object - if ret == "None": - # Can't marshall None over RPC calls. - return None - return ret + ret = json.loads(ret) + if ret == "None": + # Can't marshall None over RPC calls. + return None + return ret def write_to_xenstore(self, vm, path, value): """Writes the passed value to the xenstore record for the given VM -- cgit From 468bc4745f002b521f21c5d621bdcb596b8ddfcd Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 4 Jan 2011 15:18:28 -0800 Subject: Merge from trunk again -- get rid of twistd dependencies --- nova/virt/hyperv.py | 30 +++++++++++++++--------------- nova/virt/images.py | 6 ++++-- 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index a254aaf8a..6aeeb0837 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -64,8 +64,6 @@ import os import logging import time -from twisted.internet import defer - from nova import exception from nova import flags from nova.auth import manager @@ -112,16 +110,20 @@ class HyperVConnection(object): self._conn = wmi.WMI(moniker='//./root/virtualization') self._cim_conn = wmi.WMI(moniker='//./root/cimv2') + def init_host(self): + #FIXME(chiradeep): implement this + logging.debug('In init host') + pass + def list_instances(self): """ Return the names of all the instances known to Hyper-V. """ vms = [v.ElementName \ for v in self._conn.Msvm_ComputerSystem(['ElementName'])] return vms - @defer.inlineCallbacks def spawn(self, instance): """ Create a new VM and start it.""" - vm = yield self._lookup(instance.name) + vm = self._lookup(instance.name) if vm is not None: raise exception.Duplicate('Attempted to create duplicate name %s' % instance.name) @@ -130,18 +132,18 @@ class HyperVConnection(object): project = manager.AuthManager().get_project(instance['project_id']) #Fetch the file, assume it is a VHD file. base_vhd_filename = os.path.join(FLAGS.instances_path, - instance['str_id']) + instance.name) vhdfile = "%s.vhd" % (base_vhd_filename) - yield images.fetch(instance['image_id'], vhdfile, user, project) + images.fetch(instance['image_id'], vhdfile, user, project) try: - yield self._create_vm(instance) + self._create_vm(instance) - yield self._create_disk(instance['name'], vhdfile) - yield self._create_nic(instance['name'], instance['mac_address']) + self._create_disk(instance['name'], vhdfile) + self._create_nic(instance['name'], instance['mac_address']) logging.debug('Starting VM %s ', instance.name) - yield self._set_vm_state(instance['name'], 'Enabled') + self._set_vm_state(instance['name'], 'Enabled') logging.info('Started VM %s ', instance.name) except Exception as exn: logging.error('spawn vm failed: %s', exn) @@ -341,21 +343,19 @@ class HyperVConnection(object): wmi_obj.Properties_.Item(prop).Value return newinst - @defer.inlineCallbacks def reboot(self, instance): """Reboot the specified instance.""" - vm = yield self._lookup(instance.name) + vm = self._lookup(instance.name) if vm is None: raise exception.NotFound('instance not present %s' % instance.name) self._set_vm_state(instance.name, 'Reboot') - @defer.inlineCallbacks def destroy(self, instance): """Destroy the VM. Also destroy the associated VHD disk files""" logging.debug("Got request to destroy vm %s", instance.name) - vm = yield self._lookup(instance.name) + vm = self._lookup(instance.name) if vm is None: - defer.returnValue(None) + return vm = self._conn.Msvm_ComputerSystem(ElementName=instance.name)[0] vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] #Stop the VM first. diff --git a/nova/virt/images.py b/nova/virt/images.py index bd7426ad0..417197538 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -60,7 +60,9 @@ def _fetch_image_no_curl(url, path, headers): while 1: data = urlfile.read(chunk) if not data: - break f.write(data) + break + f.write(data) + urlopened = urllib2.urlopen(request) urlretrieve(urlopened, path) logging.debug("Finished retreving %s -- placed in %s", url, path) @@ -68,7 +70,6 @@ def _fetch_image_no_curl(url, path, headers): def _fetch_s3_image(image, path, user, project): url = image_url(image) - # This should probably move somewhere else, like e.g. a download_as # method on User objects and at the same time get rewritten to use # a web client. @@ -88,6 +89,7 @@ def _fetch_s3_image(image, path, user, project): cmd = ['/usr/bin/curl', '--fail', '--silent', url] for (k, v) in headers.iteritems(): cmd += ['-H', '%s: %s' % (k, v)] + cmd += ['-o', path] cmd_out = ' '.join(cmd) return utils.execute(cmd_out) -- cgit From 91e44607d1454a9c2e258910f009a034fb9cff1c Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 4 Jan 2011 15:42:29 -0800 Subject: i18n logging and exception strings --- nova/virt/hyperv.py | 62 ++++++++++++++++++++++++++--------------------------- nova/virt/images.py | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py index 6aeeb0837..4b9f6f946 100644 --- a/nova/virt/hyperv.py +++ b/nova/virt/hyperv.py @@ -112,7 +112,7 @@ class HyperVConnection(object): def init_host(self): #FIXME(chiradeep): implement this - logging.debug('In init host') + logging.debug(_('In init host')) pass def list_instances(self): @@ -125,7 +125,7 @@ class HyperVConnection(object): """ Create a new VM and start it.""" vm = self._lookup(instance.name) if vm is not None: - raise exception.Duplicate('Attempted to create duplicate name %s' % + raise exception.Duplicate(_('Attempt to create duplicate vm %s') % instance.name) user = manager.AuthManager().get_user(instance['user_id']) @@ -142,11 +142,11 @@ class HyperVConnection(object): self._create_disk(instance['name'], vhdfile) self._create_nic(instance['name'], instance['mac_address']) - logging.debug('Starting VM %s ', instance.name) + logging.debug(_('Starting VM %s '), instance.name) self._set_vm_state(instance['name'], 'Enabled') - logging.info('Started VM %s ', instance.name) + logging.info(_('Started VM %s '), instance.name) except Exception as exn: - logging.error('spawn vm failed: %s', exn) + logging.error(_('spawn vm failed: %s'), exn) self.destroy(instance) def _create_vm(self, instance): @@ -163,9 +163,9 @@ class HyperVConnection(object): success = (ret_val == 0) if not success: - raise Exception('Failed to create VM %s', instance.name) + raise Exception(_('Failed to create VM %s'), instance.name) - logging.debug('Created VM %s...', instance.name) + logging.debug(_('Created VM %s...'), instance.name) vm = self._conn.Msvm_ComputerSystem(ElementName=instance.name)[0] vmsettings = vm.associators( @@ -182,7 +182,7 @@ class HyperVConnection(object): (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources( vm.path_(), [memsetting.GetText_(1)]) - logging.debug('Set memory for vm %s...', instance.name) + logging.debug(_('Set memory for vm %s...'), instance.name) procsetting = vmsetting.associators( wmi_result_class='Msvm_ProcessorSettingData')[0] vcpus = long(instance['vcpus']) @@ -192,11 +192,11 @@ class HyperVConnection(object): (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources( vm.path_(), [procsetting.GetText_(1)]) - logging.debug('Set vcpus for vm %s...', instance.name) + logging.debug(_('Set vcpus for vm %s...'), instance.name) def _create_disk(self, vm_name, vhdfile): """Create a disk and attach it to the vm""" - logging.debug("Creating disk for %s by attaching disk file %s", + logging.debug(_('Creating disk for %s by attaching disk file %s'), vm_name, vhdfile) #Find the IDE controller for the vm. vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name) @@ -221,10 +221,10 @@ class HyperVConnection(object): #Add the cloned disk drive object to the vm. new_resources = self._add_virt_resource(diskdrive, vm) if new_resources is None: - raise Exception('Failed to add diskdrive to VM %s', + raise Exception(_('Failed to add diskdrive to VM %s'), vm_name) diskdrive_path = new_resources[0] - logging.debug("New disk drive path is %s", diskdrive_path) + logging.debug(_('New disk drive path is %s'), diskdrive_path) #Find the default VHD disk object. vhddefault = self._conn.query( "SELECT * FROM Msvm_ResourceAllocationSettingData \ @@ -241,13 +241,13 @@ class HyperVConnection(object): #Add the new vhd object as a virtual hard disk to the vm. new_resources = self._add_virt_resource(vhddisk, vm) if new_resources is None: - raise Exception('Failed to add vhd file to VM %s', + raise Exception(_('Failed to add vhd file to VM %s'), vm_name) - logging.info("Created disk for %s ", vm_name) + logging.info(_('Created disk for %s'), vm_name) def _create_nic(self, vm_name, mac): """Create a (emulated) nic and attach it to the vm""" - logging.debug("Creating nic for %s ", vm_name) + logging.debug(_('Creating nic for %s '), vm_name) #Find the vswitch that is connected to the physical nic. vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name) extswitch = self._find_external_network() @@ -266,10 +266,10 @@ class HyperVConnection(object): (new_port, ret_val) = switch_svc.CreateSwitchPort(vm_name, vm_name, "", extswitch.path_()) if ret_val != 0: - logging.error("Failed creating a new port on the external vswitch") - raise Exception('Failed creating port for %s', + logging.error(_('Failed creating a port on the external vswitch')) + raise Exception(_('Failed creating port for %s'), vm_name) - logging.debug("Created switch port %s on switch %s", + logging.debug(_("Created switch port %s on switch %s"), vm_name, extswitch.path_()) #Connect the new nic to the new port. new_nic_data.Connection = [new_port] @@ -279,9 +279,9 @@ class HyperVConnection(object): #Add the new nic to the vm. new_resources = self._add_virt_resource(new_nic_data, vm) if new_resources is None: - raise Exception('Failed to add nic to VM %s', + raise Exception(_('Failed to add nic to VM %s'), vm_name) - logging.info("Created nic for %s ", vm_name) + logging.info(_("Created nic for %s "), vm_name) def _add_virt_resource(self, res_setting_data, target_vm): """Add a new resource (disk/nic) to the VM""" @@ -314,9 +314,9 @@ class HyperVConnection(object): time.sleep(0.1) job = self._conn.Msvm_ConcreteJob(InstanceID=inst_id)[0] if job.JobState != WMI_JOB_STATE_COMPLETED: - logging.debug("WMI job failed: %s", job.ErrorSummaryDescription) + logging.debug(_("WMI job failed: %s"), job.ErrorSummaryDescription) return False - logging.debug("WMI job succeeded: %s, Elapsed=%s ", job.Description, + logging.debug(_("WMI job succeeded: %s, Elapsed=%s "), job.Description, job.ElapsedTime) return True @@ -352,7 +352,7 @@ class HyperVConnection(object): def destroy(self, instance): """Destroy the VM. Also destroy the associated VHD disk files""" - logging.debug("Got request to destroy vm %s", instance.name) + logging.debug(_("Got request to destroy vm %s"), instance.name) vm = self._lookup(instance.name) if vm is None: return @@ -377,13 +377,13 @@ class HyperVConnection(object): elif ret_val == 0: success = True if not success: - raise Exception('Failed to destroy vm %s' % instance.name) + raise Exception(_('Failed to destroy vm %s') % instance.name) #Delete associated vhd disk files. for disk in diskfiles: vhdfile = self._cim_conn.CIM_DataFile(Name=disk) for vf in vhdfile: vf.Delete() - logging.debug("Deleted disk %s vm %s", vhdfile, instance.name) + logging.debug(_("Del: disk %s vm %s"), vhdfile, instance.name) def get_info(self, instance_id): """Get information about the VM""" @@ -399,8 +399,8 @@ class HyperVConnection(object): summary_info = vs_man_svc.GetSummaryInformation( [4, 100, 103, 105], settings_paths)[1] info = summary_info[0] - logging.debug("Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, \ - cpu_time=%s", instance_id, + logging.debug(_("Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, \ + cpu_time=%s"), instance_id, str(HYPERV_POWER_STATE[info.EnabledState]), str(info.MemoryUsage), str(info.NumberOfProcessors), @@ -418,7 +418,7 @@ class HyperVConnection(object): if n == 0: return None elif n > 1: - raise Exception('duplicate name found: %s' % i) + raise Exception(_('duplicate name found: %s') % i) else: return vms[0].ElementName @@ -438,12 +438,12 @@ class HyperVConnection(object): #already in the state requested success = True if success: - logging.info("Successfully changed vm state of %s to %s", + logging.info(_("Successfully changed vm state of %s to %s"), vm_name, req_state) else: - logging.error("Failed to change vm state of %s to %s", + logging.error(_("Failed to change vm state of %s to %s"), vm_name, req_state) - raise Exception("Failed to change vm state of %s to %s", + raise Exception(_("Failed to change vm state of %s to %s"), vm_name, req_state) def attach_volume(self, instance_name, device_path, mountpoint): diff --git a/nova/virt/images.py b/nova/virt/images.py index 417197538..be162b5b1 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -65,7 +65,7 @@ def _fetch_image_no_curl(url, path, headers): urlopened = urllib2.urlopen(request) urlretrieve(urlopened, path) - logging.debug("Finished retreving %s -- placed in %s", url, path) + logging.debug(_("Finished retreving %s -- placed in %s"), url, path) def _fetch_s3_image(image, path, user, project): -- cgit From 46a249eaa1db7d0f5b765cff701bb13005e3db49 Mon Sep 17 00:00:00 2001 From: Chiradeep Vittal Date: Tue, 4 Jan 2011 16:06:03 -0800 Subject: Revert some unneeded formatting since twistd is no longer used --- nova/virt/images.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index be162b5b1..2d03da4b4 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -70,6 +70,7 @@ def _fetch_image_no_curl(url, path, headers): def _fetch_s3_image(image, path, user, project): url = image_url(image) + # This should probably move somewhere else, like e.g. a download_as # method on User objects and at the same time get rewritten to use # a web client. -- cgit