From abfd82d89653482e21e1139fb8ce8bf89c2b4d2c Mon Sep 17 00:00:00 2001 From: "jaypipes@gmail.com" <> Date: Tue, 10 Aug 2010 09:54:13 -0400 Subject: pylint fixes for nova/virt/connection.py --- nova/virt/connection.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/connection.py b/nova/virt/connection.py index 004adb19d..bb54f5db7 100644 --- a/nova/virt/connection.py +++ b/nova/virt/connection.py @@ -17,6 +17,11 @@ # License for the specific language governing permissions and limitations # under the License. +"""Abstraction of the underlying virtualization API""" + +import logging +import sys + from nova import flags from nova.virt import fake from nova.virt import libvirt_conn @@ -27,6 +32,11 @@ FLAGS = flags.FLAGS def get_connection(read_only=False): + """Returns a connection to the underlying virtualization API + + The read_only parameter is passed through to the underlying API's + get_connection() method if applicable + """ # TODO(termie): maybe lazy load after initial check for permissions # TODO(termie): check whether we can be disconnected t = FLAGS.connection_type -- cgit From 3ee748bb6f55ad341606919901c4c17a82d069fd Mon Sep 17 00:00:00 2001 From: Jesse Andrews Date: Sat, 14 Aug 2010 22:55:04 -0700 Subject: ComputeConnectionTestCase is almost working again --- nova/virt/fake.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index d9ae5ac96..90ea9d053 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -46,14 +46,14 @@ class FakeConnection(object): def spawn(self, instance): fake_instance = FakeInstance() - self.instances[instance.name] = fake_instance + self.instances[instance.id] = fake_instance fake_instance._state = power_state.RUNNING def reboot(self, instance): pass def destroy(self, instance): - del self.instances[instance.name] + del self.instances[instance.id] def get_info(self, instance_id): i = self.instances[instance_id] -- cgit From fb6bf337bc2fe702307842b57e33b9f5f9011147 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Sun, 15 Aug 2010 22:48:54 +0100 Subject: Rework virt.xenapi's concurrency model. There were many places where we were inadvertently blocking the reactor thread. The reworking puts all calls to XenAPI on background threads, so that they won't block the reactor thread. Long-lived operations (VM start, reboot, etc) are invoked asynchronously at the XenAPI level (Async.VM.start, etc). These return a XenAPI task. We relinquish the background thread at this point, so as not to hold threads in the pool for too long, and use reactor.callLater to poll the task. This combination of techniques means that we don't block the reactor thread at all, and at the same time we don't hold lots of threads waiting for long-running operations. There is a FIXME in here: get_info does not conform to these new rules. Changes are required in compute.service before we can make get_info non-blocking. --- nova/virt/xenapi.py | 178 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 147 insertions(+), 31 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index 9fe15644f..6b41061c1 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -16,15 +16,33 @@ """ 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, or 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 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. + +This combination of techniques means that we don't block the reactor thread at +all, and at the same time we don't hold lots of threads waiting for +long-running operations. + +FIXME: get_info currently doesn't conform to these rules, and will block the +reactor thread if the VM.get_by_name_label or VM.get_record calls block. """ import logging import xmlrpclib from twisted.internet import defer +from twisted.internet import reactor from twisted.internet import task +from twisted.internet.threads import deferToThread -from nova import exception from nova import flags from nova import process from nova.auth.manager import AuthManager @@ -43,6 +61,9 @@ flags.DEFINE_string('xenapi_connection_username', flags.DEFINE_string('xenapi_connection_password', None, 'Password for connection to XenServer/Xen Cloud Platform. Used only if connection_type=xenapi.') +flags.DEFINE_float('xenapi_task_poll_interval', + 0.5, + 'The interval used for polling of remote tasks (Async.VM.start, etc). Used only if connection_type=xenapi.') def get_connection(_): @@ -61,6 +82,12 @@ def get_connection(_): return XenAPIConnection(url, username, password) +def deferredToThread(f): + def g(*args, **kwargs): + return deferToThread(f, *args, **kwargs) + return g + + class XenAPIConnection(object): def __init__(self, url, user, pw): @@ -72,9 +99,8 @@ class XenAPIConnection(object): for vm in self._conn.xenapi.VM.get_all()] @defer.inlineCallbacks - @exception.wrap_exception def spawn(self, instance): - vm = yield self.lookup(instance.name) + vm = yield self._lookup(instance.name) if vm is not None: raise Exception('Attempted to create non-unique name %s' % instance.name) @@ -93,22 +119,28 @@ class XenAPIConnection(object): user = AuthManager().get_user(instance.datamodel['user_id']) project = AuthManager().get_project(instance.datamodel['project_id']) - vdi_uuid = yield self.fetch_image( + vdi_uuid = yield self._fetch_image( instance.datamodel['image_id'], user, project, True) - kernel = yield self.fetch_image( + kernel = yield self._fetch_image( instance.datamodel['kernel_id'], user, project, False) - ramdisk = yield self.fetch_image( + ramdisk = yield self._fetch_image( instance.datamodel['ramdisk_id'], user, project, False) - vdi_ref = yield self._conn.xenapi.VDI.get_by_uuid(vdi_uuid) + vdi_ref = yield self._call_xenapi('VDI.get_by_uuid', vdi_uuid) - vm_ref = yield self.create_vm(instance, kernel, ramdisk) - yield self.create_vbd(vm_ref, vdi_ref, 0, True) + vm_ref = yield self._create_vm(instance, kernel, ramdisk) + yield self._create_vbd(vm_ref, vdi_ref, 0, True) if network_ref: yield self._create_vif(vm_ref, network_ref, mac_address) - yield self._conn.xenapi.VM.start(vm_ref, False, False) + logging.debug('Starting VM %s...', vm_ref) + yield self._call_xenapi('VM.start', vm_ref, False, False) + logging.info('Spawning VM %s created %s.', instance.name, vm_ref) - def create_vm(self, instance, kernel, ramdisk): + @defer.inlineCallbacks + def _create_vm(self, instance, kernel, ramdisk): + """Create a VM record. Returns a Deferred that gives the new + VM reference.""" + mem = str(long(instance.datamodel['memory_kb']) * 1024) vcpus = str(instance.datamodel['vcpus']) rec = { @@ -141,12 +173,16 @@ class XenAPIConnection(object): 'other_config': {}, } logging.debug('Created VM %s...', instance.name) - vm_ref = self._conn.xenapi.VM.create(rec) + vm_ref = yield self._call_xenapi('VM.create', rec) logging.debug('Created VM %s as %s.', instance.name, vm_ref) - return vm_ref + defer.returnValue(vm_ref) - def create_vbd(self, vm_ref, vdi_ref, userdevice, bootable): + @defer.inlineCallbacks + def _create_vbd(self, vm_ref, vdi_ref, userdevice, bootable): + """Create a VBD record. Returns a Deferred that gives the new + VBD reference.""" + vbd_rec = {} vbd_rec['VM'] = vm_ref vbd_rec['VDI'] = vdi_ref @@ -161,13 +197,17 @@ class XenAPIConnection(object): vbd_rec['qos_algorithm_params'] = {} vbd_rec['qos_supported_algorithms'] = [] logging.debug('Creating VBD for VM %s, VDI %s ... ', vm_ref, vdi_ref) - vbd_ref = self._conn.xenapi.VBD.create(vbd_rec) + vbd_ref = yield self._call_xenapi('VBD.create', vbd_rec) logging.debug('Created VBD %s for VM %s, VDI %s.', vbd_ref, vm_ref, vdi_ref) - return vbd_ref + defer.returnValue(vbd_ref) + @defer.inlineCallbacks def _create_vif(self, vm_ref, network_ref, mac_address): + """Create a VIF record. Returns a Deferred that gives the new + VIF reference.""" + vif_rec = {} vif_rec['device'] = '0' vif_rec['network']= network_ref @@ -179,27 +219,31 @@ class XenAPIConnection(object): vif_rec['qos_algorithm_params'] = {} logging.debug('Creating VIF for VM %s, network %s ... ', vm_ref, network_ref) - vif_ref = self._conn.xenapi.VIF.create(vif_rec) + vif_ref = yield self._call_xenapi('VIF.create', vif_rec) logging.debug('Created VIF %s for VM %s, network %s.', vif_ref, vm_ref, network_ref) - return vif_ref + defer.returnValue(vif_ref) + @defer.inlineCallbacks def _find_network_with_bridge(self, bridge): expr = 'field "bridge" = "%s"' % bridge - networks = self._conn.xenapi.network.get_all_records_where(expr) + networks = yield self._call_xenapi('network.get_all_records_where', + expr) if len(networks) == 1: - return networks.keys()[0] + defer.returnValue(networks.keys()[0]) elif len(networks) > 1: raise Exception('Found non-unique network for bridge %s' % bridge) else: raise Exception('Found no network for bridge %s' % bridge) - def fetch_image(self, image, user, project, use_sr): + @defer.inlineCallbacks + def _fetch_image(self, image, user, project, use_sr): """use_sr: True to put the image as a VDI in an SR, False to place it on dom0's filesystem. The former is for VM disks, the latter for - its kernel and ramdisk (if external kernels are being used).""" + its kernel and ramdisk (if external kernels are being used). + Returns a Deferred that gives the new VDI UUID.""" url = images.image_url(image) access = AuthManager().get_access_key(user, project) @@ -211,23 +255,31 @@ class XenAPIConnection(object): args['password'] = user.secret if use_sr: args['add_partition'] = 'true' - return self._call_plugin('objectstore', fn, args) + task = yield self._async_call_plugin('objectstore', fn, args) + uuid = yield self._wait_for_task(task) + defer.returnValue(uuid) + @defer.inlineCallbacks def reboot(self, instance): - vm = self.lookup(instance.name) + vm = yield self._lookup(instance.name) if vm is None: raise Exception('instance not present %s' % instance.name) - yield self._conn.xenapi.VM.clean_reboot(vm) + task = yield self._call_xenapi('Async.VM.clean_reboot', vm) + yield self._wait_for_task(task) + + @defer.inlineCallbacks def destroy(self, instance): - vm = self.lookup(instance.name) + vm = yield self._lookup(instance.name) if vm is None: raise Exception('instance not present %s' % instance.name) - yield self._conn.xenapi.VM.destroy(vm) + task = yield self._call_xenapi('Async.VM.destroy', vm) + yield self._wait_for_task(task) + def get_info(self, instance_id): - vm = self.lookup(instance_id) + vm = self._lookup_blocking(instance_id) if vm is None: raise Exception('instance not present %s' % instance_id) rec = self._conn.xenapi.VM.get_record(vm) @@ -237,7 +289,13 @@ class XenAPIConnection(object): 'num_cpu': rec['VCPUs_max'], 'cpu_time': 0} - def lookup(self, i): + + @deferredToThread + def _lookup(self, i): + return self._lookup_blocking(i) + + + def _lookup_blocking(self, i): vms = self._conn.xenapi.VM.get_by_name_label(i) n = len(vms) if n == 0: @@ -248,9 +306,55 @@ class XenAPIConnection(object): return vms[0] - def _call_plugin(self, plugin, fn, args): + def _wait_for_task(self, task): + """Return a Deferred that will give the result of the given task. + The task is polled until it completes.""" + d = defer.Deferred() + reactor.callLater(0, self._poll_task, task, d) + return d + + + @deferredToThread + def _poll_task(self, task, deferred): + """Poll the given XenAPI task, and fire the given Deferred if we + get a result.""" + try: + #logging.debug('Polling task %s...', task) + status = self._conn.xenapi.task.get_status(task) + if status == 'pending': + reactor.callLater(FLAGS.xenapi_task_poll_interval, + self._poll_task, task, deferred) + elif status == 'success': + result = self._conn.xenapi.task.get_result(task) + logging.info('Task %s status: success. %s', task, result) + deferred.callback(_parse_xmlrpc_value(result)) + else: + error_info = self._conn.xenapi.task.get_error_info(task) + logging.warn('Task %s status: %s. %s', task, status, + error_info) + deferred.errback(XenAPI.Failure(error_info)) + #logging.debug('Polling task %s done.', task) + except Exception, exn: + logging.warn(exn) + deferred.errback(exn) + + + @deferredToThread + def _call_xenapi(self, method, *args): + """Call the specified XenAPI method on a background thread. Returns + a Deferred for the result.""" + f = self._conn.xenapi + for m in method.split('.'): + f = f.__getattr__(m) + return f(*args) + + + @deferredToThread + def _async_call_plugin(self, plugin, fn, args): + """Call Async.host.call_plugin on a background thread. Returns a + Deferred with the task reference.""" return _unwrap_plugin_exceptions( - self._conn.xenapi.host.call_plugin, + self._conn.xenapi.Async.host.call_plugin, self._get_xenapi_host(), plugin, fn, args) @@ -286,3 +390,15 @@ def _unwrap_plugin_exceptions(func, *args, **kwargs): except xmlrpclib.ProtocolError, exn: logging.debug("Got exception: %s", exn) raise + + +def _parse_xmlrpc_value(val): + """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 + x = xmlrpclib.loads( + '' + + val + + '') + return x[0][0] -- cgit From fa70aefb00e487102564b92f6d32047dd8998054 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 16 Aug 2010 01:51:28 -0700 Subject: fix launching and describing instances to work with sqlalchemy --- nova/virt/libvirt_conn.py | 109 +++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 55 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 13305be0f..ef285b86e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -86,7 +86,7 @@ class LibvirtConnection(object): try: virt_dom = self._conn.lookupByName(instance.name) virt_dom.destroy() - except Exception, _err: + except Exception as _err: pass # If the instance is already terminated, we're still happy d = defer.Deferred() @@ -98,7 +98,7 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_shutdown(): try: - instance.update_state() + instance.set_state(self.get_info(instance.name)['state']) if instance.state == power_state.SHUTDOWN: timer.stop() d.callback(None) @@ -112,7 +112,7 @@ class LibvirtConnection(object): def _cleanup(self, instance): - target = os.path.abspath(instance.datamodel['basepath']) + target = os.path.join(FLAGS.instances_path, instance.name) logging.info("Deleting instance files at %s", target) if os.path.exists(target): shutil.rmtree(target) @@ -121,7 +121,7 @@ class LibvirtConnection(object): @defer.inlineCallbacks @exception.wrap_exception def reboot(self, instance): - xml = self.toXml(instance) + xml = self.to_xml(instance) yield self._conn.lookupByName(instance.name).destroy() yield self._conn.createXML(xml, 0) @@ -129,8 +129,8 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_reboot(): try: - instance.update_state() - if instance.is_running(): + instance.set_state(self.get_info(instance.name)['state']) + if instance.state == power_state.RUNNING: logging.debug('rebooted instance %s' % instance.name) timer.stop() d.callback(None) @@ -147,7 +147,7 @@ class LibvirtConnection(object): @defer.inlineCallbacks @exception.wrap_exception def spawn(self, instance): - xml = self.toXml(instance) + xml = self.to_xml(instance) instance.set_state(power_state.NOSTATE, 'launching') yield self._create_image(instance, xml) yield self._conn.createXML(xml, 0) @@ -159,15 +159,14 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_boot(): try: - instance.update_state() - if instance.is_running(): + instance.set_state(self.get_info(instance.name)['state']) + if instance.state == power_state.RUNNING: logging.debug('booted instance %s' % instance.name) timer.stop() local_d.callback(None) - except Exception, exn: - logging.error("_wait_for_boot exception %s" % exn) - self.set_state(power_state.SHUTDOWN) - logging.error('Failed to boot instance %s' % instance.name) + except: + logging.exception('Failed to boot instance %s' % instance.name) + instance.set_state(power_state.SHUTDOWN) timer.stop() local_d.callback(None) timer.f = _wait_for_boot @@ -176,10 +175,9 @@ class LibvirtConnection(object): @defer.inlineCallbacks - def _create_image(self, instance, libvirt_xml): + def _create_image(self, inst, libvirt_xml): # syntactic nicety - data = instance.datamodel - basepath = lambda x='': self.basepath(instance, x) + basepath = lambda x='': os.path.join(FLAGS.instances_path, inst.name, x) # ensure directories exist and are writable yield process.simple_execute('mkdir -p %s' % basepath()) @@ -188,70 +186,71 @@ class LibvirtConnection(object): # TODO(termie): these are blocking calls, it would be great # if they weren't. - logging.info('Creating image for: %s', data['instance_id']) + logging.info('Creating image for: %s', inst.name) f = open(basepath('libvirt.xml'), 'w') f.write(libvirt_xml) f.close() - user = manager.AuthManager().get_user(data['user_id']) - project = manager.AuthManager().get_project(data['project_id']) + user = manager.AuthManager().get_user(inst.user_id) + project = manager.AuthManager().get_project(inst.project_id) if not os.path.exists(basepath('disk')): - yield images.fetch(data['image_id'], basepath('disk-raw'), user, project) + yield images.fetch(inst.image_id, basepath('disk-raw'), user, project) if not os.path.exists(basepath('kernel')): - yield images.fetch(data['kernel_id'], basepath('kernel'), user, project) + yield images.fetch(inst.kernel_id, basepath('kernel'), user, project) if not os.path.exists(basepath('ramdisk')): - yield images.fetch(data['ramdisk_id'], basepath('ramdisk'), user, project) + yield images.fetch(inst.ramdisk_id, basepath('ramdisk'), user, project) execute = lambda cmd, input=None: \ process.simple_execute(cmd=cmd, input=input, error_ok=1) - key = data['key_data'] + key = inst.key_data net = None - if data.get('inject_network', False): + network = inst.project.network + if False: # should be network.is_injected: with open(FLAGS.injected_network_template) as f: - net = f.read() % {'address': data['private_dns_name'], - 'network': data['network_network'], - 'netmask': data['network_netmask'], - 'gateway': data['network_gateway'], - 'broadcast': data['network_broadcast'], - 'dns': data['network_dns']} + net = f.read() % {'address': inst.fixed_ip, + 'network': network.network, + 'netmask': network.netmask, + 'gateway': network.gateway, + 'broadcast': network.broadcast, + 'dns': network.network.dns} if key or net: - logging.info('Injecting data into image %s', data['image_id']) + logging.info('Injecting data into image %s', inst.image_id) yield disk.inject_data(basepath('disk-raw'), key, net, execute=execute) if os.path.exists(basepath('disk')): yield process.simple_execute('rm -f %s' % basepath('disk')) - bytes = (instance_types.INSTANCE_TYPES[data['instance_type']]['local_gb'] + bytes = (instance_types.INSTANCE_TYPES[inst.instance_type]['local_gb'] * 1024 * 1024 * 1024) yield disk.partition( basepath('disk-raw'), basepath('disk'), bytes, execute=execute) - - def basepath(self, instance, path=''): - return os.path.abspath(os.path.join(instance.datamodel['basepath'], path)) - - - def toXml(self, instance): + def to_xml(self, instance): # TODO(termie): cache? logging.debug("Starting the toXML method") - libvirt_xml = open(FLAGS.libvirt_xml_template).read() - xml_info = instance.datamodel.copy() + with open(FLAGS.libvirt_xml_template) as f: + libvirt_xml = f.read() + network = instance.project.network + # FIXME(vish): stick this in db + instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] + xml_info = {'type': FLAGS.libvirt_type, + 'name': instance.name, + 'basepath': os.path.join(FLAGS.instances_path, instance.name), + 'memory_kb': instance_type['memory_mb'] * 1024, + 'vcpus': instance_type['vcpus'], + 'bridge_name': network.bridge_name, + 'mac_address': instance.mac_address} # TODO(joshua): Make this xml express the attached disks as well - - # TODO(termie): lazy lazy hack because xml is annoying - xml_info['nova'] = json.dumps(instance.datamodel.copy()) - xml_info['type'] = FLAGS.libvirt_type libvirt_xml = libvirt_xml % xml_info logging.debug("Finished the toXML method") return libvirt_xml - - def get_info(self, instance_id): - virt_dom = self._conn.lookupByName(instance_id) + def get_info(self, instance_name): + virt_dom = self._conn.lookupByName(instance_name) (state, max_mem, mem, num_cpu, cpu_time) = virt_dom.info() return {'state': state, 'max_mem': max_mem, @@ -260,14 +259,14 @@ class LibvirtConnection(object): 'cpu_time': cpu_time} - def get_disks(self, instance_id): + def get_disks(self, instance_name): """ Note that this function takes an instance ID, not an Instance, so that it can be called by monitor. Returns a list of all block devices for this domain. """ - domain = self._conn.lookupByName(instance_id) + domain = self._conn.lookupByName(instance_name) # TODO(devcamcar): Replace libxml2 with etree. xml = domain.XMLDesc(0) doc = None @@ -303,14 +302,14 @@ class LibvirtConnection(object): return disks - def get_interfaces(self, instance_id): + def get_interfaces(self, instance_name): """ Note that this function takes an instance ID, not an Instance, so that it can be called by monitor. Returns a list of all network interfaces for this instance. """ - domain = self._conn.lookupByName(instance_id) + domain = self._conn.lookupByName(instance_name) # TODO(devcamcar): Replace libxml2 with etree. xml = domain.XMLDesc(0) doc = None @@ -346,19 +345,19 @@ class LibvirtConnection(object): return interfaces - def block_stats(self, instance_id, disk): + def block_stats(self, instance_name, disk): """ Note that this function takes an instance ID, not an Instance, so that it can be called by monitor. """ - domain = self._conn.lookupByName(instance_id) + domain = self._conn.lookupByName(instance_name) return domain.blockStats(disk) - def interface_stats(self, instance_id, interface): + def interface_stats(self, instance_name, interface): """ Note that this function takes an instance ID, not an Instance, so that it can be called by monitor. """ - domain = self._conn.lookupByName(instance_id) + domain = self._conn.lookupByName(instance_name) return domain.interfaceStats(interface) -- cgit From c41d9601555c78e3c91fb481fdfb3d50ffdf440b Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 17 Aug 2010 19:41:17 -0700 Subject: progress on tests passing --- nova/virt/fake.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index f7ee34695..060b53729 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -103,7 +103,7 @@ class FakeConnection(object): """ fake_instance = FakeInstance() - self.instances[instance.id] = fake_instance + self.instances[instance.name] = fake_instance fake_instance._state = power_state.RUNNING return defer.succeed(None) @@ -132,7 +132,7 @@ class FakeConnection(object): del self.instances[instance.name] return defer.succeed(None) - def get_info(self, instance_id): + def get_info(self, instance_name): """ Get a block of information about the given instance. This is returned as a dictionary containing 'state': The power_state of the instance, @@ -141,14 +141,14 @@ class FakeConnection(object): of virtual CPUs the instance has, 'cpu_time': The total CPU time used by the instance, in nanoseconds. """ - i = self.instances[instance_id] + i = self.instances[instance_name] return {'state': i._state, 'max_mem': 0, 'mem': 0, 'num_cpu': 2, 'cpu_time': 0} - def list_disks(self, instance_id): + def list_disks(self, instance_name): """ Return the IDs of all the virtual disks attached to the specified instance, as a list. These IDs are opaque to the caller (they are @@ -160,7 +160,7 @@ class FakeConnection(object): """ return ['A_DISK'] - def list_interfaces(self, instance_id): + def list_interfaces(self, instance_name): """ Return the IDs of all the virtual network interfaces attached to the specified instance, as a list. These IDs are opaque to the caller @@ -173,10 +173,10 @@ class FakeConnection(object): """ return ['A_VIF'] - def block_stats(self, instance_id, disk_id): + def block_stats(self, instance_name, disk_id): """ Return performance counters associated with the given disk_id on the - given instance_id. These are returned as [rd_req, rd_bytes, wr_req, + given instance_name. These are returned as [rd_req, rd_bytes, wr_req, wr_bytes, errs], where rd indicates read, wr indicates write, req is the total number of I/O requests made, bytes is the total number of bytes transferred, and errs is the number of requests held up due to a @@ -194,7 +194,7 @@ class FakeConnection(object): """ return [0L, 0L, 0L, 0L, null] - def interface_stats(self, instance_id, iface_id): + def interface_stats(self, instance_name, iface_id): """ Return performance counters associated with the given iface_id on the given instance_id. These are returned as [rx_bytes, rx_packets, -- cgit From 567aa0ac862f0cb18786f20d949ab75bd800c3c7 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 19 Aug 2010 15:05:13 +0100 Subject: Remove whitespace to match style guide. --- nova/virt/xenapi.py | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index aed4c4fb5..f0bbbbe1f 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -149,7 +149,6 @@ class XenAPIConnection(object): yield self._call_xenapi('VM.start', vm_ref, False, False) logging.info('Spawning VM %s created %s.', instance.name, vm_ref) - @defer.inlineCallbacks def _create_vm(self, instance, kernel, ramdisk): """Create a VM record. Returns a Deferred that gives the new @@ -191,7 +190,6 @@ class XenAPIConnection(object): logging.debug('Created VM %s as %s.', instance.name, vm_ref) defer.returnValue(vm_ref) - @defer.inlineCallbacks def _create_vbd(self, vm_ref, vdi_ref, userdevice, bootable): """Create a VBD record. Returns a Deferred that gives the new @@ -216,7 +214,6 @@ class XenAPIConnection(object): vdi_ref) defer.returnValue(vbd_ref) - @defer.inlineCallbacks def _create_vif(self, vm_ref, network_ref, mac_address): """Create a VIF record. Returns a Deferred that gives the new @@ -238,7 +235,6 @@ class XenAPIConnection(object): vm_ref, network_ref) defer.returnValue(vif_ref) - @defer.inlineCallbacks def _find_network_with_bridge(self, bridge): expr = 'field "bridge" = "%s"' % bridge @@ -251,7 +247,6 @@ class XenAPIConnection(object): else: raise Exception('Found no network for bridge %s' % bridge) - @defer.inlineCallbacks def _fetch_image(self, image, user, project, use_sr): """use_sr: True to put the image as a VDI in an SR, False to place @@ -273,7 +268,6 @@ class XenAPIConnection(object): uuid = yield self._wait_for_task(task) defer.returnValue(uuid) - @defer.inlineCallbacks def reboot(self, instance): vm = yield self._lookup(instance.name) @@ -282,7 +276,6 @@ class XenAPIConnection(object): task = yield self._call_xenapi('Async.VM.clean_reboot', vm) yield self._wait_for_task(task) - @defer.inlineCallbacks def destroy(self, instance): vm = yield self._lookup(instance.name) @@ -291,7 +284,6 @@ class XenAPIConnection(object): task = yield self._call_xenapi('Async.VM.destroy', vm) yield self._wait_for_task(task) - def get_info(self, instance_id): vm = self._lookup_blocking(instance_id) if vm is None: @@ -303,12 +295,10 @@ class XenAPIConnection(object): 'num_cpu': rec['VCPUs_max'], 'cpu_time': 0} - @deferredToThread def _lookup(self, i): return self._lookup_blocking(i) - def _lookup_blocking(self, i): vms = self._conn.xenapi.VM.get_by_name_label(i) n = len(vms) @@ -319,7 +309,6 @@ class XenAPIConnection(object): else: return vms[0] - def _wait_for_task(self, task): """Return a Deferred that will give the result of the given task. The task is polled until it completes.""" @@ -327,7 +316,6 @@ class XenAPIConnection(object): reactor.callLater(0, self._poll_task, task, d) return d - @deferredToThread def _poll_task(self, task, deferred): """Poll the given XenAPI task, and fire the given Deferred if we @@ -352,7 +340,6 @@ class XenAPIConnection(object): logging.warn(exn) deferred.errback(exn) - @deferredToThread def _call_xenapi(self, method, *args): """Call the specified XenAPI method on a background thread. Returns @@ -362,7 +349,6 @@ class XenAPIConnection(object): f = f.__getattr__(m) return f(*args) - @deferredToThread def _async_call_plugin(self, plugin, fn, args): """Call Async.host.call_plugin on a background thread. Returns a @@ -371,7 +357,6 @@ class XenAPIConnection(object): self._conn.xenapi.Async.host.call_plugin, self._get_xenapi_host(), plugin, fn, args) - def _get_xenapi_host(self): return self._conn.xenapi.session.get_this_host(self._conn.handle) -- cgit From 4a23d5d9091823e9b4dc364383a14b566af80cd6 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 19 Aug 2010 15:12:46 +0100 Subject: Move deferredToThread into utils, as suggested by termie. --- nova/virt/xenapi.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index f0bbbbe1f..b44ac383a 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -19,7 +19,7 @@ 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, or the decorator +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. @@ -41,10 +41,10 @@ import xmlrpclib from twisted.internet import defer from twisted.internet import reactor from twisted.internet import task -from twisted.internet.threads import deferToThread from nova import flags from nova import process +from nova import utils from nova.auth.manager import AuthManager from nova.compute import power_state from nova.virt import images @@ -97,12 +97,6 @@ def get_connection(_): return XenAPIConnection(url, username, password) -def deferredToThread(f): - def g(*args, **kwargs): - return deferToThread(f, *args, **kwargs) - return g - - class XenAPIConnection(object): def __init__(self, url, user, pw): self._conn = XenAPI.Session(url) @@ -295,7 +289,7 @@ class XenAPIConnection(object): 'num_cpu': rec['VCPUs_max'], 'cpu_time': 0} - @deferredToThread + @utils.deferredToThread def _lookup(self, i): return self._lookup_blocking(i) @@ -316,7 +310,7 @@ class XenAPIConnection(object): reactor.callLater(0, self._poll_task, task, d) return d - @deferredToThread + @utils.deferredToThread def _poll_task(self, task, deferred): """Poll the given XenAPI task, and fire the given Deferred if we get a result.""" @@ -340,7 +334,7 @@ class XenAPIConnection(object): logging.warn(exn) deferred.errback(exn) - @deferredToThread + @utils.deferredToThread def _call_xenapi(self, method, *args): """Call the specified XenAPI method on a background thread. Returns a Deferred for the result.""" @@ -349,7 +343,7 @@ class XenAPIConnection(object): f = f.__getattr__(m) return f(*args) - @deferredToThread + @utils.deferredToThread def _async_call_plugin(self, plugin, fn, args): """Call Async.host.call_plugin on a background thread. Returns a Deferred with the task reference.""" -- cgit From d3b8fea0d9d1a4ab8a8a2f2dc7daea1c512b1ea8 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Thu, 26 Aug 2010 15:20:01 +0200 Subject: Set UML guests to use a file as their console. This halfway fixes get-console-output for them. --- nova/virt/libvirt.qemu.xml.template | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt.qemu.xml.template b/nova/virt/libvirt.qemu.xml.template index 307f9d03a..6cba84f3f 100644 --- a/nova/virt/libvirt.qemu.xml.template +++ b/nova/virt/libvirt.qemu.xml.template @@ -4,7 +4,7 @@ hvm %(basepath)s/kernel %(basepath)s/ramdisk - root=/dev/vda1 console=ttyS0 + root=/dev/vda1 @@ -21,10 +21,9 @@ - + - - + %(nova)s -- cgit From 58e1886113e6408d502d095d382a250d33fa0195 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 27 Aug 2010 20:17:30 +0200 Subject: Create console.log ahead of time. This ensures that the user running nova-compute maintains read privileges. --- nova/virt/libvirt_conn.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 524646ee5..ee8efde7f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -23,7 +23,7 @@ A connection to a hypervisor (e.g. KVM) through libvirt. import json import logging -import os.path +import os import shutil from twisted.internet import defer @@ -218,6 +218,8 @@ class LibvirtConnection(object): f.write(libvirt_xml) f.close() + os.close(os.open(basepath('console.log'), os.O_CREAT | os.O_WRONLY, 0660)) + user = manager.AuthManager().get_user(data['user_id']) project = manager.AuthManager().get_project(data['project_id']) if not os.path.exists(basepath('disk')): -- cgit From d2a0d2ceabac76ed3069bf265335b3e857093959 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 27 Aug 2010 20:20:49 +0200 Subject: chown disk images to root for uml. Due to libvirt dropping CAP_DAC_OVERRIDE for uml, root needs to have explicit access to the disk images for stuff to work. --- nova/virt/libvirt_conn.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 524646ee5..75d91ab64 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -254,6 +254,9 @@ class LibvirtConnection(object): yield disk.partition( basepath('disk-raw'), basepath('disk'), bytes, execute=execute) + if FLAGS.libvirt_type == 'uml': + execute('sudo chown root %s' % (basepath('disk'),)) + def basepath(self, instance, path=''): return os.path.abspath(os.path.join(instance.datamodel['basepath'], path)) -- cgit From f2776632fb94bd55a428bfb9272472e4bd2517bb Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 30 Aug 2010 14:50:50 +0200 Subject: Detect if libvirt connection has been broken and reestablish it. --- nova/virt/libvirt_conn.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 524646ee5..1ff8175d0 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -84,10 +84,22 @@ class LibvirtConnection(object): @property def _conn(self): - if not self._wrapped_conn: + if not self._wrapped_conn or not self._test_connection(): + logging.debug('Connecting to libvirt: %s' % self.libvirt_uri) self._wrapped_conn = self._connect(self.libvirt_uri, self.read_only) return self._wrapped_conn + def _test_connection(self): + try: + self._wrapped_conn.getVersion() + return True + except libvirt.libvirtError as e: + if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and \ + e.get_error_domain() == libvirt.VIR_FROM_REMOTE: + logging.debug('Connection to libvirt broke') + return False + raise + def get_uri_and_template(self): if FLAGS.libvirt_type == 'uml': uri = FLAGS.libvirt_uri or 'uml:///system' -- cgit From 4374bef0536846afe9be1156b340b34e6d4c8d2d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 30 Aug 2010 20:42:31 -0700 Subject: more cleanup and pylint fixes --- nova/virt/libvirt_conn.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 823eb1e0b..b353fc44b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -126,7 +126,7 @@ class LibvirtConnection(object): def destroy(self, instance): try: - virt_dom = self._conn.lookupByName(instance.name) + virt_dom = self._conn.lookupByName(instance['name']) virt_dom.destroy() except Exception as _err: pass @@ -140,7 +140,7 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_shutdown(): try: - instance.set_state(self.get_info(instance.name)['state']) + instance.set_state(self.get_info(instance['name'])['state']) if instance.state == power_state.SHUTDOWN: timer.stop() d.callback(None) @@ -153,7 +153,7 @@ class LibvirtConnection(object): return d def _cleanup(self, instance): - target = os.path.join(FLAGS.instances_path, instance.name) + target = os.path.join(FLAGS.instances_path, instance['name']) logging.info("Deleting instance files at %s", target) if os.path.exists(target): shutil.rmtree(target) @@ -162,20 +162,20 @@ class LibvirtConnection(object): @exception.wrap_exception def reboot(self, instance): xml = self.to_xml(instance) - yield self._conn.lookupByName(instance.name).destroy() + yield self._conn.lookupByName(instance['name']).destroy() yield self._conn.createXML(xml, 0) d = defer.Deferred() timer = task.LoopingCall(f=None) def _wait_for_reboot(): try: - instance.set_state(self.get_info(instance.name)['state']) + instance.set_state(self.get_info(instance['name'])['state']) if instance.state == power_state.RUNNING: - logging.debug('rebooted instance %s' % instance.name) + logging.debug('rebooted instance %s' % instance['name']) timer.stop() d.callback(None) except Exception, exn: - logging.error('_wait_for_reboot failed: %s' % exn) + logging.error('_wait_for_reboot failed: %s', exn) instance.set_state(power_state.SHUTDOWN) timer.stop() d.callback(None) @@ -198,13 +198,14 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_boot(): try: - instance.set_state(self.get_info(instance.name)['state']) + instance.set_state(self.get_info(instance['name'])['state']) if instance.state == power_state.RUNNING: - logging.debug('booted instance %s' % instance.name) + logging.debug('booted instance %s', instance['name']) timer.stop() local_d.callback(None) except: - logging.exception('Failed to boot instance %s' % instance.name) + logging.exception('Failed to boot instance %s', + instance['name']) instance.set_state(power_state.SHUTDOWN) timer.stop() local_d.callback(None) @@ -215,7 +216,9 @@ class LibvirtConnection(object): @defer.inlineCallbacks def _create_image(self, inst, libvirt_xml): # syntactic nicety - basepath = lambda x='': os.path.join(FLAGS.instances_path, inst.name, x) + basepath = lambda fname='': os.path.join(FLAGS.instances_path, + inst['name'], + fname) # ensure directories exist and are writable yield process.simple_execute('mkdir -p %s' % basepath()) @@ -224,7 +227,7 @@ class LibvirtConnection(object): # TODO(termie): these are blocking calls, it would be great # if they weren't. - logging.info('Creating image for: %s', inst.name) + logging.info('Creating image for: %s', inst['name']) f = open(basepath('libvirt.xml'), 'w') f.write(libvirt_xml) f.close() @@ -245,10 +248,11 @@ class LibvirtConnection(object): key = inst.key_data net = None - network_ref = db.project_get_network(None, project.id) # FIXME + network_ref = db.project_get_network(None, project.id) if network_ref['injected']: + address = db.instance_get_fixed_address(None, inst['id']) with open(FLAGS.injected_network_template) as f: - net = f.read() % {'address': inst.fixed_ip['ip_str'], # FIXME + net = f.read() % {'address': address, 'network': network_ref['network'], 'netmask': network_ref['netmask'], 'gateway': network_ref['gateway'], @@ -269,12 +273,13 @@ class LibvirtConnection(object): def to_xml(self, instance): # TODO(termie): cache? logging.debug("Starting the toXML method") - network = db.project_get_network(None, instance['project_id']) # FIXME + network = db.project_get_network(None, instance['project_id']) # FIXME(vish): stick this in db - instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] + instance_type = instance_types.INSTANCE_TYPES[instance['instance_type']] xml_info = {'type': FLAGS.libvirt_type, - 'name': instance.name, - 'basepath': os.path.join(FLAGS.instances_path, instance.name), + 'name': instance['name'], + 'basepath': os.path.join(FLAGS.instances_path, + instance['name']), 'memory_kb': instance_type['memory_mb'] * 1024, 'vcpus': instance_type['vcpus'], 'bridge_name': network['bridge'], -- cgit From 871c49adc3c824b9b1e095b0d7135c1fdab486c1 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 31 Aug 2010 10:15:20 +0200 Subject: Call getInfo() instead of getVersion() on the libvirt connection object. virConnectGetVersion was not exposed properly in the python bindings until quite recently, so this makes us rather more backwards compatible. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 1ff8175d0..c29bdb466 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -91,7 +91,7 @@ class LibvirtConnection(object): def _test_connection(self): try: - self._wrapped_conn.getVersion() + self._wrapped_conn.getInfo() return True except libvirt.libvirtError as e: if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and \ -- cgit From 3afc7623dd8bd8a3af7539f0a3eb8f0405dd002c Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Wed, 1 Sep 2010 21:00:26 +0200 Subject: Revert the changes to the qemu libvirt template and make the appropriate changes in the UML template where they belong. --- nova/virt/libvirt.qemu.xml.template | 7 ++++--- nova/virt/libvirt.uml.xml.template | 6 ++---- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt.qemu.xml.template b/nova/virt/libvirt.qemu.xml.template index 6cba84f3f..307f9d03a 100644 --- a/nova/virt/libvirt.qemu.xml.template +++ b/nova/virt/libvirt.qemu.xml.template @@ -4,7 +4,7 @@ hvm %(basepath)s/kernel %(basepath)s/ramdisk - root=/dev/vda1 + root=/dev/vda1 console=ttyS0 @@ -21,9 +21,10 @@ - + - + + %(nova)s diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template index 6f4290f98..a72a6b8c3 100644 --- a/nova/virt/libvirt.uml.xml.template +++ b/nova/virt/libvirt.uml.xml.template @@ -15,11 +15,9 @@ - - + - - + %(nova)s -- cgit From 19d4c3a6f411b3b96d4a3dffc16b9b272a01971f Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Sun, 5 Sep 2010 05:33:56 +0100 Subject: Bug #630636: XenAPI VM destroy fails when the VM is still running When destroying a VM using the XenAPI backend, if the VM is still running (the usual case) the destroy fails. It needs to be powered-off first. --- nova/virt/xenapi.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index b44ac383a..04069e459 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -274,9 +274,19 @@ class XenAPIConnection(object): def destroy(self, instance): vm = yield self._lookup(instance.name) if vm is None: - raise Exception('instance not present %s' % instance.name) - task = yield self._call_xenapi('Async.VM.destroy', vm) - yield self._wait_for_task(task) + # Don't complain, just return. This lets us clean up instances + # that have already disappeared from the underlying platform. + defer.returnValue(None) + try: + task = yield self._call_xenapi('Async.VM.hard_shutdown', vm) + yield self._wait_for_task(task) + except Exception, exn: + logging.warn(exn) + try: + task = yield self._call_xenapi('Async.VM.destroy', vm) + yield self._wait_for_task(task) + except Exception, exn: + logging.warn(exn) def get_info(self, instance_id): vm = self._lookup_blocking(instance_id) -- cgit From 9db707dda70bbb11d944ab357841c9bdd5ef5b07 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 7 Sep 2010 05:26:08 -0700 Subject: Lots of fixes to make the nova commands work properly and make datamodel work with mysql properly --- nova/virt/fake.py | 8 ++++++++ nova/virt/libvirt_conn.py | 44 +++++++++++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 11 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 060b53729..4ae6afcc4 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -132,6 +132,14 @@ class FakeConnection(object): del self.instances[instance.name] return defer.succeed(None) + def attach_volume(self, instance_name, device_path, mountpoint): + """Attach the disk at device_path to the instance at mountpoint""" + return True + + def detach_volume(self, instance_name, mountpoint): + """Detach the disk attached to the instance at mountpoint""" + return True + def get_info(self, instance_name): """ Get a block of information about the given instance. This is returned diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 621b7d576..73d0a366f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -21,7 +21,6 @@ A connection to a hypervisor (e.g. KVM) through libvirt. """ -import json import logging import os import shutil @@ -154,10 +153,28 @@ class LibvirtConnection(object): def _cleanup(self, instance): target = os.path.join(FLAGS.instances_path, instance['name']) - logging.info("Deleting instance files at %s", target) + logging.info('instance %s: deleting instance files %s', + instance['name'], target) if os.path.exists(target): shutil.rmtree(target) + @defer.inlineCallbacks + @exception.wrap_exception + def attach_volume(self, instance_name, device_path, mountpoint): + yield process.simple_execute("sudo virsh attach-disk %s %s %s" % + (instance_name, + device_path, + mountpoint.rpartition('/dev/')[2])) + + @defer.inlineCallbacks + @exception.wrap_exception + def detach_volume(self, instance_name, mountpoint): + # NOTE(vish): despite the documentation, virsh detach-disk just + # wants the device name without the leading /dev/ + yield process.simple_execute("sudo virsh detach-disk %s %s" % + (instance_name, + mountpoint.rpartition('/dev/')[2])) + @defer.inlineCallbacks @exception.wrap_exception def reboot(self, instance): @@ -171,7 +188,7 @@ class LibvirtConnection(object): try: instance.set_state(self.get_info(instance['name'])['state']) if instance.state == power_state.RUNNING: - logging.debug('rebooted instance %s' % instance['name']) + logging.debug('instance %s: rebooted', instance['name']) timer.stop() d.callback(None) except Exception, exn: @@ -192,7 +209,7 @@ class LibvirtConnection(object): yield self._conn.createXML(xml, 0) # TODO(termie): this should actually register # a callback to check for successful boot - logging.debug("Instance is running") + logging.debug("instance %s: is running", instance['name']) local_d = defer.Deferred() timer = task.LoopingCall(f=None) @@ -200,11 +217,11 @@ class LibvirtConnection(object): try: instance.set_state(self.get_info(instance['name'])['state']) if instance.state == power_state.RUNNING: - logging.debug('booted instance %s', instance['name']) + logging.debug('instance %s: booted', instance['name']) timer.stop() local_d.callback(None) except: - logging.exception('Failed to boot instance %s', + logging.exception('instance %s: failed to boot', instance['name']) instance.set_state(power_state.SHUTDOWN) timer.stop() @@ -227,7 +244,7 @@ class LibvirtConnection(object): # TODO(termie): these are blocking calls, it would be great # if they weren't. - logging.info('Creating image for: %s', inst['name']) + logging.info('instance %s: Creating image', inst['name']) f = open(basepath('libvirt.xml'), 'w') f.write(libvirt_xml) f.close() @@ -249,7 +266,7 @@ class LibvirtConnection(object): process_input=process_input, check_exit_code=True) - key = inst.key_data + key = str(inst['key_data']) net = None network_ref = db.project_get_network(None, project.id) if network_ref['injected']: @@ -262,7 +279,12 @@ class LibvirtConnection(object): 'broadcast': network_ref['broadcast'], 'dns': network_ref['dns']} if key or net: - logging.info('Injecting data into image %s', inst.image_id) + if key: + logging.info('instance %s: injecting key into image %s', + inst['name'], inst.image_id) + if net: + logging.info('instance %s: injecting net into image %s', + inst['name'], inst.image_id) yield disk.inject_data(basepath('disk-raw'), key, net, execute=execute) if os.path.exists(basepath('disk')): @@ -275,7 +297,7 @@ class LibvirtConnection(object): def to_xml(self, instance): # TODO(termie): cache? - logging.debug("Starting the toXML method") + logging.debug('instance %s: starting toXML method', instance['name']) network = db.project_get_network(None, instance['project_id']) # FIXME(vish): stick this in db instance_type = instance_types.INSTANCE_TYPES[instance['instance_type']] @@ -288,7 +310,7 @@ class LibvirtConnection(object): 'bridge_name': network['bridge'], 'mac_address': instance['mac_address']} libvirt_xml = self.libvirt_xml % xml_info - logging.debug("Finished the toXML method") + logging.debug('instance %s: finished toXML method', instance['name']) return libvirt_xml -- cgit From f21d8510bb3f55b2b76aab251b0427dbfa69c5d9 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 7 Sep 2010 14:34:27 +0200 Subject: Add a clean-traffic filterref to the libvirt templates to prevent spoofing and snooping attacks from the guests. --- nova/virt/libvirt.qemu.xml.template | 3 +++ nova/virt/libvirt.uml.xml.template | 3 +++ 2 files changed, 6 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/libvirt.qemu.xml.template b/nova/virt/libvirt.qemu.xml.template index 307f9d03a..3de1e5009 100644 --- a/nova/virt/libvirt.qemu.xml.template +++ b/nova/virt/libvirt.qemu.xml.template @@ -20,6 +20,9 @@ + + + diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template index 6f4290f98..e64b172d8 100644 --- a/nova/virt/libvirt.uml.xml.template +++ b/nova/virt/libvirt.uml.xml.template @@ -14,6 +14,9 @@ + + + -- cgit From 08f3d9b52b3a759b64a15433e920b1a6db217288 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 8 Sep 2010 00:59:09 -0700 Subject: set state moved to db layer --- nova/virt/libvirt_conn.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index e26030158..febb0ce9b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -139,12 +139,16 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_shutdown(): try: - instance.set_state(self.get_info(instance['name'])['state']) + db.instance_set_state(None, + instance['id'], + self.get_info(instance['name'])['state']) if instance.state == power_state.SHUTDOWN: timer.stop() d.callback(None) except Exception: - instance.set_state(power_state.SHUTDOWN) + db.instance_set_state(None, + instance['id'], + power_state.SHUTDOWN) timer.stop() d.callback(None) timer.f = _wait_for_shutdown @@ -186,14 +190,18 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_reboot(): try: - instance.set_state(self.get_info(instance['name'])['state']) + db.instance_set_state(None, + instance['id'], + self.get_info(instance['name'])['state']) if instance.state == power_state.RUNNING: logging.debug('instance %s: rebooted', instance['name']) timer.stop() d.callback(None) except Exception, exn: logging.error('_wait_for_reboot failed: %s', exn) - instance.set_state(power_state.SHUTDOWN) + db.instance_set_state(None, + instance['id'], + power_state.SHUTDOWN) timer.stop() d.callback(None) timer.f = _wait_for_reboot @@ -204,7 +212,10 @@ class LibvirtConnection(object): @exception.wrap_exception def spawn(self, instance): xml = self.to_xml(instance) - instance.set_state(power_state.NOSTATE, 'launching') + db.instance_set_state(None, + instance['id'], + power_state.NOSTATE, + 'launching') yield self._create_image(instance, xml) yield self._conn.createXML(xml, 0) # TODO(termie): this should actually register @@ -215,7 +226,9 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_boot(): try: - instance.set_state(self.get_info(instance['name'])['state']) + db.instance_set_state(None, + instance['id'], + self.get_info(instance['name'])['state']) if instance.state == power_state.RUNNING: logging.debug('instance %s: booted', instance['name']) timer.stop() @@ -223,7 +236,9 @@ class LibvirtConnection(object): except: logging.exception('instance %s: failed to boot', instance['name']) - instance.set_state(power_state.SHUTDOWN) + db.instance_set_state(None, + instance['id'], + power_state.SHUTDOWN) timer.stop() local_d.callback(None) timer.f = _wait_for_boot -- cgit From 2cd0ac795a67bb7416df8c8a6fccccf78fc5e430 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 9 Sep 2010 08:55:09 -0700 Subject: fixed logic in set_state code to stop endless loops --- nova/virt/libvirt_conn.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index febb0ce9b..d868e083c 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -139,10 +139,9 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_shutdown(): try: - db.instance_set_state(None, - instance['id'], - self.get_info(instance['name'])['state']) - if instance.state == power_state.SHUTDOWN: + state = self.get_info(instance['name'])['state'] + db.instance_set_state(None, instance['id'], state) + if state == power_state.SHUTDOWN: timer.stop() d.callback(None) except Exception: @@ -190,10 +189,9 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_reboot(): try: - db.instance_set_state(None, - instance['id'], - self.get_info(instance['name'])['state']) - if instance.state == power_state.RUNNING: + state = self.get_info(instance['name'])['state'] + db.instance_set_state(None, instance['id'], state) + if state == power_state.RUNNING: logging.debug('instance %s: rebooted', instance['name']) timer.stop() d.callback(None) @@ -226,10 +224,9 @@ class LibvirtConnection(object): timer = task.LoopingCall(f=None) def _wait_for_boot(): try: - db.instance_set_state(None, - instance['id'], - self.get_info(instance['name'])['state']) - if instance.state == power_state.RUNNING: + state = self.get_info(instance['name'])['state'] + db.instance_set_state(None, instance['id'], state) + if state == power_state.RUNNING: logging.debug('instance %s: booted', instance['name']) timer.stop() local_d.callback(None) -- cgit From f1e45e3294622e22e6044027c1d2514f107d6134 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Fri, 10 Sep 2010 10:56:22 +0100 Subject: Change "exn" to "exc" to fit with the common style. --- nova/virt/xenapi.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index 04069e459..1c6de4403 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -280,13 +280,13 @@ class XenAPIConnection(object): try: task = yield self._call_xenapi('Async.VM.hard_shutdown', vm) yield self._wait_for_task(task) - except Exception, exn: - logging.warn(exn) + except Exception, exc: + logging.warn(exc) try: task = yield self._call_xenapi('Async.VM.destroy', vm) yield self._wait_for_task(task) - except Exception, exn: - logging.warn(exn) + except Exception, exc: + logging.warn(exc) def get_info(self, instance_id): vm = self._lookup_blocking(instance_id) @@ -340,9 +340,9 @@ class XenAPIConnection(object): error_info) deferred.errback(XenAPI.Failure(error_info)) #logging.debug('Polling task %s done.', task) - except Exception, exn: - logging.warn(exn) - deferred.errback(exn) + except Exception, exc: + logging.warn(exc) + deferred.errback(exc) @utils.deferredToThread def _call_xenapi(self, method, *args): @@ -368,21 +368,21 @@ class XenAPIConnection(object): def _unwrap_plugin_exceptions(func, *args, **kwargs): try: return func(*args, **kwargs) - except XenAPI.Failure, exn: - logging.debug("Got exception: %s", exn) - if (len(exn.details) == 4 and - exn.details[0] == 'XENAPI_PLUGIN_EXCEPTION' and - exn.details[2] == 'Failure'): + except XenAPI.Failure, exc: + logging.debug("Got exception: %s", exc) + if (len(exc.details) == 4 and + exc.details[0] == 'XENAPI_PLUGIN_EXCEPTION' and + exc.details[2] == 'Failure'): params = None try: - params = eval(exn.details[3]) + params = eval(exc.details[3]) except: - raise exn + raise exc raise XenAPI.Failure(params) else: raise - except xmlrpclib.ProtocolError, exn: - logging.debug("Got exception: %s", exn) + except xmlrpclib.ProtocolError, exc: + logging.debug("Got exception: %s", exc) raise -- cgit From c3dd0aa79d982d8f34172e6023d4b632ea23f2b9 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Fri, 10 Sep 2010 14:56:36 +0200 Subject: First pass of nwfilter based security group implementation. It is not where it is supposed to be and it does not actually do anything yet. --- nova/virt/libvirt_conn.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index e26030158..7bf2a68b1 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -426,3 +426,66 @@ class LibvirtConnection(object): """ domain = self._conn.lookupByName(instance_name) return domain.interfaceStats(interface) + + +class NWFilterFirewall(object): + """ + This class implements a network filtering mechanism versatile + enough for EC2 style Security Group filtering by leveraging + libvirt's nwfilter. + + First, all instances get a filter ("nova-base-filter") applied. + This filter drops all incoming ipv4 and ipv6 connections. + Outgoing connections are never blocked. + + Second, every security group maps to a nwfilter filter(*). + NWFilters can be updated at runtime and changes are applied + immediately, so changes to security groups can be applied at + runtime (as mandated by the spec). + + Security group rules are named "nova-secgroup-" where + is the internal id of the security group. They're applied only on + hosts that have instances in the security group in question. + + Updates to security groups are done by updating the data model + (in response to API calls) followed by a request sent to all + the nodes with instances in the security group to refresh the + security group. + + Outstanding questions: + + The name is unique, so would there be any good reason to sync + the uuid across the nodes (by assigning it from the datamodel)? + + + (*) This sentence brought to you by the redundancy department of + redundancy. + """ + + def __init__(self): + pass + + def nova_base_filter(self): + return ''' + 26717364-50cf-42d1-8185-29bf893ab110 + + + + + + +''' + + def security_group_to_nwfilter_xml(self, security_group_id): + security_group = db.security_group_get_by_id({}, security_group_id) + rule_xml = "" + for rule in security_group.rules: + rule_xml += "" + if rule.cidr: + rule_xml += ("") % \ + (rule.cidr, rule.protocol, + rule.from_port, rule.to_port) + rule_xml += "" + xml = '''%s''' % (security_group_id, rule_xml,) + return xml -- cgit From e53676bb32b70ff01ca27c310e558b651590be3d Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Fri, 10 Sep 2010 15:26:13 -0700 Subject: Refactored to security group api to support projects --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 6f708bb80..09c94577c 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -492,7 +492,7 @@ class NWFilterFirewall(object): ''' def security_group_to_nwfilter_xml(self, security_group_id): - security_group = db.security_group_get_by_id({}, security_group_id) + security_group = db.security_group_get({}, security_group_id) rule_xml = "" for rule in security_group.rules: rule_xml += "" -- cgit From 2a782110bc51f147bdb35264445badac3b3e8e65 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 13 Sep 2010 11:45:28 +0200 Subject: Filters all get defined when running an instance. --- nova/virt/libvirt.qemu.xml.template | 2 +- nova/virt/libvirt.uml.xml.template | 2 +- nova/virt/libvirt_conn.py | 74 +++++++++++++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 6 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt.qemu.xml.template b/nova/virt/libvirt.qemu.xml.template index 5d3755b65..cbf501f9c 100644 --- a/nova/virt/libvirt.qemu.xml.template +++ b/nova/virt/libvirt.qemu.xml.template @@ -20,7 +20,7 @@ - + diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template index 1000da5ab..2030b87d2 100644 --- a/nova/virt/libvirt.uml.xml.template +++ b/nova/virt/libvirt.uml.xml.template @@ -14,7 +14,7 @@ - + diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 09c94577c..89ede1d1a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -27,6 +27,7 @@ import shutil from twisted.internet import defer from twisted.internet import task +from twisted.internet import threads from nova import db from nova import exception @@ -216,6 +217,7 @@ class LibvirtConnection(object): instance['id'], power_state.NOSTATE, 'launching') + yield NWFilterFirewall(self._conn).setup_nwfilters_for_instance(instance) yield self._create_image(instance, xml) yield self._conn.createXML(xml, 0) # TODO(termie): this should actually register @@ -442,7 +444,6 @@ class LibvirtConnection(object): domain = self._conn.lookupByName(instance_name) return domain.interfaceStats(interface) - class NWFilterFirewall(object): """ This class implements a network filtering mechanism versatile @@ -467,6 +468,14 @@ class NWFilterFirewall(object): the nodes with instances in the security group to refresh the security group. + Each instance has its own NWFilter, which references the above + mentioned security group NWFilters. This was done because + interfaces can only reference one filter while filters can + reference multiple other filters. This has the added benefit of + actually being able to add and remove security groups from an + instance at run time. This functionality is not exposed anywhere, + though. + Outstanding questions: The name is unique, so would there be any good reason to sync @@ -477,12 +486,14 @@ class NWFilterFirewall(object): redundancy. """ - def __init__(self): - pass + def __init__(self, get_connection): + self._conn = get_connection + def nova_base_filter(self): return ''' 26717364-50cf-42d1-8185-29bf893ab110 + @@ -491,6 +502,60 @@ class NWFilterFirewall(object): ''' + + def setup_nwfilters_for_instance(self, instance): + """ + Creates an NWFilter for the given instance. In the process, + it makes sure the filters for the security groups as well as + the base filter are all in place. + """ + + d = self.ensure_base_filter() + + nwfilter_xml = ("\n" + + " \n" + ) % instance['name'] + + for security_group in instance.security_groups: + d.addCallback(lambda _:self.ensure_security_group_filter(security_group.id)) + + nwfilter_xml += (" \n" + ) % security_group.id + nwfilter_xml += "" + + d.addCallback(lambda _: threads.deferToThread( + self._conn.nwfilterDefineXML, + nwfilter_xml)) + return d + + + def _nwfilter_name_for_security_group(self, security_group_id): + return 'nova-secgroup-%d' % (security_group_id,) + + + def ensure_filter(self, name, xml_generator): + def _already_exists_check(filterlist, filter): + return filter in filterlist + def _define_if_not_exists(exists, xml_generator): + if not exists: + xml = xml_generator() + return threads.deferToThread(self._conn.nwfilterDefineXML, xml) + d = threads.deferToThread(self._conn.listNWFilter) + d.addCallback(_already_exists_check, name) + d.addCallback(_define_if_not_exists, xml_generator) + return d + + + def ensure_base_filter(self): + return self.ensure_filter('nova-base-filter', self.nova_base_filter) + + + def ensure_security_group_filter(self, security_group_id): + return self.ensure_filter( + self._nwfilter_name_for_security_group(security_group_id), + lambda:self.security_group_to_nwfilter_xml(security_group_id)) + + def security_group_to_nwfilter_xml(self, security_group_id): security_group = db.security_group_get({}, security_group_id) rule_xml = "" @@ -498,7 +563,8 @@ class NWFilterFirewall(object): rule_xml += "" if rule.cidr: rule_xml += ("") % \ + "dstportstart='%s' dstportend='%s' />" + + "priority='900'\n") % \ (rule.cidr, rule.protocol, rule.from_port, rule.to_port) rule_xml += "" -- cgit From 077fc783c4f94de427da98818d262aeb09a31044 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 13 Sep 2010 12:04:06 +0200 Subject: (Untested) Make changes to security group rules propagate to the relevant compute nodes. --- nova/virt/libvirt_conn.py | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 89ede1d1a..a343267dc 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -444,6 +444,12 @@ class LibvirtConnection(object): domain = self._conn.lookupByName(instance_name) return domain.interfaceStats(interface) + + def refresh_security_group(self, security_group_id): + fw = self.NWFilterFirewall(self._conn) + fw.ensure_security_group_filter(security_group_id, override=True) + + class NWFilterFirewall(object): """ This class implements a network filtering mechanism versatile @@ -533,27 +539,32 @@ class NWFilterFirewall(object): return 'nova-secgroup-%d' % (security_group_id,) - def ensure_filter(self, name, xml_generator): - def _already_exists_check(filterlist, filter): - return filter in filterlist - def _define_if_not_exists(exists, xml_generator): - if not exists: - xml = xml_generator() - return threads.deferToThread(self._conn.nwfilterDefineXML, xml) - d = threads.deferToThread(self._conn.listNWFilter) - d.addCallback(_already_exists_check, name) + def define_filter(self, name, xml_generator, override=False): + if not override: + def _already_exists_check(filterlist, filter): + return filter in filterlist + def _define_if_not_exists(exists, xml_generator): + if not exists: + xml = xml_generator() + return threads.deferToThread(self._conn.nwfilterDefineXML, xml) + d = threads.deferToThread(self._conn.listNWFilter) + d.addCallback(_already_exists_check, name) + else: + # Pretend we looked it up and it wasn't defined + d = defer.succeed(False) d.addCallback(_define_if_not_exists, xml_generator) return d def ensure_base_filter(self): - return self.ensure_filter('nova-base-filter', self.nova_base_filter) + return self.define_filter('nova-base-filter', self.nova_base_filter) - def ensure_security_group_filter(self, security_group_id): - return self.ensure_filter( + def ensure_security_group_filter(self, security_group_id, override=False): + return self.define_filter( self._nwfilter_name_for_security_group(security_group_id), - lambda:self.security_group_to_nwfilter_xml(security_group_id)) + lambda:self.security_group_to_nwfilter_xml(security_group_id), + override=override) def security_group_to_nwfilter_xml(self, security_group_id): -- cgit From b15bde79b71e474d96674c8eae4108ac9c063731 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 13 Sep 2010 14:18:08 +0200 Subject: Fix call to listNWFilters --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index a343267dc..2e1dfcefc 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -547,7 +547,7 @@ class NWFilterFirewall(object): if not exists: xml = xml_generator() return threads.deferToThread(self._conn.nwfilterDefineXML, xml) - d = threads.deferToThread(self._conn.listNWFilter) + d = threads.deferToThread(self._conn.listNWFilters) d.addCallback(_already_exists_check, name) else: # Pretend we looked it up and it wasn't defined -- cgit From 9c4b6612e65d548542b1bf37373200e4e6abc98d Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 13 Sep 2010 14:20:32 +0200 Subject: Correctly pass ip_address to templates. --- nova/virt/libvirt.qemu.xml.template | 4 ++-- nova/virt/libvirt.uml.xml.template | 4 ++-- nova/virt/libvirt_conn.py | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt.qemu.xml.template b/nova/virt/libvirt.qemu.xml.template index cbf501f9c..d02aa9114 100644 --- a/nova/virt/libvirt.qemu.xml.template +++ b/nova/virt/libvirt.qemu.xml.template @@ -20,8 +20,8 @@ - - + + diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template index 2030b87d2..bf3f2f86a 100644 --- a/nova/virt/libvirt.uml.xml.template +++ b/nova/virt/libvirt.uml.xml.template @@ -14,8 +14,8 @@ - - + + diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 2e1dfcefc..00a80989f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -322,6 +322,7 @@ class LibvirtConnection(object): network = db.project_get_network(None, instance['project_id']) # FIXME(vish): stick this in db instance_type = instance_types.INSTANCE_TYPES[instance['instance_type']] + ip_address = db.instance_get_fixed_address({}, instance['id']) xml_info = {'type': FLAGS.libvirt_type, 'name': instance['name'], 'basepath': os.path.join(FLAGS.instances_path, @@ -329,7 +330,8 @@ class LibvirtConnection(object): 'memory_kb': instance_type['memory_mb'] * 1024, 'vcpus': instance_type['vcpus'], 'bridge_name': network['bridge'], - 'mac_address': instance['mac_address']} + 'mac_address': instance['mac_address'], + 'ip_address': ip_address } libvirt_xml = self.libvirt_xml % xml_info logging.debug('instance %s: finished toXML method', instance['name']) -- cgit From 01a041dd732ae9c56533f6eac25f08c34917d733 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 14 Sep 2010 15:17:52 +0200 Subject: Fix up rule generation. It turns out nwfilter gets very, very wonky indeed if you mix rules and rules. Setting a TCP rule adds an early rule to ebtables that ends up overriding the rules which are last in that table. --- nova/virt/libvirt_conn.py | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 00a80989f..aaa2c69b6 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -290,7 +290,6 @@ class LibvirtConnection(object): address = db.instance_get_fixed_address(None, inst['id']) with open(FLAGS.injected_network_template) as f: net = f.read() % {'address': address, - 'network': network_ref['network'], 'netmask': network_ref['netmask'], 'gateway': network_ref['gateway'], 'broadcast': network_ref['broadcast'], @@ -448,7 +447,7 @@ class LibvirtConnection(object): def refresh_security_group(self, security_group_id): - fw = self.NWFilterFirewall(self._conn) + fw = NWFilterFirewall(self._conn) fw.ensure_security_group_filter(security_group_id, override=True) @@ -541,19 +540,26 @@ class NWFilterFirewall(object): return 'nova-secgroup-%d' % (security_group_id,) + # TODO(soren): Should override be the default (and should it even + # be optional? We save a bit of processing time in + # libvirt by only defining this conditionally, but + # we still have to go and ask libvirt if the group + # is already defined, and there's the off chance of + # of inconsitencies having snuck in which would get + # fixed by just redefining the filter. def define_filter(self, name, xml_generator, override=False): if not override: def _already_exists_check(filterlist, filter): return filter in filterlist - def _define_if_not_exists(exists, xml_generator): - if not exists: - xml = xml_generator() - return threads.deferToThread(self._conn.nwfilterDefineXML, xml) d = threads.deferToThread(self._conn.listNWFilters) d.addCallback(_already_exists_check, name) else: # Pretend we looked it up and it wasn't defined d = defer.succeed(False) + def _define_if_not_exists(exists, xml_generator): + if not exists: + xml = xml_generator() + return threads.deferToThread(self._conn.nwfilterDefineXML, xml) d.addCallback(_define_if_not_exists, xml_generator) return d @@ -573,13 +579,20 @@ class NWFilterFirewall(object): security_group = db.security_group_get({}, security_group_id) rule_xml = "" for rule in security_group.rules: - rule_xml += "" + rule_xml += "" if rule.cidr: - rule_xml += ("" + - "priority='900'\n") % \ - (rule.cidr, rule.protocol, - rule.from_port, rule.to_port) - rule_xml += "" - xml = '''%s''' % (security_group_id, rule_xml,) + rule_xml += "<%s srcipaddr='%s' " % (rule.protocol, rule.cidr) + if rule.protocol in ['tcp', 'udp']: + rule_xml += "dstportstart='%s' dstportend='%s' " % \ + (rule.from_port, rule.to_port) + elif rule.protocol == 'icmp': + logging.info('rule.protocol: %r, rule.from_port: %r, rule.to_port: %r' % (rule.protocol, rule.from_port, rule.to_port)) + if rule.from_port != -1: + rule_xml += "type='%s' " % rule.from_port + if rule.to_port != -1: + rule_xml += "code='%s' " % rule.to_port + + rule_xml += '/>\n' + rule_xml += "\n" + xml = '''%s''' % (security_group_id, rule_xml,) return xml -- cgit From 587b21cc00919cc29e2f815fc9de3e3ad6e6fa30 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 14 Sep 2010 15:23:58 +0200 Subject: Leave out the network setting from the interfaces template. It does not get passed anymore. --- nova/virt/interfaces.template | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/interfaces.template b/nova/virt/interfaces.template index 11df301f6..87b92b84a 100644 --- a/nova/virt/interfaces.template +++ b/nova/virt/interfaces.template @@ -10,7 +10,6 @@ auto eth0 iface eth0 inet static address %(address)s netmask %(netmask)s - network %(network)s broadcast %(broadcast)s gateway %(gateway)s dns-nameservers %(dns)s -- cgit From 75a1815aa2724d64d1f487996265ba9136017029 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 20 Sep 2010 11:33:35 +0200 Subject: Add Xen template and use it by default if libvirt_type=xen. --- nova/virt/libvirt.xen.xml.template | 30 ++++++++++++++++++++++++++++++ nova/virt/libvirt_conn.py | 8 +++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 nova/virt/libvirt.xen.xml.template (limited to 'nova/virt') diff --git a/nova/virt/libvirt.xen.xml.template b/nova/virt/libvirt.xen.xml.template new file mode 100644 index 000000000..9677902c6 --- /dev/null +++ b/nova/virt/libvirt.xen.xml.template @@ -0,0 +1,30 @@ + + %(name)s + + linux + %(basepath)s/kernel + %(basepath)s/ramdisk + /dev/xvda1 + ro + + + + + %(memory_kb)s + %(vcpus)s + + + + + + + + + + + + + + + + diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d868e083c..09f178c2a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -47,6 +47,9 @@ FLAGS = flags.FLAGS flags.DEFINE_string('libvirt_xml_template', utils.abspath('virt/libvirt.qemu.xml.template'), 'Libvirt XML Template for QEmu/KVM') +flags.DEFINE_string('libvirt_xen_xml_template', + utils.abspath('virt/libvirt.xen.xml.template'), + 'Libvirt XML Template for Xen') flags.DEFINE_string('libvirt_uml_xml_template', utils.abspath('virt/libvirt.uml.xml.template'), 'Libvirt XML Template for user-mode-linux') @@ -55,7 +58,7 @@ flags.DEFINE_string('injected_network_template', 'Template file for injected network') flags.DEFINE_string('libvirt_type', 'kvm', - 'Libvirt domain type (valid options are: kvm, qemu, uml)') + 'Libvirt domain type (valid options are: kvm, qemu, uml, xen)') flags.DEFINE_string('libvirt_uri', '', 'Override the default libvirt URI (which is dependent' @@ -104,6 +107,9 @@ class LibvirtConnection(object): if FLAGS.libvirt_type == 'uml': uri = FLAGS.libvirt_uri or 'uml:///system' template_file = FLAGS.libvirt_uml_xml_template + elif FLAGS.libvirt_type == 'xen': + uri = FLAGS.libvirt_uri or 'xen:///' + template_file = FLAGS.libvirt_xen_xml_template else: uri = FLAGS.libvirt_uri or 'qemu:///system' template_file = FLAGS.libvirt_xml_template -- cgit From a3d003d7ec92f3ae23a667954a790c71efdbfdbe Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 20 Sep 2010 11:46:18 +0200 Subject: Move the code that extracts the console output into the virt drivers. Move the code that formats it up into the API layer. Add support for Xen console. --- nova/virt/fake.py | 2 ++ nova/virt/libvirt_conn.py | 40 ++++++++++++++++++++++++++++++++++++++++ nova/virt/xenapi.py | 3 +++ 3 files changed, 45 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 4ae6afcc4..dc6112f20 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -223,6 +223,8 @@ class FakeConnection(object): """ return [0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L] + def get_console_output(self, instance): + return 'FAKE CONSOLE OUTPUT' class FakeInstance(object): def __init__(self): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 09f178c2a..addd9c997 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -248,6 +248,46 @@ class LibvirtConnection(object): timer.start(interval=0.5, now=True) yield local_d + def _flush_xen_console(self, virsh_output): + logging.info('virsh said: %r' % (virsh_output,)) + virsh_output = virsh_output[0].strip() + + if virsh_output.startswith('/dev/'): + logging.info('cool, it\'s a device') + d = process.simple_execute("sudo dd if=%s iflag=nonblock" % virsh_output, check_exit_code=False) + d.addCallback(lambda r:r[0]) + return d + else: + return '' + + def _append_to_file(self, data, fpath): + logging.info('data: %r, fpath: %r' % (data, fpath)) + fp = open(fpath, 'a+') + fp.write(data) + return fpath + + def _dump_file(self, fpath): + fp = open(fpath, 'r+') + contents = fp.read() + logging.info('Contents: %r' % (contents,)) + return contents + + @exception.wrap_exception + def get_console_output(self, instance): + console_log = os.path.join(FLAGS.instances_path, instance['name'], 'console.log') + logging.info('console_log: %s' % console_log) + logging.info('FLAGS.libvirt_type: %s' % FLAGS.libvirt_type) + if FLAGS.libvirt_type == 'xen': + # Xen is spethial + d = process.simple_execute("virsh ttyconsole %s" % instance['name']) + d.addCallback(self._flush_xen_console) + d.addCallback(self._append_to_file, console_log) + else: + d = defer.succeed(console_log) + d.addCallback(self._dump_file) + return d + + @defer.inlineCallbacks def _create_image(self, inst, libvirt_xml): # syntactic nicety diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index 1c6de4403..5fdd2b9fc 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -299,6 +299,9 @@ class XenAPIConnection(object): 'num_cpu': rec['VCPUs_max'], 'cpu_time': 0} + def get_console_output(self, instance): + return 'FAKE CONSOLE OUTPUT' + @utils.deferredToThread def _lookup(self, i): return self._lookup_blocking(i) -- cgit From 9dbdca83a8233110e94356415629ab9589b580d5 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 27 Sep 2010 13:13:29 +0200 Subject: Allow DHCP requests through, pass the IP of the gateway as the dhcp server. --- nova/virt/libvirt.qemu.xml.template | 1 + nova/virt/libvirt.uml.xml.template | 1 + nova/virt/libvirt_conn.py | 6 +++++- 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt.qemu.xml.template b/nova/virt/libvirt.qemu.xml.template index d02aa9114..2538b1ade 100644 --- a/nova/virt/libvirt.qemu.xml.template +++ b/nova/virt/libvirt.qemu.xml.template @@ -22,6 +22,7 @@ + diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template index bf3f2f86a..bb8b47911 100644 --- a/nova/virt/libvirt.uml.xml.template +++ b/nova/virt/libvirt.uml.xml.template @@ -16,6 +16,7 @@ + diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4c4c7980b..93f6977d4 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -319,6 +319,8 @@ class LibvirtConnection(object): # FIXME(vish): stick this in db instance_type = instance_types.INSTANCE_TYPES[instance['instance_type']] ip_address = db.instance_get_fixed_address({}, instance['id']) + # Assume that the gateway also acts as the dhcp server. + dhcp_server = network['gateway'] xml_info = {'type': FLAGS.libvirt_type, 'name': instance['name'], 'basepath': os.path.join(FLAGS.instances_path, @@ -327,7 +329,8 @@ class LibvirtConnection(object): 'vcpus': instance_type['vcpus'], 'bridge_name': network['bridge'], 'mac_address': instance['mac_address'], - 'ip_address': ip_address } + 'ip_address': ip_address, + 'dhcp_server': dhcp_server } libvirt_xml = self.libvirt_xml % xml_info logging.debug('instance %s: finished toXML method', instance['name']) @@ -498,6 +501,7 @@ class NWFilterFirewall(object): return ''' 26717364-50cf-42d1-8185-29bf893ab110 + -- cgit From ab31fa628f4d9148aae8d42bbb41d721716c18e3 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 27 Sep 2010 21:49:53 +0200 Subject: Clean up nwfilter code. Move our filters into the ipv4 chain. --- nova/virt/libvirt_conn.py | 99 ++++++++++++++++++----------------------------- 1 file changed, 38 insertions(+), 61 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 93f6977d4..558854c38 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -497,20 +497,36 @@ class NWFilterFirewall(object): self._conn = get_connection - def nova_base_filter(self): - return ''' - 26717364-50cf-42d1-8185-29bf893ab110 - - - - - - - - -''' + nova_base_filter = ''' + 26717364-50cf-42d1-8185-29bf893ab110 + + + + + + + ''' + + nova_base_ipv4_filter = ''' + + ''' + + + nova_base_ipv6_filter = ''' + + ''' + + + def _define_filter(self, xml): + if callable(xml): + xml = xml() + d = threads.deferToThread(self._conn.nwfilterDefineXML, xml) + return d + @defer.inlineCallbacks def setup_nwfilters_for_instance(self, instance): """ Creates an NWFilter for the given instance. In the process, @@ -518,63 +534,24 @@ class NWFilterFirewall(object): the base filter are all in place. """ - d = self.ensure_base_filter() + yield self._define_filter(self.nova_base_ipv4_filter) + yield self._define_filter(self.nova_base_ipv6_filter) + yield self._define_filter(self.nova_base_filter) nwfilter_xml = ("\n" + - " \n" + " \n" ) % instance['name'] for security_group in instance.security_groups: - d.addCallback(lambda _:self.ensure_security_group_filter(security_group.id)) + yield self._define_filter( + self.security_group_to_nwfilter_xml(security_group['id'])) nwfilter_xml += (" \n" ) % security_group.id nwfilter_xml += "" - d.addCallback(lambda _: threads.deferToThread( - self._conn.nwfilterDefineXML, - nwfilter_xml)) - return d - - - def _nwfilter_name_for_security_group(self, security_group_id): - return 'nova-secgroup-%d' % (security_group_id,) - - - # TODO(soren): Should override be the default (and should it even - # be optional? We save a bit of processing time in - # libvirt by only defining this conditionally, but - # we still have to go and ask libvirt if the group - # is already defined, and there's the off chance of - # of inconsitencies having snuck in which would get - # fixed by just redefining the filter. - def define_filter(self, name, xml_generator, override=False): - if not override: - def _already_exists_check(filterlist, filter): - return filter in filterlist - d = threads.deferToThread(self._conn.listNWFilters) - d.addCallback(_already_exists_check, name) - else: - # Pretend we looked it up and it wasn't defined - d = defer.succeed(False) - def _define_if_not_exists(exists, xml_generator): - if not exists: - xml = xml_generator() - return threads.deferToThread(self._conn.nwfilterDefineXML, xml) - d.addCallback(_define_if_not_exists, xml_generator) - return d - - - def ensure_base_filter(self): - return self.define_filter('nova-base-filter', self.nova_base_filter) - - - def ensure_security_group_filter(self, security_group_id, override=False): - return self.define_filter( - self._nwfilter_name_for_security_group(security_group_id), - lambda:self.security_group_to_nwfilter_xml(security_group_id), - override=override) - + yield self._define_filter(nwfilter_xml) + return def security_group_to_nwfilter_xml(self, security_group_id): security_group = db.security_group_get({}, security_group_id) @@ -593,7 +570,7 @@ class NWFilterFirewall(object): if rule.to_port != -1: rule_xml += "code='%s' " % rule.to_port - rule_xml += '/>\n' + rule_xml += '/>\n' rule_xml += "\n" - xml = '''%s''' % (security_group_id, rule_xml,) + xml = '''%s''' % (security_group_id, rule_xml,) return xml -- cgit From e705b666679ecccfc3e91c8029f2c646849509ee Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 27 Sep 2010 21:57:13 +0200 Subject: Recreate ensure_security_group_filter. Needed for refresh. --- nova/virt/libvirt_conn.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 558854c38..a7370e036 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -448,7 +448,7 @@ class LibvirtConnection(object): def refresh_security_group(self, security_group_id): fw = NWFilterFirewall(self._conn) - fw.ensure_security_group_filter(security_group_id, override=True) + fw.ensure_security_group_filter(security_group_id) class NWFilterFirewall(object): @@ -543,16 +543,20 @@ class NWFilterFirewall(object): ) % instance['name'] for security_group in instance.security_groups: - yield self._define_filter( - self.security_group_to_nwfilter_xml(security_group['id'])) + yield self.ensure_security_group_filter(security_group['id']) nwfilter_xml += (" \n" - ) % security_group.id + ) % security_group['id'] nwfilter_xml += "" yield self._define_filter(nwfilter_xml) return + def ensure_security_group_filter(self, security_group_id): + return self._define_filter( + self.security_group_to_nwfilter_xml(security_group_id)) + + def security_group_to_nwfilter_xml(self, security_group_id): security_group = db.security_group_get({}, security_group_id) rule_xml = "" -- cgit From 9140cd991e5507f65ff1d6a608bd8fd4c9956dbf Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 27 Sep 2010 22:00:17 +0200 Subject: Set priority of security group rules to 300 to make sure they override the defaults. --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index a7370e036..d90853084 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -561,7 +561,7 @@ class NWFilterFirewall(object): security_group = db.security_group_get({}, security_group_id) rule_xml = "" for rule in security_group.rules: - rule_xml += "" + rule_xml += "" if rule.cidr: rule_xml += "<%s srcipaddr='%s' " % (rule.protocol, rule.cidr) if rule.protocol in ['tcp', 'udp']: -- cgit From 574aa4bb03c6e79c204d73a8f2a146460cbdb848 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 28 Sep 2010 00:21:36 +0200 Subject: This is getting ridiculous. --- nova/virt/libvirt_conn.py | 50 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d90853084..854fa6761 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -503,20 +503,49 @@ class NWFilterFirewall(object): + ''' - nova_base_ipv4_filter = ''' - - ''' - - - nova_base_ipv6_filter = ''' - - ''' + nova_dhcp_filter = ''' + 891e4787-e5c0-d59b-cbd6-41bc3c6b36fc + + + + + + + ''' + + def nova_base_ipv4_filter(self): + retval = "" + for protocol in ['tcp', 'udp', 'icmp']: + for direction,action in [('out','accept'), + ('in','drop')]: + retval += """ + <%s /> + """ % (action, direction, protocol) + retval += '' + return retval + + + def nova_base_ipv6_filter(self): + retval = "" + for protocol in ['tcp', 'udp', 'icmp']: + for direction,action in [('out','accept'), + ('in','drop')]: + retval += """ + <%s-ipv6 /> + """ % (action, direction, protocol) + retval += '' + return retval def _define_filter(self, xml): @@ -536,6 +565,7 @@ class NWFilterFirewall(object): yield self._define_filter(self.nova_base_ipv4_filter) yield self._define_filter(self.nova_base_ipv6_filter) + yield self._define_filter(self.nova_dhcp_filter) yield self._define_filter(self.nova_base_filter) nwfilter_xml = ("\n" + -- cgit From 886534ba4d0281afc0d169546a8d55d3a5c8ece9 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 28 Sep 2010 09:07:48 +0200 Subject: Make the incoming blocking rules take precedence over the output accept rules. --- nova/virt/libvirt_conn.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 854fa6761..40a921743 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -527,11 +527,11 @@ class NWFilterFirewall(object): def nova_base_ipv4_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: - for direction,action in [('out','accept'), - ('in','drop')]: - retval += """ + for direction,action,priority in [('out','accept', 400), + ('in','drop', 399)]: + retval += """ <%s /> - """ % (action, direction, protocol) + """ % (action, direction, protocol, priority) retval += '' return retval @@ -539,11 +539,12 @@ class NWFilterFirewall(object): def nova_base_ipv6_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: - for direction,action in [('out','accept'), - ('in','drop')]: - retval += """ + for direction,action,priority in [('out','accept',400), + ('in','drop',399)]: + retval += """ <%s-ipv6 /> - """ % (action, direction, protocol) + """ % (action, direction, + protocol, priority) retval += '' return retval -- cgit From 0dcf2e7e593cce4be1654fb4923ec4bb4524198f Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 28 Sep 2010 09:47:25 +0200 Subject: Make sure arguments to string format are in the correct order. --- nova/virt/libvirt_conn.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 40a921743..c86f3ffb7 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -531,7 +531,8 @@ class NWFilterFirewall(object): ('in','drop', 399)]: retval += """ <%s /> - """ % (action, direction, protocol, priority) + """ % (action, direction, + priority, protocol) retval += '' return retval @@ -544,7 +545,7 @@ class NWFilterFirewall(object): retval += """ <%s-ipv6 /> """ % (action, direction, - protocol, priority) + priority, protocol) retval += '' return retval -- cgit From a86507b3224eb051fea97f65bd5653758fa91668 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Wed, 29 Sep 2010 06:17:39 -0700 Subject: fix ordering of rules to actually allow out and drop in --- nova/virt/libvirt_conn.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c86f3ffb7..9d889cf29 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -527,8 +527,8 @@ class NWFilterFirewall(object): def nova_base_ipv4_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: - for direction,action,priority in [('out','accept', 400), - ('in','drop', 399)]: + for direction,action,priority in [('out','accept', 399), + ('inout','drop', 400)]: retval += """ <%s /> """ % (action, direction, @@ -540,8 +540,8 @@ class NWFilterFirewall(object): def nova_base_ipv6_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: - for direction,action,priority in [('out','accept',400), - ('in','drop',399)]: + for direction,action,priority in [('out','accept',399), + ('inout','drop',400)]: retval += """ <%s-ipv6 /> """ % (action, direction, -- cgit From 8c21cc52b1ba007fc12964ea5973290a3f660662 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 30 Sep 2010 10:21:44 +0100 Subject: Bug #651887: xenapi list_instances completely broken Don't just compute the result for list_instances -- return it! --- nova/virt/xenapi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index 1c6de4403..0d06b1fce 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -103,8 +103,8 @@ class XenAPIConnection(object): self._conn.login_with_password(user, pw) def list_instances(self): - result = [self._conn.xenapi.VM.get_name_label(vm) \ - for vm in self._conn.xenapi.VM.get_all()] + return [self._conn.xenapi.VM.get_name_label(vm) \ + for vm in self._conn.xenapi.VM.get_all()] @defer.inlineCallbacks def spawn(self, instance): -- cgit From c9d2b8bcb365f326a47df93920c11be2ca054b18 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 30 Sep 2010 23:04:53 -0700 Subject: Fixed flat network manager with network index gone. Both managers use ips created through nova manage. Use of project_get_network is minimized to make way for managers that would prefer to use cluste or host based ips instead of project based ips. --- nova/virt/libvirt_conn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index d868e083c..f6d8aace6 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -280,7 +280,7 @@ class LibvirtConnection(object): key = str(inst['key_data']) net = None - network_ref = db.project_get_network(None, project.id) + network_ref = db.network_get_by_instance(None, inst['id']) if network_ref['injected']: address = db.instance_get_fixed_address(None, inst['id']) with open(FLAGS.injected_network_template) as f: @@ -314,7 +314,7 @@ class LibvirtConnection(object): def to_xml(self, instance): # TODO(termie): cache? logging.debug('instance %s: starting toXML method', instance['name']) - network = db.project_get_network(None, instance['project_id']) + network = db.instance_get_fixed_by_instance(None, inst['id']) # FIXME(vish): stick this in db instance_type = instance_types.INSTANCE_TYPES[instance['instance_type']] xml_info = {'type': FLAGS.libvirt_type, -- cgit From 12e43d9deb3984d2b7ccc91490ffa4c13eedbe2b Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Sat, 2 Oct 2010 16:55:57 +0100 Subject: Bug #653651: XenAPI support completely broken by orm-refactor merge Matches changes in the database / model layer with corresponding fixes to nova.virt.xenapi. --- nova/virt/xenapi.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index 0d06b1fce..118e0b687 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -42,10 +42,12 @@ from twisted.internet import defer from twisted.internet import reactor from twisted.internet import task +from nova import db from nova import flags from nova import process from nova import utils from nova.auth.manager import AuthManager +from nova.compute import instance_types from nova.compute import power_state from nova.virt import images @@ -113,32 +115,24 @@ class XenAPIConnection(object): raise Exception('Attempted to create non-unique name %s' % instance.name) - if 'bridge_name' in instance.datamodel: - network_ref = \ - yield self._find_network_with_bridge( - instance.datamodel['bridge_name']) - else: - network_ref = None - - if 'mac_address' in instance.datamodel: - mac_address = instance.datamodel['mac_address'] - else: - mac_address = '' + network = db.project_get_network(None, instance.project_id) + network_ref = \ + yield self._find_network_with_bridge(network.bridge) - user = AuthManager().get_user(instance.datamodel['user_id']) - project = AuthManager().get_project(instance.datamodel['project_id']) + user = AuthManager().get_user(instance.user_id) + project = AuthManager().get_project(instance.project_id) vdi_uuid = yield self._fetch_image( - instance.datamodel['image_id'], user, project, True) + instance.image_id, user, project, True) kernel = yield self._fetch_image( - instance.datamodel['kernel_id'], user, project, False) + instance.kernel_id, user, project, False) ramdisk = yield self._fetch_image( - instance.datamodel['ramdisk_id'], user, project, False) + instance.ramdisk_id, user, project, False) vdi_ref = yield self._call_xenapi('VDI.get_by_uuid', vdi_uuid) vm_ref = yield self._create_vm(instance, kernel, ramdisk) yield self._create_vbd(vm_ref, vdi_ref, 0, True) if network_ref: - yield self._create_vif(vm_ref, network_ref, mac_address) + yield self._create_vif(vm_ref, network_ref, instance.mac_address) logging.debug('Starting VM %s...', vm_ref) yield self._call_xenapi('VM.start', vm_ref, False, False) logging.info('Spawning VM %s created %s.', instance.name, vm_ref) @@ -148,8 +142,9 @@ class XenAPIConnection(object): """Create a VM record. Returns a Deferred that gives the new VM reference.""" - mem = str(long(instance.datamodel['memory_kb']) * 1024) - vcpus = str(instance.datamodel['vcpus']) + instance_type = instance_types.INSTANCE_TYPES[instance.instance_type] + mem = str(long(instance_type['memory_mb']) * 1024 * 1024) + vcpus = str(instance_type['vcpus']) rec = { 'name_label': instance.name, 'name_description': '', -- cgit From a4720c03a8260fb920035d072799d3ecc478db99 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Mon, 4 Oct 2010 21:58:22 +0200 Subject: Merge security group related changes from lp:~anso/nova/deploy --- nova/virt/libvirt_conn.py | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9d889cf29..319f7d2af 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -25,6 +25,7 @@ import logging import os import shutil +import IPy from twisted.internet import defer from twisted.internet import task from twisted.internet import threads @@ -34,6 +35,7 @@ from nova import exception from nova import flags from nova import process from nova import utils +#from nova.api import context from nova.auth import manager from nova.compute import disk from nova.compute import instance_types @@ -61,6 +63,9 @@ flags.DEFINE_string('libvirt_uri', '', 'Override the default libvirt URI (which is dependent' ' on libvirt_type)') +flags.DEFINE_bool('allow_project_net_traffic', + True, + 'Whether to allow in project network traffic') def get_connection(read_only): @@ -135,7 +140,7 @@ class LibvirtConnection(object): d.addCallback(lambda _: self._cleanup(instance)) # FIXME: What does this comment mean? # TODO(termie): short-circuit me for tests - # WE'LL save this for when we do shutdown, + # WE'LL save this for when we do shutdown, # instead of destroy - but destroy returns immediately timer = task.LoopingCall(f=None) def _wait_for_shutdown(): @@ -550,6 +555,16 @@ class NWFilterFirewall(object): return retval + def nova_project_filter(self, project, net, mask): + retval = "" % project + for protocol in ['tcp', 'udp', 'icmp']: + retval += """ + <%s srcipaddr='%s' srcipmask='%s' /> + """ % (protocol, net, mask) + retval += '' + return retval + + def _define_filter(self, xml): if callable(xml): xml = xml() @@ -557,6 +572,11 @@ class NWFilterFirewall(object): return d + @staticmethod + def _get_net_and_mask(cidr): + net = IPy.IP(cidr) + return str(net.net()), str(net.netmask()) + @defer.inlineCallbacks def setup_nwfilters_for_instance(self, instance): """ @@ -570,9 +590,19 @@ class NWFilterFirewall(object): yield self._define_filter(self.nova_dhcp_filter) yield self._define_filter(self.nova_base_filter) - nwfilter_xml = ("\n" + + nwfilter_xml = ("\n" + " \n" - ) % instance['name'] + ) % instance['name'] + + if FLAGS.allow_project_net_traffic: + network_ref = db.project_get_network({}, instance['project_id']) + net, mask = self._get_net_and_mask(network_ref['cidr']) + project_filter = self.nova_project_filter(instance['project_id'], + net, mask) + yield self._define_filter(project_filter) + + nwfilter_xml += (" \n" + ) % instance['project_id'] for security_group in instance.security_groups: yield self.ensure_security_group_filter(security_group['id']) @@ -595,7 +625,8 @@ class NWFilterFirewall(object): for rule in security_group.rules: rule_xml += "" if rule.cidr: - rule_xml += "<%s srcipaddr='%s' " % (rule.protocol, rule.cidr) + net, mask = self._get_net_and_mask(rule.cidr) + rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % (rule.protocol, net, mask) if rule.protocol in ['tcp', 'udp']: rule_xml += "dstportstart='%s' dstportend='%s' " % \ (rule.from_port, rule.to_port) -- cgit From 8f524607856dbf4cecf7c7503e53e14c42888307 Mon Sep 17 00:00:00 2001 From: Hisaki Ohara Date: Wed, 6 Oct 2010 18:04:18 +0900 Subject: Defined images_path for nova-compute. Without its setting, it fails to launch instances by exception at _fetch_local_image. --- nova/virt/images.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index a60bcc4c1..9ba5b7890 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -26,6 +26,7 @@ import time import urlparse from nova import flags +from nova import utils from nova import process from nova.auth import manager from nova.auth import signer @@ -34,6 +35,8 @@ from nova.auth import signer FLAGS = flags.FLAGS flags.DEFINE_bool('use_s3', True, 'whether to get images from s3 or use local copy') +flags.DEFINE_string('images_path', utils.abspath('../images'), + 'path to decrypted images') def fetch(image, path, user, project): -- cgit From b7028c0d0262d3d4395077a8bd2d95664c6bf16e Mon Sep 17 00:00:00 2001 From: Hisaki Ohara Date: Thu, 7 Oct 2010 23:03:43 +0900 Subject: Imported images_path from nova.objectstore for nova-compute. Without its setting, it fails to launch instances by exception at _fetch_local_image. --- nova/virt/images.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/images.py b/nova/virt/images.py index 9ba5b7890..dc50764d9 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -26,17 +26,15 @@ import time import urlparse from nova import flags -from nova import utils from nova import process from nova.auth import manager from nova.auth import signer +from nova.objectstore import image FLAGS = flags.FLAGS flags.DEFINE_bool('use_s3', True, 'whether to get images from s3 or use local copy') -flags.DEFINE_string('images_path', utils.abspath('../images'), - 'path to decrypted images') def fetch(image, path, user, project): -- cgit From b2a95bc859b3b52adb71efc4445924e1dbbdd06a Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 12 Oct 2010 19:27:48 -0700 Subject: fix tests --- nova/virt/libvirt_conn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 6ef5aa472..94a36a530 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -320,7 +320,8 @@ class LibvirtConnection(object): def to_xml(self, instance): # TODO(termie): cache? logging.debug('instance %s: starting toXML method', instance['name']) - network = db.instance_get_fixed_by_instance(None, inst['id']) + network = db.project_get_network(None, + instance['project_id']) # FIXME(vish): stick this in db instance_type = instance_types.INSTANCE_TYPES[instance['instance_type']] ip_address = db.instance_get_fixed_address({}, instance['id']) -- cgit