From c5c58cb20def79401a374f863983a343139b53f3 Mon Sep 17 00:00:00 2001 From: "NTT PF Lab." Date: Fri, 24 Dec 2010 20:38:49 +0900 Subject: Support IPv6 --- bin/nova-manage | 12 ++- contrib/boto_v6/__init__.py | 37 +++++++ contrib/boto_v6/ec2/__init__.py | 0 contrib/boto_v6/ec2/connection.py | 41 ++++++++ contrib/boto_v6/ec2/instance.py | 33 ++++++ contrib/nova.sh | 6 ++ nova/api/ec2/cloud.py | 9 +- nova/db/api.py | 9 ++ nova/db/sqlalchemy/api.py | 23 ++++ nova/db/sqlalchemy/models.py | 4 + nova/network/linux_net.py | 88 ++++++++++++++++ nova/network/manager.py | 30 +++++- nova/test.py | 3 +- nova/tests/api_unittest.py | 67 ++++++++++++ nova/tests/network_unittest.py | 21 ++++ nova/utils.py | 39 +++++++ nova/virt/libvirt.qemu.xml.template | 1 + nova/virt/libvirt.uml.xml.template | 1 + nova/virt/libvirt_conn.py | 68 ++++++++++-- smoketests/admin_smoketests.py | 3 +- smoketests/base.py | 17 ++- smoketests/flags.py | 3 +- smoketests/public_network_smoketests.py | 180 ++++++++++++++++++++++++++++++++ smoketests/user_smoketests.py | 39 +++++-- 24 files changed, 702 insertions(+), 32 deletions(-) create mode 100644 contrib/boto_v6/__init__.py create mode 100644 contrib/boto_v6/ec2/__init__.py create mode 100644 contrib/boto_v6/ec2/connection.py create mode 100644 contrib/boto_v6/ec2/instance.py create mode 100644 smoketests/public_network_smoketests.py diff --git a/bin/nova-manage b/bin/nova-manage index 0c1b621ed..e0f6f9323 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -87,7 +87,7 @@ flags.DECLARE('num_networks', 'nova.network.manager') flags.DECLARE('network_size', 'nova.network.manager') flags.DECLARE('vlan_start', 'nova.network.manager') flags.DECLARE('vpn_start', 'nova.network.manager') - +flags.DECLARE('fixed_range_v6', 'nova.network.manager') class VpnCommands(object): """Class for managing VPNs.""" @@ -411,11 +411,11 @@ class NetworkCommands(object): """Class for managing networks.""" def create(self, fixed_range=None, num_networks=None, - network_size=None, vlan_start=None, vpn_start=None): + network_size=None, vlan_start=None, vpn_start=None,fixed_range_v6=None): """Creates fixed ips for host by range arguments: [fixed_range=FLAG], [num_networks=FLAG], [network_size=FLAG], [vlan_start=FLAG], - [vpn_start=FLAG]""" + [vpn_start=FLAG],[fixed_range_v6=FLAG]""" if not fixed_range: fixed_range = FLAGS.fixed_range if not num_networks: @@ -426,11 +426,15 @@ class NetworkCommands(object): vlan_start = FLAGS.vlan_start if not vpn_start: vpn_start = FLAGS.vpn_start + if not fixed_range_v6: + fixed_range_v6 = FLAGS.fixed_range_v6 net_manager = utils.import_object(FLAGS.network_manager) net_manager.create_networks(context.get_admin_context(), fixed_range, int(num_networks), int(network_size), int(vlan_start), - int(vpn_start)) + int(vpn_start),fixed_range_v6) + + CATEGORIES = [ ('user', UserCommands), diff --git a/contrib/boto_v6/__init__.py b/contrib/boto_v6/__init__.py new file mode 100644 index 000000000..9fec157f1 --- /dev/null +++ b/contrib/boto_v6/__init__.py @@ -0,0 +1,37 @@ +# Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ +# Copyright (c) 2010, Eucalyptus Systems, Inc. +# All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, dis- +# tribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the fol- +# lowing conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- +# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + + +def connect_ec2(aws_access_key_id=None, aws_secret_access_key=None, **kwargs): + """ + :type aws_access_key_id: string + :param aws_access_key_id: Your AWS Access Key ID + + :type aws_secret_access_key: string + :param aws_secret_access_key: Your AWS Secret Access Key + + :rtype: :class:`boto.ec2.connection.EC2Connection` + :return: A connection to Amazon's EC2 + """ + from boto_v6.ec2.connection import EC2ConnectionV6 + return EC2ConnectionV6(aws_access_key_id, aws_secret_access_key, **kwargs) diff --git a/contrib/boto_v6/ec2/__init__.py b/contrib/boto_v6/ec2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/boto_v6/ec2/connection.py b/contrib/boto_v6/ec2/connection.py new file mode 100644 index 000000000..151b76a55 --- /dev/null +++ b/contrib/boto_v6/ec2/connection.py @@ -0,0 +1,41 @@ +''' +Created on 2010/12/20 + +@author: Nachi Ueno +''' +import boto +import boto.ec2 +from boto_v6.ec2.instance import ReservationV6 + + +class EC2ConnectionV6(boto.ec2.EC2Connection): + ''' + EC2Connection for OpenStack IPV6 mode + ''' + def get_all_instances(self, instance_ids=None, filters=None): + """ + Retrieve all the instances associated with your account. + + :type instance_ids: list + :param instance_ids: A list of strings of instance IDs + + :type filters: dict + :param filters: Optional filters that can be used to limit + the results returned. Filters are provided + in the form of a dictionary consisting of + filter names as the key and filter values + as the value. The set of allowable filter + names/values is dependent on the request + being performed. Check the EC2 API guide + for details. + + :rtype: list + :return: A list of :class:`boto.ec2.instance.Reservation` + """ + params = {} + if instance_ids: + self.build_list_params(params, instance_ids, 'InstanceId') + if filters: + self.build_filter_params(params, filters) + return self.get_list('DescribeInstances', params, + [('item', ReservationV6)]) diff --git a/contrib/boto_v6/ec2/instance.py b/contrib/boto_v6/ec2/instance.py new file mode 100644 index 000000000..255114935 --- /dev/null +++ b/contrib/boto_v6/ec2/instance.py @@ -0,0 +1,33 @@ +''' +Created on 2010/12/20 + +@author: Nachi Ueno +''' +import boto +from boto.resultset import ResultSet +from boto.ec2.instance import Reservation +from boto.ec2.instance import Group +from boto.ec2.instance import Instance + + +class ReservationV6(Reservation): + def startElement(self, name, attrs, connection): + if name == 'instancesSet': + self.instances = ResultSet([('item', InstanceV6)]) + return self.instances + elif name == 'groupSet': + self.groups = ResultSet([('item', Group)]) + return self.groups + else: + return None + + +class InstanceV6(Instance): + def __init__(self, connection=None): + Instance.__init__(self, connection) + self.public_dns_name_v6 = None + + def endElement(self, name, value, connection): + Instance.endElement(self, name, value, connection) + if name == 'dnsNameV6': + self.dns_name_v6 = value diff --git a/contrib/nova.sh b/contrib/nova.sh index 30df4edb6..bc46740ad 100755 --- a/contrib/nova.sh +++ b/contrib/nova.sh @@ -85,6 +85,10 @@ if [ "$CMD" == "install" ]; then sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot sudo apt-get install -y python-daemon python-eventlet python-gflags python-tornado python-ipy sudo apt-get install -y python-libvirt python-libxml2 python-routes +#For IPV6 + sudo apt-get install -y python-netaddr + sudo apt-get install -y radvd + if [ "$USE_MYSQL" == 1 ]; then cat < /proc/sys/net/ipv6/conf/all/forwarding"') + _execute('sudo bash -c ' + + '"echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"') def bind_floating_ip(floating_ip): @@ -158,6 +164,10 @@ def ensure_bridge(bridge, interface, net_attrs=None): net_attrs['gateway'], net_attrs['broadcast'], net_attrs['netmask'])) + if(FLAGS.use_ipv6): + _execute("sudo ifconfig %s add %s up" % \ + (bridge, + net_attrs['cidr_v6'])) else: _execute("sudo ifconfig %s up" % bridge) _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) @@ -213,6 +223,50 @@ def update_dhcp(context, network_id): _execute(command, addl_env=env) +def update_ra(context, network_id): + network_ref = db.network_get(context, network_id) + + conffile = _ra_file(network_ref['bridge'], 'conf') + with open(conffile, 'w') as f: + conf_str = """ +interface %s +{ + AdvSendAdvert on; + MinRtrAdvInterval 3; + MaxRtrAdvInterval 10; + prefix %s + { + AdvOnLink on; + AdvAutonomous on; + }; +}; +""" % (network_ref['bridge'], network_ref['cidr_v6']) + f.write(conf_str) + + # Make sure dnsmasq can actually read it (it setuid()s to "nobody") + os.chmod(conffile, 0644) + + pid = _ra_pid_for(network_ref['bridge']) + + # if dnsmasq is already running, then tell it to reload + if pid: + out, _err = _execute('cat /proc/%d/cmdline' + % pid, check_exit_code=False) + if conffile in out: + try: + _execute('sudo kill -HUP %d' % pid) + return + except Exception as exc: # pylint: disable-msg=W0703 + logging.debug("Hupping radvd threw %s", exc) + else: + logging.debug("Pid %d is stale, relaunching radvd", pid) + command = _ra_cmd(network_ref) + _execute(command) + db.network_update(context, network_id, + {"ra_server": + utils.get_my_linklocal(network_ref['bridge'])}) + + def _host_dhcp(fixed_ip_ref): """Return a host string for an address""" instance_ref = fixed_ip_ref['instance'] @@ -268,6 +322,15 @@ def _dnsmasq_cmd(net): return ''.join(cmd) +def _ra_cmd(net): + """Builds dnsmasq command""" + cmd = ['sudo -E radvd', +# ' -u nobody', + ' -C %s' % _ra_file(net['bridge'], 'conf'), + ' -p %s' % _ra_file(net['bridge'], 'pid')] + return ''.join(cmd) + + def _stop_dnsmasq(network): """Stops the dnsmasq instance for a given network""" pid = _dnsmasq_pid_for(network) @@ -289,6 +352,16 @@ def _dhcp_file(bridge, kind): kind)) +def _ra_file(bridge, kind): + """Return path to a pid, leases or conf file for a bridge""" + + if not os.path.exists(FLAGS.networks_path): + os.makedirs(FLAGS.networks_path) + return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path, + bridge, + kind)) + + def _dnsmasq_pid_for(bridge): """Returns the pid for prior dnsmasq instance for a bridge @@ -302,3 +375,18 @@ def _dnsmasq_pid_for(bridge): if os.path.exists(pid_file): with open(pid_file, 'r') as f: return int(f.read()) + + +def _ra_pid_for(bridge): + """Returns the pid for prior dnsmasq instance for a bridge + + Returns None if no pid file exists + + If machine has rebooted pid might be incorrect (caller should check) + """ + + pid_file = _ra_file(bridge, 'pid') + + if os.path.exists(pid_file): + with open(pid_file, 'r') as f: + return int(f.read()) diff --git a/nova/network/manager.py b/nova/network/manager.py index a7298b47f..ceea6966f 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -80,6 +80,7 @@ flags.DEFINE_integer('network_size', 256, flags.DEFINE_string('floating_range', '4.4.4.0/24', 'Floating IP address block') flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block') +flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block') flags.DEFINE_integer('cnt_vpn_clients', 5, 'Number of addresses reserved for vpn clients') flags.DEFINE_string('network_driver', 'nova.network.linux_net', @@ -88,6 +89,8 @@ flags.DEFINE_bool('update_dhcp_on_disassociate', False, 'Whether to update dhcp when fixed_ip is disassociated') flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600, 'Seconds after which a deallocated ip is disassociated') +flags.DEFINE_bool('use_ipv6', True, + 'use the ipv6') class AddressAlreadyAllocated(exception.Error): @@ -217,7 +220,7 @@ class NetworkManager(manager.Manager): """Get the network for the current context.""" raise NotImplementedError() - def create_networks(self, context, num_networks, network_size, + def create_networks(self, context, num_networks, network_size, cidr_v6, *args, **kwargs): """Create networks based on parameters.""" raise NotImplementedError() @@ -307,9 +310,11 @@ class FlatManager(NetworkManager): pass def create_networks(self, context, cidr, num_networks, network_size, - *args, **kwargs): + cidr_v6, *args, **kwargs): """Create networks based on parameters.""" fixed_net = IPy.IP(cidr) + fixed_net_v6 = IPy.IP(cidr_v6) + significant_bits_v6 = 64 for index in range(num_networks): start = index * network_size significant_bits = 32 - int(math.log(network_size, 2)) @@ -322,7 +327,13 @@ class FlatManager(NetworkManager): net['gateway'] = str(project_net[1]) net['broadcast'] = str(project_net.broadcast()) net['dhcp_start'] = str(project_net[2]) + + if(FLAGS.use_ipv6): + cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6) + net['cidr_v6'] = cidr_v6 + network_ref = self.db.network_create_safe(context, net) + if network_ref: self._create_fixed_ips(context, network_ref['id']) @@ -466,12 +477,16 @@ class VlanManager(NetworkManager): pass def create_networks(self, context, cidr, num_networks, network_size, - vlan_start, vpn_start): + vlan_start, vpn_start, cidr_v6): """Create networks based on parameters.""" fixed_net = IPy.IP(cidr) + fixed_net_v6 = IPy.IP(cidr_v6) + network_size_v6 = 1 << 64 + significant_bits_v6 = 64 for index in range(num_networks): vlan = vlan_start + index start = index * network_size + 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) @@ -484,6 +499,13 @@ class VlanManager(NetworkManager): net['dhcp_start'] = str(project_net[3]) net['vlan'] = vlan net['bridge'] = 'br%s' % vlan + if(FLAGS.use_ipv6): + cidr_v6 = "%s/%s" % ( + fixed_net_v6[start_v6], + significant_bits_v6 + ) + net['cidr_v6'] = cidr_v6 + # NOTE(vish): This makes ports unique accross the cloud, a more # robust solution would be to make them unique per ip net['vpn_public_port'] = vpn_start + index @@ -506,6 +528,8 @@ class VlanManager(NetworkManager): network_ref['bridge'], network_ref) self.driver.update_dhcp(context, network_id) + if(FLAGS.use_ipv6): + self.driver.update_ra(context, network_id) @property def _bottom_reserved_ips(self): diff --git a/nova/test.py b/nova/test.py index 5c2a72819..ee2fc2720 100644 --- a/nova/test.py +++ b/nova/test.py @@ -70,7 +70,8 @@ class TrialTestCase(unittest.TestCase): FLAGS.fixed_range, 5, 16, FLAGS.vlan_start, - FLAGS.vpn_start) + FLAGS.vpn_start, + FLAGS.fixed_range_v6) # emulate some of the mox stuff, we can't use the metaclass # because it screws with our generators diff --git a/nova/tests/api_unittest.py b/nova/tests/api_unittest.py index 33d4cb294..a508235c4 100644 --- a/nova/tests/api_unittest.py +++ b/nova/tests/api_unittest.py @@ -24,6 +24,7 @@ import httplib import random import StringIO import webob +import logging from nova import context from nova import flags @@ -265,6 +266,72 @@ class ApiEc2TestCase(test.TrialTestCase): return + def test_authorize_revoke_security_group_cidr_v6(self): + """ + Test that we can add and remove CIDR based rules + to a security group for IPv6 + """ + self.expect_http() + self.mox.ReplayAll() + user = self.manager.create_user('fake', 'fake', 'fake') + project = self.manager.create_project('fake', 'fake', 'fake') + + # At the moment, you need both of these to actually be netadmin + self.manager.add_role('fake', 'netadmin') + project.add_role('fake', 'netadmin') + + security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd") + for x in range(random.randint(4, 8))) + + group = self.ec2.create_security_group(security_group_name, + 'test group') + + self.expect_http() + self.mox.ReplayAll() + group.connection = self.ec2 + + group.authorize('tcp', 80, 81, '::/0') + + self.expect_http() + self.mox.ReplayAll() + + rv = self.ec2.get_all_security_groups() + # I don't bother checkng that we actually find it here, + # because the create/delete unit test further up should + # be good enough for that. + for group in rv: + if group.name == security_group_name: + self.assertEquals(len(group.rules), 1) + self.assertEquals(int(group.rules[0].from_port), 80) + self.assertEquals(int(group.rules[0].to_port), 81) + self.assertEquals(len(group.rules[0].grants), 1) + self.assertEquals(str(group.rules[0].grants[0]), '::/0') + + self.expect_http() + self.mox.ReplayAll() + group.connection = self.ec2 + + group.revoke('tcp', 80, 81, '::/0') + + self.expect_http() + self.mox.ReplayAll() + + self.ec2.delete_security_group(security_group_name) + + self.expect_http() + self.mox.ReplayAll() + group.connection = self.ec2 + + rv = self.ec2.get_all_security_groups() + + self.assertEqual(len(rv), 1) + self.assertEqual(rv[0].name, 'default') + + self.manager.delete_project(project) + self.manager.delete_user(user) + + return + def test_authorize_revoke_security_group_foreign_group(self): """ Test that we can grant and revoke another security group access diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py index 6f4705719..0a4b50e96 100644 --- a/nova/tests/network_unittest.py +++ b/nova/tests/network_unittest.py @@ -97,6 +97,27 @@ class NetworkTestCase(test.TrialTestCase): self.context.project_id = self.projects[project_num].id self.network.deallocate_fixed_ip(self.context, address) + def test_private_ipv6(self): + """Make sure ipv6 is OK""" + if FLAGS.use_ipv6: + instance_ref = self._create_instance(1) + network_ref = db.project_get_network( + self.context, + self.context.project_id) + address_v6 = db.instance_get_fixed_address_v6( + self.context, + instance_ref['id']) + self.assertEqual(instance_ref['mac_address'], + utils.to_mac(address_v6)) + instance_ref2 = db.fixed_ip_get_instance_v6( + self.context, + address_v6) + self.assertEqual(instance_ref['id'], instance_ref2['id']) + self.assertEqual(address_v6, + utils.to_global_ipv6( + network_ref['cidr_v6'], + instance_ref['mac_address'])) + def test_public_network_association(self): """Makes sure that we can allocaate a public ip""" # TODO(vish): better way of adding floating ips diff --git a/nova/utils.py b/nova/utils.py index 142584df8..211a2cb75 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -30,6 +30,8 @@ import subprocess import socket import sys from xml.sax import saxutils +import re +import netaddr from twisted.internet.threads import deferToThread @@ -45,10 +47,14 @@ TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" def import_class(import_str): """Returns a class from a string including module and class""" mod_str, _sep, class_str = import_str.rpartition('.') + logging.debug(import_str) try: __import__(mod_str) return getattr(sys.modules[mod_str], class_str) except (ImportError, ValueError, AttributeError): + logging.debug(ImportError) + logging.debug(ValueError) + logging.debug(AttributeError) raise exception.NotFound('Class %s cannot be found' % class_str) @@ -165,6 +171,39 @@ def get_my_ip(): return "127.0.0.1" +def get_my_linklocal(interface): + if getattr(FLAGS, 'fake_tests', None): + return 'fe00::' + try: + if_str = execute("ifconfig %s" % interface) + condition = "\s+inet6\s+addr:\s+([0-9a-f:]+/\d+)\s+Scope:Link" + links = [re.search(condition, x) for x in if_str[0].split('\n')] + address = [w.group(1) for w in links if w is not None] + if address[0] is not None: + return address[0] + else: + return None + except RuntimeError as ex: + logging.warn("Couldn't get Link Local IP of %s :%s", interface, ex) + return None + + +def to_global_ipv6(prefix, mac): + mac64 = netaddr.EUI(mac).eui64().words + int_addr = int(''.join(['%02x' % i for i in mac64]), 16) + mac64_addr = netaddr.IPAddress(int_addr) + maskIP = netaddr.IPNetwork(prefix).ip + return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).format() + + +def to_mac(ipv6_address): + address = netaddr.IPAddress(ipv6_address) + mask1 = netaddr.IPAddress("::ffff:ffff:ffff:ffff") + mask2 = netaddr.IPAddress("::0200:0:0:0") + mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words + return ":".join(["%02x" % i for i in mac64[0:3] + mac64[5:8]]) + + def isotime(at=None): if not at: at = datetime.datetime.utcnow() diff --git a/nova/virt/libvirt.qemu.xml.template b/nova/virt/libvirt.qemu.xml.template index 2538b1ade..0ffe17e8d 100644 --- a/nova/virt/libvirt.qemu.xml.template +++ b/nova/virt/libvirt.qemu.xml.template @@ -23,6 +23,7 @@ + diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template index bb8b47911..0d355b81c 100644 --- a/nova/virt/libvirt.uml.xml.template +++ b/nova/virt/libvirt.uml.xml.template @@ -17,6 +17,7 @@ + diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 18085089f..71d9f781d 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -514,6 +514,8 @@ class LibvirtConnection(object): instance['id']) # Assume that the gateway also acts as the dhcp server. dhcp_server = network['gateway'] + #TODO ipv6 + ra_server = network['ra_server'] xml_info = {'type': FLAGS.libvirt_type, 'name': instance['name'], 'basepath': os.path.join(FLAGS.instances_path, @@ -523,11 +525,13 @@ class LibvirtConnection(object): 'bridge_name': network['bridge'], 'mac_address': instance['mac_address'], 'ip_address': ip_address, - 'dhcp_server': dhcp_server} + 'dhcp_server': dhcp_server, + 'ra_server': ra_server} if rescue: libvirt_xml = self.rescue_xml % xml_info else: libvirt_xml = self.libvirt_xml % xml_info + logging.debug('instance %s: finished toXML method', instance['name']) return libvirt_xml @@ -701,6 +705,7 @@ class NWFilterFirewall(object): + ''' @@ -722,6 +727,14 @@ class NWFilterFirewall(object): ''' + nova_ra_filter = ''' + d707fa71-4fb5-4b27-9ab7-ba5ca19c8804 + + + + ''' + def nova_base_ipv4_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: @@ -736,13 +749,13 @@ class NWFilterFirewall(object): def nova_base_ipv6_filter(self): retval = "" - for protocol in ['tcp', 'udp', 'icmp']: + for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: for direction, action, priority in [('out', 'accept', 399), ('inout', 'drop', 400)]: retval += """ - <%s-ipv6 /> + <%s /> """ % (action, direction, - priority, protocol) + priority, protocol) retval += '' return retval @@ -755,6 +768,15 @@ class NWFilterFirewall(object): retval += '' return retval + def nova_project_filter_v6(self, project, net, mask): + retval = "" % project + for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: + retval += """ + <%s srcipaddr='%s' srcipmask='%s' /> + """ % (protocol, net, mask) + retval += '' + return retval + def _define_filter(self, xml): if callable(xml): xml = xml() @@ -766,6 +788,11 @@ class NWFilterFirewall(object): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) + @staticmethod + def _get_ip_version(cidr): + net = IPy.IP(cidr) + return int(net.version()) + @defer.inlineCallbacks def setup_nwfilters_for_instance(self, instance): """ @@ -777,6 +804,7 @@ class NWFilterFirewall(object): yield self._define_filter(self.nova_base_ipv4_filter) yield self._define_filter(self.nova_base_ipv6_filter) yield self._define_filter(self.nova_dhcp_filter) + yield self._define_filter(self.nova_ra_filter) yield self._define_filter(self.nova_base_filter) nwfilter_xml = "\n" \ @@ -787,12 +815,22 @@ class NWFilterFirewall(object): network_ref = db.project_get_network(context.get_admin_context(), instance['project_id']) net, mask = self._get_net_and_mask(network_ref['cidr']) + if(FLAGS.use_ipv6): + net_v6, mask_v6 = self._get_net_and_mask( + network_ref['cidr_v6']) project_filter = self.nova_project_filter(instance['project_id'], net, mask) yield self._define_filter(project_filter) - nwfilter_xml += " \n" % \ instance['project_id'] + if(FLAGS.use_ipv6): + project_filter_v6 = self.nova_project_filter_v6( + instance['project_id'], + net_v6, mask_v6) + yield self._define_filter(project_filter_v6) + nwfilter_xml += \ + " \n" % \ + instance['project_id'] for security_group in instance.security_groups: yield self.ensure_security_group_filter(security_group['id']) @@ -812,12 +850,21 @@ class NWFilterFirewall(object): security_group = db.security_group_get(context.get_admin_context(), security_group_id) rule_xml = "" + version = 4 + v6protocol = {'tcp':'tcp-ipv6', 'udp':'udp-ipv6', 'icmp':'icmpv6'} for rule in security_group.rules: rule_xml += "" if rule.cidr: + version = self._get_ip_version(rule.cidr) net, mask = self._get_net_and_mask(rule.cidr) - rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ - (rule.protocol, net, mask) + if(FLAGS.use_ipv6 and version == 6): + rule_xml += "<%s " % v6protocol[rule.protocol] + rule_xml += "srcipaddr='%s' " % net + rule_xml += "srcipmask='%s' " % mask + else: + rule_xml += "<%s " % rule.protocol + rule_xml += "srcipaddr='%s' " % net + rule_xml += "srcipmask='%s' " % mask if rule.protocol in ['tcp', 'udp']: rule_xml += "dstportstart='%s' dstportend='%s' " % \ (rule.from_port, rule.to_port) @@ -832,6 +879,9 @@ class NWFilterFirewall(object): rule_xml += '/>\n' rule_xml += "\n" - xml = "%s" % \ - (security_group_id, rule_xml,) + xml = " \n %s' % (cmd, output) @@ -130,6 +143,7 @@ class SmokeTestCase(unittest.TestCase): raise Exception(output) return True + def run_tests(suites): argv = FLAGS(sys.argv) @@ -151,4 +165,3 @@ def run_tests(suites): else: for suite in suites.itervalues(): unittest.TextTestRunner(verbosity=2).run(suite) - diff --git a/smoketests/flags.py b/smoketests/flags.py index ae4d09508..35f432a77 100644 --- a/smoketests/flags.py +++ b/smoketests/flags.py @@ -33,6 +33,7 @@ DEFINE_bool = DEFINE_bool # __GLOBAL FLAGS ONLY__ # Define any app-specific flags in their own files, docs at: # http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39 + DEFINE_string('region', 'nova', 'Region to use') DEFINE_string('test_image', 'ami-tiny', 'Image to use for launch tests') - +DEFINE_string('use_ipv6', True, 'use the ipv6 or not') diff --git a/smoketests/public_network_smoketests.py b/smoketests/public_network_smoketests.py new file mode 100644 index 000000000..bfc2b20ba --- /dev/null +++ b/smoketests/public_network_smoketests.py @@ -0,0 +1,180 @@ +# 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. + +import commands +import os +import random +import socket +import sys +import time +import unittest + +from smoketests import flags +from smoketests import base +from smoketests import user_smoketests + +#Note that this test should run from +#public network (outside of private network segments) +#Please set EC2_URL correctly +#You should use admin account in this test + +FLAGS = flags.FLAGS + +TEST_PREFIX = 'test%s' % int(random.random() * 1000000) +TEST_BUCKET = '%s_bucket' % TEST_PREFIX +TEST_KEY = '%s_key' % TEST_PREFIX +TEST_KEY2 = '%s_key2' % TEST_PREFIX +TEST_DATA = {} + + +class InstanceTestsFromPublic(user_smoketests.UserSmokeTestCase): + def test_001_can_create_keypair(self): + key = self.create_key_pair(self.conn, TEST_KEY) + self.assertEqual(key.name, TEST_KEY) + + def test_002_security_group(self): + security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd") + for x in range(random.randint(4, 8))) + group = self.conn.create_security_group(security_group_name, + 'test group') + group.connection = self.conn + group.authorize('tcp', 22, 22, '0.0.0.0/0') + if FLAGS.use_ipv6: + group.authorize('tcp', 22, 22, '::/0') + + reservation = self.conn.run_instances(FLAGS.test_image, + key_name=TEST_KEY, + security_groups=[security_group_name], + instance_type='m1.tiny') + self.data['security_group_name'] = security_group_name + self.data['group'] = group + self.data['instance_id'] = reservation.instances[0].id + + def test_003_instance_with_group_runs_within_60_seconds(self): + reservations = self.conn.get_all_instances([self.data['instance_id']]) + instance = reservations[0].instances[0] + # allow 60 seconds to exit pending with IP + for x in xrange(60): + instance.update() + if instance.state == u'running': + break + time.sleep(1) + else: + self.fail('instance failed to start') + ip = reservations[0].instances[0].private_dns_name + self.failIf(ip == '0.0.0.0') + self.data['private_ip'] = ip + if FLAGS.use_ipv6: + ipv6 = reservations[0].instances[0].dns_name_v6 + self.failIf(ipv6 is None) + self.data['ip_v6'] = ipv6 + + def test_004_can_ssh_to_ipv6(self): + if FLAGS.use_ipv6: + for x in xrange(20): + try: + conn = self.connect_ssh( + self.data['ip_v6'], TEST_KEY) + conn.close() + except Exception as ex: + print ex + time.sleep(1) + else: + break + else: + self.fail('could not ssh to instance') + + def test_012_can_create_instance_with_keypair(self): + if 'instance_id' in self.data: + self.conn.terminate_instances([self.data['instance_id']]) + reservation = self.conn.run_instances(FLAGS.test_image, + key_name=TEST_KEY, + instance_type='m1.tiny') + self.assertEqual(len(reservation.instances), 1) + self.data['instance_id'] = reservation.instances[0].id + + def test_013_instance_runs_within_60_seconds(self): + reservations = self.conn.get_all_instances([self.data['instance_id']]) + instance = reservations[0].instances[0] + # allow 60 seconds to exit pending with IP + for x in xrange(60): + instance.update() + if instance.state == u'running': + break + time.sleep(1) + else: + self.fail('instance failed to start') + ip = reservations[0].instances[0].private_dns_name + self.failIf(ip == '0.0.0.0') + self.data['private_ip'] = ip + if FLAGS.use_ipv6: + ipv6 = reservations[0].instances[0].dns_name_v6 + self.failIf(ipv6 is None) + self.data['ip_v6'] = ipv6 + + def test_014_can_not_ping_private_ip(self): + for x in xrange(4): + # ping waits for 1 second + status, output = commands.getstatusoutput( + 'ping -c1 %s' % self.data['private_ip']) + if status == 0: + self.fail('can ping private ip from public network') + if FLAGS.use_ipv6: + status, output = commands.getstatusoutput( + 'ping6 -c1 %s' % self.data['ip_v6']) + if status == 0: + self.fail('can ping ipv6 from public network') + else: + pass + + def test_015_can_not_ssh_to_private_ip(self): + for x in xrange(1): + try: + conn = self.connect_ssh(self.data['private_ip'], TEST_KEY) + conn.close() + except Exception: + time.sleep(1) + else: + self.fail('can ssh for ipv4 address from public network') + + if FLAGS.use_ipv6: + for x in xrange(1): + try: + conn = self.connect_ssh( + self.data['ip_v6'], TEST_KEY) + conn.close() + except Exception: + time.sleep(1) + else: + self.fail('can ssh for ipv6 address from public network') + + def test_999_tearDown(self): + self.delete_key_pair(self.conn, TEST_KEY) + security_group_name = self.data['security_group_name'] + group = self.data['group'] + if group: + group.revoke('tcp', 22, 22, '0.0.0.0/0') + if FLAGS.use_ipv6: + group.revoke('tcp', 22, 22, '::/0') + self.conn.delete_security_group(security_group_name) + if 'instance_id' in self.data: + self.conn.terminate_instances([self.data['instance_id']]) + +if __name__ == "__main__": + suites = {'instance': unittest.makeSuite(InstanceTestsFromPublic)} + sys.exit(base.run_tests(suites)) diff --git a/smoketests/user_smoketests.py b/smoketests/user_smoketests.py index d29e3aea3..8bb2ddebb 100644 --- a/smoketests/user_smoketests.py +++ b/smoketests/user_smoketests.py @@ -37,7 +37,7 @@ flags.DEFINE_string('bundle_kernel', 'openwrt-x86-vmlinuz', flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image', 'Local image file to use for bundling tests') -TEST_PREFIX = 'test%s' % int (random.random()*1000000) +TEST_PREFIX = 'test%s' % int(random.random() * 1000000) TEST_BUCKET = '%s_bucket' % TEST_PREFIX TEST_KEY = '%s_key' % TEST_PREFIX TEST_DATA = {} @@ -71,7 +71,7 @@ class ImageTests(UserSmokeTestCase): def test_006_can_register_kernel(self): kernel_id = self.conn.register_image('%s/%s.manifest.xml' % - (TEST_BUCKET, FLAGS.bundle_kernel)) + (TEST_BUCKET, FLAGS.bundle_kernel)) self.assert_(kernel_id is not None) self.data['kernel_id'] = kernel_id @@ -83,7 +83,7 @@ class ImageTests(UserSmokeTestCase): time.sleep(1) else: print image.state - self.assert_(False) # wasn't available within 10 seconds + self.assert_(False) # wasn't available within 10 seconds self.assert_(image.type == 'machine') for i in xrange(10): @@ -92,7 +92,7 @@ class ImageTests(UserSmokeTestCase): break time.sleep(1) else: - self.assert_(False) # wasn't available within 10 seconds + self.assert_(False) # wasn't available within 10 seconds self.assert_(kernel.type == 'kernel') def test_008_can_describe_image_attribute(self): @@ -137,20 +137,23 @@ class InstanceTests(UserSmokeTestCase): self.data['instance_id'] = reservation.instances[0].id def test_003_instance_runs_within_60_seconds(self): - reservations = self.conn.get_all_instances([data['instance_id']]) + reservations = self.conn.get_all_instances([self.data['instance_id']]) instance = reservations[0].instances[0] # allow 60 seconds to exit pending with IP for x in xrange(60): instance.update() if instance.state == u'running': - break + break time.sleep(1) else: self.fail('instance failed to start') ip = reservations[0].instances[0].private_dns_name self.failIf(ip == '0.0.0.0') self.data['private_ip'] = ip - print self.data['private_ip'] + if FLAGS.use_ipv6: + ipv6 = reservations[0].instances[0].dns_name_v6 + self.failIf(ipv6 is None) + self.data['ip_v6'] = ipv6 def test_004_can_ping_private_ip(self): for x in xrange(120): @@ -159,6 +162,11 @@ class InstanceTests(UserSmokeTestCase): 'ping -c1 %s' % self.data['private_ip']) if status == 0: break + if FLAGS.use_ipv6: + status, output = commands.getstatusoutput( + 'ping6 -c1 %s' % self.data['ip_v6']) + if status == 0: + break else: self.fail('could not ping instance') @@ -174,6 +182,19 @@ class InstanceTests(UserSmokeTestCase): else: self.fail('could not ssh to instance') + if FLAGS.use_ipv6: + for x in xrange(30): + try: + conn = self.connect_ssh( + self.data['ip_v6'], TEST_KEY) + conn.close() + except Exception: + time.sleep(1) + else: + break + else: + self.fail('could not ssh to instance') + def test_006_can_allocate_elastic_ip(self): result = self.conn.allocate_address() self.assertTrue(hasattr(result, 'public_ip')) @@ -206,8 +227,8 @@ class InstanceTests(UserSmokeTestCase): def test_999_tearDown(self): self.delete_key_pair(self.conn, TEST_KEY) - if self.data.has_key('instance_id'): - self.conn.terminate_instances([data['instance_id']]) + if 'instance_id' in self.data: + self.conn.terminate_instances([self.data['instance_id']]) class VolumeTests(UserSmokeTestCase): -- cgit From 56969837fdf1a9e5316443ce72b32ae268ed2947 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 4 Jan 2011 04:21:50 -0500 Subject: Fixed conflict with r515 --- nova/virt/libvirt.uml.xml.template | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 nova/virt/libvirt.uml.xml.template diff --git a/nova/virt/libvirt.uml.xml.template b/nova/virt/libvirt.uml.xml.template deleted file mode 100644 index 0d355b81c..000000000 --- a/nova/virt/libvirt.uml.xml.template +++ /dev/null @@ -1,27 +0,0 @@ - - %(name)s - %(memory_kb)s - - %(type)s - /usr/bin/linux - /dev/ubda1 - - - - - - - - - - - - - - - - - - - - -- cgit From 96facdeee025cdf33df0b16abeeeb97f9ec87e70 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 4 Jan 2011 04:27:39 -0500 Subject: Fixed for pep8 --- nova/virt/libvirt_conn.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9a99b1a51..c656931d6 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -550,7 +550,7 @@ class LibvirtConnection(object): "\n" "\n") % (net, mask,net_v6,mask_v6) + "value=\"%s\" />\n") % (net, mask, net_v6, mask_v6) else: extra_params = "\n" @@ -777,7 +777,6 @@ class NWFilterFirewall(object): ''' - nova_ra_filter = ''' d707fa71-4fb5-4b27-9ab7-ba5ca19c8804 ''' - nova_vpn_filter = ''' 2086015e-cf03-11df-8c5d-080027c27973 @@ -795,7 +793,6 @@ class NWFilterFirewall(object): ''' - def nova_base_ipv4_filter(self): retval = "" for protocol in ['tcp', 'udp', 'icmp']: @@ -832,8 +829,9 @@ class NWFilterFirewall(object): def nova_project_filter_v6(self): retval = "" % project for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: - retval += """ - <%s srcipaddr='$PROJNETV6' srcipmask='$PROJMASKV6' /> + retval += """ + <%s srcipaddr='$PROJNETV6' + srcipmask='$PROJMASKV6' /> """ % (protocol) retval += '' return retval @@ -872,7 +870,7 @@ class NWFilterFirewall(object): if FLAGS.allow_project_net_traffic: nwfilter_xml += " \n" if(FLAGS.use_ipv6): - nwfilter_xml += " \n" + nwfilter_xml += " \n" for security_group in instance.security_groups: self.ensure_security_group_filter(security_group['id']) @@ -892,7 +890,7 @@ class NWFilterFirewall(object): security_group_id) rule_xml = "" version = 4 - v6protocol = {'tcp':'tcp-ipv6', 'udp':'udp-ipv6', 'icmp':'icmpv6'} + v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} for rule in security_group.rules: rule_xml += "" if rule.cidr: @@ -904,7 +902,6 @@ class NWFilterFirewall(object): else: rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ (rule.protocol, net, mask) - if rule.protocol in ['tcp', 'udp']: rule_xml += "dstportstart='%s' dstportend='%s' " % \ (rule.from_port, rule.to_port) -- cgit From 505becef0704cc801f957d2931c8b994e2df92ca Mon Sep 17 00:00:00 2001 From: nova Date: Tue, 4 Jan 2011 05:00:21 -0500 Subject: Fixed bug --- nova/virt/libvirt_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c656931d6..de7f6341f 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -827,7 +827,7 @@ class NWFilterFirewall(object): return retval def nova_project_filter_v6(self): - retval = "" % project + retval = "" for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: retval += """ <%s srcipaddr='$PROJNETV6' -- cgit From c528be81a5d0acaea5077c183ec4d15356d457d5 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Tue, 4 Jan 2011 05:35:13 -0500 Subject: Fixed bug in libvirt --- nova/virt/libvirt_conn.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index de7f6341f..f95544188 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -114,6 +114,9 @@ def _get_net_and_mask(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) +def _get_ip_version(cidr): + net = IPy.IP(cidr) + return int(net.version()) class LibvirtConnection(object): @@ -484,7 +487,8 @@ class LibvirtConnection(object): 'netmask': network_ref['netmask'], 'gateway': network_ref['gateway'], 'broadcast': network_ref['broadcast'], - 'dns': network_ref['dns']} + 'dns': network_ref['dns'], + 'ra_server': network_ref['ra_server']} if key or net: if key: logging.info(_('instance %s: injecting key into image %s'), @@ -541,8 +545,8 @@ class LibvirtConnection(object): if FLAGS.allow_project_net_traffic: net, mask = _get_net_and_mask(network['cidr']) - net_v6, mask_v6 = self._get_net_and_mask( - network_ref['cidr_v6']) + net_v6, mask_v6 = _get_net_and_mask( + network['cidr_v6']) extra_params = ("\n" " Date: Tue, 4 Jan 2011 07:40:29 -0500 Subject: Some Bug Fix --- nova/utils.py | 4 +--- nova/virt/libvirt.xml.template | 2 +- nova/virt/libvirt_conn.py | 16 +++++++++++----- smoketests/user_smoketests.py | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/nova/utils.py b/nova/utils.py index 0eab4b152..c5e2f7517 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -211,8 +211,6 @@ def get_my_ip(): def get_my_linklocal(interface): - if getattr(FLAGS, 'fake_tests', None): - return 'fe00::' try: if_str = execute("ifconfig %s" % interface) condition = "\s+inet6\s+addr:\s+([0-9a-f:]+/\d+)\s+Scope:Link" @@ -224,7 +222,7 @@ def get_my_linklocal(interface): return None except RuntimeError as ex: logging.warn("Couldn't get Link Local IP of %s :%s", interface, ex) - return None + return 'fe00::' def to_global_ipv6(prefix, mac): diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template index f0f56fc62..52c95ee57 100644 --- a/nova/virt/libvirt.xml.template +++ b/nova/virt/libvirt.xml.template @@ -66,7 +66,7 @@ - + #if $getVar('extra_params', False) ${extra_params} #end if diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f95544188..521ac97fc 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -114,6 +114,10 @@ def _get_net_and_mask(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) +def _get_net_and_prefixlen(cidr): + net = IPy.IP(cidr) + return str(net.net()), str(net.prefixlen()) + def _get_ip_version(cidr): net = IPy.IP(cidr) return int(net.version()) @@ -354,6 +358,7 @@ class LibvirtConnection(object): power_state.NOSTATE, 'launching') NWFilterFirewall(self._conn).setup_nwfilters_for_instance(instance) + self._create_image(instance, xml) self._conn.createXML(xml, 0) logging.debug(_("instance %s: is running"), instance['name']) @@ -545,7 +550,7 @@ class LibvirtConnection(object): if FLAGS.allow_project_net_traffic: net, mask = _get_net_and_mask(network['cidr']) - net_v6, mask_v6 = _get_net_and_mask( + net_v6, prefixlen_v6 = _get_net_and_prefixlen( network['cidr_v6']) extra_params = ("\n" @@ -554,7 +559,7 @@ class LibvirtConnection(object): "\n" "\n") % (net, mask, net_v6, mask_v6) + "value=\"%s\" />\n") % (net, mask, net_v6, prefixlen_v6) else: extra_params = "\n" @@ -882,7 +887,7 @@ class NWFilterFirewall(object): nwfilter_xml += (" \n") % security_group['id'] nwfilter_xml += "" - + logging.debug(nwfilter_xml) self._define_filter(nwfilter_xml) def ensure_security_group_filter(self, security_group_id): @@ -899,11 +904,12 @@ class NWFilterFirewall(object): rule_xml += "" if rule.cidr: version = _get_ip_version(rule.cidr) - net, mask = _get_net_and_mask(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[rrule.protocol], net, mask) + (v6protocol[rrule.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']: diff --git a/smoketests/user_smoketests.py b/smoketests/user_smoketests.py index 8bb2ddebb..f4ee30542 100644 --- a/smoketests/user_smoketests.py +++ b/smoketests/user_smoketests.py @@ -193,7 +193,7 @@ class InstanceTests(UserSmokeTestCase): else: break else: - self.fail('could not ssh to instance') + self.fail('could not ssh to instance v6') def test_006_can_allocate_elastic_ip(self): result = self.conn.allocate_address() -- cgit From b14a8975dece3be18216cf2ad790be0c8fd22f7a Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 09:52:45 -0500 Subject: The _update method in base Instance class overides dns_name_v6,so fixed it. --- contrib/boto_v6/ec2/instance.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contrib/boto_v6/ec2/instance.py b/contrib/boto_v6/ec2/instance.py index 255114935..18b5cd33a 100644 --- a/contrib/boto_v6/ec2/instance.py +++ b/contrib/boto_v6/ec2/instance.py @@ -25,9 +25,13 @@ class ReservationV6(Reservation): class InstanceV6(Instance): def __init__(self, connection=None): Instance.__init__(self, connection) - self.public_dns_name_v6 = None + self.dns_name_v6 = None def endElement(self, name, value, connection): Instance.endElement(self, name, value, connection) if name == 'dnsNameV6': self.dns_name_v6 = value + + def _update(self, updated): + self.__dict__.update(updated.__dict__) + self.dns_name_v6 = updated.dns_name_v6 -- cgit From 28bf4e2df324db79a81a853d39cb5912985c2e45 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 10:25:16 -0500 Subject: Fixed bug in nova_project_filter_v6 --- nova/virt/libvirt_conn.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 521ac97fc..a592f5d6b 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -114,14 +114,17 @@ def _get_net_and_mask(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.netmask()) + def _get_net_and_prefixlen(cidr): net = IPy.IP(cidr) return str(net.net()), str(net.prefixlen()) + def _get_ip_version(cidr): net = IPy.IP(cidr) return int(net.version()) + class LibvirtConnection(object): def __init__(self, read_only): @@ -559,7 +562,8 @@ class LibvirtConnection(object): "\n" "\n") % (net, mask, net_v6, prefixlen_v6) + "value=\"%s\" />\n") % \ + (net, mask, net_v6, prefixlen_v6) else: extra_params = "\n" @@ -838,7 +842,8 @@ class NWFilterFirewall(object): def nova_project_filter_v6(self): retval = "" for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']: - retval += """ + retval += """ <%s srcipaddr='$PROJNETV6' srcipmask='$PROJMASKV6' /> """ % (protocol) -- cgit From b47f37d0f9c06f2c4bc5adcf3afcececa2354324 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 10:35:15 -0500 Subject: Fixed misspelled variable --- nova/virt/libvirt_conn.py | 2 +- smoketests/user_smoketests.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index a592f5d6b..1f3c69f65 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -912,7 +912,7 @@ class NWFilterFirewall(object): if(FLAGS.use_ipv6 and version == 6): net, prefixlen = _get_net_and_prefixlen(rule.cidr) rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ - (v6protocol[rrule.protocol], net, prefixlen) + (v6protocol[rule.protocol], net, prefixlen) else: net, mask = _get_net_and_mask(rule.cidr) rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \ diff --git a/smoketests/user_smoketests.py b/smoketests/user_smoketests.py index f4ee30542..181acdfe5 100644 --- a/smoketests/user_smoketests.py +++ b/smoketests/user_smoketests.py @@ -162,13 +162,18 @@ class InstanceTests(UserSmokeTestCase): 'ping -c1 %s' % self.data['private_ip']) if status == 0: break - if FLAGS.use_ipv6: + else: + self.fail('could not ping instance') + + if FLAGS.use_ipv6: + for x in xrange(120): + # ping waits for 1 second status, output = commands.getstatusoutput( 'ping6 -c1 %s' % self.data['ip_v6']) if status == 0: break - else: - self.fail('could not ping instance') + else: + self.fail('could not ping instance') def test_005_can_ssh_to_private_ip(self): for x in xrange(30): -- cgit From 69b7a0d69c3ac79b84c2bda19d379606c5a323ab Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 10:42:15 -0500 Subject: Removed debug message which is not needed. --- nova/virt/libvirt_conn.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 1f3c69f65..4fba164a9 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -892,7 +892,6 @@ class NWFilterFirewall(object): nwfilter_xml += (" \n") % security_group['id'] nwfilter_xml += "" - logging.debug(nwfilter_xml) self._define_filter(nwfilter_xml) def ensure_security_group_filter(self, security_group_id): -- cgit From 40b156f74e90a94abb255950f29d714f4bc4c428 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 12:36:47 -0500 Subject: Fixed:Create instance fails when use_ipv6=False --- nova/virt/libvirt_conn.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4fba164a9..8197342df 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -490,13 +490,16 @@ class LibvirtConnection(object): if network_ref['injected']: admin_context = context.get_admin_context() address = db.instance_get_fixed_address(admin_context, inst['id']) + ra_server = network_ref['ra_server'] + if not ra_server: + ra_server = "fd00::" with open(FLAGS.injected_network_template) as f: net = f.read() % {'address': address, 'netmask': network_ref['netmask'], 'gateway': network_ref['gateway'], 'broadcast': network_ref['broadcast'], 'dns': network_ref['dns'], - 'ra_server': network_ref['ra_server']} + 'ra_server': ra_server} if key or net: if key: logging.info(_('instance %s: injecting key into image %s'), @@ -550,12 +553,14 @@ class LibvirtConnection(object): # Assume that the gateway also acts as the dhcp server. dhcp_server = network['gateway'] ra_server = network['ra_server'] - + if not ra_server: + ra_server = 'fd00::' if FLAGS.allow_project_net_traffic: - net, mask = _get_net_and_mask(network['cidr']) - net_v6, prefixlen_v6 = _get_net_and_prefixlen( + if FLAGS.use_ipv6: + net, mask = _get_net_and_mask(network['cidr']) + net_v6, prefixlen_v6 = _get_net_and_prefixlen( network['cidr_v6']) - extra_params = ("\n" "\n" @@ -564,6 +569,13 @@ class LibvirtConnection(object): "\n") % \ (net, mask, net_v6, prefixlen_v6) + else: + net, mask = _get_net_and_mask(network['cidr']) + extra_params = ("\n" + "\n") % \ + (net, mask) else: extra_params = "\n" @@ -860,12 +872,14 @@ class NWFilterFirewall(object): self._define_filter(self.nova_base_ipv4_filter) self._define_filter(self.nova_base_ipv6_filter) self._define_filter(self.nova_dhcp_filter) - self._define_filter(self.nova_ra_filter) + if FLAGS.use_ipv6: + self._define_filter(self.nova_ra_filter) self._define_filter(self.nova_base_filter) self._define_filter(self.nova_vpn_filter) if FLAGS.allow_project_net_traffic: self._define_filter(self.nova_project_filter) - self._define_filter(self.nova_project_filter_v6) + if FLAGS.use_ipv6: + self._define_filter(self.nova_project_filter_v6) def setup_nwfilters_for_instance(self, instance): """ -- cgit From 90ced5f211c3a53389d2f5d7413f9289770b279a Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 12:38:34 -0500 Subject: Fixed for pep8 --- nova/virt/libvirt_conn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 8197342df..b19988822 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -490,9 +490,9 @@ class LibvirtConnection(object): if network_ref['injected']: admin_context = context.get_admin_context() address = db.instance_get_fixed_address(admin_context, inst['id']) - ra_server = network_ref['ra_server'] + ra_server = network_ref['ra_server'] if not ra_server: - ra_server = "fd00::" + ra_server = "fd00::" with open(FLAGS.injected_network_template) as f: net = f.read() % {'address': address, 'netmask': network_ref['netmask'], @@ -554,7 +554,7 @@ class LibvirtConnection(object): dhcp_server = network['gateway'] ra_server = network['ra_server'] if not ra_server: - ra_server = 'fd00::' + ra_server = 'fd00::' if FLAGS.allow_project_net_traffic: if FLAGS.use_ipv6: net, mask = _get_net_and_mask(network['cidr']) -- cgit From f85eba86b04253612e2272b3eb6a9fd79fab6567 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 5 Jan 2011 12:39:35 -0500 Subject: missing _() --- nova/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/utils.py b/nova/utils.py index c5e2f7517..afe7422d9 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -221,7 +221,7 @@ def get_my_linklocal(interface): else: return None except RuntimeError as ex: - logging.warn("Couldn't get Link Local IP of %s :%s", interface, ex) + logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) return 'fe00::' -- cgit From c14425541a1e77eb2049b94060bc0c4cd1df578f Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 12 Jan 2011 02:15:09 -0500 Subject: changed exception class --- nova/utils.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/nova/utils.py b/nova/utils.py index afe7422d9..888ebfd81 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -219,10 +219,13 @@ def get_my_linklocal(interface): if address[0] is not None: return address[0] else: - return None - except RuntimeError as ex: - logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) - return 'fe00::' + return 'fe00::' + except IndexError as ex: + logging.warn(_("Couldn't parse command from get Link Local IP of %s :%s"), interface, ex) + except ProcessExecutionError as ex: + logging.warn(_("Couldn't execute command to get Link Local IP of %s :%s"), interface, ex) + except: + return 'fe00::' def to_global_ipv6(prefix, mac): -- cgit From 1629dcf935a29c01d4e4ad509e33356daa93b051 Mon Sep 17 00:00:00 2001 From: Hisaharu Ishii Date: Wed, 12 Jan 2011 11:26:22 +0900 Subject: Fixed for pep8 Remove temporary debugging --- bin/nova-manage | 10 +++++----- nova/db/api.py | 3 +++ nova/db/sqlalchemy/api.py | 4 ++++ nova/utils.py | 1 - 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 04ecc8c10..7331186c7 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -91,6 +91,7 @@ flags.DECLARE('vlan_start', 'nova.network.manager') flags.DECLARE('vpn_start', 'nova.network.manager') flags.DECLARE('fixed_range_v6', 'nova.network.manager') + class VpnCommands(object): """Class for managing VPNs.""" @@ -432,11 +433,12 @@ class NetworkCommands(object): """Class for managing networks.""" def create(self, fixed_range=None, num_networks=None, - network_size=None, vlan_start=None, vpn_start=None,fixed_range_v6=None): + network_size=None, vlan_start=None, vpn_start=None, + fixed_range_v6=None): """Creates fixed ips for host by range arguments: [fixed_range=FLAG], [num_networks=FLAG], [network_size=FLAG], [vlan_start=FLAG], - [vpn_start=FLAG],[fixed_range_v6=FLAG]""" + [vpn_start=FLAG], [fixed_range_v6=FLAG]""" if not fixed_range: fixed_range = FLAGS.fixed_range if not num_networks: @@ -453,9 +455,7 @@ class NetworkCommands(object): net_manager.create_networks(context.get_admin_context(), fixed_range, int(num_networks), int(network_size), int(vlan_start), - int(vpn_start),fixed_range_v6) - - + int(vpn_start), fixed_range_v6) class ServiceCommands(object): diff --git a/nova/db/api.py b/nova/db/api.py index 8684a3aef..03e800466 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -284,6 +284,7 @@ def fixed_ip_get_instance(context, address): """Get an instance for a fixed ip by address.""" return IMPL.fixed_ip_get_instance(context, address) + def fixed_ip_get_instance_v6(context, address): return IMPL.fixed_ip_get_instance_v6(context, address) @@ -345,6 +346,7 @@ def instance_get_fixed_address(context, instance_id): """Get the fixed ip address of an instance.""" return IMPL.instance_get_fixed_address(context, instance_id) + def instance_get_fixed_address_v6(context, instance_id): return IMPL.instance_get_fixed_address_v6(context, instance_id) @@ -543,6 +545,7 @@ def project_get_network(context, project_id, associate=True): return IMPL.project_get_network(context, project_id) + def project_get_network_v6(context, project_id): return IMPL.project_get_network_v6(context, project_id) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ffc0ec221..7c3afa4ad 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -585,6 +585,7 @@ def fixed_ip_get_instance(context, address): fixed_ip_ref = fixed_ip_get_by_address(context, address) return fixed_ip_ref.instance + @require_context def fixed_ip_get_instance_v6(context, address): session = get_session() @@ -801,6 +802,7 @@ def instance_get_fixed_address(context, instance_id): return None return instance_ref.fixed_ip['address'] + @require_context def instance_get_fixed_address_v6(context, instance_id): session = get_session() @@ -811,6 +813,7 @@ def instance_get_fixed_address_v6(context, instance_id): mac = instance_ref.mac_address return utils.to_global_ipv6(prefix, mac) + @require_context def instance_get_floating_address(context, instance_id): session = get_session() @@ -1150,6 +1153,7 @@ def project_get_network(context, project_id, associate=True): first() return result + @require_context def project_get_network_v6(context, project_id): return project_get_network(context, project_id) diff --git a/nova/utils.py b/nova/utils.py index 888ebfd81..b08d5d620 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -47,7 +47,6 @@ TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" def import_class(import_str): """Returns a class from a string including module and class""" mod_str, _sep, class_str = import_str.rpartition('.') - logging.debug(import_str) try: __import__(mod_str) return getattr(sys.modules[mod_str], class_str) -- cgit From d368d182d7fa4b0f0cd9c7c5ad1e804b19365b26 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 12 Jan 2011 12:05:27 +0900 Subject: Add DescribeInstanceV6 for backward compatibility --- contrib/boto_v6/ec2/connection.py | 2 +- nova/api/ec2/cloud.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/contrib/boto_v6/ec2/connection.py b/contrib/boto_v6/ec2/connection.py index 151b76a55..23466e5d7 100644 --- a/contrib/boto_v6/ec2/connection.py +++ b/contrib/boto_v6/ec2/connection.py @@ -37,5 +37,5 @@ class EC2ConnectionV6(boto.ec2.EC2Connection): self.build_list_params(params, instance_ids, 'InstanceId') if filters: self.build_filter_params(params, filters) - return self.get_list('DescribeInstances', params, + return self.get_list('DescribeInstancesV6', params, [('item', ReservationV6)]) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 8c925ffee..3d071782c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -643,15 +643,21 @@ class CloudController(object): def describe_instances(self, context, **kwargs): return self._format_describe_instances(context) + def describe_instances_v6(self, context, **kwargs): + return self._format_describe_instances(context) + def _format_describe_instances(self, context): return {'reservationSet': self._format_instances(context)} + def _format_describe_instances_v6(self, context): + return {'reservationSet': self._format_instances(context,None,True)} + def _format_run_instances(self, context, reservation_id): i = self._format_instances(context, reservation_id) assert len(i) == 1 return i[0] - def _format_instances(self, context, reservation_id=None): + def _format_instances(self, context, reservation_id=None,use_v6=False): reservations = {} if reservation_id: instances = db.instance_get_all_by_reservation(context, @@ -677,7 +683,7 @@ class CloudController(object): if instance['fixed_ip']['floating_ips']: fixed = instance['fixed_ip'] floating_addr = fixed['floating_ips'][0]['address'] - if instance['fixed_ip']['network'] and FLAGS.use_ipv6: + if instance['fixed_ip']['network'] and use_v6: i['dnsNameV6'] = utils.to_global_ipv6( instance['fixed_ip']['network']['cidr_v6'], instance['mac_address']) -- cgit From 7a6b7c32ed25d1edc58b924ce5621dc0d8de9686 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 12 Jan 2011 03:50:09 -0500 Subject: Fixed bug --- nova/api/ec2/cloud.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 3d071782c..d6f164b5c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -644,7 +644,7 @@ class CloudController(object): return self._format_describe_instances(context) def describe_instances_v6(self, context, **kwargs): - return self._format_describe_instances(context) + return self._format_describe_instances_v6(context) def _format_describe_instances(self, context): return {'reservationSet': self._format_instances(context)} -- cgit From 04cd3241f442f1c6a9fd030ab47b4d15e79ec032 Mon Sep 17 00:00:00 2001 From: Hisaharu Ishii Date: Wed, 12 Jan 2011 18:37:18 +0900 Subject: Change command to get link local address Remove superfluous code --- nova/utils.py | 4 ++-- nova/virt/libvirt_conn.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/nova/utils.py b/nova/utils.py index 02bafc6c8..a13fa214c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -212,8 +212,8 @@ def get_my_ip(): def get_my_linklocal(interface): try: - if_str = execute("ifconfig %s" % interface) - condition = "\s+inet6\s+addr:\s+([0-9a-f:]+/\d+)\s+Scope:Link" + if_str = execute("ip -f inet6 -o addr show %s" % interface) + condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope:link" links = [re.search(condition, x) for x in if_str[0].split('\n')] address = [w.group(1) for w in links if w is not None] if address[0] is not None: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index c6827edbe..e5f61f9aa 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1024,7 +1024,6 @@ class NWFilterFirewall(FirewallDriver): security_group = db.security_group_get(context.get_admin_context(), security_group_id) rule_xml = "" - version = 4 v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'} for rule in security_group.rules: rule_xml += "" -- cgit From a6a2a057d8a027781e4270c9abc4f815c67293ec Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Wed, 12 Jan 2011 10:12:18 -0500 Subject: Fixed syntax errors --- nova/api/ec2/cloud.py | 2 +- nova/utils.py | 2 +- nova/virt/libvirt_conn.py | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 72d7bcc95..c5c99f132 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -610,7 +610,7 @@ class CloudController(object): def describe_instances_v6(self, context, **kwargs): kwargs['use_v6'] = True - return self._format_describe_instances_v6(context, **kwargs) + return self._format_describe_instances(context, **kwargs) def _format_describe_instances(self, context, **kwargs): return {'reservationSet': self._format_instances(context, **kwargs)} diff --git a/nova/utils.py b/nova/utils.py index 49344699a..09c9a5f89 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -218,7 +218,7 @@ def get_my_ip(): def get_my_linklocal(interface): try: if_str = execute("ip -f inet6 -o addr show %s" % interface) - condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope:link" + condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope\s+link" links = [re.search(condition, x) for x in if_str[0].split('\n')] address = [w.group(1) for w in links if w is not None] if address[0] is not None: diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index ec17e4db0..263138710 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -940,8 +940,8 @@ class NWFilterFirewall(FirewallDriver): ['no-mac-spoofing', 'no-ip-spoofing', 'no-arp-spoofing', - 'allow-dhcp-server', - 'allow-ra-server'])) + 'allow-dhcp-server' + ])) self._define_filter(self.nova_base_ipv4_filter) self._define_filter(self.nova_base_ipv6_filter) self._define_filter(self.nova_dhcp_filter) @@ -1035,6 +1035,8 @@ class NWFilterFirewall(FirewallDriver): instance_secgroup_filter_children = ['nova-base-ipv4', 'nova-base-ipv6', 'nova-allow-dhcp-server'] + if FLAGS.use_ipv6: + instance_secgroup_filter_children += ['nova-allow-ra-server'] ctxt = context.get_admin_context() -- cgit From b945fed7779bddf799aa4a180d44745052d2da8c Mon Sep 17 00:00:00 2001 From: Hisaharu Ishii Date: Wed, 12 Jan 2011 21:55:36 +0900 Subject: Support IPv6 firewall with IptablesFirewallDriver --- nova/db/sqlalchemy/api.py | 2 +- nova/virt/libvirt_conn.py | 66 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 3b3a88170..2ca16283f 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -798,7 +798,7 @@ def instance_get_fixed_address_v6(context, instance_id): session = get_session() with session.begin(): instance_ref = instance_get(context, instance_id, session=session) - network_ref = project_get_network(context, context.project_id) + network_ref = network_get_by_instance(context, instance_id) prefix = network_ref.cidr_v6 mac = instance_ref.mac_address return utils.to_global_ipv6(prefix, mac) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 263138710..f2ffbf180 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1131,11 +1131,17 @@ class IptablesFirewallDriver(FirewallDriver): def apply_ruleset(self): current_filter, _ = self.execute('sudo iptables-save -t filter') current_lines = current_filter.split('\n') - new_filter = self.modify_rules(current_lines) + new_filter = self.modify_rules(current_lines, 4) self.execute('sudo iptables-restore', process_input='\n'.join(new_filter)) + if(FLAGS.use_ipv6): + current_filter, _ = self.execute('sudo ip6tables-save -t filter') + current_lines = current_filter.split('\n') + new_filter = self.modify_rules(current_lines, 6) + self.execute('sudo ip6tables-restore', + process_input='\n'.join(new_filter)) - def modify_rules(self, current_lines): + def modify_rules(self, current_lines, ip_version): ctxt = context.get_admin_context() # Remove any trace of nova rules. new_filter = filter(lambda l: 'nova-' not in l, current_lines) @@ -1149,8 +1155,8 @@ class IptablesFirewallDriver(FirewallDriver): if not new_filter[rules_index].startswith(':'): break - our_chains = [':nova-ipv4-fallback - [0:0]'] - our_rules = ['-A nova-ipv4-fallback -j DROP'] + our_chains = [':nova-fallback - [0:0]'] + our_rules = ['-A nova-fallback -j DROP'] our_chains += [':nova-local - [0:0]'] our_rules += ['-A FORWARD -j nova-local'] @@ -1160,7 +1166,10 @@ class IptablesFirewallDriver(FirewallDriver): # First, we add instance chains and rules for instance in self.instances: chain_name = self._instance_chain_name(instance) - ip_address = self._ip_for_instance(instance) + if(ip_version == 4): + ip_address = self._ip_for_instance(instance) + elif(ip_version == 6): + ip_address = self._ip_for_instance_v6(instance) our_chains += [':%s - [0:0]' % chain_name] @@ -1186,13 +1195,19 @@ class IptablesFirewallDriver(FirewallDriver): our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)] - # Allow DHCP responses - dhcp_server = self._dhcp_server_for_instance(instance) - our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' % - (chain_name, dhcp_server)] + if(ip_version == 4): + # Allow DHCP responses + dhcp_server = self._dhcp_server_for_instance(instance) + our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' % + (chain_name, dhcp_server)] + elif(ip_version == 6): + # Allow RA responses + ra_server = self._ra_server_for_instance(instance) + our_rules += ['-A %s -s %s -p icmpv6' % + (chain_name, ra_server)] # If nothing matches, jump to the fallback chain - our_rules += ['-A %s -j nova-ipv4-fallback' % (chain_name,)] + our_rules += ['-A %s -j nova-fallback' % (chain_name,)] # then, security group chains and rules for security_group in security_groups: @@ -1205,15 +1220,22 @@ class IptablesFirewallDriver(FirewallDriver): for rule in rules: logging.info('%r', rule) - args = ['-A', chain_name, '-p', rule.protocol] - if rule.cidr: - args += ['-s', rule.cidr] - else: + 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 != ip_version: + continue + + protocol = rule.protocol + if version == 6 and rule.protocol == 'icmp': + protocol = 'icmpv6' + + args = ['-A', chain_name, '-p', protocol, '-s', rule.cidr] + if rule.protocol in ['udp', 'tcp']: if rule.from_port == rule.to_port: args += ['--dport', '%s' % (rule.from_port,)] @@ -1233,7 +1255,12 @@ class IptablesFirewallDriver(FirewallDriver): icmp_type_arg += '/%s' % icmp_code if icmp_type_arg: - args += ['-m', 'icmp', '--icmp-type', 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 ACCEPT'] our_rules += [' '.join(args)] @@ -1259,7 +1286,16 @@ class IptablesFirewallDriver(FirewallDriver): return db.instance_get_fixed_address(context.get_admin_context(), instance['id']) + def _ip_for_instance_v6(self, instance): + return db.instance_get_fixed_address_v6(context.get_admin_context(), + instance['id']) + def _dhcp_server_for_instance(self, instance): network = db.project_get_network(context.get_admin_context(), instance['project_id']) return network['gateway'] + + def _ra_server_for_instance(self, instance): + network = db.project_get_network(context.get_admin_context(), + instance['project_id']) + return network['ra_server'] -- cgit From df0be0318cf22d250bdf9abdd9ed3b91bb83f0ea Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Thu, 13 Jan 2011 09:10:44 +0900 Subject: fixed method signature of modify_rules fixed unit_test for ipv6 --- nova/tests/test_network.py | 17 +++++++++-------- nova/virt/libvirt_conn.py | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index 407dbc769..00f9323f3 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -99,23 +99,24 @@ class NetworkTestCase(test.TestCase): def test_private_ipv6(self): """Make sure ipv6 is OK""" if FLAGS.use_ipv6: - instance_ref = self._create_instance(1) + instance_ref = self._create_instance(0) + address = self._create_address(0, instance_ref['id']) network_ref = db.project_get_network( - self.context, + context.get_admin_context(), self.context.project_id) address_v6 = db.instance_get_fixed_address_v6( - self.context, - instance_ref['id']) + context.get_admin_context(), + instance_ref['id']) self.assertEqual(instance_ref['mac_address'], utils.to_mac(address_v6)) instance_ref2 = db.fixed_ip_get_instance_v6( - self.context, - address_v6) + context.get_admin_context(), + address_v6) self.assertEqual(instance_ref['id'], instance_ref2['id']) self.assertEqual(address_v6, utils.to_global_ipv6( - network_ref['cidr_v6'], - instance_ref['mac_address'])) + network_ref['cidr_v6'], + instance_ref['mac_address'])) def test_public_network_association(self): """Makes sure that we can allocaate a public ip""" diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index f2ffbf180..191292a9d 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -1141,7 +1141,7 @@ class IptablesFirewallDriver(FirewallDriver): self.execute('sudo ip6tables-restore', process_input='\n'.join(new_filter)) - def modify_rules(self, current_lines, ip_version): + def modify_rules(self, current_lines, ip_version=4): ctxt = context.get_admin_context() # Remove any trace of nova rules. new_filter = filter(lambda l: 'nova-' not in l, current_lines) -- cgit From a5026320b4ae14f0171ee450fe79ea687ab5647a Mon Sep 17 00:00:00 2001 From: Koji Iida Date: Thu, 13 Jan 2011 15:58:05 +0900 Subject: Fixed missing _(). Fixed to follow logging to LOG changes. Fixed merge miss (get_fixed_ip was moved away). Update some missing comments. --- nova/network/linux_net.py | 14 +++++++------- nova/tests/test_api.py | 1 - nova/utils.py | 17 ++--------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 50256db2d..677931603 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -292,12 +292,12 @@ interface %s """ % (network_ref['bridge'], network_ref['cidr_v6']) f.write(conf_str) - # Make sure dnsmasq can actually read it (it setuid()s to "nobody") + # Make sure radvd can actually read it (it setuid()s to "nobody") os.chmod(conffile, 0644) pid = _ra_pid_for(network_ref['bridge']) - # if dnsmasq is already running, then tell it to reload + # if radvd is already running, then tell it to reload if pid: out, _err = _execute('cat /proc/%d/cmdline' % pid, check_exit_code=False) @@ -306,9 +306,9 @@ interface %s _execute('sudo kill -HUP %d' % pid) return except Exception as exc: # pylint: disable-msg=W0703 - logging.debug("Hupping radvd threw %s", exc) + LOG.debug(_("Hupping radvd threw %s"), exc) else: - logging.debug("Pid %d is stale, relaunching radvd", pid) + LOG.debug(_("Pid %d is stale, relaunching radvd"), pid) command = _ra_cmd(network_ref) _execute(command) db.network_update(context, network_id, @@ -378,7 +378,7 @@ def _dnsmasq_cmd(net): def _ra_cmd(net): - """Builds dnsmasq command""" + """Builds radvd command""" cmd = ['sudo -E radvd', # ' -u nobody', ' -C %s' % _ra_file(net['bridge'], 'conf'), @@ -408,7 +408,7 @@ def _dhcp_file(bridge, kind): def _ra_file(bridge, kind): - """Return path to a pid, leases or conf file for a bridge""" + """Return path to a pid or conf file for a bridge""" if not os.path.exists(FLAGS.networks_path): os.makedirs(FLAGS.networks_path) @@ -433,7 +433,7 @@ def _dnsmasq_pid_for(bridge): def _ra_pid_for(bridge): - """Returns the pid for prior dnsmasq instance for a bridge + """Returns the pid for prior radvd instance for a bridge Returns None if no pid file exists diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py index a508235c4..d22d7beb1 100644 --- a/nova/tests/test_api.py +++ b/nova/tests/test_api.py @@ -24,7 +24,6 @@ import httplib import random import StringIO import webob -import logging from nova import context from nova import flags diff --git a/nova/utils.py b/nova/utils.py index 09c9a5f89..27589c30c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -202,19 +202,6 @@ def last_octet(address): return int(address.split(".")[-1]) -def get_my_ip(): - """Returns the actual ip of the local machine.""" - try: - csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - csock.connect(('8.8.8.8', 80)) - (addr, port) = csock.getsockname() - csock.close() - return addr - except socket.gaierror as ex: - logging.warn(_("Couldn't get IP, using 127.0.0.1 %s"), ex) - return "127.0.0.1" - - def get_my_linklocal(interface): try: if_str = execute("ip -f inet6 -o addr show %s" % interface) @@ -226,9 +213,9 @@ def get_my_linklocal(interface): else: return 'fe00::' except IndexError as ex: - logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) + LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) except ProcessExecutionError as ex: - logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) + LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex) except: return 'fe00::' -- cgit From 515b4fb9ec53f1fbcfc8e502a51e52f67706cc40 Mon Sep 17 00:00:00 2001 From: Hisaharu Ishii Date: Thu, 13 Jan 2011 16:59:29 +0900 Subject: Fixed Authors --- Authors | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Authors b/Authors index 7bcd5981f..479b796ba 100644 --- a/Authors +++ b/Authors @@ -45,7 +45,7 @@ Trey Morris Vishvananda Ishaya Youcef Laribi Zhixue Wu -Hisaharu Ishii -Koji Iida +Hisaharu Ishii +Koji Iida Nachi Ueno -- cgit From 2d642f64dcf0bcabf739aa1373eb2794112f9be4 Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Fri, 14 Jan 2011 06:18:16 +0900 Subject: Added netaddr for pip-requires --- tools/pip-requires | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/pip-requires b/tools/pip-requires index 341043114..3aa76c24d 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -25,3 +25,4 @@ bzr Twisted>=10.1.0 PasteDeploy paste +netaddr -- cgit From bc0c5ba5026610013759fa731d21e2287e3d709a Mon Sep 17 00:00:00 2001 From: Nachi Ueno Date: Fri, 14 Jan 2011 06:25:41 +0900 Subject: Moved commands which needs sudo to nova.sh --- contrib/nova.sh | 3 +++ nova/network/linux_net.py | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/contrib/nova.sh b/contrib/nova.sh index 3a8955526..1f36d7f89 100755 --- a/contrib/nova.sh +++ b/contrib/nova.sh @@ -89,6 +89,9 @@ if [ "$CMD" == "install" ]; then #For IPV6 sudo apt-get install -y python-netaddr sudo apt-get install -y radvd +#(Nati) Note that this configuration is only needed for nova-network node. + sudo bash -c "echo 1 > /proc/sys/net/ipv6/conf/all/forwarding" + sudo bash -c "echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra" if [ "$USE_MYSQL" == 1 ]; then cat < /proc/sys/net/ipv6/conf/all/forwarding"') - _execute('sudo bash -c ' + - '"echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"') def bind_floating_ip(floating_ip, check_exit_code=True): -- cgit From 4ff82fe82729c46e64242a3a3a8aea1aff6ffeb1 Mon Sep 17 00:00:00 2001 From: Hisaharu Ishii Date: Fri, 14 Jan 2011 11:44:35 +0900 Subject: sort Authors --- Authors | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Authors b/Authors index 479b796ba..74736562e 100644 --- a/Authors +++ b/Authors @@ -1,11 +1,12 @@ + Andy Smith Anne Gentle Anthony Young Antony Messerli Armando Migliaccio Chiradeep Vittal -Chris Behrens Chmouel Boudjnah +Chris Behrens Cory Wright David Pravec Dean Troyer @@ -14,6 +15,7 @@ Ed Leafe Eldar Nugaev Eric Day Ewan Mellor +Hisaharu Ishii Hisaki Ohara Ilya Alekseyev Jay Pipes @@ -26,11 +28,13 @@ Josh Kearney Joshua McKenty Justin Santa Barbara Ken Pepple +Koji Iida Lorin Hochstein Matt Dietz Michael Gundlach Monsyne Dragon Monty Taylor +Nachi Ueno Paul Voccio Rick Clark Rick Harris @@ -45,7 +49,3 @@ Trey Morris Vishvananda Ishaya Youcef Laribi Zhixue Wu -Hisaharu Ishii -Koji Iida -Nachi Ueno - -- cgit