From 0bcfe0b990fb8df799df2c2bb95f324beeccc974 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Sat, 3 Sep 2011 14:32:35 +0100 Subject: Add iptables filter rules for dnsmasq On Fedora, the default policy for the INPUT chain in the filter table is DROP. This means that DHCP and DNS request packets from the guest get dropped. Add these rules to allow the traffic through: $> sudo iptables -t filter -A nova-network-INPUT -i br0 -p udp -m udp --dport 67 -j ACCEPT $> sudo iptables -t filter -A nova-network-INPUT -i br0 -p tcp -m tcp --dport 67 -j ACCEPT $> sudo iptables -t filter -A nova-network-INPUT -i br0 -p udp -m udp --dport 53 -j ACCEPT $> sudo iptables -t filter -A nova-network-INPUT -i br0 -p tcp -m tcp --dport 53 -j ACCEPT --- nova/network/linux_net.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 57c1d0c28..dc0d2caa0 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -511,6 +511,17 @@ def get_dhcp_hosts(context, network_ref): return '\n'.join(hosts) +def _add_dnsmasq_accept_rules(dev): + """Allow DHCP and DNS traffic through to dnsmasq.""" + table = iptables_manager.ipv4['filter'] + for port in [67, 53]: + for proto in ['udp', 'tcp']: + args = {'dev' : dev, 'port' : port, 'proto' : proto} + table.add_rule('INPUT', + '-i %(dev)s -p %(proto)s -m %(proto)s ' + '--dport %(port)s -j ACCEPT' % args) + iptables_manager.apply() + # NOTE(ja): Sending a HUP only reloads the hostfile, so any # configuration options (like dchp-range, vlan, ...) # aren't reloaded. @@ -565,6 +576,7 @@ def update_dhcp(context, dev, network_ref): _execute(*cmd, run_as_root=True) + _add_dnsmasq_accept_rules(dev) @utils.synchronized('radvd_start') def update_ra(context, dev, network_ref): -- cgit From a3ccd0f8454a0c381debf180b589d5837ef6674d Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Fri, 16 Sep 2011 15:38:29 -0400 Subject: create disk.local the same way ephemerals are created (LP: #851145) If the user did not specify '--block-device-mapping /dev/vdb=ephemeral0', then then the first non-root device would end up being created differently then if they had. It would not have a filesystem on it. This makes 'local_gb' in the libvirt connection created the same way that it would be if it were named ephemeral0. --- nova/virt/libvirt/connection.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 64cd2c47f..034259d73 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -865,9 +865,13 @@ class LibvirtConnection(driver.ComputeDriver): local_gb = inst['local_gb'] if local_gb and not self._volume_in_mapping( self.default_local_device, block_device_info): - self._cache_image(fn=self._create_local, + fn = functools.partial(self._create_ephemeral, + fs_label='ephemeral0', + os_type=inst.os_type) + self._cache_image(fn=fn, target=basepath('disk.local'), - fname="local_%s" % local_gb, + fname="ephemeral_%s_%s_%s" % + ("0", local_gb, inst.os_type), cow=FLAGS.use_cow_images, local_size=local_gb) -- cgit From a1229e5dbc3c03887dec49d93f55a0e4f60d96be Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Sat, 17 Sep 2011 08:28:16 +0100 Subject: Fix pep8 issues --- nova/network/linux_net.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 577a98c84..f87307651 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -529,7 +529,7 @@ def _add_dnsmasq_accept_rules(dev): table = iptables_manager.ipv4['filter'] for port in [67, 53]: for proto in ['udp', 'tcp']: - args = {'dev' : dev, 'port' : port, 'proto' : proto} + args = {'dev': dev, 'port': port, 'proto': proto} table.add_rule('INPUT', '-i %(dev)s -p %(proto)s -m %(proto)s ' '--dport %(port)s -j ACCEPT' % args) @@ -627,6 +627,7 @@ def update_dhcp(context, dev, network_ref): _add_dnsmasq_accept_rules(dev) + @utils.synchronized('radvd_start') def update_ra(context, dev, network_ref): conffile = _ra_file(dev, 'conf') -- cgit From ff9d353b2f4fee469e530fbc8dc231a41f6fed84 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 19 Sep 2011 16:57:44 -0400 Subject: convert images that are not 'raw' to 'raw' during caching to node This uses 'qemu-img' to convert images that are not 'raw' to be 'raw'. By doing so, it a.) refuses to run uploaded images that have a backing image reference (LP: #853330, CVE-2011-3147) b.) ensures that when FLAGS.use_cow_images is False, and the libvirt xml written specifies 'driver_type="raw"' that the disk referenced is also raw format. (LP: #837102) c.) removes compression that might be present to avoid cpu bottlenecks (LP: #837100) It does have the negative side affect of using more space in the case where the user uploaded a qcow2 (or other advanced image format) that could have been used directly by the hypervisor. That could, later, be remedied by another 'qemu-img convert' being done to the "preferred" format of the hypervisor. --- nova/virt/images.py | 55 +++++++++++++++++++++++++++++++++++++++++ nova/virt/libvirt/connection.py | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/images.py b/nova/virt/images.py index 810b359d9..c8a95bb00 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -21,6 +21,9 @@ Handling of VM disk images. """ +import os + +from nova import exception from nova import flags from nova.image import glance as glance_image_service import nova.image @@ -42,3 +45,55 @@ def fetch(context, image_href, path, _user_id, _project_id): with open(path, "wb") as image_file: metadata = image_service.get(context, image_id, image_file) return metadata + + +def fetch_to_raw(context, image_href, path, _user_id, _project_id): + path_tmp = "%s.part" % path + metadata = fetch(context, image_href, path_tmp, _user_id, _project_id) + + def _qemu_img_info(path): + + out, err = utils.execute('qemu-img', 'info', path) + + # output of qemu-img is 'field: value' + # the fields of interest are 'file format' and 'backing file' + data = {} + for line in out.splitlines(): + (field, val) = line.split(':', 1) + if val[0] == " ": + val = val[1:] + data[field] = val + + return(data) + + data = _qemu_img_info(path_tmp) + + fmt = data.get("file format", None) + if fmt == None: + raise exception.ImageUnacceptable( + reason="'qemu-img info' parsing failed.", image_id=image_href) + + if fmt != "raw": + staged = "%s.converted" % path + if "backing file" in data: + raise exception.ImageUnacceptable(image_id=image_href, + reason="Dangerous! fmt=%s with backing file: %s" % + (fmt, data['backing file'])) + + LOG.debug("%s was %s, converting to raw" % (image_href, fmt)) + out, err = utils.execute('qemu-img', 'convert', '-O', 'raw', + path_tmp, staged) + os.unlink(path_tmp) + + data = _qemu_img_info(staged) + if data.get('file format', None) != "raw": + raise exception.ImageUnacceptable(image_id=image_href, + reason="Dangerous! Converted to raw, but format is now %s" % + data.get('file format', None)) + + os.rename(staged, path) + + else: + os.rename(path_tmp, path) + + return metadata diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 4a0849127..44a733340 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -769,7 +769,7 @@ class LibvirtConnection(driver.ComputeDriver): def _fetch_image(self, context, target, image_id, user_id, project_id, size=None): """Grab image and optionally attempt to resize it""" - images.fetch(context, image_id, target, user_id, project_id) + images.fetch_to_raw(context, image_id, target, user_id, project_id) if size: disk.extend(target, size) -- cgit From cb363ba54a55f0f73eae741701dc2256d571b802 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 19 Sep 2011 17:10:48 -0400 Subject: use '_(' for exception messages --- nova/virt/images.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/images.py b/nova/virt/images.py index c8a95bb00..3aee83e2d 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -71,13 +71,13 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): fmt = data.get("file format", None) if fmt == None: raise exception.ImageUnacceptable( - reason="'qemu-img info' parsing failed.", image_id=image_href) + reason=_("'qemu-img info' parsing failed."), image_id=image_href) if fmt != "raw": staged = "%s.converted" % path if "backing file" in data: raise exception.ImageUnacceptable(image_id=image_href, - reason="Dangerous! fmt=%s with backing file: %s" % + reason=_("Dangerous! fmt=%s with backing file: %s") % (fmt, data['backing file'])) LOG.debug("%s was %s, converting to raw" % (image_href, fmt)) @@ -88,7 +88,7 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): data = _qemu_img_info(staged) if data.get('file format', None) != "raw": raise exception.ImageUnacceptable(image_id=image_href, - reason="Dangerous! Converted to raw, but format is now %s" % + reason=_("Dangerous! Converted to raw, but format is now %s") % data.get('file format', None)) os.rename(staged, path) -- cgit From cd392de955e442d93cfca6efab8a51d64e7f9940 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 19 Sep 2011 18:13:08 -0400 Subject: use dictionary format for exception message --- nova/virt/images.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/images.py b/nova/virt/images.py index 3aee83e2d..3ea579c7a 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -76,9 +76,10 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): if fmt != "raw": staged = "%s.converted" % path if "backing file" in data: + backing_file = data['backing file'] raise exception.ImageUnacceptable(image_id=image_href, - reason=_("Dangerous! fmt=%s with backing file: %s") % - (fmt, data['backing file'])) + reason=_("Dangerous! fmt=%(fmt) backed by: %(backing_file)") % + locals) LOG.debug("%s was %s, converting to raw" % (image_href, fmt)) out, err = utils.execute('qemu-img', 'convert', '-O', 'raw', -- cgit From 5428587ed2085abfd909834a890251cb4cfb372b Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 19 Sep 2011 20:39:02 -0400 Subject: fix syntax error in exception, remove "Dangerous!" comment --- nova/virt/images.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/images.py b/nova/virt/images.py index 3ea579c7a..573c8aecf 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -78,8 +78,8 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): if "backing file" in data: backing_file = data['backing file'] raise exception.ImageUnacceptable(image_id=image_href, - reason=_("Dangerous! fmt=%(fmt) backed by: %(backing_file)") % - locals) + reason=_("fmt=%(fmt)s backed by: %(backing_file)s" % + locals())) LOG.debug("%s was %s, converting to raw" % (image_href, fmt)) out, err = utils.execute('qemu-img', 'convert', '-O', 'raw', @@ -89,7 +89,7 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): data = _qemu_img_info(staged) if data.get('file format', None) != "raw": raise exception.ImageUnacceptable(image_id=image_href, - reason=_("Dangerous! Converted to raw, but format is now %s") % + reason=_("Converted to raw, but format is now %s") % data.get('file format', None)) os.rename(staged, path) -- cgit From 869b0724e897d75695c2a1a559d1447745f9dce8 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Mon, 19 Sep 2011 22:04:32 -0400 Subject: Update migration 047 to dynamically lookup the name of the instance_id fkey before dropping it. We can't hard code the name of the fkey since we didn't name it explicitly on create. --- .../versions/047_remove_instances_fk_from_vif.py | 32 ++++++++-------------- 1 file changed, 12 insertions(+), 20 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py b/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py index ae9486e58..dadcefc39 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/047_remove_instances_fk_from_vif.py @@ -17,21 +17,9 @@ from migrate import ForeignKeyConstraint from nova import log as logging - meta = MetaData() -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -vifs = Table('virtual_interfaces', meta, - Column('id', Integer(), primary_key=True, nullable=False), - Column('instance_id', Integer()), - ) - - def upgrade(migrate_engine): # Upgrade operations go here. Don't create your own engine; # bind migrate_engine to your metadata @@ -40,17 +28,18 @@ def upgrade(migrate_engine): if dialect.startswith('sqlite'): return + instances = Table('instances', meta, autoload=True) + vifs = Table('virtual_interfaces', meta, autoload=True) + try: + fkey_name = vifs.c.instance_id.foreign_keys[0].constraint.name ForeignKeyConstraint(columns=[vifs.c.instance_id], - refcolumns=[instances.c.id]).drop() + refcolumns=[instances.c.id], + name=fkey_name).drop() + except Exception: - try: - migrate_engine.execute("ALTER TABLE virtual_interfaces DROP " \ - "FOREIGN KEY " \ - "`virtual_interfaces_ibfk_2`;") - except Exception: - logging.error(_("foreign key constraint couldn't be removed")) - raise + logging.error(_("foreign key constraint couldn't be removed")) + raise def downgrade(migrate_engine): @@ -60,6 +49,9 @@ def downgrade(migrate_engine): if dialect.startswith('sqlite'): return + instances = Table('instances', meta, autoload=True) + vifs = Table('virtual_interfaces', meta, autoload=True) + try: ForeignKeyConstraint(columns=[vifs.c.instance_id], refcolumns=[instances.c.id]).create() -- cgit From b399ac7b6e80d16fddd61c9b2d505cff09cb8889 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 20 Sep 2011 03:18:45 -0400 Subject: fix call to gettext --- nova/virt/images.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/images.py b/nova/virt/images.py index 573c8aecf..ea04377c5 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -78,8 +78,7 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): if "backing file" in data: backing_file = data['backing file'] raise exception.ImageUnacceptable(image_id=image_href, - reason=_("fmt=%(fmt)s backed by: %(backing_file)s" % - locals())) + reason=_("fmt=%(fmt)s backed by: %(backing_file)s") % locals()) LOG.debug("%s was %s, converting to raw" % (image_href, fmt)) out, err = utils.execute('qemu-img', 'convert', '-O', 'raw', -- cgit From 382f1dc728c2503e28237e9b2b455d46570943e6 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 20 Sep 2011 05:40:18 -0400 Subject: Address Soren's comments: * clean up temp files if an ImageUnacceptable is going to be raised Note, a qemu-img execution error will not clean up the image, but I think thats reasonable. We leave the image on disk so the user can easily investigate. * Change final 2 arguments to fetch_to_raw to not start with an _ * use 'env' utility to change environment variables LC_ALL and LANG so that qemu-img output parsing is not locale dependent. Note, I considered the following, but found using 'env' more readable out, err = utils.execute('sh', '-c', 'export LC_ALL=C LANG=C && exec "$@"', 'qemu-img', 'info', path) --- nova/virt/images.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/images.py b/nova/virt/images.py index ea04377c5..968fe1692 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -47,13 +47,14 @@ def fetch(context, image_href, path, _user_id, _project_id): return metadata -def fetch_to_raw(context, image_href, path, _user_id, _project_id): +def fetch_to_raw(context, image_href, path, user_id, project_id): path_tmp = "%s.part" % path - metadata = fetch(context, image_href, path_tmp, _user_id, _project_id) + metadata = fetch(context, image_href, path_tmp, user_id, project_id) def _qemu_img_info(path): - out, err = utils.execute('qemu-img', 'info', path) + out, err = utils.execute('env', 'LC_ALL=C', 'LANG=C', + 'qemu-img', 'info', path) # output of qemu-img is 'field: value' # the fields of interest are 'file format' and 'backing file' @@ -70,6 +71,7 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): fmt = data.get("file format", None) if fmt == None: + os.unlink(path_tmp) raise exception.ImageUnacceptable( reason=_("'qemu-img info' parsing failed."), image_id=image_href) @@ -77,6 +79,7 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): staged = "%s.converted" % path if "backing file" in data: backing_file = data['backing file'] + os.unlink(path_tmp) raise exception.ImageUnacceptable(image_id=image_href, reason=_("fmt=%(fmt)s backed by: %(backing_file)s") % locals()) @@ -87,6 +90,7 @@ def fetch_to_raw(context, image_href, path, _user_id, _project_id): data = _qemu_img_info(staged) if data.get('file format', None) != "raw": + os.unlink(staged) raise exception.ImageUnacceptable(image_id=image_href, reason=_("Converted to raw, but format is now %s") % data.get('file format', None)) -- cgit