From 3403e773f5a38c5d415e4ab66799c6e239223a0d Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Mon, 17 Jan 2011 19:40:35 -0500 Subject: Stubbed-out code for working with provider-firewalls. --- nova/adminclient.py | 21 +++++++++++++++++++++ nova/api/ec2/admin.py | 8 ++++++++ 2 files changed, 29 insertions(+) (limited to 'nova') diff --git a/nova/adminclient.py b/nova/adminclient.py index b2609c8c4..90ad4d9dd 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -190,6 +190,22 @@ class HostInfo(object): setattr(self, name, value) +class SimpleResponse(object): + def __init__(self, connection=None): + self.connection = connection + self.status = None + self.message = '' + + def __repr__(self): + return 'Status:%s' % self.status + + def startElement(self, name, attrs, connection): + return None + + def endElement(self, name, value, connection): + setattr(self, name.lower(), str(value)) + + class NovaAdminClient(object): def __init__( @@ -373,3 +389,8 @@ class NovaAdminClient(object): def get_hosts(self): return self.apiconn.get_list('DescribeHosts', {}, [('item', HostInfo)]) + + def block_ips(self, cidr): + """Block incoming traffic from specified hosts.""" + return self.apiconn.get_object('BlockExternalAddresses', + {'Cidr': cidr}, SimpleResponse) diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 758b612e8..b784c5a00 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -209,3 +209,11 @@ class AdminController(object): def describe_host(self, _context, name, **_kwargs): """Returns status info for single node.""" return host_dict(db.host_get(name)) + + def block_external_addresses(self, context, cidr): + """Add provider-level firewall rules to block incoming traffic.""" + LOG.audit(_("Blocking access to all projects incoming from %s"), + cidr, context=context) + raise NotImplementedError(_("Awaiting implementation.")) + # TODO(todd): implement + # return {'status': 'OK', 'message': 'Disabled (number) IPs'} -- cgit From d4e7eb818c9f4ec51fd3a88a0e92d557867511d4 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Mon, 17 Jan 2011 23:18:46 -0500 Subject: Add rules to database, cast refresh message and trickle down to firewall driver. --- nova/api/ec2/admin.py | 43 +++++++++++++++++++++++++++++++++++++++---- nova/compute/api.py | 9 +++++++++ nova/compute/manager.py | 5 +++++ nova/db/api.py | 8 ++++++++ nova/db/sqlalchemy/api.py | 12 ++++++++++++ nova/db/sqlalchemy/models.py | 11 +++++++++++ nova/virt/connection.py | 1 + nova/virt/libvirt_conn.py | 10 ++++++++++ 8 files changed, 95 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index b784c5a00..1ae5f7094 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -21,7 +21,10 @@ Admin API controller, exposed through http via the api worker. """ import base64 +import IPy +import urllib +from nova import compute from nova import db from nova import exception from nova import log as logging @@ -70,6 +73,9 @@ class AdminController(object): def __str__(self): return 'AdminController' + def __init__(self): + self.compute_api = compute.API() + def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" return user_dict(manager.AuthManager().get_user(name)) @@ -210,10 +216,39 @@ class AdminController(object): """Returns status info for single node.""" return host_dict(db.host_get(name)) + def _provider_fw_rule_exists(context, rule): + for old_rule in db.provider_fw_rule_get_all(context): + for key in ('cidr', 'from_port', 'to_port', 'protocol'): + dupe = True + if rule[key] != old_rule[key]: + dupe = False + if dupe: + return dupe + return False + + def block_external_addresses(self, context, cidr): """Add provider-level firewall rules to block incoming traffic.""" - LOG.audit(_("Blocking access to all projects incoming from %s"), + LOG.audit(_("Blocking traffic to all projects incoming from %s"), cidr, context=context) - raise NotImplementedError(_("Awaiting implementation.")) - # TODO(todd): implement - # return {'status': 'OK', 'message': 'Disabled (number) IPs'} + rule = {'cidr': IPy.IP(urllib.unquote(cidr).decode())} + tcp_rule = rule.copy() + tcp_rule.update({"protocol": "TCP", "from_port": 1, "to_port": 65535}) + udp_rule = rule.copy() + udp_rule.update({"protocol": "UDP", "from_port": 1, "to_port": 65535}) + icmp_rule = rule.copy() + icmp_rule.update({"protocol": "ICMP", "from_port": -1, "to_port": -1}) + rules_added = 0 + if not self._provider_fw_rule_exists(context, tcp_rule): + db.provider_fw_rule_create(context, tcp_rule) + rules_added += 1 + if not self._provider_fw_rule_exists(context, udp_rule): + db.provider_fw_rule_create(context, udp_rule) + rules_added += 1 + if not self._provider_fw_rule_exists(context, icmp_rule): + db.provider_fw_rule_create(context, icmp_rule) + rules_added += 1 + if rules_added == 0: + raise exception.ApiError(_('Duplicate rule')) + self.compute_api.trigger_provider_fw_rules_refresh(context) + return {'status': 'OK', 'message': 'Disabled (number) IPs'} diff --git a/nova/compute/api.py b/nova/compute/api.py index a6b99c1cb..607a9ef25 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -268,6 +268,15 @@ class API(base.Base): {"method": "refresh_security_group_members", "args": {"security_group_id": group_id}}) + def trigger_provider_fw_rules_refresh(self, context): + """Called when a rule is added to or removed from a security_group""" + + hosts = [x['host'] for x in db.service_get_all_compute_sorted(context)] + for host in hosts: + rpc.cast(context, + self.db.queue_get_for(context, FLAGS.compute_topic, host), + {"method": "refresh_provider_fw_rules", "args": {}}) + def update(self, context, instance_id, **kwargs): """Updates the instance in the datastore. diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 6f09ce674..9c4a23d08 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -169,6 +169,11 @@ class ComputeManager(manager.Manager): """This call passes straight through to the virtualization driver.""" return self.driver.refresh_security_group_members(security_group_id) + @exception.wrap_exception + def refresh_provider_fw_rules(self, context, **_kwargs): + """This call passes straight through to the virtualization driver.""" + return self.driver.refresh_security_group_rules() + @exception.wrap_exception def run_instance(self, context, instance_id, **_kwargs): """Launch a new instance with specified options.""" diff --git a/nova/db/api.py b/nova/db/api.py index f9d561587..a05c8159e 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -813,6 +813,14 @@ def security_group_rule_destroy(context, security_group_rule_id): ################### +def provider_fw_rule_create(context, rule): + """Add a firewall rule at the provider level (all hosts & instances).""" + return IMPL.provider_fw_rule_create(context, rule) + + +################### + + def user_get(context, id): """Get user by id.""" return IMPL.user_get(context, id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index b63b84bed..4223aa0f7 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1687,6 +1687,18 @@ def security_group_rule_destroy(context, security_group_rule_id): ################### + +@require_admin_context +def provider_fw_rule_create(context, rule): + fw_rule_ref = models.ProviderFirewallRule() + fw_rule_ref.update(rule) + fw_rule_ref.save() + return fw_rule_ref + + +################### + + @require_admin_context def user_get(context, id, session=None): if not session: diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index bf5e48b04..1d82cff18 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -394,6 +394,17 @@ class SecurityGroupIngressRule(BASE, NovaBase): group_id = Column(Integer, ForeignKey('security_groups.id')) +class ProviderFirewallRule(BASE, NovaBase): + """Represents a rule in a security group.""" + __tablename__ = 'provider_fw_rules' + id = Column(Integer, primary_key=True) + + protocol = Column(String(5)) # "tcp", "udp", or "icmp" + from_port = Column(Integer) + to_port = Column(Integer) + cidr = Column(String(255)) + + class KeyPair(BASE, NovaBase): """Represents a public key pair for ssh.""" __tablename__ = 'key_pairs' diff --git a/nova/virt/connection.py b/nova/virt/connection.py index 13181b730..f5a978997 100644 --- a/nova/virt/connection.py +++ b/nova/virt/connection.py @@ -54,6 +54,7 @@ def get_connection(read_only=False): * fake * libvirt * xenapi + * hyperv """ # TODO(termie): maybe lazy load after initial check for permissions # TODO(termie): check whether we can be disconnected diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f38af5ed8..fa5dc502e 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -848,6 +848,9 @@ class LibvirtConnection(object): def refresh_security_group_members(self, security_group_id): self.firewall_driver.refresh_security_group_members(security_group_id) + def refresh_provier_fw_rules(self): + self.firewall_driver.refresh_provider_fw_rules() + class FirewallDriver(object): def prepare_instance_filter(self, instance): @@ -884,6 +887,13 @@ class FirewallDriver(object): the security group.""" raise NotImplementedError() + def refresh_provider_fw_rules(self): + """Refresh common rules for all hosts/instances from data store. + + Gets called when a rule has been added to or removed from + the list of rules (via admin api).""" + raise NotImplementedError() + class NWFilterFirewall(FirewallDriver): """ -- cgit From 46c1c554e7d98959a2b20597d6b0f2b0f648cdc9 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 19 Jan 2011 15:16:12 -0500 Subject: Whitespace (pep8) cleanups. --- nova/adminclient.py | 29 ++++++++++++++--------------- nova/api/ec2/admin.py | 1 - 2 files changed, 14 insertions(+), 16 deletions(-) (limited to 'nova') diff --git a/nova/adminclient.py b/nova/adminclient.py index 90ad4d9dd..0cdb8c6fb 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -190,20 +190,20 @@ class HostInfo(object): setattr(self, name, value) -class SimpleResponse(object): - def __init__(self, connection=None): - self.connection = connection - self.status = None +class SimpleResponse(object): + def __init__(self, connection=None): + self.connection = connection + self.status = None self.message = '' - - def __repr__(self): - return 'Status:%s' % self.status - - def startElement(self, name, attrs, connection): - return None - - def endElement(self, name, value, connection): - setattr(self, name.lower(), str(value)) + + def __repr__(self): + return 'Status:%s' % self.status + + def startElement(self, name, attrs, connection): + return None + + def endElement(self, name, value, connection): + setattr(self, name.lower(), str(value)) class NovaAdminClient(object): @@ -224,8 +224,7 @@ class NovaAdminClient(object): self.apiconn = boto.connect_ec2(aws_access_key_id=access_key, aws_secret_access_key=secret_key, is_secure=parts['is_secure'], - region=RegionInfo(None, - region, + region=RegionInfo(None, region, parts['ip']), port=parts['port'], path='/services/Admin', diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 1ae5f7094..3a8ed39eb 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -226,7 +226,6 @@ class AdminController(object): return dupe return False - def block_external_addresses(self, context, cidr): """Add provider-level firewall rules to block incoming traffic.""" LOG.audit(_("Blocking traffic to all projects incoming from %s"), -- cgit From c58a8edb5c282f661d5be361ce68131516c741ba Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 19 Jan 2011 15:17:06 -0500 Subject: Implement provider-level firewall rules in nwfilter. --- nova/db/api.py | 5 +++++ nova/db/sqlalchemy/api.py | 11 ++++++++++ nova/virt/libvirt_conn.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index a42e06e3b..f55bde908 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -848,6 +848,11 @@ def provider_fw_rule_create(context, rule): return IMPL.provider_fw_rule_create(context, rule) +def provider_fw_rule_get_all(context): + """Get all provider-level firewall rules.""" + return IMPL.provider_fw_rule_get_all(context) + + ################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 95ed2d59e..7a3a6e89a 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1760,6 +1760,17 @@ def provider_fw_rule_create(context, rule): return fw_rule_ref +def provider_fw_rule_get_all(context): + session = get_session() + return session.query(models.ProviderFirewallRule).\ + filter_by(deleted=can_read_deleted(context)).\ + all() + fw_rule_ref = models.ProviderFirewallRule() + fw_rule_ref.update(rule) + fw_rule_ref.save() + return fw_rule_ref + + ################### diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index b189a5b31..68503ef68 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1408,6 +1408,8 @@ class NWFilterFirewall(FirewallDriver): instance_secgroup_filter_children += [('nova-secgroup-%s' % security_group['id'])] + instance_filter_children += ['nova-provider-rules'] + self._define_filter( self._filter_container(instance_secgroup_filter_name, instance_secgroup_filter_children)) @@ -1422,6 +1424,18 @@ class NWFilterFirewall(FirewallDriver): return self._define_filter( self.security_group_to_nwfilter_xml(security_group_id)) + def refresh_provider_fw_rules(self): + """Update rules for all instances. + + This is part of the FirewallDriver API and is called when the + provider firewall rules change in the database. In the + `prepare_instance_filter` we add a reference to the + 'nova-provider-rules' filter for each instance's firewall, and + by changing that filter we update them all. + """ + xml = self.provider_fw_to_nwfilter_xml(self) + return self._define_filter(xml) + def security_group_to_nwfilter_xml(self, security_group_id): security_group = db.security_group_get(context.get_admin_context(), security_group_id) @@ -1460,6 +1474,43 @@ class NWFilterFirewall(FirewallDriver): xml += "chain='ipv4'>%s" % rule_xml return xml + def provider_fw_to_nwfilter_xml(self): + """Compose a filter of drop rules from specified cidrs.""" + rule_xml = "" + v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} + rules = db.provider_fw_rule_get_all(context.get_admin_context()) + for rule in rules: + rule_xml += "" + version = _get_ip_version(rule.cidr) + if(FLAGS.use_ipv6 and version == 6): + net, prefixlen = _get_net_and_prefixlen(rule.cidr) + rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ + (v6protocol[rule.protocol], net, prefixlen) + else: + net, mask = _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) + elif rule.protocol == 'icmp': + LOG.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 = " Date: Thu, 20 Jan 2011 11:37:15 -0500 Subject: A couple of copypasta errors. --- nova/compute/manager.py | 2 +- nova/virt/libvirt_conn.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 9c4a23d08..a9734f13b 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -172,7 +172,7 @@ class ComputeManager(manager.Manager): @exception.wrap_exception def refresh_provider_fw_rules(self, context, **_kwargs): """This call passes straight through to the virtualization driver.""" - return self.driver.refresh_security_group_rules() + return self.driver.refresh_provider_fw_rules() @exception.wrap_exception def run_instance(self, context, instance_id, **_kwargs): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index dc5a9fc06..4f3107c88 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -848,7 +848,7 @@ class LibvirtConnection(object): def refresh_security_group_members(self, security_group_id): self.firewall_driver.refresh_security_group_members(security_group_id) - def refresh_provier_fw_rules(self): + def refresh_provider_fw_rules(self): self.firewall_driver.refresh_provider_fw_rules() -- cgit From f02c9e781bdddd609601da81b97a438b6d5b9781 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Fri, 21 Jan 2011 12:30:26 -0500 Subject: Add provider_fw_rules awareness to iptables firewall driver. --- nova/api/ec2/admin.py | 3 ++- nova/virt/libvirt_conn.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 3a8ed39eb..c83f84b15 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -236,7 +236,8 @@ class AdminController(object): udp_rule = rule.copy() udp_rule.update({"protocol": "UDP", "from_port": 1, "to_port": 65535}) icmp_rule = rule.copy() - icmp_rule.update({"protocol": "ICMP", "from_port": -1, "to_port": -1}) + icmp_rule.update({"protocol": "ICMP", "from_port": -1, + "to_port": None}) rules_added = 0 if not self._provider_fw_rule_exists(context, tcp_rule): db.provider_fw_rule_create(context, tcp_rule) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4f3107c88..38eddf748 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1290,7 +1290,51 @@ class IptablesFirewallDriver(FirewallDriver): our_rules = ['-A nova-fallback -j DROP'] our_chains += [':nova-local - [0:0]'] - our_rules += ['-A FORWARD -j nova-local'] + + our_chains += [':nova-provider - [0:0]'] + our_rules += ['-A FORWARD -j nova-provider'] + + rules = db.provider_fw_rule_get_all(ctxt) + for rule in rules: + logging.info('%r', rule) + version = _get_ip_version(rule.cidr) + if version != ip_version: + continue + protocol = rule.protocol + if version == 6 and rule.protocol == 'icmp': + protocol = 'icmpv6' + args = ['-A nova-provider -p', protocol, '-s', rule.cidr] + + if rule.protocol in ['udp', 'tcp']: + if rule.from_port == rule.to_port: + args += ['--dport', '%s' % (rule.from_port,)] + else: + args += ['-m', 'multiport', + '--dports', '%s:%s' % (rule.from_port, + rule.to_port)] + elif rule.protocol == 'icmp': + icmp_type = rule.from_port + icmp_code = rule.to_port + + if icmp_type == -1: + icmp_type_arg = None + else: + icmp_type_arg = '%s' % icmp_type + if not icmp_code == -1: + icmp_type_arg += '/%s' % icmp_code + + if icmp_type_arg: + if(ip_version == 4): + args += ['-m', 'icmp', '--icmp-type', + icmp_type_arg] + elif(ip_version == 6): + args += ['-m', 'icmp6', '--icmpv6-type', + icmp_type_arg] + + args += ['-j DROP'] + our_rules += [' '.join(args)] + + our_rules += ['-A nova-provider -j nova-local'] security_groups = {} # Add our chains @@ -1409,6 +1453,9 @@ class IptablesFirewallDriver(FirewallDriver): def refresh_security_group_rules(self, security_group): self.apply_ruleset() + def refresh_provider_fw_rules(self): + self.apply_ruleset() + def _security_group_chain_name(self, security_group_id): return 'nova-sg-%s' % (security_group_id,) -- cgit From 4e3524c57f6fa0f917bdb30ec15c8d4633a307e5 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 25 Jan 2011 12:52:00 -0800 Subject: Updates for provider_fw_rules in admin api. --- nova/api/ec2/admin.py | 7 +++++-- nova/compute/api.py | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 4a34476d3..0dabf2092 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -223,7 +223,7 @@ class AdminController(object): """Returns status info for single node.""" return host_dict(db.host_get(name)) - def _provider_fw_rule_exists(context, rule): + def _provider_fw_rule_exists(self, context, rule): for old_rule in db.provider_fw_rule_get_all(context): for key in ('cidr', 'from_port', 'to_port', 'protocol'): dupe = True @@ -237,7 +237,10 @@ class AdminController(object): """Add provider-level firewall rules to block incoming traffic.""" LOG.audit(_("Blocking traffic to all projects incoming from %s"), cidr, context=context) - rule = {'cidr': IPy.IP(urllib.unquote(cidr).decode())} + cidr = urllib.unquote(cidr).decode() + # raise if invalid + IPy.IP(cidr) + rule = {'cidr': cidr} tcp_rule = rule.copy() tcp_rule.update({"protocol": "TCP", "from_port": 1, "to_port": 65535}) udp_rule = rule.copy() diff --git a/nova/compute/api.py b/nova/compute/api.py index cb1a57a44..a8eed7aa5 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -277,7 +277,8 @@ class API(base.Base): def trigger_provider_fw_rules_refresh(self, context): """Called when a rule is added to or removed from a security_group""" - hosts = [x['host'] for x in db.service_get_all_compute_sorted(context)] + hosts = [x['host'] for (x,idx) + in db.service_get_all_compute_sorted(context)] for host in hosts: rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), -- cgit From bbea3a093f3e9be5052a2e64b6d5d0b909ae33ee Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 25 Jan 2011 13:05:47 -0800 Subject: Migration for provider firewall rules. --- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py new file mode 100644 index 000000000..7e5c55811 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +services = Table('services', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +networks = Table('networks', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# +provider_fw_rules = Table('provider_fw_rules', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('protocol', + String(length=5, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('from_port', Integer()), + Column('to_port', Integer()), + Column('cidr', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)) + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (provider_fw_rules,): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise + -- cgit From d6c6d8115b9dda07716d85fb1201cde0e907a9bd Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 26 Jan 2011 22:54:39 -0800 Subject: A couple of bugfixes. --- nova/adminclient.py | 3 ++- nova/api/ec2/admin.py | 3 ++- nova/virt/libvirt_conn.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/adminclient.py b/nova/adminclient.py index 9b43995fe..9652dcb9c 100644 --- a/nova/adminclient.py +++ b/nova/adminclient.py @@ -230,7 +230,8 @@ class InstanceType(object): class SimpleResponse(object): - def __init__(self): + def __init__(self, connection=None): + self.connection = connection self.status = None self.message = '' diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 478032ce9..b019e8e8b 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -237,9 +237,10 @@ class AdminController(object): return host_dict(db.host_get(name)) def _provider_fw_rule_exists(self, context, rule): + # TODO(todd): we call this repeatedly, can we filter by protocol? for old_rule in db.provider_fw_rule_get_all(context): + dupe = True for key in ('cidr', 'from_port', 'to_port', 'protocol'): - dupe = True if rule[key] != old_rule[key]: dupe = False if dupe: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 453824d82..5b5b329ed 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1171,7 +1171,7 @@ class NWFilterFirewall(FirewallDriver): 'nova-provider-rules' filter for each instance's firewall, and by changing that filter we update them all. """ - xml = self.provider_fw_to_nwfilter_xml(self) + xml = self.provider_fw_to_nwfilter_xml() return self._define_filter(xml) def security_group_to_nwfilter_xml(self, security_group_id): -- cgit From ece7d2fa493e901c2a826e42a86ca93bb0afaed4 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 26 Jan 2011 22:56:34 -0800 Subject: Apply lp:707675 to this branch to be able to test. --- nova/tests/test_virt.py | 7 ++++++- nova/virt/libvirt_conn.py | 29 +++++++++++++++++++---------- 2 files changed, 25 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 0b9b847a0..1008f32ae 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -221,7 +221,12 @@ class IptablesFirewallTestCase(test.TestCase): self.project = self.manager.create_project('fake', 'fake', 'fake') self.context = context.RequestContext('fake', 'fake') self.network = utils.import_object(FLAGS.network_manager) - self.fw = libvirt_conn.IptablesFirewallDriver() + + class FakeLibvirtConnection(object): + pass + self.fake_libvirt_connection = FakeLibvirtConnection() + self.fw = libvirt_conn.IptablesFirewallDriver( + get_connection=lambda: self.fake_libvirt_connection) def tearDown(self): self.manager.delete_project(self.project) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 5b5b329ed..cac6a4440 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -149,13 +149,8 @@ class LibvirtConnection(object): self._wrapped_conn = None self.read_only = read_only - self.nwfilter = NWFilterFirewall(self._get_connection) - - if not FLAGS.firewall_driver: - self.firewall_driver = self.nwfilter - self.nwfilter.handle_security_groups = True - else: - self.firewall_driver = utils.import_object(FLAGS.firewall_driver) + fw_class = utils.import_class(FLAGS.firewall_driver) + self.firewall_driver = fw_class(get_connection=self._get_connection) def init_host(self, host): # Adopt existing VM's running here @@ -409,7 +404,7 @@ class LibvirtConnection(object): instance['id'], power_state.NOSTATE, 'launching') - self.nwfilter.setup_basic_filtering(instance) + self.firewall_driver.setup_basic_filtering(instance) self.firewall_driver.prepare_instance_filter(instance) self._create_image(instance, xml) self._conn.createXML(xml, 0) @@ -915,6 +910,15 @@ class FirewallDriver(object): the list of rules (via admin api).""" raise NotImplementedError() + def setup_basic_filtering(self, instance): + """Create rules to block spoofing and allow dhcp. + + This gets called when spawning an instance, before + :method:`prepare_instance_filter`. + + """ + raise NotImplementedError() + class NWFilterFirewall(FirewallDriver): """ @@ -962,7 +966,7 @@ class NWFilterFirewall(FirewallDriver): """ - def __init__(self, get_connection): + def __init__(self, get_connection, **kwargs): self._libvirt_get_connection = get_connection self.static_filters_configured = False self.handle_security_groups = False @@ -1254,9 +1258,14 @@ class NWFilterFirewall(FirewallDriver): class IptablesFirewallDriver(FirewallDriver): - def __init__(self, execute=None): + def __init__(self, execute=None, **kwargs): self.execute = execute or utils.execute self.instances = {} + self.nwfilter = NWFilterFirewall(kwargs['get_connection']) + + def setup_basic_filtering(self, instance): + """Use NWFilter from libvirt for this.""" + return self.nwfilter.setup_basic_filtering(instance) def apply_instance_filter(self, instance): """No-op. Everything is done in prepare_instance_filter""" -- cgit From d47886e16504cc92d0f9b33e02417229970d3efb Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Mon, 31 Jan 2011 16:02:29 -0500 Subject: Reorder insance rules for provider rules immediately after base, before secgroups. --- nova/virt/libvirt_conn.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 2f99a0bb1..ec6572d3f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1161,7 +1161,8 @@ class NWFilterFirewall(FirewallDriver): instance_filter_name = self._instance_filter_name(instance) instance_secgroup_filter_name = '%s-secgroup' % (instance_filter_name,) - instance_filter_children = [base_filter, instance_secgroup_filter_name] + instance_filter_children = [base_filter, 'nova-provider-rules', + instance_secgroup_filter_name] instance_secgroup_filter_children = ['nova-base-ipv4', 'nova-base-ipv6', 'nova-allow-dhcp-server'] @@ -1185,8 +1186,6 @@ class NWFilterFirewall(FirewallDriver): instance_secgroup_filter_children += [('nova-secgroup-%s' % security_group['id'])] - instance_filter_children += ['nova-provider-rules'] - self._define_filter( self._filter_container(instance_secgroup_filter_name, instance_secgroup_filter_children)) -- cgit From d552158b19bf1652da795e1681c9dc904bdc425b Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 1 Feb 2011 12:32:58 -0500 Subject: Add and document the provider_fw method in virt/FakeConnection. --- nova/virt/fake.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'nova') diff --git a/nova/virt/fake.py b/nova/virt/fake.py index 161445b86..b16d53634 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -359,6 +359,22 @@ class FakeConnection(object): """ return True + def refresh_provider_fw_rules(self): + """This triggers a firewall update based on database changes. + + When this is called, rules have either been added or removed from the + datastore. You can retrieve rules with + :method:`nova.db.api.provider_fw_rule_get_all`. + + Provider rules take precedence over security group rules. If an IP + would be allowed by a security group ingress rule, but blocked by + a provider rule, then packets from the IP are dropped. This includes + intra-project traffic in the case of the allow_project_net_traffic + flag for the libvirt-derived classes. + + """ + pass + class FakeInstance(object): -- cgit From 5da75737ddfb876fd397b71986af42a5f8d0d04c Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 16 Feb 2011 12:49:04 -0500 Subject: Merge bfschott's patch for migations in. --- nova/db/sqlalchemy/migration.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index 2a13c5466..f11f9f67e 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -17,12 +17,21 @@ # under the License. import os +import sys from nova import flags import sqlalchemy from migrate.versioning import api as versioning_api -from migrate.versioning import exceptions as versioning_exceptions +try: + from migrate.versioning import exceptions as versioning_exceptions +except ImportError: + try: + # python-migration changed location of exceptions after 1.6.3 + # See LP Bug #717467 + from migrate import exceptions as versioning_exceptions + except ImportError: + sys.exit(_("python-migrate is not installed. Exiting.")) FLAGS = flags.FLAGS -- cgit From dc887ab39641039817cdddce062bd398e69d07e5 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 5 Apr 2011 15:17:17 -0400 Subject: Remove file leftover from conflict. --- nova/adminclient.py.THIS | 451 ----------------------------------------------- 1 file changed, 451 deletions(-) delete mode 100644 nova/adminclient.py.THIS (limited to 'nova') diff --git a/nova/adminclient.py.THIS b/nova/adminclient.py.THIS deleted file mode 100644 index c96d6e1fe..000000000 --- a/nova/adminclient.py.THIS +++ /dev/null @@ -1,451 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -Nova User API client library. -""" - -import base64 -import boto -import boto.exception -import httplib - -from boto.ec2.regioninfo import RegionInfo - - -DEFAULT_CLC_URL = 'http://127.0.0.1:8773' -DEFAULT_REGION = 'nova' - - -class UserInfo(object): - """ - Information about a Nova user, as parsed through SAX. - - **Fields Include** - - * username - * accesskey - * secretkey - * file (optional) containing zip of X509 cert & rc file - - """ - - def __init__(self, connection=None, username=None, endpoint=None): - self.connection = connection - self.username = username - self.endpoint = endpoint - - def __repr__(self): - return 'UserInfo:%s' % self.username - - def startElement(self, name, attrs, connection): - return None - - def endElement(self, name, value, connection): - if name == 'username': - self.username = str(value) - elif name == 'file': - self.file = base64.b64decode(str(value)) - elif name == 'accesskey': - self.accesskey = str(value) - elif name == 'secretkey': - self.secretkey = str(value) - - -class UserRole(object): - """ - Information about a Nova user's role, as parsed through SAX. - - **Fields include** - - * role - - """ - - def __init__(self, connection=None): - self.connection = connection - self.role = None - - def __repr__(self): - return 'UserRole:%s' % self.role - - def startElement(self, name, attrs, connection): - return None - - def endElement(self, name, value, connection): - if name == 'role': - self.role = value - else: - setattr(self, name, str(value)) - - -class ProjectInfo(object): - """ - Information about a Nova project, as parsed through SAX. - - **Fields include** - - * projectname - * description - * projectManagerId - * memberIds - - """ - - def __init__(self, connection=None): - self.connection = connection - self.projectname = None - self.description = None - self.projectManagerId = None - self.memberIds = [] - - def __repr__(self): - return 'ProjectInfo:%s' % self.projectname - - def startElement(self, name, attrs, connection): - return None - - def endElement(self, name, value, connection): - if name == 'projectname': - self.projectname = value - elif name == 'description': - self.description = value - elif name == 'projectManagerId': - self.projectManagerId = value - elif name == 'memberId': - self.memberIds.append(value) - else: - setattr(self, name, str(value)) - - -class ProjectMember(object): - """ - Information about a Nova project member, as parsed through SAX. - - **Fields include** - - * memberId - - """ - - def __init__(self, connection=None): - self.connection = connection - self.memberId = None - - def __repr__(self): - return 'ProjectMember:%s' % self.memberId - - def startElement(self, name, attrs, connection): - return None - - def endElement(self, name, value, connection): - if name == 'member': - self.memberId = value - else: - setattr(self, name, str(value)) - - -class HostInfo(object): - """ - Information about a Nova Host, as parsed through SAX. - - **Fields Include** - - * Disk stats - * Running Instances - * Memory stats - * CPU stats - * Network address info - * Firewall info - * Bridge and devices - - """ - - def __init__(self, connection=None): - self.connection = connection - self.hostname = None - - def __repr__(self): - return 'Host:%s' % self.hostname - - # this is needed by the sax parser, so ignore the ugly name - def startElement(self, name, attrs, connection): - return None - - # this is needed by the sax parser, so ignore the ugly name - def endElement(self, name, value, connection): - setattr(self, name, value) - - -class InstanceType(object): - """ - Information about a Nova instance type, as parsed through SAX. - - **Fields include** - - * name - * vcpus - * disk_gb - * memory_mb - * flavor_id - - """ - - def __init__(self, connection=None): - self.connection = connection - self.name = None - self.vcpus = None - self.disk_gb = None - self.memory_mb = None - self.flavor_id = None - - def __repr__(self): - return 'InstanceType:%s' % self.name - - def startElement(self, name, attrs, connection): - return None - - def endElement(self, name, value, connection): - if name == "memoryMb": - self.memory_mb = str(value) - elif name == "flavorId": - self.flavor_id = str(value) - elif name == "diskGb": - self.disk_gb = str(value) - else: - setattr(self, name, str(value)) - - -class SimpleResponse(object): - def __init__(self, connection=None): - self.connection = connection - self.status = None - self.message = '' - - def __repr__(self): - return 'Status:%s' % self.status - - def startElement(self, name, attrs, connection): - return None - - def endElement(self, name, value, connection): - setattr(self, name.lower(), str(value)) - - -class NovaAdminClient(object): - - def __init__( - self, - clc_url=DEFAULT_CLC_URL, - region=DEFAULT_REGION, - access_key=None, - secret_key=None, - **kwargs): - parts = self.split_clc_url(clc_url) - - self.clc_url = clc_url - self.region = region - self.access = access_key - self.secret = secret_key - self.apiconn = boto.connect_ec2(aws_access_key_id=access_key, - aws_secret_access_key=secret_key, - is_secure=parts['is_secure'], - region=RegionInfo(None, region, - parts['ip']), - port=parts['port'], - path='/services/Admin', - **kwargs) - self.apiconn.APIVersion = 'nova' - - def connection_for(self, username, project, clc_url=None, region=None, - **kwargs): - """Returns a boto ec2 connection for the given username.""" - if not clc_url: - clc_url = self.clc_url - if not region: - region = self.region - parts = self.split_clc_url(clc_url) - user = self.get_user(username) - access_key = '%s:%s' % (user.accesskey, project) - return boto.connect_ec2(aws_access_key_id=access_key, - aws_secret_access_key=user.secretkey, - is_secure=parts['is_secure'], - region=RegionInfo(None, - self.region, - parts['ip']), - port=parts['port'], - path='/services/Cloud', - **kwargs) - - def split_clc_url(self, clc_url): - """Splits a cloud controller endpoint url.""" - parts = httplib.urlsplit(clc_url) - is_secure = parts.scheme == 'https' - ip, port = parts.netloc.split(':') - return {'ip': ip, 'port': int(port), 'is_secure': is_secure} - - def get_users(self): - """Grabs the list of all users.""" - return self.apiconn.get_list('DescribeUsers', {}, [('item', UserInfo)]) - - def get_user(self, name): - """Grab a single user by name.""" - try: - return self.apiconn.get_object('DescribeUser', - {'Name': name}, - UserInfo) - except boto.exception.BotoServerError, e: - if e.status == 400 and e.error_code == 'NotFound': - return None - raise - - def has_user(self, username): - """Determine if user exists.""" - return self.get_user(username) != None - - def create_user(self, username): - """Creates a new user, returning the userinfo object with - access/secret.""" - return self.apiconn.get_object('RegisterUser', {'Name': username}, - UserInfo) - - def delete_user(self, username): - """Deletes a user.""" - return self.apiconn.get_object('DeregisterUser', {'Name': username}, - UserInfo) - - def get_roles(self, project_roles=True): - """Returns a list of available roles.""" - return self.apiconn.get_list('DescribeRoles', - {'ProjectRoles': project_roles}, - [('item', UserRole)]) - - def get_user_roles(self, user, project=None): - """Returns a list of roles for the given user. - - Omitting project will return any global roles that the user has. - Specifying project will return only project specific roles. - - """ - params = {'User': user} - if project: - params['Project'] = project - return self.apiconn.get_list('DescribeUserRoles', - params, - [('item', UserRole)]) - - def add_user_role(self, user, role, project=None): - """Add a role to a user either globally or for a specific project.""" - return self.modify_user_role(user, role, project=project, - operation='add') - - def remove_user_role(self, user, role, project=None): - """Remove a role from a user either globally or for a specific - project.""" - return self.modify_user_role(user, role, project=project, - operation='remove') - - def modify_user_role(self, user, role, project=None, operation='add', - **kwargs): - """Add or remove a role for a user and project.""" - params = {'User': user, - 'Role': role, - 'Project': project, - 'Operation': operation} - return self.apiconn.get_status('ModifyUserRole', params) - - def get_projects(self, user=None): - """Returns a list of all projects.""" - if user: - params = {'User': user} - else: - params = {} - return self.apiconn.get_list('DescribeProjects', - params, - [('item', ProjectInfo)]) - - def get_project(self, name): - """Returns a single project with the specified name.""" - project = self.apiconn.get_object('DescribeProject', - {'Name': name}, - ProjectInfo) - - if project.projectname != None: - return project - - def create_project(self, projectname, manager_user, description=None, - member_users=None): - """Creates a new project.""" - params = {'Name': projectname, - 'ManagerUser': manager_user, - 'Description': description, - 'MemberUsers': member_users} - return self.apiconn.get_object('RegisterProject', params, ProjectInfo) - - def modify_project(self, projectname, manager_user=None, description=None): - """Modifies an existing project.""" - params = {'Name': projectname, - 'ManagerUser': manager_user, - 'Description': description} - return self.apiconn.get_status('ModifyProject', params) - - def delete_project(self, projectname): - """Permanently deletes the specified project.""" - return self.apiconn.get_object('DeregisterProject', - {'Name': projectname}, - ProjectInfo) - - def get_project_members(self, name): - """Returns a list of members of a project.""" - return self.apiconn.get_list('DescribeProjectMembers', - {'Name': name}, - [('item', ProjectMember)]) - - def add_project_member(self, user, project): - """Adds a user to a project.""" - return self.modify_project_member(user, project, operation='add') - - def remove_project_member(self, user, project): - """Removes a user from a project.""" - return self.modify_project_member(user, project, operation='remove') - - def modify_project_member(self, user, project, operation='add'): - """Adds or removes a user from a project.""" - params = {'User': user, - 'Project': project, - 'Operation': operation} - return self.apiconn.get_status('ModifyProjectMember', params) - - def get_zip(self, user, project): - """Returns the content of a zip file containing novarc and access - credentials.""" - params = {'Name': user, 'Project': project} - zip = self.apiconn.get_object('GenerateX509ForUser', params, UserInfo) - return zip.file - - def get_hosts(self): - return self.apiconn.get_list('DescribeHosts', {}, [('item', HostInfo)]) - - def get_instance_types(self): - """Grabs the list of all users.""" - return self.apiconn.get_list('DescribeInstanceTypes', {}, - [('item', InstanceType)]) - - def block_ips(self, cidr): - """Block incoming traffic from specified hosts.""" - return self.apiconn.get_object('BlockExternalAddresses', - {'Cidr': cidr}, SimpleResponse) -- cgit From 29b7a087efc64965e079733fe62e552fac70d13a Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 5 Apr 2011 15:17:37 -0400 Subject: Fix a giant batch of copypasta. --- nova/compute/manager.py | 1 + nova/virt/libvirt_conn.py | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'nova') diff --git a/nova/compute/manager.py b/nova/compute/manager.py index b0c301925..916c1db5e 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -195,6 +195,7 @@ class ComputeManager(manager.SchedulerDependentManager): """This call passes straight through to the virtualization driver.""" return self.driver.refresh_provider_fw_rules() + @exception.wrap_exception def run_instance(self, context, instance_id, **kwargs): """Launch a new instance with specified options.""" context = context.elevated() diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f3d73103b..c5ada729a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1790,6 +1790,11 @@ class NWFilterFirewall(FirewallDriver): else: base_filter = 'nova-base' + ctxt = context.get_admin_context() + + instance_secgroup_filter_name = \ + '%s-secgroup' % (self._instance_filter_name(instance)) + instance_secgroup_filter_children = ['nova-base-ipv4', 'nova-base-ipv6', 'nova-allow-dhcp-server'] -- cgit From cd4748abfdc5014aac1d867c2ede261060375e2e Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 5 Apr 2011 19:31:12 -0400 Subject: Don't double-apply provider fw rules in NWFilter and Iptables. Don't create provider fw rules for each instance, use a chain and jump to it. Fix docstrings. --- nova/network/linux_net.py | 7 ++ nova/virt/connection.py | 1 - nova/virt/libvirt_conn.py | 184 ++++++++++++++++++++++++++-------------------- 3 files changed, 112 insertions(+), 80 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index d11d21dad..322524a41 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -185,6 +185,13 @@ class IptablesTable(object): {'chain': chain, 'rule': rule, 'top': top, 'wrap': wrap}) + def empty_chain(self, chain, wrap=True): + """Remove all rules from a chain.""" + chained_rules = [rule for rule in self.rules + if rule.chain == chain and rule.wrap == wrap] + for rule in chained_rules: + self.rules.remove(rule) + class IptablesManager(object): """Wrapper for iptables diff --git a/nova/virt/connection.py b/nova/virt/connection.py index 875d558a0..99a8849f1 100644 --- a/nova/virt/connection.py +++ b/nova/virt/connection.py @@ -57,7 +57,6 @@ def get_connection(read_only=False): * fake * libvirt * xenapi - * hyperv """ # TODO(termie): maybe lazy load after initial check for permissions # TODO(termie): check whether we can be disconnected diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c5ada729a..9bc7ca05a 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1539,7 +1539,9 @@ class FirewallDriver(object): """Refresh common rules for all hosts/instances from data store. Gets called when a rule has been added to or removed from - the list of rules (via admin api).""" + the list of rules (via admin api). + + """ raise NotImplementedError() def setup_basic_filtering(self, instance, network_info=None): @@ -1601,7 +1603,6 @@ class NWFilterFirewall(FirewallDriver): def __init__(self, get_connection, **kwargs): self._libvirt_get_connection = get_connection self.static_filters_configured = False - self.intermediate_filters_configured = False self.handle_security_groups = False def apply_instance_filter(self, instance): @@ -1658,9 +1659,6 @@ class NWFilterFirewall(FirewallDriver): logging.info('ensuring static filters') self._ensure_static_filters() - logging.info('ensuring intermediate filters') - self._ensure_intermediate_filters() - for (network, mapping) in network_info: nic_id = mapping['mac'].replace(':', '') instance_filter_name = self._instance_filter_name(instance, nic_id) @@ -1695,20 +1693,6 @@ class NWFilterFirewall(FirewallDriver): self.static_filters_configured = True - def _ensure_intermediate_filters(self): - """Intermediate filters are filters that are configurable nova-wide. - - Unlike static filters, they must be set up and maintainted based - on the network topology of nova. They are still required to be setup - before any instance can be launched. - - """ - - if self.intermediate_filters_configured: - return - self.refresh_provider_fw_rules() - self.intermediate_filters_configured = True - def _filter_container(self, name, filters): xml = '''%s''' % ( name, @@ -1778,10 +1762,11 @@ class NWFilterFirewall(FirewallDriver): pass def prepare_instance_filter(self, instance, network_info=None): - """ - 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. + """Creates an NWFilter for the given instance. + + In the process, it makes sure the filters for the provider blocks, + security groups, and base filter are all in place. + """ if not network_info: network_info = _get_network_info(instance) @@ -1790,6 +1775,8 @@ class NWFilterFirewall(FirewallDriver): else: base_filter = 'nova-base' + self.refresh_provider_fw_rules() + ctxt = context.get_admin_context() instance_secgroup_filter_name = \ @@ -1847,6 +1834,7 @@ class NWFilterFirewall(FirewallDriver): `prepare_instance_filter` we add a reference to the 'nova-provider-rules' filter for each instance's firewall, and by changing that filter we update them all. + """ xml = self.provider_fw_to_nwfilter_xml() return self._define_filter(xml) @@ -1890,7 +1878,7 @@ class NWFilterFirewall(FirewallDriver): return xml def provider_fw_to_nwfilter_xml(self): - """Compose a filter of drop rules from specified cidrs.""" + """Compose a filter of drop rules from specified cidrs.""" rule_xml = "" v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} rules = db.provider_fw_rule_get_all(context.get_admin_context()) @@ -1938,6 +1926,7 @@ class IptablesFirewallDriver(FirewallDriver): self.iptables = linux_net.iptables_manager self.instances = {} self.nwfilter = NWFilterFirewall(kwargs['get_connection']) + self.basicly_filtered = False self.iptables.ipv4['filter'].add_chain('sg-fallback') self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP') @@ -1945,10 +1934,13 @@ class IptablesFirewallDriver(FirewallDriver): self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP') def setup_basic_filtering(self, instance, network_info=None): - """Use NWFilter from libvirt for this.""" + """Set up provider rules and basic NWFilter.""" if not network_info: network_info = _get_network_info(instance) - return self.nwfilter.setup_basic_filtering(instance, network_info) + self.nwfilter.setup_basic_filtering(instance, network_info) + if not self.basicly_filtered: + self.refresh_provider_fw_rules() + self.basicly_filtered = True def apply_instance_filter(self, instance): """No-op. Everything is done in prepare_instance_filter""" @@ -1996,6 +1988,8 @@ class IptablesFirewallDriver(FirewallDriver): chain_name)) ipv4_rules, ipv6_rules = self.instance_rules(instance, network_info) + for rule in ipv4_rules: + self.iptables.ipv4['filter'].add_rule(chain_name, rule) for rule in ipv4_rules: self.iptables.ipv4['filter'].add_rule(chain_name, rule) @@ -2023,6 +2017,10 @@ class IptablesFirewallDriver(FirewallDriver): ipv4_rules += ['-m state --state ' 'INVALID -j DROP'] ipv6_rules += ['-m state --state ' 'INVALID -j DROP'] + # Pass through provider-wide drops + ipv4_rules += ['-j $provider'] + ipv6_rules += ['-j $provider'] + # Allow established connections ipv4_rules += ['-m state --state ESTABLISHED,RELATED -j ACCEPT'] ipv6_rules += ['-m state --state ESTABLISHED,RELATED -j ACCEPT'] @@ -2058,57 +2056,6 @@ class IptablesFirewallDriver(FirewallDriver): for cidrv6 in cidrv6s: ipv6_rules.append('-s %s -j ACCEPT' % (cidrv6,)) - # block provider-identified sources - rules = db.provider_fw_rule_get_all(ctxt) - for rule in rules: - logging.info('%r', rule) - - if not rule.cidr: - # Eventually, a mechanism to grant access for security - # groups will turn up here. It'll use ipsets. - continue - - version = _get_ip_version(rule.cidr) - if version == 4: - fw_rules = ipv4_rules - else: - fw_rules = ipv6_rules - - protocol = rule.protocol - if version == 6 and rule.protocol == 'icmp': - protocol = 'icmpv6' - - args = ['-A', 'nova-provider', '-p', protocol, '-s', rule.cidr] - - if rule.protocol in ['udp', 'tcp']: - if rule.from_port == rule.to_port: - args += ['--dport', '%s' % (rule.from_port,)] - else: - args += ['-m', 'multiport', - '--dports', '%s:%s' % (rule.from_port, - rule.to_port)] - elif rule.protocol == 'icmp': - icmp_type = rule.from_port - icmp_code = rule.to_port - - if icmp_type == -1: - icmp_type_arg = None - else: - icmp_type_arg = '%s' % icmp_type - if not icmp_code == -1: - icmp_type_arg += '/%s' % icmp_code - - if icmp_type_arg: - if version == 4: - args += ['-m', 'icmp', '--icmp-type', - icmp_type_arg] - elif version == 6: - args += ['-m', 'icmp6', '--icmpv6-type', - icmp_type_arg] - - args += ['-j DROP'] - fw_rules += [' '.join(args)] - security_groups = db.security_group_get_by_instance(ctxt, instance['id']) @@ -2118,7 +2065,7 @@ class IptablesFirewallDriver(FirewallDriver): security_group['id']) for rule in rules: - LOG.debug('%r', rule) + LOG.debug(_('Adding security group rule: %r'), rule) if not rule.cidr: # Eventually, a mechanism to grant access for security @@ -2185,7 +2132,86 @@ class IptablesFirewallDriver(FirewallDriver): self.add_filters_for_instance(instance) def refresh_provider_fw_rules(self): - self.apply_ruleset() + """See class:FirewallDriver: docs.""" + self._do_refresh_provider_fw_rules() + self.iptables.apply() + + @utils.synchronized('iptables', external=True) + def _do_refresh_provider_fw_rules(self): + """Internal, synchronized version of refresh_provider_fw_rules.""" + self.purge_provider_fw_rules(self) + self.build_provider_fw_rules(self) + + def _purge_provider_fw_rules(self): + """Remove all rules from the $provider chains.""" + self.iptables.ipv4['filter'].empty_chain('$provider') + if FLAGS.use_ipv6: + self.iptables.ipv6['filter'].empty_chain('$provider') + + def _build_provider_fw_rules(self): + """Create all rules for the provider IP DROPs.""" + ipv4_rules, ipv6_rules = self._provider_rules() + for rule in ipv4_rules: + self.iptables.ipv4['filter'].add_rule('$provider', rule) + + if FLAGS.use_ipv6: + for rule in ipv6_rules: + self.iptables.ipv6['filter'].add_rule('$provider', rule) + + def _provider_rules(self): + """Generate a list of rules from provider for IP4 & IP6.""" + ctxt = context.get_admin_context() + ipv4_rules = [] + ipv6_rules = [] + rules = db.provider_fw_rule_get_all(ctxt) + for rule in rules: + LOG.debug(_('Adding prvider rule: %r'), rule) + + if not rule.cidr: + # Eventually, a mechanism to grant access for security + # groups will turn up here. It'll use ipsets. + continue + + version = _get_ip_version(rule.cidr) + if version == 4: + fw_rules = ipv4_rules + else: + fw_rules = ipv6_rules + + protocol = rule.protocol + if version == 6 and rule.protocol == 'icmp': + protocol = 'icmpv6' + + args = ['-A', '$provider', '-p', protocol, '-s', rule.cidr] + + if rule.protocol in ['udp', 'tcp']: + if rule.from_port == rule.to_port: + args += ['--dport', '%s' % (rule.from_port,)] + else: + args += ['-m', 'multiport', + '--dports', '%s:%s' % (rule.from_port, + rule.to_port)] + elif rule.protocol == 'icmp': + icmp_type = rule.from_port + icmp_code = rule.to_port + + if icmp_type == -1: + icmp_type_arg = None + else: + icmp_type_arg = '%s' % icmp_type + if not icmp_code == -1: + icmp_type_arg += '/%s' % icmp_code + + if icmp_type_arg: + if version == 4: + args += ['-m', 'icmp', '--icmp-type', + icmp_type_arg] + elif version == 6: + args += ['-m', 'icmp6', '--icmpv6-type', + icmp_type_arg] + args += ['-j DROP'] + fw_rules += [' '.join(args)] + return ipv4_rules, ipv6_rules def _security_group_chain_name(self, security_group_id): return 'nova-sg-%s' % (security_group_id,) -- cgit From 2b7da3f2e9fa45f9bfca03bb6bcb713dcb6c58fe Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 5 Apr 2011 20:03:29 -0400 Subject: Testing for iptables manager changes. --- .../sqlalchemy/migrate_repo/versions/003_cactus.py | 75 ---------------------- .../versions/014_add_provider_fw_rules.py | 75 ++++++++++++++++++++++ nova/tests/test_network.py | 30 +++++++++ 3 files changed, 105 insertions(+), 75 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/014_add_provider_fw_rules.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py b/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py deleted file mode 100644 index 5aa30f7a8..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/003_cactus.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -services = Table('services', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -networks = Table('networks', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -# -# New Tables -# -provider_fw_rules = Table('provider_fw_rules', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('protocol', - String(length=5, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('from_port', Integer()), - Column('to_port', Integer()), - Column('cidr', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)) - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (provider_fw_rules,): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/014_add_provider_fw_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/014_add_provider_fw_rules.py new file mode 100644 index 000000000..5aa30f7a8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/014_add_provider_fw_rules.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +services = Table('services', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +networks = Table('networks', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# +provider_fw_rules = Table('provider_fw_rules', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('protocol', + String(length=5, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('from_port', Integer()), + Column('to_port', Integer()), + Column('cidr', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)) + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (provider_fw_rules,): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 77f6aaff3..a67c846dd 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -164,3 +164,33 @@ class IptablesManagerTestCase(test.TestCase): self.assertTrue('-A %s -j run_tests.py-%s' \ % (chain, chain) in new_lines, "Built-in chain %s not wrapped" % (chain,)) + + def test_will_empty_chain(self): + self.manager.ipv4['filter'].add_chain('test-chain') + self.manager.ipv4['filter'].add_rule('test-chain', '-j DROP') + old_count = len(self.manager.ipv4['filter'].rules) + self.manager.ipv4['filter'].empty_chain('test-chain') + self.assertEqual(old_count - 1, len(self.manager.ipv4['filter'].rules)) + + def test_will_empty_unwrapped_chain(self): + self.manager.ipv4['filter'].add_chain('test-chain', wrap=False) + self.manager.ipv4['filter'].add_rule('test-chain', '-j DROP', + wrap=False) + old_count = len(self.manager.ipv4['filter'].rules) + self.manager.ipv4['filter'].empty_chain('test-chain', wrap=False) + self.assertEqual(old_count - 1, len(self.manager.ipv4['filter'].rules)) + + def test_will_not_empty_wrapped_when_unwrapped(self): + self.manager.ipv4['filter'].add_chain('test-chain') + self.manager.ipv4['filter'].add_rule('test-chain', '-j DROP') + old_count = len(self.manager.ipv4['filter'].rules) + self.manager.ipv4['filter'].empty_chain('test-chain', wrap=False) + self.assertEqual(old_count, len(self.manager.ipv4['filter'].rules)) + + def test_will_not_empty_unwrapped_when_wrapped(self): + self.manager.ipv4['filter'].add_chain('test-chain', wrap=False) + self.manager.ipv4['filter'].add_rule('test-chain', '-j DROP', + wrap=False) + old_count = len(self.manager.ipv4['filter'].rules) + self.manager.ipv4['filter'].empty_chain('test-chain') + self.assertEqual(old_count, len(self.manager.ipv4['filter'].rules)) -- cgit From 26d2a6ca8939156e8957e31dd17906070283ff24 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 5 Apr 2011 20:07:46 -0400 Subject: Undo use of $ in chain name where not needed. --- nova/virt/libvirt_conn.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9bc7ca05a..0d92e2e70 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -2143,20 +2143,20 @@ class IptablesFirewallDriver(FirewallDriver): self.build_provider_fw_rules(self) def _purge_provider_fw_rules(self): - """Remove all rules from the $provider chains.""" - self.iptables.ipv4['filter'].empty_chain('$provider') + """Remove all rules from the provider chains.""" + self.iptables.ipv4['filter'].empty_chain('provider') if FLAGS.use_ipv6: - self.iptables.ipv6['filter'].empty_chain('$provider') + self.iptables.ipv6['filter'].empty_chain('provider') def _build_provider_fw_rules(self): """Create all rules for the provider IP DROPs.""" ipv4_rules, ipv6_rules = self._provider_rules() for rule in ipv4_rules: - self.iptables.ipv4['filter'].add_rule('$provider', rule) + self.iptables.ipv4['filter'].add_rule('provider', rule) if FLAGS.use_ipv6: for rule in ipv6_rules: - self.iptables.ipv6['filter'].add_rule('$provider', rule) + self.iptables.ipv6['filter'].add_rule('provider', rule) def _provider_rules(self): """Generate a list of rules from provider for IP4 & IP6.""" @@ -2182,7 +2182,7 @@ class IptablesFirewallDriver(FirewallDriver): if version == 6 and rule.protocol == 'icmp': protocol = 'icmpv6' - args = ['-A', '$provider', '-p', protocol, '-s', rule.cidr] + args = ['-p', protocol, '-s', rule.cidr] if rule.protocol in ['udp', 'tcp']: if rule.from_port == rule.to_port: -- cgit From 2b79fa82872c55368167fc7433cb28a2369f5191 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 7 Apr 2011 01:42:49 -0400 Subject: test provider fw rules at the virt/ipteables layer. lowercase protocol names in admin api to match what the firewall driver expects. add provider fw rule chain in iptables6 as well. fix a couple of small typos and copy-paste errors. --- nova/api/ec2/admin.py | 6 ++--- nova/tests/test_virt.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++- nova/virt/libvirt_conn.py | 23 +++++++++-------- 3 files changed, 80 insertions(+), 14 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 0b27854ef..c0c2bcd0d 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -356,11 +356,11 @@ class AdminController(object): IPy.IP(cidr) rule = {'cidr': cidr} tcp_rule = rule.copy() - tcp_rule.update({"protocol": "TCP", "from_port": 1, "to_port": 65535}) + tcp_rule.update({"protocol": "tcp", "from_port": 1, "to_port": 65535}) udp_rule = rule.copy() - udp_rule.update({"protocol": "UDP", "from_port": 1, "to_port": 65535}) + udp_rule.update({"protocol": "udp", "from_port": 1, "to_port": 65535}) icmp_rule = rule.copy() - icmp_rule.update({"protocol": "ICMP", "from_port": -1, + icmp_rule.update({"protocol": "icmp", "from_port": -1, "to_port": None}) rules_added = 0 if not self._provider_fw_rule_exists(context, tcp_rule): diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 958c8e3e2..34ecd09c6 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -566,7 +566,9 @@ class IptablesFirewallTestCase(test.TestCase): self.network = utils.import_object(FLAGS.network_manager) class FakeLibvirtConnection(object): - pass + def nwfilterDefineXML(*args, **kwargs): + """setup_basic_rules in nwfilter calls this.""" + pass self.fake_libvirt_connection = FakeLibvirtConnection() self.fw = libvirt_conn.IptablesFirewallDriver( get_connection=lambda: self.fake_libvirt_connection) @@ -728,6 +730,67 @@ class IptablesFirewallTestCase(test.TestCase): "TCP port 80/81 acceptance rule wasn't added") db.instance_destroy(admin_ctxt, instance_ref['id']) + def test_provider_firewall_rules(self): + # keep from changing state of actual firewall + #def fake_function(*args, **kwargs): + # pass + #self.fw.iptables.apply = fake_function + #self.fw.nwfilter.setup_basic_filtering = fake_function + + # setup basic instance data + instance_ref = db.instance_create(self.context, + {'user_id': 'fake', + 'project_id': 'fake', + 'mac_address': '56:12:12:12:12:12'}) + ip = '10.11.12.13' + network_ref = db.project_get_network(self.context, 'fake') + admin_ctxt = context.get_admin_context() + fixed_ip = {'address': ip, 'network_id': network_ref['id']} + db.fixed_ip_create(admin_ctxt, fixed_ip) + db.fixed_ip_update(admin_ctxt, ip, {'allocated': True, + 'instance_id': instance_ref['id']}) + # FRAGILE: peeks at how the firewall names chains + chain_name = 'inst-%s' % instance_ref['id'] + + # create a firewall via setup_basic_filtering like libvirt_conn.spawn + # should have a chain with 0 rules + self.fw.setup_basic_filtering(instance_ref, network_info=None) + self.assertTrue('provider' in self.fw.iptables.ipv4['filter'].chains) + rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules + if rule.chain == 'provider'] + self.assertEqual(0, len(rules)) + + # add a rule and send the update message, check for 1 rule + provider_fw0 = db.provider_fw_rule_create(admin_ctxt, + {'protocol': 'tcp', + 'cidr': '10.99.99.99/32', + 'from_port': 1, + 'to_port': 65535}) + self.fw.refresh_provider_fw_rules() + rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules + if rule.chain == 'provider'] + self.assertEqual(1, len(rules)) + + # Add another, refresh, and make sure number of rules goes to two + provider_fw1 = db.provider_fw_rule_create(admin_ctxt, + {'protocol': 'udp', + 'cidr': '10.99.99.99/32', + 'from_port': 1, + 'to_port': 65535}) + self.fw.refresh_provider_fw_rules() + rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules + if rule.chain == 'provider'] + self.assertEqual(2, len(rules)) + + # create the instance filter and make sure it has a jump rule + self.fw.prepare_instance_filter(instance_ref, network_info=None) + self.fw.apply_instance_filter(instance_ref) + inst_rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules + if rule.chain == chain_name] + jump_rules = [rule for rule in inst_rules if '-j' in rule.rule] + prov_rules = [rule for rule in jump_rules if 'provider' in rule.rule] + self.assertEqual(1, len(prov_rules)) + class NWFilterTestCase(test.TestCase): def setUp(self): diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 0d92e2e70..38ba21521 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1939,6 +1939,7 @@ class IptablesFirewallDriver(FirewallDriver): network_info = _get_network_info(instance) self.nwfilter.setup_basic_filtering(instance, network_info) if not self.basicly_filtered: + LOG.debug("Setup Basic Filtering") self.refresh_provider_fw_rules() self.basicly_filtered = True @@ -1967,6 +1968,7 @@ class IptablesFirewallDriver(FirewallDriver): chain_name = self._instance_chain_name(instance) self.iptables.ipv4['filter'].add_chain(chain_name) + self.iptables.ipv4['filter'].empty_chain(chain_name) ips_v4 = [ip['ip'] for (_, mapping) in network_info for ip in mapping['ips']] @@ -1978,6 +1980,7 @@ class IptablesFirewallDriver(FirewallDriver): if FLAGS.use_ipv6: self.iptables.ipv6['filter'].add_chain(chain_name) + self.iptables.ipv6['filter'].empty_chain(chain_name) ips_v6 = [ip['ip'] for (_, mapping) in network_info for ip in mapping['ip6s']] @@ -1991,9 +1994,6 @@ class IptablesFirewallDriver(FirewallDriver): for rule in ipv4_rules: self.iptables.ipv4['filter'].add_rule(chain_name, rule) - for rule in ipv4_rules: - self.iptables.ipv4['filter'].add_rule(chain_name, rule) - if FLAGS.use_ipv6: for rule in ipv6_rules: self.iptables.ipv6['filter'].add_rule(chain_name, rule) @@ -2042,7 +2042,7 @@ class IptablesFirewallDriver(FirewallDriver): # they're not worth the clutter. if FLAGS.use_ipv6: # Allow RA responses - gateways_v6 = [network['gateway_v6'] for (network, _) in + gateways_v6 = [network['gateway_v6'] for (network, _m) in network_info] for gateway_v6 in gateways_v6: ipv6_rules.append( @@ -2065,7 +2065,7 @@ class IptablesFirewallDriver(FirewallDriver): security_group['id']) for rule in rules: - LOG.debug(_('Adding security group rule: %r'), rule) + LOG.debug(_("Adding security group rule: %r"), rule) if not rule.cidr: # Eventually, a mechanism to grant access for security @@ -2139,8 +2139,8 @@ class IptablesFirewallDriver(FirewallDriver): @utils.synchronized('iptables', external=True) def _do_refresh_provider_fw_rules(self): """Internal, synchronized version of refresh_provider_fw_rules.""" - self.purge_provider_fw_rules(self) - self.build_provider_fw_rules(self) + self._purge_provider_fw_rules() + self._build_provider_fw_rules() def _purge_provider_fw_rules(self): """Remove all rules from the provider chains.""" @@ -2150,6 +2150,9 @@ class IptablesFirewallDriver(FirewallDriver): def _build_provider_fw_rules(self): """Create all rules for the provider IP DROPs.""" + self.iptables.ipv4['filter'].add_chain('provider') + if FLAGS.use_ipv6: + self.iptables.ipv6['filter'].add_chain('provider') ipv4_rules, ipv6_rules = self._provider_rules() for rule in ipv4_rules: self.iptables.ipv4['filter'].add_rule('provider', rule) @@ -2179,19 +2182,19 @@ class IptablesFirewallDriver(FirewallDriver): fw_rules = ipv6_rules protocol = rule.protocol - if version == 6 and rule.protocol == 'icmp': + if version == 6 and protocol == 'icmp': protocol = 'icmpv6' args = ['-p', protocol, '-s', rule.cidr] - if rule.protocol in ['udp', 'tcp']: + if protocol in ['udp', 'tcp']: if rule.from_port == rule.to_port: args += ['--dport', '%s' % (rule.from_port,)] else: args += ['-m', 'multiport', '--dports', '%s:%s' % (rule.from_port, rule.to_port)] - elif rule.protocol == 'icmp': + elif protocol == 'icmp': icmp_type = rule.from_port icmp_code = rule.to_port -- cgit From 2278f2886d369af285f914a7b5883d3a7a5727cc Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 7 Apr 2011 13:22:03 -0400 Subject: remove unused code. --- nova/tests/test_virt.py | 6 ------ 1 file changed, 6 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 34ecd09c6..7c3dbf654 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -731,12 +731,6 @@ class IptablesFirewallTestCase(test.TestCase): db.instance_destroy(admin_ctxt, instance_ref['id']) def test_provider_firewall_rules(self): - # keep from changing state of actual firewall - #def fake_function(*args, **kwargs): - # pass - #self.fw.iptables.apply = fake_function - #self.fw.nwfilter.setup_basic_filtering = fake_function - # setup basic instance data instance_ref = db.instance_create(self.context, {'user_id': 'fake', -- cgit From 0be9e06c09c1d08802a8963e34090b5fcedb19be Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 11:35:58 -0400 Subject: Double quotes are ugly. --- nova/api/ec2/admin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 0d08aba7a..3c7a60277 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -349,19 +349,19 @@ class AdminController(object): def block_external_addresses(self, context, cidr): """Add provider-level firewall rules to block incoming traffic.""" - LOG.audit(_("Blocking traffic to all projects incoming from %s"), + LOG.audit(_('Blocking traffic to all projects incoming from %s'), cidr, context=context) cidr = urllib.unquote(cidr).decode() # raise if invalid IPy.IP(cidr) rule = {'cidr': cidr} tcp_rule = rule.copy() - tcp_rule.update({"protocol": "tcp", "from_port": 1, "to_port": 65535}) + tcp_rule.update({'protocol': 'tcp', 'from_port': 1, 'to_port': 65535}) udp_rule = rule.copy() - udp_rule.update({"protocol": "udp", "from_port": 1, "to_port": 65535}) + udp_rule.update({'protocol': 'udp', 'from_port': 1, 'to_port': 65535}) icmp_rule = rule.copy() - icmp_rule.update({"protocol": "icmp", "from_port": -1, - "to_port": None}) + icmp_rule.update({'protocol': 'icmp', 'from_port': -1, + 'to_port': None}) rules_added = 0 if not self._provider_fw_rule_exists(context, tcp_rule): db.provider_fw_rule_create(context, tcp_rule) @@ -375,4 +375,4 @@ class AdminController(object): if rules_added == 0: raise exception.ApiError(_('Duplicate rule')) self.compute_api.trigger_provider_fw_rules_refresh(context) - return {'status': 'OK', 'message': 'Disabled (number) IPs'} + return {'status': 'OK', 'message': 'Added %s rules' % rules_added} -- cgit From ace6c5f82810c9984fc3e0bb24a9c37c00e8ac39 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 11:36:45 -0400 Subject: Double quotes are ugly #2. --- nova/compute/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index e9e8e10e7..660c7666f 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -372,7 +372,7 @@ class API(base.Base): for host in hosts: rpc.cast(context, self.db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "refresh_provider_fw_rules", "args": {}}) + {'method': 'refresh_provider_fw_rules', 'args': {}}) def update(self, context, instance_id, **kwargs): """Updates the instance in the datastore. -- cgit From b114b618e4ce112589773cbe63daf0ee70b900be Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 11:39:03 -0400 Subject: remove dead/duplicate code. --- nova/db/sqlalchemy/api.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 939e02a84..5f5d46b86 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2011,10 +2011,6 @@ def provider_fw_rule_get_all(context): return session.query(models.ProviderFirewallRule).\ filter_by(deleted=can_read_deleted(context)).\ all() - fw_rule_ref = models.ProviderFirewallRule() - fw_rule_ref.update(rule) - fw_rule_ref.save() - return fw_rule_ref ################### -- cgit From a8f2a6444f4198db5fd5f05f7d2ae94e953a0fa2 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 11:40:08 -0400 Subject: Move migration to fix ordering. --- .../versions/014_add_provider_fw_rules.py | 75 ---------------------- .../versions/019_add_provider_fw_rules.py | 75 ++++++++++++++++++++++ 2 files changed, 75 insertions(+), 75 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/014_add_provider_fw_rules.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/019_add_provider_fw_rules.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/014_add_provider_fw_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/014_add_provider_fw_rules.py deleted file mode 100644 index 5aa30f7a8..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/014_add_provider_fw_rules.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -services = Table('services', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -networks = Table('networks', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -# -# New Tables -# -provider_fw_rules = Table('provider_fw_rules', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('protocol', - String(length=5, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('from_port', Integer()), - Column('to_port', Integer()), - Column('cidr', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)) - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (provider_fw_rules,): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/019_add_provider_fw_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/019_add_provider_fw_rules.py new file mode 100644 index 000000000..5aa30f7a8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/019_add_provider_fw_rules.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +services = Table('services', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +networks = Table('networks', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# +provider_fw_rules = Table('provider_fw_rules', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('protocol', + String(length=5, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('from_port', Integer()), + Column('to_port', Integer()), + Column('cidr', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)) + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (provider_fw_rules,): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise -- cgit From dfd6e6e3a46c2fbbb4e771d38396348c9659a0bd Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 11:44:18 -0400 Subject: Remove spurious newline at end of file. --- nova/virt/libvirt/connection.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index dc2dd1219..dfeda3814 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -1532,4 +1532,3 @@ class LibvirtConnection(driver.ComputeDriver): def get_host_stats(self, refresh=False): """See xenapi_conn.py implementation.""" pass - -- cgit From 459864dc0a05e6a0db642e9cb80ceade7b000ce8 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 11:45:46 -0400 Subject: fix typo introduced during merge conflict resolution. --- nova/virt/libvirt/firewall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index fd4c120a6..4e0df323d 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -376,7 +376,7 @@ class NWFilterFirewall(FirewallDriver): return result def _define_filters(self, filter_name, filter_children): - self._define_fitler(self._filter_container(filter_name, + self._define_filter(self._filter_container(filter_name, filter_children)) def refresh_security_group_rules(self, -- cgit From a5c9f44295df4054e9afb135aaa76c5e34cc3624 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 11:53:25 -0400 Subject: Double quotes are ugly #3. --- nova/virt/libvirt/firewall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index 4e0df323d..5bdc0c1c6 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -513,7 +513,7 @@ class IptablesFirewallDriver(FirewallDriver): network_info = netutils.get_network_info(instance) self.nwfilter.setup_basic_filtering(instance, network_info) if not self.basicly_filtered: - LOG.debug(_("iptables firewall: Setup Basic Filtering")) + LOG.debug(_('iptables firewall: Setup Basic Filtering')) self.refresh_provider_fw_rules() self.basicly_filtered = True @@ -638,7 +638,7 @@ class IptablesFirewallDriver(FirewallDriver): security_group['id']) for rule in rules: - LOG.debug(_("Adding security group rule: %r"), rule) + LOG.debug(_('Adding security group rule: %r'), rule) if not rule.cidr: # Eventually, a mechanism to grant access for security -- cgit From 93bfea42bdd594030c8ae046f87291ff184ef3f6 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 12:09:04 -0400 Subject: Make a cleaner log message and use [] instead of . to get database fields. --- nova/tests/test_libvirt.py | 5 +---- nova/virt/libvirt/firewall.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 14 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 1743b09a2..0eaf069fb 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -884,10 +884,7 @@ class IptablesFirewallTestCase(test.TestCase): def test_provider_firewall_rules(self): # setup basic instance data - instance_ref = db.instance_create(self.context, - {'user_id': 'fake', - 'project_id': 'fake', - 'mac_address': '56:12:12:12:12:12'}) + instance_ref = self._create_instance_ref() ip = '10.11.12.13' network_ref = db.project_get_network(self.context, 'fake') admin_ctxt = context.get_admin_context() diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index 5bdc0c1c6..c4192fac0 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -749,29 +749,29 @@ class IptablesFirewallDriver(FirewallDriver): ipv6_rules = [] rules = db.provider_fw_rule_get_all(ctxt) for rule in rules: - LOG.debug(_('Adding prvider rule: %r'), rule) - version = netutils.get_ip_version(rule.cidr) + LOG.debug(_('Adding provider rule: %s'), rule['cidr']) + version = netutils.get_ip_version(rule['cidr']) if version == 4: fw_rules = ipv4_rules else: fw_rules = ipv6_rules - protocol = rule.protocol + protocol = rule['protocol'] if version == 6 and protocol == 'icmp': protocol = 'icmpv6' - args = ['-p', protocol, '-s', rule.cidr] + args = ['-p', protocol, '-s', rule['cidr']] if protocol in ['udp', 'tcp']: - if rule.from_port == rule.to_port: - args += ['--dport', '%s' % (rule.from_port,)] + if rule['from_port'] == rule['to_port']: + args += ['--dport', '%s' % (rule['from_port'],)] else: args += ['-m', 'multiport', - '--dports', '%s:%s' % (rule.from_port, - rule.to_port)] + '--dports', '%s:%s' % (rule['from_port'], + rule['to_port'])] elif protocol == 'icmp': - icmp_type = rule.from_port - icmp_code = rule.to_port + icmp_type = rule['from_port'] + icmp_code = rule['to_port'] if icmp_type == -1: icmp_type_arg = None -- cgit From 168d4556365fafcb34e9536f7f932d4da24b30a6 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 12:48:07 -0400 Subject: Test tweaks. --- nova/tests/test_libvirt.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 0eaf069fb..bb7c8cf33 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -885,6 +885,7 @@ class IptablesFirewallTestCase(test.TestCase): def test_provider_firewall_rules(self): # setup basic instance data instance_ref = self._create_instance_ref() + nw_info = _create_network_info(1) ip = '10.11.12.13' network_ref = db.project_get_network(self.context, 'fake') admin_ctxt = context.get_admin_context() @@ -897,7 +898,7 @@ class IptablesFirewallTestCase(test.TestCase): # create a firewall via setup_basic_filtering like libvirt_conn.spawn # should have a chain with 0 rules - self.fw.setup_basic_filtering(instance_ref, network_info=None) + self.fw.setup_basic_filtering(instance_ref, network_info=nw_info) self.assertTrue('provider' in self.fw.iptables.ipv4['filter'].chains) rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules if rule.chain == 'provider'] @@ -926,13 +927,17 @@ class IptablesFirewallTestCase(test.TestCase): self.assertEqual(2, len(rules)) # create the instance filter and make sure it has a jump rule - self.fw.prepare_instance_filter(instance_ref, network_info=None) + self.fw.prepare_instance_filter(instance_ref, network_info=nw_info) self.fw.apply_instance_filter(instance_ref) inst_rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules if rule.chain == chain_name] jump_rules = [rule for rule in inst_rules if '-j' in rule.rule] - prov_rules = [rule for rule in jump_rules if 'provider' in rule.rule] - self.assertEqual(1, len(prov_rules)) + provjump_rules = [] + # IptablesTable doesn't make rules unique internally + for rule in jump_rules: + if 'provider' in rule.rule and rule not in provjump_rules: + provjump_rules.append(rule) + self.assertEqual(1, len(provjump_rules)) class NWFilterTestCase(test.TestCase): -- cgit From 1fd3f5a6edf911dc84e11130bc2c590567d780c3 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 17:05:55 -0400 Subject: Fixes from Ed Leafe's review suggestions. --- nova/api/ec2/admin.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 3c7a60277..1d4cffff3 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -339,12 +339,9 @@ class AdminController(object): def _provider_fw_rule_exists(self, context, rule): # TODO(todd): we call this repeatedly, can we filter by protocol? for old_rule in db.provider_fw_rule_get_all(context): - dupe = True - for key in ('cidr', 'from_port', 'to_port', 'protocol'): - if rule[key] != old_rule[key]: - dupe = False - if dupe: - return dupe + if all([rule[k] == old_rule[k] for k in ('cidr', 'from_port', + 'to_port', 'protocol')]): + return True return False def block_external_addresses(self, context, cidr): @@ -372,7 +369,7 @@ class AdminController(object): if not self._provider_fw_rule_exists(context, icmp_rule): db.provider_fw_rule_create(context, icmp_rule) rules_added += 1 - if rules_added == 0: + if not rules_added: raise exception.ApiError(_('Duplicate rule')) self.compute_api.trigger_provider_fw_rules_refresh(context) return {'status': 'OK', 'message': 'Added %s rules' % rules_added} -- cgit From add164c45db31baf8f12c3e5dede140c51a2e498 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 31 May 2011 14:10:29 -0400 Subject: Add refresh_provider_fw_rules to virt/driver.py#ComputeDriver so virtualization drivers other than libvirt will raise NotImplemented. --- nova/virt/driver.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'nova') diff --git a/nova/virt/driver.py b/nova/virt/driver.py index eb9626d08..23581ab49 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -191,6 +191,10 @@ class ComputeDriver(object): def refresh_security_group_members(self, security_group_id): raise NotImplementedError() + def refresh_provider_fw_rules(self, security_group_id): + """See: nova/virt/fake.py for docs.""" + raise NotImplementedError() + def reset_network(self, instance): """reset networking for specified instance""" raise NotImplementedError() -- cgit From 4d7dbdc96e30afbd19ab525e9667f6e3aaaafbe9 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 31 May 2011 16:20:07 -0400 Subject: Change version number of migration. --- .../versions/019_add_provider_fw_rules.py | 75 ---------------------- .../versions/021_add_provider_fw_rules.py | 75 ++++++++++++++++++++++ 2 files changed, 75 insertions(+), 75 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/019_add_provider_fw_rules.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/021_add_provider_fw_rules.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/019_add_provider_fw_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/019_add_provider_fw_rules.py deleted file mode 100644 index 5aa30f7a8..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/019_add_provider_fw_rules.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -services = Table('services', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -networks = Table('networks', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -# -# New Tables -# -provider_fw_rules = Table('provider_fw_rules', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('protocol', - String(length=5, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('from_port', Integer()), - Column('to_port', Integer()), - Column('cidr', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)) - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (provider_fw_rules,): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/021_add_provider_fw_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/021_add_provider_fw_rules.py new file mode 100644 index 000000000..5aa30f7a8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/021_add_provider_fw_rules.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +services = Table('services', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +networks = Table('networks', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# +provider_fw_rules = Table('provider_fw_rules', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('protocol', + String(length=5, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('from_port', Integer()), + Column('to_port', Integer()), + Column('cidr', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)) + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (provider_fw_rules,): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise -- cgit From 2c1dd72060fccbe7f32a6aa08c1ce67476806680 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 31 May 2011 16:28:46 -0400 Subject: Whitespace cleanups. --- nova/virt/libvirt/firewall.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index c4192fac0..28cd9fe9c 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -81,7 +81,7 @@ class FirewallDriver(object): Gets called when a rule has been added to or removed from the list of rules (via admin api). - + """ raise NotImplementedError() @@ -306,7 +306,7 @@ class NWFilterFirewall(FirewallDriver): def prepare_instance_filter(self, instance, network_info=None): """Creates an NWFilter for the given instance. - + In the process, it makes sure the filters for the provider blocks, security groups, and base filter are all in place. -- cgit From 93f61c18134e6b4091114a7126f52d74e0d20df8 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 6 Jun 2011 15:33:35 -0400 Subject: Remove ipy from nova/api/ec2/cloud.py and use netaddr --- nova/api/ec2/cloud.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b7a9a8633..ceeaa3e48 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -23,7 +23,7 @@ datastore. """ import base64 -import IPy +import netaddr import os import urllib import tempfile @@ -444,7 +444,7 @@ class CloudController(object): elif cidr_ip: # If this fails, it throws an exception. This is what we want. cidr_ip = urllib.unquote(cidr_ip).decode() - IPy.IP(cidr_ip) + netaddr.IPNetwork(cidr_ip) values['cidr'] = cidr_ip else: values['cidr'] = '0.0.0.0/0' -- cgit From b7390c78054f839ac3340d771b9aae0109f5a98e Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 6 Jun 2011 15:34:51 -0400 Subject: Remove ipy from network code and replace with netaddr --- nova/network/linux_net.py | 4 ++-- nova/network/manager.py | 43 ++++++++++++++++++++--------------------- nova/tests/test_flat_network.py | 6 +++--- nova/tests/test_vlan_network.py | 6 +++--- 4 files changed, 29 insertions(+), 30 deletions(-) (limited to 'nova') diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 815cd29c3..7b6f4497d 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -20,6 +20,7 @@ import calendar import inspect +import netaddr import os from nova import db @@ -27,7 +28,6 @@ from nova import exception from nova import flags from nova import log as logging from nova import utils -from IPy import IP LOG = logging.getLogger("nova.linux_net") @@ -700,7 +700,7 @@ def _dnsmasq_cmd(net): '--listen-address=%s' % net['gateway'], '--except-interface=lo', '--dhcp-range=%s,static,120s' % net['dhcp_start'], - '--dhcp-lease-max=%s' % IP(net['cidr']).len(), + '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(net['cidr'])), '--dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'), '--dhcp-script=%s' % FLAGS.dhcpbridge, '--leasefile-ro'] diff --git a/nova/network/manager.py b/nova/network/manager.py index f726c4b26..f037312db 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -45,10 +45,9 @@ topologies. All of the network commands are issued to a subclass of import datetime import math +import netaddr import socket -import IPy - from nova import context from nova import db from nova import exception @@ -294,8 +293,8 @@ class NetworkManager(manager.SchedulerDependentManager): def create_networks(self, context, cidr, num_networks, network_size, cidr_v6, label, *args, **kwargs): """Create networks based on parameters.""" - fixed_net = IPy.IP(cidr) - fixed_net_v6 = IPy.IP(cidr_v6) + fixed_net = netaddr.IPNetwork(cidr) + fixed_net_v6 = netaddr.IPNetwork(cidr_v6) significant_bits_v6 = 64 network_size_v6 = 1 << 64 count = 1 @@ -304,15 +303,15 @@ class NetworkManager(manager.SchedulerDependentManager): start_v6 = index * network_size_v6 significant_bits = 32 - int(math.log(network_size, 2)) cidr = '%s/%s' % (fixed_net[start], significant_bits) - project_net = IPy.IP(cidr) + project_net = netaddr.IPNetwork(cidr) net = {} net['bridge'] = FLAGS.flat_network_bridge net['dns'] = FLAGS.flat_network_dns net['cidr'] = cidr - net['netmask'] = str(project_net.netmask()) - net['gateway'] = str(project_net[1]) - net['broadcast'] = str(project_net.broadcast()) - net['dhcp_start'] = str(project_net[2]) + net['netmask'] = str(project_net.netmask) + net['gateway'] = str(list(project_net)[1]) + net['broadcast'] = str(project_net.broadcast) + net['dhcp_start'] = str(list(project_net)[2]) if num_networks > 1: net['label'] = '%s_%d' % (label, count) else: @@ -323,9 +322,9 @@ class NetworkManager(manager.SchedulerDependentManager): cidr_v6 = '%s/%s' % (fixed_net_v6[start_v6], significant_bits_v6) net['cidr_v6'] = cidr_v6 - project_net_v6 = IPy.IP(cidr_v6) - net['gateway_v6'] = str(project_net_v6[1]) - net['netmask_v6'] = str(project_net_v6.prefixlen()) + project_net_v6 = netaddr.IPNetwork(cidr_v6) + net['gateway_v6'] = str(list(project_net_v6)[1]) + net['netmask_v6'] = str(project_net_v6._prefixlen) network_ref = self.db.network_create_safe(context, net) @@ -349,7 +348,7 @@ class NetworkManager(manager.SchedulerDependentManager): # to properties of the manager class? bottom_reserved = self._bottom_reserved_ips top_reserved = self._top_reserved_ips - project_net = IPy.IP(network_ref['cidr']) + project_net = netaddr.IPNetwork(network_ref['cidr']) num_ips = len(project_net) for index in range(num_ips): address = str(project_net[index]) @@ -539,13 +538,13 @@ class VlanManager(NetworkManager): ' the vlan start cannot be greater' ' than 4094')) - fixed_net = IPy.IP(cidr) - if fixed_net.len() < num_networks * network_size: + fixed_net = netaddr.IPNetwork(cidr) + if len(fixed_net) < num_networks * network_size: raise ValueError(_('The network range is not big enough to fit ' '%(num_networks)s. Network size is %(network_size)s' % locals())) - fixed_net_v6 = IPy.IP(cidr_v6) + fixed_net_v6 = netaddr.IPNetwork(cidr_v6) network_size_v6 = 1 << 64 significant_bits_v6 = 64 for index in range(num_networks): @@ -554,14 +553,14 @@ class VlanManager(NetworkManager): start_v6 = index * network_size_v6 significant_bits = 32 - int(math.log(network_size, 2)) cidr = "%s/%s" % (fixed_net[start], significant_bits) - project_net = IPy.IP(cidr) + project_net = netaddr.IPNetwork(cidr) net = {} net['cidr'] = cidr - net['netmask'] = str(project_net.netmask()) - net['gateway'] = str(project_net[1]) - net['broadcast'] = str(project_net.broadcast()) - net['vpn_private_address'] = str(project_net[2]) - net['dhcp_start'] = str(project_net[3]) + net['netmask'] = str(project_net.netmask) + net['gateway'] = str(list(project_net)[1]) + net['broadcast'] = str(project_net.broadcast) + net['vpn_private_address'] = str(list(project_net)[2]) + net['dhcp_start'] = str(list(project_net)[3]) net['vlan'] = vlan net['bridge'] = 'br%s' % vlan if(FLAGS.use_ipv6): diff --git a/nova/tests/test_flat_network.py b/nova/tests/test_flat_network.py index dcc617e25..8544019c0 100644 --- a/nova/tests/test_flat_network.py +++ b/nova/tests/test_flat_network.py @@ -18,7 +18,7 @@ """ Unit Tests for flat network code """ -import IPy +import netaddr import os import unittest @@ -45,8 +45,8 @@ class FlatNetworkTestCase(base.NetworkTestCase): self.context._project = self.projects[0] self.context.project_id = self.projects[0].id - pubnet = IPy.IP(flags.FLAGS.floating_range) - address = str(pubnet[0]) + pubnet = netaddr.IPRange(flags.FLAGS.floating_range) + address = str(list(pubnet)[0]) try: db.floating_ip_get_by_address(context.get_admin_context(), address) except exception.NotFound: diff --git a/nova/tests/test_vlan_network.py b/nova/tests/test_vlan_network.py index 063b81832..a1c8ab11c 100644 --- a/nova/tests/test_vlan_network.py +++ b/nova/tests/test_vlan_network.py @@ -18,7 +18,7 @@ """ Unit Tests for vlan network code """ -import IPy +import netaddr import os from nova import context @@ -44,8 +44,8 @@ class VlanNetworkTestCase(base.NetworkTestCase): # TODO(vish): better way of adding floating ips self.context._project = self.projects[0] self.context.project_id = self.projects[0].id - pubnet = IPy.IP(flags.FLAGS.floating_range) - address = str(pubnet[0]) + pubnet = netaddr.IPNetwork(flags.FLAGS.floating_range) + address = str(list(pubnet)[0]) try: db.floating_ip_get_by_address(context.get_admin_context(), address) except exception.NotFound: -- cgit From 46bd8cbd1358a44534a620408b828ad08eef9cec Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 6 Jun 2011 15:35:33 -0400 Subject: Remove ipy from virt code and replace with netaddr --- nova/virt/libvirt/connection.py | 3 +-- nova/virt/libvirt/netutils.py | 14 +++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index c491418ae..da5911a1a 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -38,6 +38,7 @@ Supports KVM, LXC, QEMU, UML, and XEN. import hashlib import multiprocessing +import netaddr import os import random import shutil @@ -52,8 +53,6 @@ from xml.etree import ElementTree from eventlet import greenthread from eventlet import tpool -import IPy - from nova import context from nova import db from nova import exception diff --git a/nova/virt/libvirt/netutils.py b/nova/virt/libvirt/netutils.py index 4d596078a..0bad84f7c 100644 --- a/nova/virt/libvirt/netutils.py +++ b/nova/virt/libvirt/netutils.py @@ -21,7 +21,7 @@ """Network-releated utilities for supporting libvirt connection code.""" -import IPy +import netaddr from nova import context from nova import db @@ -34,18 +34,18 @@ FLAGS = flags.FLAGS def get_net_and_mask(cidr): - net = IPy.IP(cidr) - return str(net.net()), str(net.netmask()) + net = netaddr.IPNetwork(cidr) + return str(net.ip), str(net.netmask) def get_net_and_prefixlen(cidr): - net = IPy.IP(cidr) - return str(net.net()), str(net.prefixlen()) + net = netaddr.IPNetwork(cidr) + return str(net.ip), str(net._prefixlen) def get_ip_version(cidr): - net = IPy.IP(cidr) - return int(net.version()) + net = netaddr.IPNetwork(cidr) + return int(net.version) def get_network_info(instance): -- cgit From de42ebea7a8d055caeddd40edcdcaa6d64f1548e Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 6 Jun 2011 17:03:50 -0400 Subject: Convert stray import IPy --- nova/tests/test_network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 77f6aaff3..da92f2066 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -18,7 +18,7 @@ """ Unit Tests for network code """ -import IPy +import netaddr import os from nova import test -- cgit From 4b0b0361ed8d231844344d014412f7b647baae0b Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 6 Jun 2011 17:05:28 -0400 Subject: Remove more stray import IPy --- nova/tests/network/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/network/base.py b/nova/tests/network/base.py index b06271c99..f65416824 100644 --- a/nova/tests/network/base.py +++ b/nova/tests/network/base.py @@ -18,7 +18,7 @@ """ Base class of Unit Tests for all network models """ -import IPy +import netaddr import os from nova import context -- cgit From 781f3f07ebc3236404e33189e0a76cbb877dff18 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 10 Jun 2011 16:35:35 -0400 Subject: adding xml support to /images//meta resource; moving show/update entities into meta container --- nova/api/openstack/image_metadata.py | 58 +++++++++++-- nova/api/openstack/wsgi.py | 8 +- nova/tests/api/openstack/test_image_metadata.py | 103 ++++++++++++++++++++++-- 3 files changed, 155 insertions(+), 14 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index ebfe2bde9..77028be28 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -16,6 +16,7 @@ # under the License. from webob import exc +from xml.dom import minidom from nova import flags from nova import image @@ -59,7 +60,7 @@ class Controller(object): context = req.environ['nova.context'] metadata = self._get_metadata(context, image_id) if id in metadata: - return {id: metadata[id]} + return {'meta': {id: metadata[id]}} else: return faults.Fault(exc.HTTPNotFound()) @@ -77,15 +78,15 @@ class Controller(object): def update(self, req, image_id, id, body): context = req.environ['nova.context'] - if not id in body: + if not id in body['meta']: expl = _('Request body and URI mismatch') raise exc.HTTPBadRequest(explanation=expl) - if len(body) > 1: + if len(body['meta']) > 1: expl = _('Request body contains too many items') raise exc.HTTPBadRequest(explanation=expl) img = self.image_service.show(context, image_id) metadata = self._get_metadata(context, image_id, img) - metadata[id] = body[id] + metadata[id] = body['meta'][id] self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) @@ -103,9 +104,56 @@ class Controller(object): self.image_service.update(context, image_id, img, None) +class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer): + def __init__(self): + xmlns = wsgi.XMLNS_V11 + super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns) + + def _meta_item_to_xml(self, doc, key, value): + node = doc.createElement('meta') + node.setAttribute('key', key) + text = doc.createTextNode(value) + node.appendChild(text) + return node + + def _meta_list_to_xml(self, xml_doc, meta_items): + container_node = xml_doc.createElement('metadata') + for (key, value) in meta_items: + item_node = self._meta_item_to_xml(xml_doc, key, value) + container_node.appendChild(item_node) + return container_node + + def _meta_list_to_xml_string(self, metadata_dict): + xml_doc = minidom.Document() + items = metadata_dict['metadata'].items() + container_node = self._meta_list_to_xml(xml_doc, items) + self._add_xmlns(container_node) + return container_node.toprettyxml(indent=' ') + + def index(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def create(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def _meta_item_to_xml_string(self, meta_item_dict): + xml_doc = minidom.Document() + item_key, item_value = meta_item_dict.items()[0] + item_node = self._meta_item_to_xml(xml_doc, item_key, item_value) + self._add_xmlns(item_node) + return item_node.toprettyxml(indent=' ') + + def show(self, meta_item_dict): + return self._meta_item_to_xml_string(meta_item_dict['meta']) + + def update(self, meta_item_dict): + return self._meta_item_to_xml_string(meta_item_dict['meta']) + + + def create_resource(): serializers = { - 'application/xml': wsgi.XMLDictSerializer(xmlns=wsgi.XMLNS_V11), + 'application/xml': ImageMetadataXMLSerializer(), } return wsgi.Resource(Controller(), serializers=serializers) diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index ddf4e6fa9..d1402b314 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -221,12 +221,14 @@ class XMLDictSerializer(DictSerializer): doc = minidom.Document() node = self._to_xml_node(doc, self.metadata, root_key, data[root_key]) - xmlns = node.getAttribute('xmlns') - if not xmlns and self.xmlns: - node.setAttribute('xmlns', self.xmlns) + self._add_xmlns(node) return node.toprettyxml(indent=' ') + def _add_xmlns(self, node): + if self.xmlns is not None: + node.setAttribute('xmlns', self.xmlns) + def _to_xml_node(self, doc, metadata, nodename, data): """Recursive method to convert data members to XML nodes.""" result = doc.createElement(nodename) diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index 56be0f1cc..bc1903ca5 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -19,6 +19,7 @@ import json import stubout import unittest import webob +import xml.dom.minidom as minidom from nova import flags @@ -105,13 +106,55 @@ class ImageMetaDataTest(unittest.TestCase): self.assertEqual(200, res.status_int) self.assertEqual('value1', res_dict['metadata']['key1']) + def test_index_xml(self): + serializer = openstack.image_metadata.ImageMetadataXMLSerializer() + fixture = { + 'metadata': { + 'one': 'two', + 'three': 'four', + }, + } + output = serializer.index(fixture) + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + + four + + + two + + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + def test_show(self): req = webob.Request.blank('/v1.1/images/1/meta/key1') req.environ['api.version'] = '1.1' res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(200, res.status_int) - self.assertEqual('value1', res_dict['key1']) + self.assertEqual('value1', res_dict['meta']['key1']) + + def test_show_xml(self): + serializer = openstack.image_metadata.ImageMetadataXMLSerializer() + fixture = { + 'meta': { + 'one': 'two', + }, + } + output = serializer.show(fixture) + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + two + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) def test_show_not_found(self): req = webob.Request.blank('/v1.1/images/1/meta/key9') @@ -135,22 +178,70 @@ class ImageMetaDataTest(unittest.TestCase): self.assertEqual('value2', res_dict['metadata']['key2']) self.assertEqual(1, len(res_dict)) + def test_create_xml(self): + serializer = openstack.image_metadata.ImageMetadataXMLSerializer() + fixture = { + 'metadata': { + 'key9': 'value9', + 'key2': 'value2', + 'key1': 'value1', + }, + } + output = serializer.create(fixture) + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + + value2 + + + value9 + + + value1 + + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + def test_update_item(self): req = webob.Request.blank('/v1.1/images/1/meta/key1') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"key1": "zz"}' + req.body = '{"meta": {"key1": "zz"}}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) + print res.body self.assertEqual(200, res.status_int) res_dict = json.loads(res.body) - self.assertEqual('zz', res_dict['key1']) + self.assertEqual('zz', res_dict['meta']['key1']) + + def test_update_item_xml(self): + serializer = openstack.image_metadata.ImageMetadataXMLSerializer() + fixture = { + 'meta': { + 'one': 'two', + }, + } + output = serializer.update(fixture) + actual = minidom.parseString(output.replace(" ", "")) + + expected = minidom.parseString(""" + + two + + """.replace(" ", "")) + + self.assertEqual(expected.toxml(), actual.toxml()) + def test_update_item_too_many_keys(self): req = webob.Request.blank('/v1.1/images/1/meta/key1') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"key1": "value1", "key2": "value2"}' + req.body = '{"meta": {"key1": "value1", "key2": "value2"}}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) @@ -159,7 +250,7 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/1/meta/bad') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"key1": "value1"}' + req.body = '{"meta": {"key1": "value1"}}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) @@ -195,7 +286,7 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/3/meta/blah') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"blah": "blah"}' + req.body = '{"meta": {"blah": "blah"}}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) -- cgit From 0bcb15317fede5c17c77c187e1cd9a68a0c8030c Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Fri, 10 Jun 2011 22:32:33 -0400 Subject: Reorder firewall rules so the common path is shorter. --- nova/virt/libvirt/firewall.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova') diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index 28cd9fe9c..331c73b47 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -590,14 +590,14 @@ class IptablesFirewallDriver(FirewallDriver): ipv4_rules += ['-m state --state ' 'INVALID -j DROP'] ipv6_rules += ['-m state --state ' 'INVALID -j DROP'] - # Pass through provider-wide drops - ipv4_rules += ['-j $provider'] - ipv6_rules += ['-j $provider'] - # Allow established connections ipv4_rules += ['-m state --state ESTABLISHED,RELATED -j ACCEPT'] ipv6_rules += ['-m state --state ESTABLISHED,RELATED -j ACCEPT'] + # Pass through provider-wide drops + ipv4_rules += ['-j $provider'] + ipv6_rules += ['-j $provider'] + dhcp_servers = [network['gateway'] for (network, _m) in network_info] for dhcp_server in dhcp_servers: -- cgit From 90b3f8eb676681afa491c8bac85a45fdc87b099b Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 11 Jun 2011 14:50:21 -0400 Subject: Move migration. --- .../versions/021_add_provider_fw_rules.py | 75 ---------------------- .../versions/023_add_provider_firewall_rules.py | 75 ++++++++++++++++++++++ 2 files changed, 75 insertions(+), 75 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/021_add_provider_fw_rules.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/023_add_provider_firewall_rules.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/021_add_provider_fw_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/021_add_provider_fw_rules.py deleted file mode 100644 index 5aa30f7a8..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/021_add_provider_fw_rules.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -services = Table('services', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -networks = Table('networks', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -# -# New Tables -# -provider_fw_rules = Table('provider_fw_rules', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('protocol', - String(length=5, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('from_port', Integer()), - Column('to_port', Integer()), - Column('cidr', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)) - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (provider_fw_rules,): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/023_add_provider_firewall_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/023_add_provider_firewall_rules.py new file mode 100644 index 000000000..5aa30f7a8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/023_add_provider_firewall_rules.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +services = Table('services', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +networks = Table('networks', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# +provider_fw_rules = Table('provider_fw_rules', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('protocol', + String(length=5, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('from_port', Integer()), + Column('to_port', Integer()), + Column('cidr', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)) + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (provider_fw_rules,): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise -- cgit From 27fb2cf245ae87282f3aefdf2ae4740866529101 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 11 Jun 2011 15:14:46 -0400 Subject: pep 8 whitespace fix. --- nova/tests/test_libvirt.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 07c8e81f1..ee94d3c17 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1058,7 +1058,6 @@ class IptablesFirewallTestCase(test.TestCase): db.instance_destroy(admin_ctxt, instance_ref['id']) - def test_provider_firewall_rules(self): # setup basic instance data instance_ref = self._create_instance_ref() -- cgit From d1b6ebb4009e13ac2cf2309275a66a634e4f9171 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 11 Jun 2011 16:42:58 -0400 Subject: Add ability to list ip blocks. --- nova/api/ec2/admin.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index fcf7f674c..37ae2e648 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -369,3 +369,11 @@ class AdminController(object): raise exception.ApiError(_('Duplicate rule')) self.compute_api.trigger_provider_fw_rules_refresh(context) return {'status': 'OK', 'message': 'Added %s rules' % rules_added} + + def describe_external_address_blocks(self, context): + blocks = db.provider_fw_rule_get_all(context) + # NOTE(todd): use a set since we have icmp/udp/tcp rules with same cidr + blocks = set([b.cidr for b in blocks]) + blocks = [{'cidr': b} for b in blocks] + return {'externalIpBlockInfo': + list(sorted(blocks, key=lambda k: k['cidr']))} -- cgit From ed3914eafa7d076fdcc03ee958f77528bcf20603 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 11 Jun 2011 18:03:45 -0400 Subject: Add a method to delete provider firewall rules. --- nova/api/ec2/admin.py | 12 ++++++++++++ nova/db/api.py | 10 ++++++++++ nova/db/sqlalchemy/api.py | 21 +++++++++++++++++++++ 3 files changed, 43 insertions(+) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 37ae2e648..da982d8dc 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -377,3 +377,15 @@ class AdminController(object): blocks = [{'cidr': b} for b in blocks] return {'externalIpBlockInfo': list(sorted(blocks, key=lambda k: k['cidr']))} + + def remove_external_address_block(self, context, cidr): + LOG.audit(_('Removing ip block from %s'), cidr, context=context) + cidr = urllib.unquote(cidr).decode() + # raise if invalid + IPy.IP(cidr) + rules = db.provider_fw_rule_get_all_by_cidr(context, cidr) + for rule in rules: + db.provider_fw_rule_destroy(context, rule['id']) + if rules: + self.compute_api.trigger_provider_fw_rules_refresh(context) + return {'status': 'OK', 'message': 'Deleted %s rules' % len(rules)} diff --git a/nova/db/api.py b/nova/db/api.py index 798b2881c..62368c438 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1004,6 +1004,16 @@ def provider_fw_rule_get_all(context): return IMPL.provider_fw_rule_get_all(context) +def provider_fw_rule_get_all_by_cidr(context, cidr): + """Get all provider-level firewall rules.""" + return IMPL.provider_fw_rule_get_all_by_cidr(context) + + +def provider_fw_rule_destroy(context, rule_id): + """Delete a provider firewall rule from the database.""" + return IMPL.provider_fw_rule_destroy(context) + + ################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 9612e456b..c8802e354 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2093,6 +2093,7 @@ def provider_fw_rule_create(context, rule): return fw_rule_ref +@require_admin_context def provider_fw_rule_get_all(context): session = get_session() return session.query(models.ProviderFirewallRule).\ @@ -2100,6 +2101,26 @@ def provider_fw_rule_get_all(context): all() +@require_admin_context +def provider_fw_rule_get_all_by_cidr(context, cidr): + session = get_session() + return session.query(models.ProviderFirewallRule).\ + filter_by(deleted=can_read_deleted(context)).\ + filter_by(cidr=cidr).\ + all() + + +@require_admin_context +def provider_fw_rule_destroy(context, rule_id): + session = get_session() + with session.begin(): + session.query(models.ProviderFirewallRule).\ + filter_by(id=rule_id).\ + update({'deleted': True, + 'deleted_at': utils.utcnow(), + 'updated_at': literal_column('updated_at')}) + + ################### -- cgit From 0a6aeacfedfd5e666e109b54c5c03908eeb47c31 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 11 Jun 2011 18:38:44 -0400 Subject: fix method chaining in database layer to pass right parameters. --- nova/db/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/db/api.py b/nova/db/api.py index 62368c438..6ff6ec2f3 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -1006,12 +1006,12 @@ def provider_fw_rule_get_all(context): def provider_fw_rule_get_all_by_cidr(context, cidr): """Get all provider-level firewall rules.""" - return IMPL.provider_fw_rule_get_all_by_cidr(context) + return IMPL.provider_fw_rule_get_all_by_cidr(context, cidr) def provider_fw_rule_destroy(context, rule_id): """Delete a provider firewall rule from the database.""" - return IMPL.provider_fw_rule_destroy(context) + return IMPL.provider_fw_rule_destroy(context, rule_id) ################### -- cgit From 7a2712ebf74e5565663a6723a992151f71255eff Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 14 Jun 2011 11:34:03 -0700 Subject: Move ipy commands to netaddr. --- nova/api/ec2/admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index fcf7f674c..343bc61c4 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -22,7 +22,7 @@ Admin API controller, exposed through http via the api worker. import base64 import datetime -import IPy +import netaddr import urllib from nova import compute @@ -346,7 +346,7 @@ class AdminController(object): cidr, context=context) cidr = urllib.unquote(cidr).decode() # raise if invalid - IPy.IP(cidr) + netaddr.IPNetwork(cidr) rule = {'cidr': cidr} tcp_rule = rule.copy() tcp_rule.update({'protocol': 'tcp', 'from_port': 1, 'to_port': 65535}) -- cgit From bfbb2b8e04d1cd4b761c67973b173d2ca6f84859 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 17 Jun 2011 13:39:34 -0400 Subject: adding extra image service properties to compute api snapshot; adding instance_ref property --- nova/api/openstack/images.py | 31 ++++++++++++++++++++++++++++--- nova/api/openstack/views/images.py | 30 +++++++++++++----------------- nova/compute/api.py | 3 ++- nova/tests/api/openstack/fakes.py | 7 ++++--- nova/tests/api/openstack/test_images.py | 30 ++++++++++++++++++++---------- 5 files changed, 67 insertions(+), 34 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 5ffd8e96a..4a09060c9 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import os.path + import webob.exc from nova import compute @@ -104,7 +106,10 @@ class Controller(object): except KeyError: raise webob.exc.HTTPBadRequest() - image = self._compute_service.snapshot(context, server_id, image_name) + props = self._get_extra_properties(req, body) + + image = self._compute_service.snapshot(context, server_id, + image_name, props) return dict(image=self.get_builder(req).build(image, detail=True)) def get_builder(self, request): @@ -114,6 +119,9 @@ class Controller(object): def _server_id_from_req_data(self, data): raise NotImplementedError() + def _get_extra_properties(self, req, data): + return {} + class ControllerV10(Controller): """Version 1.0 specific controller logic.""" @@ -150,7 +158,11 @@ class ControllerV10(Controller): return dict(images=[builder(image, detail=True) for image in images]) def _server_id_from_req_data(self, data): - return data['image']['serverId'] + try: + return data['image']['serverId'] + except KeyError: + msg = _("Expected serverId attribute on server entity.") + raise webob.exc.HTTPBadRequest(explanation=msg) class ControllerV11(Controller): @@ -190,7 +202,20 @@ class ControllerV11(Controller): return dict(images=[builder(image, detail=True) for image in images]) def _server_id_from_req_data(self, data): - return data['image']['serverRef'] + try: + server_ref = data['image']['serverRef'] + except KeyError: + msg = _("Expected serverRef attribute on server entity.") + raise webob.exc.HTTPBadRequest(explanation=msg) + + return os.path.split(server_ref)[1] + + def _get_extra_properties(self, req, data): + server_ref = data['image']['serverRef'] + if not server_ref.startswith('http'): + server_ref = os.path.join(req.application_url, 'servers', + server_ref) + return {'instance_ref': server_ref} def create_resource(version='1.0'): diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 2773c9c13..d6a054102 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -46,13 +46,9 @@ class ViewBuilder(object): except KeyError: image['status'] = image['status'].upper() - def _build_server(self, image, instance_id): + def _build_server(self, image, image_obj): """Indicates that you must use a ViewBuilder subclass.""" - raise NotImplementedError - - def generate_server_ref(self, server_id): - """Return an href string pointing to this server.""" - return os.path.join(self._url, "servers", str(server_id)) + raise NotImplementedError() def generate_href(self, image_id): """Return an href string pointing to this object.""" @@ -60,8 +56,6 @@ class ViewBuilder(object): def build(self, image_obj, detail=False): """Return a standardized image structure for display by the API.""" - properties = image_obj.get("properties", {}) - self._format_dates(image_obj) if "status" in image_obj: @@ -72,11 +66,7 @@ class ViewBuilder(object): "name": image_obj.get("name"), } - if "instance_id" in properties: - try: - self._build_server(image, int(properties["instance_id"])) - except ValueError: - pass + self._build_server(image, image_obj) if detail: image.update({ @@ -94,15 +84,21 @@ class ViewBuilder(object): class ViewBuilderV10(ViewBuilder): """OpenStack API v1.0 Image Builder""" - def _build_server(self, image, instance_id): - image["serverId"] = instance_id + def _build_server(self, image, image_obj): + try: + image['serverId'] = int(image_obj['properties']['instance_id']) + except (KeyError, ValueError): + pass class ViewBuilderV11(ViewBuilder): """OpenStack API v1.1 Image Builder""" - def _build_server(self, image, instance_id): - image["serverRef"] = self.generate_server_ref(instance_id) + def _build_server(self, image, image_obj): + try: + image['serverRef'] = image_obj['properties']['instance_ref'] + except KeyError: + return def build(self, image_obj, detail=False): """Return a standardized image structure for display by the API.""" diff --git a/nova/compute/api.py b/nova/compute/api.py index e2c4cf8d7..86e2b116e 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -619,7 +619,7 @@ class API(base.Base): raise exception.Error(_("Unable to find host for Instance %s") % instance_id) - def snapshot(self, context, instance_id, name): + def snapshot(self, context, instance_id, name, extra_properties=None): """Snapshot the given instance. :returns: A dict containing image metadata @@ -627,6 +627,7 @@ class API(base.Base): properties = {'instance_id': str(instance_id), 'user_id': str(context.user_id), 'image_state': 'creating'} + properties.update(extra_properties or {}) sent_meta = {'name': name, 'is_public': False, 'status': 'creating', 'properties': properties} recv_meta = self.image_service.create(context, sent_meta) diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index a10fb7433..d46608c92 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -141,9 +141,10 @@ def stub_out_networking(stubs): def stub_out_compute_api_snapshot(stubs): - def snapshot(self, context, instance_id, name): - return dict(id='123', status='ACTIVE', - properties=dict(instance_id='123')) + def snapshot(self, context, instance_id, name, extra_properties=None): + props = dict(instance_id=instance_id, instance_ref=instance_id) + props.update(extra_properties or {}) + return dict(id='123', status='ACTIVE', name=name, properties=props) stubs.Set(nova.compute.API, 'snapshot', snapshot) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index be777df9b..06983893a 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -648,7 +648,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): { 'id': 124, 'name': 'queued backup', - 'serverId': 42, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, 'status': 'QUEUED', @@ -656,7 +655,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): { 'id': 125, 'name': 'saving backup', - 'serverId': 42, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, 'status': 'SAVING', @@ -665,7 +663,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): { 'id': 126, 'name': 'active backup', - 'serverId': 42, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, 'status': 'ACTIVE' @@ -673,7 +670,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): { 'id': 127, 'name': 'killed backup', - 'serverId': 42, 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, 'status': 'FAILED', @@ -719,7 +715,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): { 'id': 124, 'name': 'queued backup', - 'serverRef': "http://localhost/v1.1/servers/42", + 'serverRef': "http://localhost:8774/v1.1/servers/42", 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, 'status': 'QUEUED', @@ -741,7 +737,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): { 'id': 125, 'name': 'saving backup', - 'serverRef': "http://localhost/v1.1/servers/42", + 'serverRef': "http://localhost:8774/v1.1/servers/42", 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, 'status': 'SAVING', @@ -764,7 +760,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): { 'id': 126, 'name': 'active backup', - 'serverRef': "http://localhost/v1.1/servers/42", + 'serverRef': "http://localhost:8774/v1.1/servers/42", 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, 'status': 'ACTIVE', @@ -786,7 +782,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): { 'id': 127, 'name': 'killed backup', - 'serverRef': "http://localhost/v1.1/servers/42", + 'serverRef': "http://localhost:8774/v1.1/servers/42", 'updated': self.NOW_API_FORMAT, 'created': self.NOW_API_FORMAT, 'status': 'FAILED', @@ -1032,6 +1028,19 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): response = req.get_response(fakes.wsgi_app()) self.assertEqual(200, response.status_int) + def test_create_image_v1_1_actual_serverRef(self): + + serverRef = 'http://localhost:8774/v1.1/servers/1' + body = dict(image=dict(serverRef=serverRef, name='Backup 1')) + req = webob.Request.blank('/v1.1/images') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + response = req.get_response(fakes.wsgi_app()) + self.assertEqual(200, response.status_int) + result = json.loads(response.body) + self.assertEqual(result['image']['serverRef'], serverRef) + def test_create_image_v1_1_xml_serialization(self): body = dict(image=dict(serverRef='123', name='Backup 1')) @@ -1048,7 +1057,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): Date: Fri, 17 Jun 2011 14:35:10 -0400 Subject: adding check for serverRef hostname matching app url --- nova/api/openstack/images.py | 16 +++++++++++----- nova/tests/api/openstack/test_images.py | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 4a09060c9..d43340e10 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -101,7 +101,7 @@ class Controller(object): raise webob.exc.HTTPBadRequest() try: - server_id = self._server_id_from_req_data(body) + server_id = self._server_id_from_req(req, body) image_name = body["image"]["name"] except KeyError: raise webob.exc.HTTPBadRequest() @@ -116,7 +116,7 @@ class Controller(object): """Indicates that you must use a Controller subclass.""" raise NotImplementedError - def _server_id_from_req_data(self, data): + def _server_id_from_req(self, req, data): raise NotImplementedError() def _get_extra_properties(self, req, data): @@ -157,7 +157,7 @@ class ControllerV10(Controller): builder = self.get_builder(req).build return dict(images=[builder(image, detail=True) for image in images]) - def _server_id_from_req_data(self, data): + def _server_id_from_req(self, req, data): try: return data['image']['serverId'] except KeyError: @@ -201,14 +201,20 @@ class ControllerV11(Controller): builder = self.get_builder(req).build return dict(images=[builder(image, detail=True) for image in images]) - def _server_id_from_req_data(self, data): + def _server_id_from_req(self, req, data): try: server_ref = data['image']['serverRef'] except KeyError: msg = _("Expected serverRef attribute on server entity.") raise webob.exc.HTTPBadRequest(explanation=msg) - return os.path.split(server_ref)[1] + head, tail = os.path.split(server_ref) + + if head and head != os.path.join(req.application_url, 'servers'): + msg = _("serverRef must match request url") + raise webob.exc.HTTPBadRequest(explanation=msg) + + return tail def _get_extra_properties(self, req, data): server_ref = data['image']['serverRef'] diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 06983893a..deef5d235 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -1028,9 +1028,9 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): response = req.get_response(fakes.wsgi_app()) self.assertEqual(200, response.status_int) - def test_create_image_v1_1_actual_serverRef(self): + def test_create_image_v1_1_actual_server_ref(self): - serverRef = 'http://localhost:8774/v1.1/servers/1' + serverRef = 'http://localhost/v1.1/servers/1' body = dict(image=dict(serverRef=serverRef, name='Backup 1')) req = webob.Request.blank('/v1.1/images') req.method = 'POST' @@ -1041,6 +1041,17 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): result = json.loads(response.body) self.assertEqual(result['image']['serverRef'], serverRef) + def test_create_image_v1_1_server_ref_bad_hostname(self): + + serverRef = 'http://asdf/v1.1/servers/1' + body = dict(image=dict(serverRef=serverRef, name='Backup 1')) + req = webob.Request.blank('/v1.1/images') + req.method = 'POST' + req.body = json.dumps(body) + req.headers["content-type"] = "application/json" + response = req.get_response(fakes.wsgi_app()) + self.assertEqual(400, response.status_int) + def test_create_image_v1_1_xml_serialization(self): body = dict(image=dict(serverRef='123', name='Backup 1')) -- cgit From 260af1f3edb7993a6b6374563ef2b46c7fa700df Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Mon, 20 Jun 2011 15:55:02 +0100 Subject: Enclosing tokens for xenapi filter in double quotes --- nova/network/xenapi_net.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py index 709ef7f34..7976a7265 100644 --- a/nova/network/xenapi_net.py +++ b/nova/network/xenapi_net.py @@ -56,8 +56,10 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): 'other_config': {}} network_ref = session.call_xenapi('network.create', network_rec) # 2 - find PIF for VLAN - expr = "field 'device' = '%s' and \ - field 'VLAN' = '-1'" % FLAGS.vlan_interface + # Note using double quotes inside single quotes as xapi filter + # only support tokens in double quotes + expr = 'field "device" = "%s" and \ + field "VLAN" = "-1"' % FLAGS.vlan_interface pifs = session.call_xenapi('PIF.get_all_records_where', expr) pif_ref = None # Multiple PIF are ok: we are dealing with a pool -- cgit From 35e922a2db9b45314108b35e438e9229bea4b977 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Tue, 21 Jun 2011 11:39:55 +0100 Subject: fix comment line --- nova/network/xenapi_net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py index 7976a7265..af295a4f8 100644 --- a/nova/network/xenapi_net.py +++ b/nova/network/xenapi_net.py @@ -56,8 +56,8 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None): 'other_config': {}} network_ref = session.call_xenapi('network.create', network_rec) # 2 - find PIF for VLAN - # Note using double quotes inside single quotes as xapi filter - # only support tokens in double quotes + # NOTE(salvatore-orlando): using double quotes inside single quotes + # as xapi filter only support tokens in double quotes expr = 'field "device" = "%s" and \ field "VLAN" = "-1"' % FLAGS.vlan_interface pifs = session.call_xenapi('PIF.get_all_records_where', expr) -- cgit From 0e2b3e932d3e5fe00fed1da95e55808391d4832e Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Tue, 21 Jun 2011 16:55:45 +0400 Subject: Filter out datetime fields from instance_type --- nova/db/sqlalchemy/api.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'nova') diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 7119f43eb..5dc2b9e7a 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2510,7 +2510,10 @@ def instance_type_get_by_id(context, id): if not inst_type: raise exception.InstanceTypeNotFound(instance_type=id) else: - return dict(inst_type) + res = dict(inst_type) + for field in ['created_at', 'updated_at', 'deleted_at']: + res.pop(field, None) + return res @require_context @@ -2523,7 +2526,10 @@ def instance_type_get_by_name(context, name): if not inst_type: raise exception.InstanceTypeNotFoundByName(instance_type_name=name) else: - return dict(inst_type) + res = dict(inst_type) + for field in ['created_at', 'updated_at', 'deleted_at']: + res.pop(field, None) + return res @require_context @@ -2536,7 +2542,10 @@ def instance_type_get_by_flavor_id(context, id): if not inst_type: raise exception.FlavorNotFound(flavor_id=id) else: - return dict(inst_type) + res = dict(inst_type) + for field in ['created_at', 'updated_at', 'deleted_at']: + res.pop(field, None) + return res @require_admin_context -- cgit From 92b0307879ae0142bc6e0ad78c7c0bbc009c0884 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 22 Jun 2011 15:11:23 -0400 Subject: updating glance image fixtures with checksum attribute; fixing glance image service to use checksum attribute --- nova/image/glance.py | 2 +- nova/tests/api/openstack/test_image_metadata.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/image/glance.py b/nova/image/glance.py index 6e058ab2f..55d948a32 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -59,7 +59,7 @@ class GlanceImageService(service.BaseImageService): """Provides storage and retrieval of disk image objects within Glance.""" GLANCE_ONLY_ATTRS = ['size', 'location', 'disk_format', - 'container_format'] + 'container_format', 'checksum'] # NOTE(sirp): Overriding to use _translate_to_service provided by # BaseImageService diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index 56be0f1cc..b55306efd 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -37,6 +37,7 @@ class ImageMetaDataTest(unittest.TestCase): 'name': 'image1', 'deleted': False, 'container_format': None, + 'checksum': None, 'created_at': '2011-03-22T17:40:15', 'disk_format': None, 'updated_at': '2011-03-22T17:40:15', @@ -52,6 +53,7 @@ class ImageMetaDataTest(unittest.TestCase): 'name': 'image2', 'deleted': False, 'container_format': None, + 'checksum': None, 'created_at': '2011-03-22T17:40:15', 'disk_format': None, 'updated_at': '2011-03-22T17:40:15', @@ -67,6 +69,7 @@ class ImageMetaDataTest(unittest.TestCase): 'name': 'image3', 'deleted': False, 'container_format': None, + 'checksum': None, 'created_at': '2011-03-22T17:40:15', 'disk_format': None, 'updated_at': '2011-03-22T17:40:15', @@ -103,7 +106,10 @@ class ImageMetaDataTest(unittest.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(200, res.status_int) - self.assertEqual('value1', res_dict['metadata']['key1']) + expected = self.IMAGE_FIXTURES[0]['properties'] + self.assertEqual(len(expected), len(res_dict['metadata'])) + for (key, value) in res_dict['metadata'].items(): + self.assertEqual(value, res_dict['metadata'][key]) def test_show(self): req = webob.Request.blank('/v1.1/images/1/meta/key1') -- cgit From 7398819cc00a078a486b4d2f11846ff32db19a88 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 22 Jun 2011 17:07:26 -0400 Subject: moving image show/update into 'meta' container --- nova/api/openstack/image_metadata.py | 15 ++++++++++---- nova/tests/api/openstack/test_image_metadata.py | 26 ++++++++++++++++++------- 2 files changed, 30 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index ebfe2bde9..07f0fb4fb 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -59,7 +59,7 @@ class Controller(object): context = req.environ['nova.context'] metadata = self._get_metadata(context, image_id) if id in metadata: - return {id: metadata[id]} + return {'meta': {id: metadata[id]}} else: return faults.Fault(exc.HTTPNotFound()) @@ -77,15 +77,22 @@ class Controller(object): def update(self, req, image_id, id, body): context = req.environ['nova.context'] - if not id in body: + + try: + meta = body['meta'] + except KeyError: + expl = _('Incorrect request body format') + raise exc.HTTPBadRequest(explanation=expl) + + if not id in meta: expl = _('Request body and URI mismatch') raise exc.HTTPBadRequest(explanation=expl) - if len(body) > 1: + if len(meta) > 1: expl = _('Request body contains too many items') raise exc.HTTPBadRequest(explanation=expl) img = self.image_service.show(context, image_id) metadata = self._get_metadata(context, image_id, img) - metadata[id] = body[id] + metadata[id] = meta[id] self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index 56be0f1cc..0faf9a26e 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -111,13 +111,14 @@ class ImageMetaDataTest(unittest.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(200, res.status_int) - self.assertEqual('value1', res_dict['key1']) + self.assertTrue('meta' in res_dict) + self.assertEqual(len(res_dict['meta']), 1) + self.assertEqual('value1', res_dict['meta']['key1']) def test_show_not_found(self): req = webob.Request.blank('/v1.1/images/1/meta/key9') req.environ['api.version'] = '1.1' res = req.get_response(fakes.wsgi_app()) - res_dict = json.loads(res.body) self.assertEqual(404, res.status_int) def test_create(self): @@ -139,18 +140,29 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/1/meta/key1') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"key1": "zz"}' + req.body = '{"meta": {"key1": "zz"}}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(200, res.status_int) res_dict = json.loads(res.body) - self.assertEqual('zz', res_dict['key1']) + self.assertTrue('meta' in res_dict) + self.assertEqual(len(res_dict['meta']), 1) + self.assertEqual('zz', res_dict['meta']['key1']) + + def test_update_item_bad_body(self): + req = webob.Request.blank('/v1.1/images/1/meta/key1') + req.environ['api.version'] = '1.1' + req.method = 'PUT' + req.body = '{"key1": "zz"}' + req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(400, res.status_int) def test_update_item_too_many_keys(self): req = webob.Request.blank('/v1.1/images/1/meta/key1') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"key1": "value1", "key2": "value2"}' + req.body = '{"meta": {"key1": "value1", "key2": "value2"}}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) @@ -159,7 +171,7 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/1/meta/bad') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"key1": "value1"}' + req.body = '{"meta": {"key1": "value1"}}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) @@ -195,7 +207,7 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/3/meta/blah') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"blah": "blah"}' + req.body = '{"meta": {"blah": "blah"}}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) -- cgit From 1f9cd3e7c97034408b5afe3fc3720c48040dea97 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 22 Jun 2011 17:14:58 -0400 Subject: reverting non-xml changes --- nova/api/openstack/image_metadata.py | 8 ++++---- nova/tests/api/openstack/test_image_metadata.py | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index 77028be28..399cd2637 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -60,7 +60,7 @@ class Controller(object): context = req.environ['nova.context'] metadata = self._get_metadata(context, image_id) if id in metadata: - return {'meta': {id: metadata[id]}} + return {id: metadata[id]} else: return faults.Fault(exc.HTTPNotFound()) @@ -78,15 +78,15 @@ class Controller(object): def update(self, req, image_id, id, body): context = req.environ['nova.context'] - if not id in body['meta']: + if not id in body: expl = _('Request body and URI mismatch') raise exc.HTTPBadRequest(explanation=expl) - if len(body['meta']) > 1: + if len(body) > 1: expl = _('Request body contains too many items') raise exc.HTTPBadRequest(explanation=expl) img = self.image_service.show(context, image_id) metadata = self._get_metadata(context, image_id, img) - metadata[id] = body['meta'][id] + metadata[id] = body[id] self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index bc1903ca5..e77ff79b9 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -136,7 +136,7 @@ class ImageMetaDataTest(unittest.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) self.assertEqual(200, res.status_int) - self.assertEqual('value1', res_dict['meta']['key1']) + self.assertEqual('value1', res_dict['key1']) def test_show_xml(self): serializer = openstack.image_metadata.ImageMetadataXMLSerializer() @@ -210,13 +210,12 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/1/meta/key1') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"meta": {"key1": "zz"}}' + req.body = '{"key1": "zz"}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) - print res.body self.assertEqual(200, res.status_int) res_dict = json.loads(res.body) - self.assertEqual('zz', res_dict['meta']['key1']) + self.assertEqual('zz', res_dict['key1']) def test_update_item_xml(self): serializer = openstack.image_metadata.ImageMetadataXMLSerializer() @@ -241,7 +240,7 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/1/meta/key1') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"meta": {"key1": "value1", "key2": "value2"}}' + req.body = '{"key1": "value1", "key2": "value2"}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) @@ -250,7 +249,7 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/1/meta/bad') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"meta": {"key1": "value1"}}' + req.body = '{"key1": "value1"}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) @@ -286,7 +285,7 @@ class ImageMetaDataTest(unittest.TestCase): req = webob.Request.blank('/v1.1/images/3/meta/blah') req.environ['api.version'] = '1.1' req.method = 'PUT' - req.body = '{"meta": {"blah": "blah"}}' + req.body = '{"blah": "blah"}' req.headers["content-type"] = "application/json" res = req.get_response(fakes.wsgi_app()) self.assertEqual(400, res.status_int) -- cgit From 548ac151cd1c7de5249fdeb651895917e83df488 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 22 Jun 2011 18:22:57 -0400 Subject: pep8 fixes --- nova/api/openstack/image_metadata.py | 1 - nova/tests/api/openstack/test_image_metadata.py | 1 - 2 files changed, 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index 399cd2637..691264553 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -150,7 +150,6 @@ class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer): return self._meta_item_to_xml_string(meta_item_dict['meta']) - def create_resource(): serializers = { 'application/xml': ImageMetadataXMLSerializer(), diff --git a/nova/tests/api/openstack/test_image_metadata.py b/nova/tests/api/openstack/test_image_metadata.py index e77ff79b9..9495eadec 100644 --- a/nova/tests/api/openstack/test_image_metadata.py +++ b/nova/tests/api/openstack/test_image_metadata.py @@ -235,7 +235,6 @@ class ImageMetaDataTest(unittest.TestCase): self.assertEqual(expected.toxml(), actual.toxml()) - def test_update_item_too_many_keys(self): req = webob.Request.blank('/v1.1/images/1/meta/key1') req.environ['api.version'] = '1.1' -- cgit From c2a0f15457ec49e95de0a2e7cd6c8b60e81a4994 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Thu, 23 Jun 2011 09:57:22 -0400 Subject: Fixed typo --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/network/manager.py b/nova/network/manager.py index 38bcbe0e5..bf0456522 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -328,7 +328,7 @@ class NetworkManager(manager.SchedulerDependentManager): if gateway_v6: # use a pre-defined gateway if one is provided - net['gateway_v6'] = str(list(gateway_v6)[1])) + net['gateway_v6'] = str(list(gateway_v6)[1]) else: net['gateway_v6'] = str(list(project_net_v6)[1]) -- cgit From 02085b96528b66b322f5c1ce5281d0284f9cbe40 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 23 Jun 2011 13:28:13 -0400 Subject: Move migration to newer version. --- .../versions/023_add_provider_firewall_rules.py | 75 ---------------------- .../versions/027_add_provider_firewall_rules.py | 75 ++++++++++++++++++++++ 2 files changed, 75 insertions(+), 75 deletions(-) delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/023_add_provider_firewall_rules.py create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py (limited to 'nova') diff --git a/nova/db/sqlalchemy/migrate_repo/versions/023_add_provider_firewall_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/023_add_provider_firewall_rules.py deleted file mode 100644 index 5aa30f7a8..000000000 --- a/nova/db/sqlalchemy/migrate_repo/versions/023_add_provider_firewall_rules.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from sqlalchemy import * -from migrate import * - -from nova import log as logging - - -meta = MetaData() - - -# Just for the ForeignKey and column creation to succeed, these are not the -# actual definitions of instances or services. -instances = Table('instances', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -services = Table('services', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -networks = Table('networks', meta, - Column('id', Integer(), primary_key=True, nullable=False), - ) - - -# -# New Tables -# -provider_fw_rules = Table('provider_fw_rules', meta, - Column('created_at', DateTime(timezone=False)), - Column('updated_at', DateTime(timezone=False)), - Column('deleted_at', DateTime(timezone=False)), - Column('deleted', Boolean(create_constraint=True, name=None)), - Column('id', Integer(), primary_key=True, nullable=False), - Column('protocol', - String(length=5, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)), - Column('from_port', Integer()), - Column('to_port', Integer()), - Column('cidr', - String(length=255, convert_unicode=False, assert_unicode=None, - unicode_error=None, _warn_on_bytestring=False)) - ) - - -def upgrade(migrate_engine): - # Upgrade operations go here. Don't create your own engine; - # bind migrate_engine to your metadata - meta.bind = migrate_engine - for table in (provider_fw_rules,): - try: - table.create() - except Exception: - logging.info(repr(table)) - logging.exception('Exception while creating table') - raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py b/nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py new file mode 100644 index 000000000..5aa30f7a8 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/027_add_provider_firewall_rules.py @@ -0,0 +1,75 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from sqlalchemy import * +from migrate import * + +from nova import log as logging + + +meta = MetaData() + + +# Just for the ForeignKey and column creation to succeed, these are not the +# actual definitions of instances or services. +instances = Table('instances', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +services = Table('services', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +networks = Table('networks', meta, + Column('id', Integer(), primary_key=True, nullable=False), + ) + + +# +# New Tables +# +provider_fw_rules = Table('provider_fw_rules', meta, + Column('created_at', DateTime(timezone=False)), + Column('updated_at', DateTime(timezone=False)), + Column('deleted_at', DateTime(timezone=False)), + Column('deleted', Boolean(create_constraint=True, name=None)), + Column('id', Integer(), primary_key=True, nullable=False), + Column('protocol', + String(length=5, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)), + Column('from_port', Integer()), + Column('to_port', Integer()), + Column('cidr', + String(length=255, convert_unicode=False, assert_unicode=None, + unicode_error=None, _warn_on_bytestring=False)) + ) + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; + # bind migrate_engine to your metadata + meta.bind = migrate_engine + for table in (provider_fw_rules,): + try: + table.create() + except Exception: + logging.info(repr(table)) + logging.exception('Exception while creating table') + raise -- cgit From 1a3fb4332401e5fb3b5b090034ecf4fdf47246cf Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 23 Jun 2011 13:54:45 -0400 Subject: Add admin api test case (like cloud test case) with a test for fw rules. --- nova/tests/test_adminapi.py | 90 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 nova/tests/test_adminapi.py (limited to 'nova') diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py new file mode 100644 index 000000000..70a00f999 --- /dev/null +++ b/nova/tests/test_adminapi.py @@ -0,0 +1,90 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from eventlet import greenthread + +from nova import context +from nova import db +from nova import flags +from nova import log as logging +from nova import rpc +from nova import test +from nova import utils +from nova.auth import manager +from nova.api.ec2 import admin +from nova.image import fake + + +FLAGS = flags.FLAGS +LOG = logging.getLogger('nova.tests.adminapi') + + +class AdminApiTestCase(test.TestCase): + def setUp(self): + super(AdminApiTestCase, self).setUp() + self.flags(connection_type='fake') + + self.conn = rpc.Connection.instance() + + # set up our cloud + self.api = admin.AdminController() + + # set up services + self.compute = self.start_service('compute') + self.scheduter = self.start_service('scheduler') + self.network = self.start_service('network') + self.volume = self.start_service('volume') + self.image_service = utils.import_object(FLAGS.image_service) + + self.manager = manager.AuthManager() + self.user = self.manager.create_user('admin', 'admin', 'admin', True) + self.project = self.manager.create_project('proj', 'admin', 'proj') + self.context = context.RequestContext(user=self.user, + project=self.project) + host = self.network.get_network_host(self.context.elevated()) + + def fake_show(meh, context, id): + return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + 'type': 'machine', 'image_state': 'available'}} + + self.stubs.Set(fake._FakeImageService, 'show', fake_show) + self.stubs.Set(fake._FakeImageService, 'show_by_name', fake_show) + + # NOTE(vish): set up a manual wait so rpc.cast has a chance to finish + rpc_cast = rpc.cast + + def finish_cast(*args, **kwargs): + rpc_cast(*args, **kwargs) + greenthread.sleep(0.2) + + self.stubs.Set(rpc, 'cast', finish_cast) + + def tearDown(self): + network_ref = db.project_get_network(self.context, + self.project.id) + db.network_disassociate(self.context, network_ref['id']) + self.manager.delete_project(self.project) + self.manager.delete_user(self.user) + super(AdminApiTestCase, self).tearDown() + + def test_block_external_ips(self): + """Make sure provider firewall rules are created.""" + result = self.api.block_external_addresses(self.context, '1.1.1.1/32') + self.assertEqual('OK', result['status']) + self.assertEqual('Added 3 rules', result['message']) + -- cgit From 203f3f85b6d66735f52013cbe5a736ef82d7a083 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 23 Jun 2011 13:59:26 -0400 Subject: pep8: remove newline at end of file. --- nova/tests/test_adminapi.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py index 70a00f999..7ecaf1c09 100644 --- a/nova/tests/test_adminapi.py +++ b/nova/tests/test_adminapi.py @@ -87,4 +87,3 @@ class AdminApiTestCase(test.TestCase): result = self.api.block_external_addresses(self.context, '1.1.1.1/32') self.assertEqual('OK', result['status']) self.assertEqual('Added 3 rules', result['message']) - -- cgit From ac4baa5990c45a6a521a1786e680426ba617c65a Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 23 Jun 2011 14:13:27 -0400 Subject: Add test for listing provider firewall rules. --- nova/tests/test_adminapi.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py index 7ecaf1c09..2b90d49e9 100644 --- a/nova/tests/test_adminapi.py +++ b/nova/tests/test_adminapi.py @@ -87,3 +87,10 @@ class AdminApiTestCase(test.TestCase): result = self.api.block_external_addresses(self.context, '1.1.1.1/32') self.assertEqual('OK', result['status']) self.assertEqual('Added 3 rules', result['message']) + + def test_list_blocked_ips(self): + """Make sure we can see the external blocks that exist.""" + result = self.api.describe_external_address_blocks(self.context) + num = len(db.provider_fw_rule_get_all(self.context)) + # we only list IP, not tcp/udp/icmp rules + self.assertEqual(num / 3, len(result['externalIpBlockInfo'])) -- cgit From 6e2ebfa1dc29e50f74f1b337d1b5349bc3c78cdc Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 23 Jun 2011 14:16:11 -0400 Subject: Make sure there are actually rules to test against. --- nova/tests/test_adminapi.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova') diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py index 2b90d49e9..4a96a3dd9 100644 --- a/nova/tests/test_adminapi.py +++ b/nova/tests/test_adminapi.py @@ -90,6 +90,7 @@ class AdminApiTestCase(test.TestCase): def test_list_blocked_ips(self): """Make sure we can see the external blocks that exist.""" + self.api.block_external_addresses(self.context, '1.1.1.2/32') result = self.api.describe_external_address_blocks(self.context) num = len(db.provider_fw_rule_get_all(self.context)) # we only list IP, not tcp/udp/icmp rules -- cgit From 9a6e9a1af9359fb4a9261f59f57113f252f0d6e9 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 23 Jun 2011 14:45:37 -0400 Subject: Make firewall rules tests idempotent, move IPy=>netaddr, add deltete test. --- nova/api/ec2/admin.py | 2 +- nova/tests/test_adminapi.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index b8fc8f114..df7876b9d 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -382,7 +382,7 @@ class AdminController(object): LOG.audit(_('Removing ip block from %s'), cidr, context=context) cidr = urllib.unquote(cidr).decode() # raise if invalid - IPy.IP(cidr) + netaddr.IPNetwork(cidr) rules = db.provider_fw_rule_get_all_by_cidr(context, cidr) for rule in rules: db.provider_fw_rule_destroy(context, rule['id']) diff --git a/nova/tests/test_adminapi.py b/nova/tests/test_adminapi.py index 4a96a3dd9..ce826fd5b 100644 --- a/nova/tests/test_adminapi.py +++ b/nova/tests/test_adminapi.py @@ -85,6 +85,7 @@ class AdminApiTestCase(test.TestCase): def test_block_external_ips(self): """Make sure provider firewall rules are created.""" result = self.api.block_external_addresses(self.context, '1.1.1.1/32') + self.api.remove_external_address_block(self.context, '1.1.1.1/32') self.assertEqual('OK', result['status']) self.assertEqual('Added 3 rules', result['message']) @@ -93,5 +94,18 @@ class AdminApiTestCase(test.TestCase): self.api.block_external_addresses(self.context, '1.1.1.2/32') result = self.api.describe_external_address_blocks(self.context) num = len(db.provider_fw_rule_get_all(self.context)) + self.api.remove_external_address_block(self.context, '1.1.1.2/32') # we only list IP, not tcp/udp/icmp rules self.assertEqual(num / 3, len(result['externalIpBlockInfo'])) + + def test_remove_ip_block(self): + """Remove ip blocks.""" + result = self.api.block_external_addresses(self.context, '1.1.1.3/32') + self.assertEqual('OK', result['status']) + num0 = len(db.provider_fw_rule_get_all(self.context)) + result = self.api.remove_external_address_block(self.context, + '1.1.1.3/32') + self.assertEqual('OK', result['status']) + self.assertEqual('Deleted 3 rules', result['message']) + num1 = len(db.provider_fw_rule_get_all(self.context)) + self.assert_(num1 < num0) -- cgit From 51d93c5b1722bef9783cd7572c1464a084ece0aa Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 23 Jun 2011 14:52:58 -0400 Subject: libvirt test for deleting provider firewall rules. --- nova/tests/test_libvirt.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'nova') diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index ee94d3c17..d12e21063 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -1115,6 +1115,13 @@ class IptablesFirewallTestCase(test.TestCase): provjump_rules.append(rule) self.assertEqual(1, len(provjump_rules)) + # remove a rule from the db, cast to compute to refresh rule + db.provider_fw_rule_destroy(admin_ctxt, provider_fw1['id']) + self.fw.refresh_provider_fw_rules() + rules = [rule for rule in self.fw.iptables.ipv4['filter'].rules + if rule.chain == 'provider'] + self.assertEqual(1, len(rules)) + class NWFilterTestCase(test.TestCase): def setUp(self): -- cgit From d4fc1d77a4b7c668453042b83e34da76ee3c3818 Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Fri, 24 Jun 2011 09:54:38 +0400 Subject: Unwind last commit, force anyjson to use our serialization methods. --- nova/compute/api.py | 1 + nova/db/sqlalchemy/api.py | 15 +++------------ nova/utils.py | 9 +++++++++ 3 files changed, 13 insertions(+), 12 deletions(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index f1c31a092..9a77a2aa2 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -291,6 +291,7 @@ class API(base.Base): 'blob': zone_blob, 'num_instances': num_instances, } + LOG.debug(request_spec) rpc.cast(context, FLAGS.scheduler_topic, diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 5dc2b9e7a..7119f43eb 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2510,10 +2510,7 @@ def instance_type_get_by_id(context, id): if not inst_type: raise exception.InstanceTypeNotFound(instance_type=id) else: - res = dict(inst_type) - for field in ['created_at', 'updated_at', 'deleted_at']: - res.pop(field, None) - return res + return dict(inst_type) @require_context @@ -2526,10 +2523,7 @@ def instance_type_get_by_name(context, name): if not inst_type: raise exception.InstanceTypeNotFoundByName(instance_type_name=name) else: - res = dict(inst_type) - for field in ['created_at', 'updated_at', 'deleted_at']: - res.pop(field, None) - return res + return dict(inst_type) @require_context @@ -2542,10 +2536,7 @@ def instance_type_get_by_flavor_id(context, id): if not inst_type: raise exception.FlavorNotFound(flavor_id=id) else: - res = dict(inst_type) - for field in ['created_at', 'updated_at', 'deleted_at']: - res.pop(field, None) - return res + return dict(inst_type) @require_admin_context diff --git a/nova/utils.py b/nova/utils.py index 691134ada..a77cf7993 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -525,6 +525,15 @@ def loads(s): return json.loads(s) +try: + import anyjson +except ImportError: + pass +else: + anyjson._modules.append(("nova.utils", "dumps", TypeError, "loads", ValueError)) + anyjson.force_implementation("nova.utils") + + _semaphores = {} -- cgit From a1c5726a9e0095de88c9d10c09999b2dcdb6211e Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Fri, 24 Jun 2011 16:07:33 +0400 Subject: Remove extra debug line. --- nova/compute/api.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova') diff --git a/nova/compute/api.py b/nova/compute/api.py index b570eb986..af18741b6 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -325,7 +325,6 @@ class API(base.Base): 'blob': zone_blob, 'num_instances': num_instances, } - LOG.debug(request_spec) rpc.cast(context, FLAGS.scheduler_topic, -- cgit From e253cd3cf01d29106daff1592a7c629307b449ff Mon Sep 17 00:00:00 2001 From: Yuriy Taraday Date: Sat, 25 Jun 2011 14:04:40 +0400 Subject: PEP8 fix --- nova/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova') diff --git a/nova/utils.py b/nova/utils.py index 8f9ca42c1..6d8324e5b 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -531,7 +531,8 @@ try: except ImportError: pass else: - anyjson._modules.append(("nova.utils", "dumps", TypeError, "loads", ValueError)) + anyjson._modules.append(("nova.utils", "dumps", TypeError, + "loads", ValueError)) anyjson.force_implementation("nova.utils") -- cgit