Merge lp:~ntt-pf-lab/nova/ipv6-support into lp:~hudson-openstack/nova/trunk

Proposed by Nachi Ueno
Status: Superseded
Proposed branch: lp:~ntt-pf-lab/nova/ipv6-support
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 1556 lines (+799/-54)
24 files modified
Authors (+5/-1)
bin/nova-manage (+7/-3)
contrib/boto_v6/__init__.py (+37/-0)
contrib/boto_v6/ec2/connection.py (+41/-0)
contrib/boto_v6/ec2/instance.py (+37/-0)
contrib/nova.sh (+9/-0)
nova/api/ec2/cloud.py (+13/-0)
nova/db/api.py (+12/-0)
nova/db/sqlalchemy/api.py (+27/-0)
nova/db/sqlalchemy/models.py (+4/-0)
nova/network/linux_net.py (+83/-0)
nova/network/manager.py (+29/-3)
nova/test.py (+2/-1)
nova/tests/test_api.py (+66/-0)
nova/tests/test_network.py (+22/-0)
nova/utils.py (+36/-0)
nova/virt/libvirt.xml.template (+1/-0)
nova/virt/libvirt_conn.py (+137/-34)
smoketests/admin_smoketests.py (+1/-2)
smoketests/base.py (+15/-2)
smoketests/flags.py (+2/-1)
smoketests/public_network_smoketests.py (+180/-0)
smoketests/user_smoketests.py (+32/-7)
tools/pip-requires (+1/-0)
To merge this branch: bzr merge lp:~ntt-pf-lab/nova/ipv6-support
Reviewer Review Type Date Requested Status
Vish Ishaya (community) Approve
Soren Hansen (community) Approve
Devin Carlen Pending
Review via email: mp+46192@code.launchpad.net

This proposal supersedes a proposal from 2011-01-13.

This proposal has been superseded by a proposal from 2011-01-14.

Description of the change

OpenStack Compute (Nova) IPv4/IPv6 dual stack support
http://wiki.openstack.org/BexarIpv6supportReadme

Tested with
 unit test
 smoke test

No conflict with current branch r 561.
Fixed comment by Soren and Vish

To post a comment you must log in.
Revision history for this message
Soren Hansen (soren) wrote : Posted in a previous version of this proposal
Download full text (9.6 KiB)

Please find my comments inline.

Please note that ipv6 is still a bit of a mystery to me, so I'll review
structure and style moreso than technical correctness.

2011/1/5 Nachi Ueno <email address hidden>:
> Nachi Ueno has proposed merging lp:~ntt-pf-lab/nova/ipv6-support into lp:nova.
>
> Requested reviews:
>  Nova Core (nova-core)
>
> For more details, see:
> https://code.launchpad.net/~ntt-pf-lab/nova/ipv6-support/+merge/45228
>
> OpenStack Compute (Nova) IPv4/IPv6 dual stack support
> http://wiki.openstack.org/BexarIpv6supportReadme
>
> Tested with
>  unit test
>  smoke test
>
> No conflict with current branch r 515.
> --
> https://code.launchpad.net/~ntt-pf-lab/nova/ipv6-support/+merge/45228
> You are subscribed to branch lp:nova.
>
> === modified file 'bin/nova-manage'
> --- bin/nova-manage     2010-12-28 20:11:41 +0000
> +++ bin/nova-manage     2011-01-05 11:56:16 +0000
> @@ -89,7 +89,7 @@
>  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."""
> @@ -432,11 +432,11 @@
>     """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):

This line is too long. Nova is pep8 clean. You can install the pep8
utility and check for yourself.

>         """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]"""

Missing a space (for consistency).

>         if not fixed_range:
>             fixed_range = FLAGS.fixed_range
>         if not num_networks:
> @@ -447,11 +447,15 @@
>             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)
> +
> +

Same here. Please add a space (pep8 will tell you this, too).

> === modified file 'nova/api/ec2/cloud.py'
> --- nova/api/ec2/cloud.py       2011-01-03 19:29:39 +0000
> +++ nova/api/ec2/cloud.py       2011-01-05 11:56:16 +0000
> @@ -30,7 +30,7 @@
>
>  from nova import context
>  import IPy
> -
> +import urllib
>  from nova import crypto
>  from nova import db
>  from nova import exception
> @@ -347,6 +347,7 @@
>             values['group_id'] = source_security_group['id']
>         elif cidr_ip:
>             # If this fails, it throws an...

Read more...

Revision history for this message
Soren Hansen (soren) : Posted in a previous version of this proposal
review: Needs Fixing
Revision history for this message
Devin Carlen (devcamcar) wrote : Posted in a previous version of this proposal

It looks like we are essentially providing our own IPV6 version of boto with this patch, which is a bit scary. If we aren't able to use the official libraries (boto, etc.) for IPV6, then maybe we shouldn't be trying to do this with the EC2 API? Forgive me if this has already been discussed by the community, but I think it's a fair question.

review: Needs Information
Revision history for this message
Nachi Ueno (nati-ueno) wrote : Posted in a previous version of this proposal

>Devin Carlen
Thank you for your comment.
We extended DescribeInstance. However we will revert DescribeInstance,and We will add DescribeInstanceV6 today.
Users,who don't need to see v6 address in a DescribeInstance response,can use standard boto libraries with no side effect in current version.
(Users can use EC2 API and DescribeInstanceV6 API)

We continue to support IPv6 version of boto.
As you can see, we created boto_v6 by extending boto classes.
So the code is really simple :).

Revision history for this message
Soren Hansen (soren) wrote : Posted in a previous version of this proposal
Download full text (5.8 KiB)

I'm getting a number of failures when I run the test suite.

======================================================================
ERROR: test_associate_disassociate_address (nova.tests.test_cloud.CloudTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/soren/src/openstack/nova/nova/nova/tests/test_cloud.py", line 112, in test_associate_disassociate_address
    public_ip=address)
  File "/home/soren/src/openstack/nova/nova/nova/api/ec2/cloud.py", line 758, in associate_address
    self.compute_api.associate_floating_ip(context, instance_id, public_ip)
  File "/home/soren/src/openstack/nova/nova/nova/compute/api.py", line 501, in associate_floating_ip
    instance['fixed_ip'])
  File "/home/soren/src/openstack/nova/nova/nova/network/api.py", line 70, in associate_floating_ip
    host = fixed_ip['network']['host']
  File "/home/soren/src/openstack/nova/nova/nova/db/sqlalchemy/models.py", line 74, in __getitem__
    return getattr(self, key)
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/attributes.py", line 163, in __get__
    instance_dict(instance))
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/attributes.py", line 382, in get
    value = callable_(passive=passive)
  File "/usr/lib/python2.6/dist-packages/sqlalchemy/orm/strategies.py", line 578, in __call__
    (mapperutil.state_str(state), self.key)
DetachedInstanceError: Parent instance <FixedIp at 0x5a13e90> is not bound to a Session; lazy load operation of attribute 'network' cannot proceed

======================================================================
ERROR: test_private_ipv6 (nova.tests.test_network.NetworkTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/soren/src/openstack/nova/nova/nova/tests/test_network.py", line 108, in test_private_ipv6
    instance_ref['id'])
  File "/home/soren/src/openstack/nova/nova/nova/db/api.py", line 361, in instance_get_fixed_address_v6
    return IMPL.instance_get_fixed_address_v6(context, instance_id)
  File "/home/soren/src/openstack/nova/nova/nova/db/sqlalchemy/api.py", line 107, in wrapper
    return f(*args, **kwargs)
  File "/home/soren/src/openstack/nova/nova/nova/db/sqlalchemy/api.py", line 810, in instance_get_fixed_address_v6
    network_ref = network_get_by_instance(context, instance_id)
  File "/home/soren/src/openstack/nova/nova/nova/db/sqlalchemy/api.py", line 95, in wrapper
    raise exception.NotAuthorized()
NotAuthorized: None

======================================================================
ERROR: test_static_filters (nova.tests.test_virt.IptablesFirewallTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/soren/src/openstack/nova/nova/nova/tests/test_virt.py", line 306, in test_static_filters
    out_rules = self.fw.modify_rules(self.in_rules)
TypeError: modify_rules() takes exactly 3 arguments (2 given)

======================================================================
FAIL: test_describe_instances (nova.tests.test_cloud.CloudTestCase)
---------------------------...

Read more...

review: Needs Fixing
Revision history for this message
Nachi Ueno (nati-ueno) wrote : Posted in a previous version of this proposal

Thank you.
We fixed some bug.

export TEST=1
nova/contrib/nova.sh run

~~~~
----------------------------------------------------------------------
Ran 277 tests in 501.898s

OK

Revision history for this message
Vish Ishaya (vishvananda) wrote : Posted in a previous version of this proposal

Tests all pass for me. It looks like we need a dependency on python-netaddr and radvd for packaging. It should probably also be added to the pip requires:

=== modified file 'tools/pip-requires'
--- tools/pip-requires 2010-12-28 00:10:26 +0000
+++ tools/pip-requires 2011-01-13 20:39:01 +0000
@@ -25,3 +25,4 @@
 Twisted>=10.1.0
 PasteDeploy
 paste
+netaddr

It appears we will need a new/updated version of Iptables Security Groups for this, but I think that can be put in as a bug and fixed later.

I don't think the following code will work:

383 + if(FLAGS.use_ipv6):
384 + _execute('sudo bash -c ' +
385 + '"echo 1 > /proc/sys/net/ipv6/conf/all/forwarding"')
386 + _execute('sudo bash -c ' +
387 + '"echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"')

we don't want to give add bash to sudo commands for nova user, so I think we're going to have to depend on admins to set this up in advance or do it in packaging. Perhaps move it to nova.sh for now?

Aside from that the branch looks very clean and well put together. I'm happy to approve after the above changes.

review: Needs Fixing
Revision history for this message
Nachi Ueno (nati-ueno) wrote :

>Vish
Thank you for your review.
We fixed branch,and merged with r561.

Unit test is OK.

export TEST=1
nova/contrib/nova.sh run

----------------------------------------------------------------------
Ran 277 tests in 142.224s

OK

Revision history for this message
Vish Ishaya (vishvananda) wrote :

Thanks nachi. LGTM. I would really appreciate it if you guys could make an effort to help keep this branch up-to-date, since no one here is testing with ipv6 yet.

Revision history for this message
Nachi Ueno (nati-ueno) wrote :

> Soren.

Get_my_linklocal is intendted to get ipv6 linklocal address.
We need this to configure RA server.

Revision history for this message
Nachi Ueno (nati-ueno) wrote :

>Soren

This is example of ip command.

root@u3:~# ip -f inet6 -o addr show eth0
2: eth0 inet6 fd00:150::254/64 scope global \ valid_lft forever preferred_lft forever
2: eth0 inet6 fe80::219:b9ff:fef1:6c73/64 scope link \ valid_lft forever preferred_lft forever

We get fe80::219:b9ff:fef1:6c73/64 by regular expression.

Revision history for this message
Soren Hansen (soren) wrote :

Ah, that explains. I didn't realise there would be two entries for ipv6. It's like magic!

Alright, lgtm, but we're going to need help keeping this up-to-date. I haven't a clue about ipv6.

review: Approve
Revision history for this message
Nachi Ueno (nati-ueno) wrote :

Thank you for your approve.
We will definitely keep ipv6 up-to-date.:)

Revision history for this message
Vish Ishaya (vishvananda) wrote : Posted in a previous version of this proposal

lgtm

review: Approve
Revision history for this message
Vish Ishaya (vishvananda) wrote :

hmm i approved in a previous version somewhere

review: Approve
Revision history for this message
OpenStack Infra (hudson-openstack) wrote :

Attempt to merge into lp:nova failed due to conflicts:

text conflict in contrib/nova.sh

lp:~ntt-pf-lab/nova/ipv6-support updated
495. By Hisaharu Ishii

Merged with r562

496. By Hisaharu Ishii

sort Authors

497. By Koji Iida

Merged to rev.563

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Authors'
2--- Authors 2011-01-12 19:39:25 +0000
3+++ Authors 2011-01-14 02:54:27 +0000
4@@ -1,11 +1,12 @@
5+
6 Andy Smith <code@term.ie>
7 Anne Gentle <anne@openstack.org>
8 Anthony Young <sleepsonthefloor@gmail.com>
9 Antony Messerli <ant@openstack.org>
10 Armando Migliaccio <Armando.Migliaccio@eu.citrix.com>
11 Chiradeep Vittal <chiradeep@cloud.com>
12+Chmouel Boudjnah <chmouel@chmouel.com>
13 Chris Behrens <cbehrens@codestud.com>
14-Chmouel Boudjnah <chmouel@chmouel.com>
15 Cory Wright <corywright@gmail.com>
16 David Pravec <David.Pravec@danix.org>
17 Dean Troyer <dtroyer@gmail.com>
18@@ -14,6 +15,7 @@
19 Eldar Nugaev <enugaev@griddynamics.com>
20 Eric Day <eday@oddments.org>
21 Ewan Mellor <ewan.mellor@citrix.com>
22+Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp>
23 Hisaki Ohara <hisaki.ohara@intel.com>
24 Ilya Alekseyev <ialekseev@griddynamics.com>
25 Jay Pipes <jaypipes@gmail.com>
26@@ -26,11 +28,13 @@
27 Joshua McKenty <jmckenty@gmail.com>
28 Justin Santa Barbara <justin@fathomdb.com>
29 Ken Pepple <ken.pepple@gmail.com>
30+Koji Iida <iida.koji@lab.ntt.co.jp>
31 Lorin Hochstein <lorin@isi.edu>
32 Matt Dietz <matt.dietz@rackspace.com>
33 Michael Gundlach <michael.gundlach@rackspace.com>
34 Monsyne Dragon <mdragon@rackspace.com>
35 Monty Taylor <mordred@inaugust.com>
36+Nachi Ueno <ueno.nachi@lab.ntt.co.jp> <openstack@lab.ntt.co.jp> <nati.ueno@gmail.com> <nova@u4>
37 Paul Voccio <paul@openstack.org>
38 Rick Clark <rick@openstack.org>
39 Rick Harris <rconradharris@gmail.com>
40
41=== modified file 'bin/nova-manage'
42--- bin/nova-manage 2011-01-12 20:12:08 +0000
43+++ bin/nova-manage 2011-01-14 02:54:27 +0000
44@@ -91,6 +91,7 @@
45 flags.DECLARE('network_size', 'nova.network.manager')
46 flags.DECLARE('vlan_start', 'nova.network.manager')
47 flags.DECLARE('vpn_start', 'nova.network.manager')
48+flags.DECLARE('fixed_range_v6', 'nova.network.manager')
49
50
51 class VpnCommands(object):
52@@ -439,11 +440,12 @@
53 """Class for managing networks."""
54
55 def create(self, fixed_range=None, num_networks=None,
56- network_size=None, vlan_start=None, vpn_start=None):
57+ network_size=None, vlan_start=None, vpn_start=None,
58+ fixed_range_v6=None):
59 """Creates fixed ips for host by range
60 arguments: [fixed_range=FLAG], [num_networks=FLAG],
61 [network_size=FLAG], [vlan_start=FLAG],
62- [vpn_start=FLAG]"""
63+ [vpn_start=FLAG], [fixed_range_v6=FLAG]"""
64 if not fixed_range:
65 fixed_range = FLAGS.fixed_range
66 if not num_networks:
67@@ -454,11 +456,13 @@
68 vlan_start = FLAGS.vlan_start
69 if not vpn_start:
70 vpn_start = FLAGS.vpn_start
71+ if not fixed_range_v6:
72+ fixed_range_v6 = FLAGS.fixed_range_v6
73 net_manager = utils.import_object(FLAGS.network_manager)
74 net_manager.create_networks(context.get_admin_context(),
75 fixed_range, int(num_networks),
76 int(network_size), int(vlan_start),
77- int(vpn_start))
78+ int(vpn_start), fixed_range_v6)
79
80
81 class ServiceCommands(object):
82
83=== added directory 'contrib/boto_v6'
84=== added file 'contrib/boto_v6/__init__.py'
85--- contrib/boto_v6/__init__.py 1970-01-01 00:00:00 +0000
86+++ contrib/boto_v6/__init__.py 2011-01-14 02:54:27 +0000
87@@ -0,0 +1,37 @@
88+# Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/
89+# Copyright (c) 2010, Eucalyptus Systems, Inc.
90+# All rights reserved.
91+#
92+# Permission is hereby granted, free of charge, to any person obtaining a
93+# copy of this software and associated documentation files (the
94+# "Software"), to deal in the Software without restriction, including
95+# without limitation the rights to use, copy, modify, merge, publish, dis-
96+# tribute, sublicense, and/or sell copies of the Software, and to permit
97+# persons to whom the Software is furnished to do so, subject to the fol-
98+# lowing conditions:
99+#
100+# The above copyright notice and this permission notice shall be included
101+# in all copies or substantial portions of the Software.
102+#
103+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
104+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
105+# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
106+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
107+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
108+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
109+# IN THE SOFTWARE.
110+
111+
112+def connect_ec2(aws_access_key_id=None, aws_secret_access_key=None, **kwargs):
113+ """
114+ :type aws_access_key_id: string
115+ :param aws_access_key_id: Your AWS Access Key ID
116+
117+ :type aws_secret_access_key: string
118+ :param aws_secret_access_key: Your AWS Secret Access Key
119+
120+ :rtype: :class:`boto.ec2.connection.EC2Connection`
121+ :return: A connection to Amazon's EC2
122+ """
123+ from boto_v6.ec2.connection import EC2ConnectionV6
124+ return EC2ConnectionV6(aws_access_key_id, aws_secret_access_key, **kwargs)
125
126=== added directory 'contrib/boto_v6/ec2'
127=== added file 'contrib/boto_v6/ec2/__init__.py'
128=== added file 'contrib/boto_v6/ec2/connection.py'
129--- contrib/boto_v6/ec2/connection.py 1970-01-01 00:00:00 +0000
130+++ contrib/boto_v6/ec2/connection.py 2011-01-14 02:54:27 +0000
131@@ -0,0 +1,41 @@
132+'''
133+Created on 2010/12/20
134+
135+@author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp>
136+'''
137+import boto
138+import boto.ec2
139+from boto_v6.ec2.instance import ReservationV6
140+
141+
142+class EC2ConnectionV6(boto.ec2.EC2Connection):
143+ '''
144+ EC2Connection for OpenStack IPV6 mode
145+ '''
146+ def get_all_instances(self, instance_ids=None, filters=None):
147+ """
148+ Retrieve all the instances associated with your account.
149+
150+ :type instance_ids: list
151+ :param instance_ids: A list of strings of instance IDs
152+
153+ :type filters: dict
154+ :param filters: Optional filters that can be used to limit
155+ the results returned. Filters are provided
156+ in the form of a dictionary consisting of
157+ filter names as the key and filter values
158+ as the value. The set of allowable filter
159+ names/values is dependent on the request
160+ being performed. Check the EC2 API guide
161+ for details.
162+
163+ :rtype: list
164+ :return: A list of :class:`boto.ec2.instance.Reservation`
165+ """
166+ params = {}
167+ if instance_ids:
168+ self.build_list_params(params, instance_ids, 'InstanceId')
169+ if filters:
170+ self.build_filter_params(params, filters)
171+ return self.get_list('DescribeInstancesV6', params,
172+ [('item', ReservationV6)])
173
174=== added file 'contrib/boto_v6/ec2/instance.py'
175--- contrib/boto_v6/ec2/instance.py 1970-01-01 00:00:00 +0000
176+++ contrib/boto_v6/ec2/instance.py 2011-01-14 02:54:27 +0000
177@@ -0,0 +1,37 @@
178+'''
179+Created on 2010/12/20
180+
181+@author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp>
182+'''
183+import boto
184+from boto.resultset import ResultSet
185+from boto.ec2.instance import Reservation
186+from boto.ec2.instance import Group
187+from boto.ec2.instance import Instance
188+
189+
190+class ReservationV6(Reservation):
191+ def startElement(self, name, attrs, connection):
192+ if name == 'instancesSet':
193+ self.instances = ResultSet([('item', InstanceV6)])
194+ return self.instances
195+ elif name == 'groupSet':
196+ self.groups = ResultSet([('item', Group)])
197+ return self.groups
198+ else:
199+ return None
200+
201+
202+class InstanceV6(Instance):
203+ def __init__(self, connection=None):
204+ Instance.__init__(self, connection)
205+ self.dns_name_v6 = None
206+
207+ def endElement(self, name, value, connection):
208+ Instance.endElement(self, name, value, connection)
209+ if name == 'dnsNameV6':
210+ self.dns_name_v6 = value
211+
212+ def _update(self, updated):
213+ self.__dict__.update(updated.__dict__)
214+ self.dns_name_v6 = updated.dns_name_v6
215
216=== modified file 'contrib/nova.sh'
217--- contrib/nova.sh 2011-01-13 09:57:53 +0000
218+++ contrib/nova.sh 2011-01-14 02:54:27 +0000
219@@ -87,6 +87,13 @@
220 sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot
221 sudo apt-get install -y python-daemon python-eventlet python-gflags python-ipy
222 sudo apt-get install -y python-libvirt python-libxml2 python-routes python-cheetah
223+#For IPV6
224+ sudo apt-get install -y python-netaddr
225+ sudo apt-get install -y radvd
226+#(Nati) Note that this configuration is only needed for nova-network node.
227+ sudo bash -c "echo 1 > /proc/sys/net/ipv6/conf/all/forwarding"
228+ sudo bash -c "echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"
229+
230 if [ "$USE_MYSQL" == 1 ]; then
231 cat <<MYSQL_PRESEED | debconf-set-selections
232 mysql-server-5.1 mysql-server/root_password password $MYSQL_PASS
233@@ -108,6 +115,8 @@
234
235 if [ "$CMD" == "run" ]; then
236 killall dnsmasq
237+ #For IPv6
238+ killall radvd
239 screen -d -m -S nova -t nova
240 sleep 1
241 if [ "$USE_MYSQL" == 1 ]; then
242
243=== modified file 'nova/api/ec2/cloud.py'
244--- nova/api/ec2/cloud.py 2011-01-13 18:24:06 +0000
245+++ nova/api/ec2/cloud.py 2011-01-14 02:54:27 +0000
246@@ -26,9 +26,11 @@
247 import datetime
248 import IPy
249 import os
250+import urllib
251
252 from nova import compute
253 from nova import context
254+
255 from nova import crypto
256 from nova import db
257 from nova import exception
258@@ -374,6 +376,7 @@
259 values['group_id'] = source_security_group['id']
260 elif cidr_ip:
261 # If this fails, it throws an exception. This is what we want.
262+ cidr_ip = urllib.unquote(cidr_ip).decode()
263 IPy.IP(cidr_ip)
264 values['cidr'] = cidr_ip
265 else:
266@@ -643,6 +646,10 @@
267 def describe_instances(self, context, **kwargs):
268 return self._format_describe_instances(context, **kwargs)
269
270+ def describe_instances_v6(self, context, **kwargs):
271+ kwargs['use_v6'] = True
272+ return self._format_describe_instances(context, **kwargs)
273+
274 def _format_describe_instances(self, context, **kwargs):
275 return {'reservationSet': self._format_instances(context, **kwargs)}
276
277@@ -678,10 +685,16 @@
278 if instance['fixed_ip']['floating_ips']:
279 fixed = instance['fixed_ip']
280 floating_addr = fixed['floating_ips'][0]['address']
281+ if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
282+ i['dnsNameV6'] = utils.to_global_ipv6(
283+ instance['fixed_ip']['network']['cidr_v6'],
284+ instance['mac_address'])
285+
286 i['privateDnsName'] = fixed_addr
287 i['publicDnsName'] = floating_addr
288 i['dnsName'] = i['publicDnsName'] or i['privateDnsName']
289 i['keyName'] = instance['key_name']
290+
291 if context.user.is_admin():
292 i['keyName'] = '%s (%s, %s)' % (i['keyName'],
293 instance['project_id'],
294
295=== modified file 'nova/db/api.py'
296--- nova/db/api.py 2011-01-12 23:03:08 +0000
297+++ nova/db/api.py 2011-01-14 02:54:27 +0000
298@@ -299,6 +299,10 @@
299 return IMPL.fixed_ip_get_instance(context, address)
300
301
302+def fixed_ip_get_instance_v6(context, address):
303+ return IMPL.fixed_ip_get_instance_v6(context, address)
304+
305+
306 def fixed_ip_get_network(context, address):
307 """Get a network for a fixed ip by address."""
308 return IMPL.fixed_ip_get_network(context, address)
309@@ -357,6 +361,10 @@
310 return IMPL.instance_get_fixed_address(context, instance_id)
311
312
313+def instance_get_fixed_address_v6(context, instance_id):
314+ return IMPL.instance_get_fixed_address_v6(context, instance_id)
315+
316+
317 def instance_get_floating_address(context, instance_id):
318 """Get the first floating ip address of an instance."""
319 return IMPL.instance_get_floating_address(context, instance_id)
320@@ -552,6 +560,10 @@
321 return IMPL.project_get_network(context, project_id)
322
323
324+def project_get_network_v6(context, project_id):
325+ return IMPL.project_get_network_v6(context, project_id)
326+
327+
328 ###################
329
330
331
332=== modified file 'nova/db/sqlalchemy/api.py'
333--- nova/db/sqlalchemy/api.py 2011-01-12 22:24:16 +0000
334+++ nova/db/sqlalchemy/api.py 2011-01-14 02:54:27 +0000
335@@ -606,6 +606,17 @@
336 return fixed_ip_ref.instance
337
338
339+@require_context
340+def fixed_ip_get_instance_v6(context, address):
341+ session = get_session()
342+ mac = utils.to_mac(address)
343+
344+ result = session.query(models.Instance
345+ ).filter_by(mac_address=mac
346+ ).first()
347+ return result
348+
349+
350 @require_admin_context
351 def fixed_ip_get_network(context, address):
352 fixed_ip_ref = fixed_ip_get_by_address(context, address)
353@@ -794,6 +805,17 @@
354
355
356 @require_context
357+def instance_get_fixed_address_v6(context, instance_id):
358+ session = get_session()
359+ with session.begin():
360+ instance_ref = instance_get(context, instance_id, session=session)
361+ network_ref = network_get_by_instance(context, instance_id)
362+ prefix = network_ref.cidr_v6
363+ mac = instance_ref.mac_address
364+ return utils.to_global_ipv6(prefix, mac)
365+
366+
367+@require_context
368 def instance_get_floating_address(context, instance_id):
369 session = get_session()
370 with session.begin():
371@@ -1130,6 +1152,11 @@
372 return result
373
374
375+@require_context
376+def project_get_network_v6(context, project_id):
377+ return project_get_network(context, project_id)
378+
379+
380 ###################
381
382
383
384=== modified file 'nova/db/sqlalchemy/models.py'
385--- nova/db/sqlalchemy/models.py 2011-01-12 23:03:08 +0000
386+++ nova/db/sqlalchemy/models.py 2011-01-14 02:54:27 +0000
387@@ -411,6 +411,10 @@
388
389 injected = Column(Boolean, default=False)
390 cidr = Column(String(255), unique=True)
391+ cidr_v6 = Column(String(255), unique=True)
392+
393+ ra_server = Column(String(255))
394+
395 netmask = Column(String(255))
396 bridge = Column(String(255))
397 gateway = Column(String(255))
398
399=== modified file 'nova/network/linux_net.py'
400--- nova/network/linux_net.py 2011-01-11 05:49:46 +0000
401+++ nova/network/linux_net.py 2011-01-14 02:54:27 +0000
402@@ -50,6 +50,7 @@
403 'Public IP of network host')
404 flags.DEFINE_bool('use_nova_chains', False,
405 'use the nova_ routing chains instead of default')
406+
407 flags.DEFINE_string('dns_server', None,
408 'if set, uses specific dns server for dnsmasq')
409 flags.DEFINE_string('dmz_cidr', '10.128.0.0/24',
410@@ -196,6 +197,10 @@
411 net_attrs['gateway'],
412 net_attrs['broadcast'],
413 net_attrs['netmask']))
414+ if(FLAGS.use_ipv6):
415+ _execute("sudo ifconfig %s add %s up" % \
416+ (bridge,
417+ net_attrs['cidr_v6']))
418 else:
419 _execute("sudo ifconfig %s up" % bridge)
420 if FLAGS.use_nova_chains:
421@@ -262,6 +267,50 @@
422 _execute(command, addl_env=env)
423
424
425+def update_ra(context, network_id):
426+ network_ref = db.network_get(context, network_id)
427+
428+ conffile = _ra_file(network_ref['bridge'], 'conf')
429+ with open(conffile, 'w') as f:
430+ conf_str = """
431+interface %s
432+{
433+ AdvSendAdvert on;
434+ MinRtrAdvInterval 3;
435+ MaxRtrAdvInterval 10;
436+ prefix %s
437+ {
438+ AdvOnLink on;
439+ AdvAutonomous on;
440+ };
441+};
442+""" % (network_ref['bridge'], network_ref['cidr_v6'])
443+ f.write(conf_str)
444+
445+ # Make sure radvd can actually read it (it setuid()s to "nobody")
446+ os.chmod(conffile, 0644)
447+
448+ pid = _ra_pid_for(network_ref['bridge'])
449+
450+ # if radvd is already running, then tell it to reload
451+ if pid:
452+ out, _err = _execute('cat /proc/%d/cmdline'
453+ % pid, check_exit_code=False)
454+ if conffile in out:
455+ try:
456+ _execute('sudo kill -HUP %d' % pid)
457+ return
458+ except Exception as exc: # pylint: disable-msg=W0703
459+ LOG.debug(_("Hupping radvd threw %s"), exc)
460+ else:
461+ LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
462+ command = _ra_cmd(network_ref)
463+ _execute(command)
464+ db.network_update(context, network_id,
465+ {"ra_server":
466+ utils.get_my_linklocal(network_ref['bridge'])})
467+
468+
469 def _host_dhcp(fixed_ip_ref):
470 """Return a host string for an address"""
471 instance_ref = fixed_ip_ref['instance']
472@@ -323,6 +372,15 @@
473 return ''.join(cmd)
474
475
476+def _ra_cmd(net):
477+ """Builds radvd command"""
478+ cmd = ['sudo -E radvd',
479+# ' -u nobody',
480+ ' -C %s' % _ra_file(net['bridge'], 'conf'),
481+ ' -p %s' % _ra_file(net['bridge'], 'pid')]
482+ return ''.join(cmd)
483+
484+
485 def _stop_dnsmasq(network):
486 """Stops the dnsmasq instance for a given network"""
487 pid = _dnsmasq_pid_for(network)
488@@ -344,6 +402,16 @@
489 kind))
490
491
492+def _ra_file(bridge, kind):
493+ """Return path to a pid or conf file for a bridge"""
494+
495+ if not os.path.exists(FLAGS.networks_path):
496+ os.makedirs(FLAGS.networks_path)
497+ return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path,
498+ bridge,
499+ kind))
500+
501+
502 def _dnsmasq_pid_for(bridge):
503 """Returns the pid for prior dnsmasq instance for a bridge
504
505@@ -357,3 +425,18 @@
506 if os.path.exists(pid_file):
507 with open(pid_file, 'r') as f:
508 return int(f.read())
509+
510+
511+def _ra_pid_for(bridge):
512+ """Returns the pid for prior radvd instance for a bridge
513+
514+ Returns None if no pid file exists
515+
516+ If machine has rebooted pid might be incorrect (caller should check)
517+ """
518+
519+ pid_file = _ra_file(bridge, 'pid')
520+
521+ if os.path.exists(pid_file):
522+ with open(pid_file, 'r') as f:
523+ return int(f.read())
524
525=== modified file 'nova/network/manager.py'
526--- nova/network/manager.py 2011-01-10 02:08:54 +0000
527+++ nova/network/manager.py 2011-01-14 02:54:27 +0000
528@@ -82,6 +82,7 @@
529 flags.DEFINE_string('floating_range', '4.4.4.0/24',
530 'Floating IP address block')
531 flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')
532+flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block')
533 flags.DEFINE_integer('cnt_vpn_clients', 5,
534 'Number of addresses reserved for vpn clients')
535 flags.DEFINE_string('network_driver', 'nova.network.linux_net',
536@@ -90,6 +91,9 @@
537 'Whether to update dhcp when fixed_ip is disassociated')
538 flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600,
539 'Seconds after which a deallocated ip is disassociated')
540+
541+flags.DEFINE_bool('use_ipv6', True,
542+ 'use the ipv6')
543 flags.DEFINE_string('network_host', socket.gethostname(),
544 'Network host to use for ip allocation in flat modes')
545 flags.DEFINE_bool('fake_call', False,
546@@ -235,7 +239,7 @@
547 """Get the network host for the current context."""
548 raise NotImplementedError()
549
550- def create_networks(self, context, num_networks, network_size,
551+ def create_networks(self, context, num_networks, network_size, cidr_v6,
552 *args, **kwargs):
553 """Create networks based on parameters."""
554 raise NotImplementedError()
555@@ -321,9 +325,11 @@
556 pass
557
558 def create_networks(self, context, cidr, num_networks, network_size,
559- *args, **kwargs):
560+ cidr_v6, *args, **kwargs):
561 """Create networks based on parameters."""
562 fixed_net = IPy.IP(cidr)
563+ fixed_net_v6 = IPy.IP(cidr_v6)
564+ significant_bits_v6 = 64
565 for index in range(num_networks):
566 start = index * network_size
567 significant_bits = 32 - int(math.log(network_size, 2))
568@@ -336,7 +342,13 @@
569 net['gateway'] = str(project_net[1])
570 net['broadcast'] = str(project_net.broadcast())
571 net['dhcp_start'] = str(project_net[2])
572+
573+ if(FLAGS.use_ipv6):
574+ cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6)
575+ net['cidr_v6'] = cidr_v6
576+
577 network_ref = self.db.network_create_safe(context, net)
578+
579 if network_ref:
580 self._create_fixed_ips(context, network_ref['id'])
581
582@@ -482,12 +494,16 @@
583 network_ref['bridge'])
584
585 def create_networks(self, context, cidr, num_networks, network_size,
586- vlan_start, vpn_start):
587+ vlan_start, vpn_start, cidr_v6):
588 """Create networks based on parameters."""
589 fixed_net = IPy.IP(cidr)
590+ fixed_net_v6 = IPy.IP(cidr_v6)
591+ network_size_v6 = 1 << 64
592+ significant_bits_v6 = 64
593 for index in range(num_networks):
594 vlan = vlan_start + index
595 start = index * network_size
596+ start_v6 = index * network_size_v6
597 significant_bits = 32 - int(math.log(network_size, 2))
598 cidr = "%s/%s" % (fixed_net[start], significant_bits)
599 project_net = IPy.IP(cidr)
600@@ -500,6 +516,13 @@
601 net['dhcp_start'] = str(project_net[3])
602 net['vlan'] = vlan
603 net['bridge'] = 'br%s' % vlan
604+ if(FLAGS.use_ipv6):
605+ cidr_v6 = "%s/%s" % (
606+ fixed_net_v6[start_v6],
607+ significant_bits_v6
608+ )
609+ net['cidr_v6'] = cidr_v6
610+
611 # NOTE(vish): This makes ports unique accross the cloud, a more
612 # robust solution would be to make them unique per ip
613 net['vpn_public_port'] = vpn_start + index
614@@ -538,6 +561,7 @@
615 self.driver.ensure_vlan_bridge(network_ref['vlan'],
616 network_ref['bridge'],
617 network_ref)
618+
619 # NOTE(vish): only ensure this forward if the address hasn't been set
620 # manually.
621 if address == FLAGS.vpn_ip:
622@@ -546,6 +570,8 @@
623 network_ref['vpn_private_address'])
624 if not FLAGS.fake_network:
625 self.driver.update_dhcp(context, network_id)
626+ if(FLAGS.use_ipv6):
627+ self.driver.update_ra(context, network_id)
628
629 @property
630 def _bottom_reserved_ips(self):
631
632=== modified file 'nova/test.py'
633--- nova/test.py 2010-12-17 01:05:54 +0000
634+++ nova/test.py 2011-01-14 02:54:27 +0000
635@@ -156,7 +156,8 @@
636 FLAGS.fixed_range,
637 5, 16,
638 FLAGS.vlan_start,
639- FLAGS.vpn_start)
640+ FLAGS.vpn_start,
641+ FLAGS.fixed_range_v6)
642
643 # emulate some of the mox stuff, we can't use the metaclass
644 # because it screws with our generators
645
646=== modified file 'nova/tests/test_api.py'
647--- nova/tests/test_api.py 2010-12-17 01:05:54 +0000
648+++ nova/tests/test_api.py 2011-01-14 02:54:27 +0000
649@@ -265,6 +265,72 @@
650
651 return
652
653+ def test_authorize_revoke_security_group_cidr_v6(self):
654+ """
655+ Test that we can add and remove CIDR based rules
656+ to a security group for IPv6
657+ """
658+ self.expect_http()
659+ self.mox.ReplayAll()
660+ user = self.manager.create_user('fake', 'fake', 'fake')
661+ project = self.manager.create_project('fake', 'fake', 'fake')
662+
663+ # At the moment, you need both of these to actually be netadmin
664+ self.manager.add_role('fake', 'netadmin')
665+ project.add_role('fake', 'netadmin')
666+
667+ security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
668+ for x in range(random.randint(4, 8)))
669+
670+ group = self.ec2.create_security_group(security_group_name,
671+ 'test group')
672+
673+ self.expect_http()
674+ self.mox.ReplayAll()
675+ group.connection = self.ec2
676+
677+ group.authorize('tcp', 80, 81, '::/0')
678+
679+ self.expect_http()
680+ self.mox.ReplayAll()
681+
682+ rv = self.ec2.get_all_security_groups()
683+ # I don't bother checkng that we actually find it here,
684+ # because the create/delete unit test further up should
685+ # be good enough for that.
686+ for group in rv:
687+ if group.name == security_group_name:
688+ self.assertEquals(len(group.rules), 1)
689+ self.assertEquals(int(group.rules[0].from_port), 80)
690+ self.assertEquals(int(group.rules[0].to_port), 81)
691+ self.assertEquals(len(group.rules[0].grants), 1)
692+ self.assertEquals(str(group.rules[0].grants[0]), '::/0')
693+
694+ self.expect_http()
695+ self.mox.ReplayAll()
696+ group.connection = self.ec2
697+
698+ group.revoke('tcp', 80, 81, '::/0')
699+
700+ self.expect_http()
701+ self.mox.ReplayAll()
702+
703+ self.ec2.delete_security_group(security_group_name)
704+
705+ self.expect_http()
706+ self.mox.ReplayAll()
707+ group.connection = self.ec2
708+
709+ rv = self.ec2.get_all_security_groups()
710+
711+ self.assertEqual(len(rv), 1)
712+ self.assertEqual(rv[0].name, 'default')
713+
714+ self.manager.delete_project(project)
715+ self.manager.delete_user(user)
716+
717+ return
718+
719 def test_authorize_revoke_security_group_foreign_group(self):
720 """
721 Test that we can grant and revoke another security group access
722
723=== modified file 'nova/tests/test_network.py'
724--- nova/tests/test_network.py 2011-01-04 05:23:35 +0000
725+++ nova/tests/test_network.py 2011-01-14 02:54:27 +0000
726@@ -96,6 +96,28 @@
727 self.context.project_id = self.projects[project_num].id
728 self.network.deallocate_fixed_ip(self.context, address)
729
730+ def test_private_ipv6(self):
731+ """Make sure ipv6 is OK"""
732+ if FLAGS.use_ipv6:
733+ instance_ref = self._create_instance(0)
734+ address = self._create_address(0, instance_ref['id'])
735+ network_ref = db.project_get_network(
736+ context.get_admin_context(),
737+ self.context.project_id)
738+ address_v6 = db.instance_get_fixed_address_v6(
739+ context.get_admin_context(),
740+ instance_ref['id'])
741+ self.assertEqual(instance_ref['mac_address'],
742+ utils.to_mac(address_v6))
743+ instance_ref2 = db.fixed_ip_get_instance_v6(
744+ context.get_admin_context(),
745+ address_v6)
746+ self.assertEqual(instance_ref['id'], instance_ref2['id'])
747+ self.assertEqual(address_v6,
748+ utils.to_global_ipv6(
749+ network_ref['cidr_v6'],
750+ instance_ref['mac_address']))
751+
752 def test_public_network_association(self):
753 """Makes sure that we can allocaate a public ip"""
754 # TODO(vish): better way of adding floating ips
755
756=== modified file 'nova/utils.py'
757--- nova/utils.py 2011-01-12 01:32:12 +0000
758+++ nova/utils.py 2011-01-14 02:54:27 +0000
759@@ -30,6 +30,8 @@
760 import sys
761 import time
762 from xml.sax import saxutils
763+import re
764+import netaddr
765
766 from eventlet import event
767 from eventlet import greenthread
768@@ -200,6 +202,40 @@
769 return int(address.split(".")[-1])
770
771
772+def get_my_linklocal(interface):
773+ try:
774+ if_str = execute("ip -f inet6 -o addr show %s" % interface)
775+ condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope\s+link"
776+ links = [re.search(condition, x) for x in if_str[0].split('\n')]
777+ address = [w.group(1) for w in links if w is not None]
778+ if address[0] is not None:
779+ return address[0]
780+ else:
781+ return 'fe00::'
782+ except IndexError as ex:
783+ LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex)
784+ except ProcessExecutionError as ex:
785+ LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex)
786+ except:
787+ return 'fe00::'
788+
789+
790+def to_global_ipv6(prefix, mac):
791+ mac64 = netaddr.EUI(mac).eui64().words
792+ int_addr = int(''.join(['%02x' % i for i in mac64]), 16)
793+ mac64_addr = netaddr.IPAddress(int_addr)
794+ maskIP = netaddr.IPNetwork(prefix).ip
795+ return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).format()
796+
797+
798+def to_mac(ipv6_address):
799+ address = netaddr.IPAddress(ipv6_address)
800+ mask1 = netaddr.IPAddress("::ffff:ffff:ffff:ffff")
801+ mask2 = netaddr.IPAddress("::0200:0:0:0")
802+ mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words
803+ return ":".join(["%02x" % i for i in mac64[0:3] + mac64[5:8]])
804+
805+
806 def utcnow():
807 """Overridable version of datetime.datetime.utcnow."""
808 if utcnow.override_time:
809
810=== modified file 'nova/virt/libvirt.xml.template'
811--- nova/virt/libvirt.xml.template 2011-01-13 09:57:53 +0000
812+++ nova/virt/libvirt.xml.template 2011-01-14 02:54:27 +0000
813@@ -76,6 +76,7 @@
814 <filterref filter="nova-instance-${name}">
815 <parameter name="IP" value="${ip_address}" />
816 <parameter name="DHCPSERVER" value="${dhcp_server}" />
817+ <parameter name="RASERVER" value="${ra_server}" />
818 #if $getVar('extra_params', False)
819 ${extra_params}
820 #end if
821
822=== modified file 'nova/virt/libvirt_conn.py'
823--- nova/virt/libvirt_conn.py 2011-01-13 23:15:25 +0000
824+++ nova/virt/libvirt_conn.py 2011-01-14 02:54:27 +0000
825@@ -130,6 +130,16 @@
826 return str(net.net()), str(net.netmask())
827
828
829+def _get_net_and_prefixlen(cidr):
830+ net = IPy.IP(cidr)
831+ return str(net.net()), str(net.prefixlen())
832+
833+
834+def _get_ip_version(cidr):
835+ net = IPy.IP(cidr)
836+ return int(net.version())
837+
838+
839 class LibvirtConnection(object):
840
841 def __init__(self, read_only):
842@@ -375,7 +385,6 @@
843 instance['id'],
844 power_state.NOSTATE,
845 'launching')
846-
847 self.nwfilter.setup_basic_filtering(instance)
848 self.firewall_driver.prepare_instance_filter(instance)
849 self._create_image(instance, xml)
850@@ -603,12 +612,16 @@
851 if network_ref['injected']:
852 admin_context = context.get_admin_context()
853 address = db.instance_get_fixed_address(admin_context, inst['id'])
854+ ra_server = network_ref['ra_server']
855+ if not ra_server:
856+ ra_server = "fd00::"
857 with open(FLAGS.injected_network_template) as f:
858 net = f.read() % {'address': address,
859 'netmask': network_ref['netmask'],
860 'gateway': network_ref['gateway'],
861 'broadcast': network_ref['broadcast'],
862- 'dns': network_ref['dns']}
863+ 'dns': network_ref['dns'],
864+ 'ra_server': ra_server}
865 if key or net:
866 if key:
867 LOG.info(_('instance %s: injecting key into image %s'),
868@@ -644,13 +657,30 @@
869 instance['id'])
870 # Assume that the gateway also acts as the dhcp server.
871 dhcp_server = network['gateway']
872-
873+ ra_server = network['ra_server']
874+ if not ra_server:
875+ ra_server = 'fd00::'
876 if FLAGS.allow_project_net_traffic:
877- net, mask = _get_net_and_mask(network['cidr'])
878- extra_params = ("<parameter name=\"PROJNET\" "
879- "value=\"%s\" />\n"
880- "<parameter name=\"PROJMASK\" "
881- "value=\"%s\" />\n") % (net, mask)
882+ if FLAGS.use_ipv6:
883+ net, mask = _get_net_and_mask(network['cidr'])
884+ net_v6, prefixlen_v6 = _get_net_and_prefixlen(
885+ network['cidr_v6'])
886+ extra_params = ("<parameter name=\"PROJNET\" "
887+ "value=\"%s\" />\n"
888+ "<parameter name=\"PROJMASK\" "
889+ "value=\"%s\" />\n"
890+ "<parameter name=\"PROJNETV6\" "
891+ "value=\"%s\" />\n"
892+ "<parameter name=\"PROJMASKV6\" "
893+ "value=\"%s\" />\n") % \
894+ (net, mask, net_v6, prefixlen_v6)
895+ else:
896+ net, mask = _get_net_and_mask(network['cidr'])
897+ extra_params = ("<parameter name=\"PROJNET\" "
898+ "value=\"%s\" />\n"
899+ "<parameter name=\"PROJMASK\" "
900+ "value=\"%s\" />\n") % \
901+ (net, mask)
902 else:
903 extra_params = "\n"
904 if FLAGS.use_cow_images:
905@@ -668,6 +698,7 @@
906 'mac_address': instance['mac_address'],
907 'ip_address': ip_address,
908 'dhcp_server': dhcp_server,
909+ 'ra_server': ra_server,
910 'extra_params': extra_params,
911 'rescue': rescue,
912 'local': instance_type['local_gb'],
913@@ -931,6 +962,15 @@
914 </rule>
915 </filter>'''
916
917+ def nova_ra_filter(self):
918+ return '''<filter name='nova-allow-ra-server' chain='root'>
919+ <uuid>d707fa71-4fb5-4b27-9ab7-ba5ca19c8804</uuid>
920+ <rule action='accept' direction='inout'
921+ priority='100'>
922+ <icmpv6 srcipaddr='$RASERVER'/>
923+ </rule>
924+ </filter>'''
925+
926 def setup_basic_filtering(self, instance):
927 """Set up basic filtering (MAC, IP, and ARP spoofing protection)"""
928 logging.info('called setup_basic_filtering in nwfilter')
929@@ -955,13 +995,17 @@
930 ['no-mac-spoofing',
931 'no-ip-spoofing',
932 'no-arp-spoofing',
933- 'allow-dhcp-server']))
934+ 'allow-dhcp-server'
935+ ]))
936 self._define_filter(self.nova_base_ipv4_filter)
937 self._define_filter(self.nova_base_ipv6_filter)
938 self._define_filter(self.nova_dhcp_filter)
939+ self._define_filter(self.nova_ra_filter)
940 self._define_filter(self.nova_vpn_filter)
941 if FLAGS.allow_project_net_traffic:
942 self._define_filter(self.nova_project_filter)
943+ if FLAGS.use_ipv6:
944+ self._define_filter(self.nova_project_filter_v6)
945
946 self.static_filters_configured = True
947
948@@ -993,13 +1037,13 @@
949
950 def nova_base_ipv6_filter(self):
951 retval = "<filter name='nova-base-ipv6' chain='ipv6'>"
952- for protocol in ['tcp', 'udp', 'icmp']:
953+ for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
954 for direction, action, priority in [('out', 'accept', 399),
955 ('in', 'drop', 400)]:
956 retval += """<rule action='%s' direction='%s' priority='%d'>
957- <%s-ipv6 />
958+ <%s />
959 </rule>""" % (action, direction,
960- priority, protocol)
961+ priority, protocol)
962 retval += '</filter>'
963 return retval
964
965@@ -1012,10 +1056,20 @@
966 retval += '</filter>'
967 return retval
968
969+ def nova_project_filter_v6(self):
970+ retval = "<filter name='nova-project-v6' chain='ipv6'>"
971+ for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
972+ retval += """<rule action='accept' direction='inout'
973+ priority='200'>
974+ <%s srcipaddr='$PROJNETV6'
975+ srcipmask='$PROJMASKV6' />
976+ </rule>""" % (protocol)
977+ retval += '</filter>'
978+ return retval
979+
980 def _define_filter(self, xml):
981 if callable(xml):
982 xml = xml()
983-
984 # execute in a native thread and block current greenthread until done
985 tpool.execute(self._conn.nwfilterDefineXML, xml)
986
987@@ -1029,7 +1083,6 @@
988 it makes sure the filters for the security groups as well as
989 the base filter are all in place.
990 """
991-
992 if instance['image_id'] == FLAGS.vpn_image_id:
993 base_filter = 'nova-vpn'
994 else:
995@@ -1041,11 +1094,15 @@
996 instance_secgroup_filter_children = ['nova-base-ipv4',
997 'nova-base-ipv6',
998 'nova-allow-dhcp-server']
999+ if FLAGS.use_ipv6:
1000+ instance_secgroup_filter_children += ['nova-allow-ra-server']
1001
1002 ctxt = context.get_admin_context()
1003
1004 if FLAGS.allow_project_net_traffic:
1005 instance_filter_children += ['nova-project']
1006+ if FLAGS.use_ipv6:
1007+ instance_filter_children += ['nova-project-v6']
1008
1009 for security_group in db.security_group_get_by_instance(ctxt,
1010 instance['id']):
1011@@ -1073,12 +1130,19 @@
1012 security_group = db.security_group_get(context.get_admin_context(),
1013 security_group_id)
1014 rule_xml = ""
1015+ v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'}
1016 for rule in security_group.rules:
1017 rule_xml += "<rule action='accept' direction='in' priority='300'>"
1018 if rule.cidr:
1019- net, mask = _get_net_and_mask(rule.cidr)
1020- rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1021- (rule.protocol, net, mask)
1022+ version = _get_ip_version(rule.cidr)
1023+ if(FLAGS.use_ipv6 and version == 6):
1024+ net, prefixlen = _get_net_and_prefixlen(rule.cidr)
1025+ rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1026+ (v6protocol[rule.protocol], net, prefixlen)
1027+ else:
1028+ net, mask = _get_net_and_mask(rule.cidr)
1029+ rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1030+ (rule.protocol, net, mask)
1031 if rule.protocol in ['tcp', 'udp']:
1032 rule_xml += "dstportstart='%s' dstportend='%s' " % \
1033 (rule.from_port, rule.to_port)
1034@@ -1093,8 +1157,11 @@
1035
1036 rule_xml += '/>\n'
1037 rule_xml += "</rule>\n"
1038- xml = "<filter name='nova-secgroup-%s' chain='ipv4'>%s</filter>" % \
1039- (security_group_id, rule_xml,)
1040+ xml = "<filter name='nova-secgroup-%s' " % security_group_id
1041+ if(FLAGS.use_ipv6):
1042+ xml += "chain='root'>%s</filter>" % rule_xml
1043+ else:
1044+ xml += "chain='ipv4'>%s</filter>" % rule_xml
1045 return xml
1046
1047 def _instance_filter_name(self, instance):
1048@@ -1131,11 +1198,17 @@
1049 def apply_ruleset(self):
1050 current_filter, _ = self.execute('sudo iptables-save -t filter')
1051 current_lines = current_filter.split('\n')
1052- new_filter = self.modify_rules(current_lines)
1053+ new_filter = self.modify_rules(current_lines, 4)
1054 self.execute('sudo iptables-restore',
1055 process_input='\n'.join(new_filter))
1056+ if(FLAGS.use_ipv6):
1057+ current_filter, _ = self.execute('sudo ip6tables-save -t filter')
1058+ current_lines = current_filter.split('\n')
1059+ new_filter = self.modify_rules(current_lines, 6)
1060+ self.execute('sudo ip6tables-restore',
1061+ process_input='\n'.join(new_filter))
1062
1063- def modify_rules(self, current_lines):
1064+ def modify_rules(self, current_lines, ip_version=4):
1065 ctxt = context.get_admin_context()
1066 # Remove any trace of nova rules.
1067 new_filter = filter(lambda l: 'nova-' not in l, current_lines)
1068@@ -1149,8 +1222,8 @@
1069 if not new_filter[rules_index].startswith(':'):
1070 break
1071
1072- our_chains = [':nova-ipv4-fallback - [0:0]']
1073- our_rules = ['-A nova-ipv4-fallback -j DROP']
1074+ our_chains = [':nova-fallback - [0:0]']
1075+ our_rules = ['-A nova-fallback -j DROP']
1076
1077 our_chains += [':nova-local - [0:0]']
1078 our_rules += ['-A FORWARD -j nova-local']
1079@@ -1161,7 +1234,10 @@
1080 for instance_id in self.instances:
1081 instance = self.instances[instance_id]
1082 chain_name = self._instance_chain_name(instance)
1083- ip_address = self._ip_for_instance(instance)
1084+ if(ip_version == 4):
1085+ ip_address = self._ip_for_instance(instance)
1086+ elif(ip_version == 6):
1087+ ip_address = self._ip_for_instance_v6(instance)
1088
1089 our_chains += [':%s - [0:0]' % chain_name]
1090
1091@@ -1188,13 +1264,19 @@
1092
1093 our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]
1094
1095- # Allow DHCP responses
1096- dhcp_server = self._dhcp_server_for_instance(instance)
1097- our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %
1098- (chain_name, dhcp_server)]
1099+ if(ip_version == 4):
1100+ # Allow DHCP responses
1101+ dhcp_server = self._dhcp_server_for_instance(instance)
1102+ our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %
1103+ (chain_name, dhcp_server)]
1104+ elif(ip_version == 6):
1105+ # Allow RA responses
1106+ ra_server = self._ra_server_for_instance(instance)
1107+ our_rules += ['-A %s -s %s -p icmpv6' %
1108+ (chain_name, ra_server)]
1109
1110 # If nothing matches, jump to the fallback chain
1111- our_rules += ['-A %s -j nova-ipv4-fallback' % (chain_name,)]
1112+ our_rules += ['-A %s -j nova-fallback' % (chain_name,)]
1113
1114 # then, security group chains and rules
1115 for security_group_id in security_groups:
1116@@ -1207,15 +1289,22 @@
1117
1118 for rule in rules:
1119 logging.info('%r', rule)
1120- args = ['-A', chain_name, '-p', rule.protocol]
1121
1122- if rule.cidr:
1123- args += ['-s', rule.cidr]
1124- else:
1125+ if not rule.cidr:
1126 # Eventually, a mechanism to grant access for security
1127 # groups will turn up here. It'll use ipsets.
1128 continue
1129
1130+ version = _get_ip_version(rule.cidr)
1131+ if version != ip_version:
1132+ continue
1133+
1134+ protocol = rule.protocol
1135+ if version == 6 and rule.protocol == 'icmp':
1136+ protocol = 'icmpv6'
1137+
1138+ args = ['-A', chain_name, '-p', protocol, '-s', rule.cidr]
1139+
1140 if rule.protocol in ['udp', 'tcp']:
1141 if rule.from_port == rule.to_port:
1142 args += ['--dport', '%s' % (rule.from_port,)]
1143@@ -1235,7 +1324,12 @@
1144 icmp_type_arg += '/%s' % icmp_code
1145
1146 if icmp_type_arg:
1147- args += ['-m', 'icmp', '--icmp-type', icmp_type_arg]
1148+ if(ip_version == 4):
1149+ args += ['-m', 'icmp', '--icmp-type',
1150+ icmp_type_arg]
1151+ elif(ip_version == 6):
1152+ args += ['-m', 'icmp6', '--icmpv6-type',
1153+ icmp_type_arg]
1154
1155 args += ['-j ACCEPT']
1156 our_rules += [' '.join(args)]
1157@@ -1261,7 +1355,16 @@
1158 return db.instance_get_fixed_address(context.get_admin_context(),
1159 instance['id'])
1160
1161+ def _ip_for_instance_v6(self, instance):
1162+ return db.instance_get_fixed_address_v6(context.get_admin_context(),
1163+ instance['id'])
1164+
1165 def _dhcp_server_for_instance(self, instance):
1166 network = db.project_get_network(context.get_admin_context(),
1167 instance['project_id'])
1168 return network['gateway']
1169+
1170+ def _ra_server_for_instance(self, instance):
1171+ network = db.project_get_network(context.get_admin_context(),
1172+ instance['project_id'])
1173+ return network['ra_server']
1174
1175=== modified file 'smoketests/admin_smoketests.py'
1176--- smoketests/admin_smoketests.py 2011-01-06 01:51:05 +0000
1177+++ smoketests/admin_smoketests.py 2011-01-14 02:54:27 +0000
1178@@ -43,7 +43,7 @@
1179 # TODO(devamcar): Use random tempfile
1180 ZIP_FILENAME = '/tmp/nova-me-x509.zip'
1181
1182-TEST_PREFIX = 'test%s' % int(random.random()*1000000)
1183+TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
1184 TEST_USERNAME = '%suser' % TEST_PREFIX
1185 TEST_PROJECTNAME = '%sproject' % TEST_PREFIX
1186
1187@@ -96,4 +96,3 @@
1188 if __name__ == "__main__":
1189 suites = {'user': unittest.makeSuite(UserTests)}
1190 sys.exit(base.run_tests(suites))
1191-
1192
1193=== modified file 'smoketests/base.py'
1194--- smoketests/base.py 2010-11-04 22:50:23 +0000
1195+++ smoketests/base.py 2011-01-14 02:54:27 +0000
1196@@ -17,6 +17,7 @@
1197 # under the License.
1198
1199 import boto
1200+import boto_v6
1201 import commands
1202 import httplib
1203 import os
1204@@ -69,6 +70,17 @@
1205 'test.')
1206
1207 parts = self.split_clc_url(clc_url)
1208+ if FLAGS.use_ipv6:
1209+ return boto_v6.connect_ec2(aws_access_key_id=access_key,
1210+ aws_secret_access_key=secret_key,
1211+ is_secure=parts['is_secure'],
1212+ region=RegionInfo(None,
1213+ 'nova',
1214+ parts['ip']),
1215+ port=parts['port'],
1216+ path='/services/Cloud',
1217+ **kwargs)
1218+
1219 return boto.connect_ec2(aws_access_key_id=access_key,
1220 aws_secret_access_key=secret_key,
1221 is_secure=parts['is_secure'],
1222@@ -115,7 +127,8 @@
1223 return True
1224
1225 def upload_image(self, bucket_name, image):
1226- cmd = 'euca-upload-bundle -b %s -m /tmp/%s.manifest.xml' % (bucket_name, image)
1227+ cmd = 'euca-upload-bundle -b '
1228+ cmd += '%s -m /tmp/%s.manifest.xml' % (bucket_name, image)
1229 status, output = commands.getstatusoutput(cmd)
1230 if status != 0:
1231 print '%s -> \n %s' % (cmd, output)
1232@@ -130,6 +143,7 @@
1233 raise Exception(output)
1234 return True
1235
1236+
1237 def run_tests(suites):
1238 argv = FLAGS(sys.argv)
1239
1240@@ -151,4 +165,3 @@
1241 else:
1242 for suite in suites.itervalues():
1243 unittest.TextTestRunner(verbosity=2).run(suite)
1244-
1245
1246=== modified file 'smoketests/flags.py'
1247--- smoketests/flags.py 2010-11-04 22:50:23 +0000
1248+++ smoketests/flags.py 2011-01-14 02:54:27 +0000
1249@@ -33,6 +33,7 @@
1250 # __GLOBAL FLAGS ONLY__
1251 # Define any app-specific flags in their own files, docs at:
1252 # http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39
1253+
1254 DEFINE_string('region', 'nova', 'Region to use')
1255 DEFINE_string('test_image', 'ami-tiny', 'Image to use for launch tests')
1256-
1257+DEFINE_string('use_ipv6', True, 'use the ipv6 or not')
1258
1259=== added file 'smoketests/public_network_smoketests.py'
1260--- smoketests/public_network_smoketests.py 1970-01-01 00:00:00 +0000
1261+++ smoketests/public_network_smoketests.py 2011-01-14 02:54:27 +0000
1262@@ -0,0 +1,180 @@
1263+# vim: tabstop=4 shiftwidth=4 softtabstop=4
1264+
1265+# Copyright 2010 United States Government as represented by the
1266+# Administrator of the National Aeronautics and Space Administration.
1267+# All Rights Reserved.
1268+#
1269+# Licensed under the Apache License, Version 2.0 (the "License"); you may
1270+# not use this file except in compliance with the License. You may obtain
1271+# a copy of the License at
1272+#
1273+# http://www.apache.org/licenses/LICENSE-2.0
1274+#
1275+# Unless required by applicable law or agreed to in writing, software
1276+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1277+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1278+# License for the specific language governing permissions and limitations
1279+# under the License.
1280+
1281+import commands
1282+import os
1283+import random
1284+import socket
1285+import sys
1286+import time
1287+import unittest
1288+
1289+from smoketests import flags
1290+from smoketests import base
1291+from smoketests import user_smoketests
1292+
1293+#Note that this test should run from
1294+#public network (outside of private network segments)
1295+#Please set EC2_URL correctly
1296+#You should use admin account in this test
1297+
1298+FLAGS = flags.FLAGS
1299+
1300+TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
1301+TEST_BUCKET = '%s_bucket' % TEST_PREFIX
1302+TEST_KEY = '%s_key' % TEST_PREFIX
1303+TEST_KEY2 = '%s_key2' % TEST_PREFIX
1304+TEST_DATA = {}
1305+
1306+
1307+class InstanceTestsFromPublic(user_smoketests.UserSmokeTestCase):
1308+ def test_001_can_create_keypair(self):
1309+ key = self.create_key_pair(self.conn, TEST_KEY)
1310+ self.assertEqual(key.name, TEST_KEY)
1311+
1312+ def test_002_security_group(self):
1313+ security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
1314+ for x in range(random.randint(4, 8)))
1315+ group = self.conn.create_security_group(security_group_name,
1316+ 'test group')
1317+ group.connection = self.conn
1318+ group.authorize('tcp', 22, 22, '0.0.0.0/0')
1319+ if FLAGS.use_ipv6:
1320+ group.authorize('tcp', 22, 22, '::/0')
1321+
1322+ reservation = self.conn.run_instances(FLAGS.test_image,
1323+ key_name=TEST_KEY,
1324+ security_groups=[security_group_name],
1325+ instance_type='m1.tiny')
1326+ self.data['security_group_name'] = security_group_name
1327+ self.data['group'] = group
1328+ self.data['instance_id'] = reservation.instances[0].id
1329+
1330+ def test_003_instance_with_group_runs_within_60_seconds(self):
1331+ reservations = self.conn.get_all_instances([self.data['instance_id']])
1332+ instance = reservations[0].instances[0]
1333+ # allow 60 seconds to exit pending with IP
1334+ for x in xrange(60):
1335+ instance.update()
1336+ if instance.state == u'running':
1337+ break
1338+ time.sleep(1)
1339+ else:
1340+ self.fail('instance failed to start')
1341+ ip = reservations[0].instances[0].private_dns_name
1342+ self.failIf(ip == '0.0.0.0')
1343+ self.data['private_ip'] = ip
1344+ if FLAGS.use_ipv6:
1345+ ipv6 = reservations[0].instances[0].dns_name_v6
1346+ self.failIf(ipv6 is None)
1347+ self.data['ip_v6'] = ipv6
1348+
1349+ def test_004_can_ssh_to_ipv6(self):
1350+ if FLAGS.use_ipv6:
1351+ for x in xrange(20):
1352+ try:
1353+ conn = self.connect_ssh(
1354+ self.data['ip_v6'], TEST_KEY)
1355+ conn.close()
1356+ except Exception as ex:
1357+ print ex
1358+ time.sleep(1)
1359+ else:
1360+ break
1361+ else:
1362+ self.fail('could not ssh to instance')
1363+
1364+ def test_012_can_create_instance_with_keypair(self):
1365+ if 'instance_id' in self.data:
1366+ self.conn.terminate_instances([self.data['instance_id']])
1367+ reservation = self.conn.run_instances(FLAGS.test_image,
1368+ key_name=TEST_KEY,
1369+ instance_type='m1.tiny')
1370+ self.assertEqual(len(reservation.instances), 1)
1371+ self.data['instance_id'] = reservation.instances[0].id
1372+
1373+ def test_013_instance_runs_within_60_seconds(self):
1374+ reservations = self.conn.get_all_instances([self.data['instance_id']])
1375+ instance = reservations[0].instances[0]
1376+ # allow 60 seconds to exit pending with IP
1377+ for x in xrange(60):
1378+ instance.update()
1379+ if instance.state == u'running':
1380+ break
1381+ time.sleep(1)
1382+ else:
1383+ self.fail('instance failed to start')
1384+ ip = reservations[0].instances[0].private_dns_name
1385+ self.failIf(ip == '0.0.0.0')
1386+ self.data['private_ip'] = ip
1387+ if FLAGS.use_ipv6:
1388+ ipv6 = reservations[0].instances[0].dns_name_v6
1389+ self.failIf(ipv6 is None)
1390+ self.data['ip_v6'] = ipv6
1391+
1392+ def test_014_can_not_ping_private_ip(self):
1393+ for x in xrange(4):
1394+ # ping waits for 1 second
1395+ status, output = commands.getstatusoutput(
1396+ 'ping -c1 %s' % self.data['private_ip'])
1397+ if status == 0:
1398+ self.fail('can ping private ip from public network')
1399+ if FLAGS.use_ipv6:
1400+ status, output = commands.getstatusoutput(
1401+ 'ping6 -c1 %s' % self.data['ip_v6'])
1402+ if status == 0:
1403+ self.fail('can ping ipv6 from public network')
1404+ else:
1405+ pass
1406+
1407+ def test_015_can_not_ssh_to_private_ip(self):
1408+ for x in xrange(1):
1409+ try:
1410+ conn = self.connect_ssh(self.data['private_ip'], TEST_KEY)
1411+ conn.close()
1412+ except Exception:
1413+ time.sleep(1)
1414+ else:
1415+ self.fail('can ssh for ipv4 address from public network')
1416+
1417+ if FLAGS.use_ipv6:
1418+ for x in xrange(1):
1419+ try:
1420+ conn = self.connect_ssh(
1421+ self.data['ip_v6'], TEST_KEY)
1422+ conn.close()
1423+ except Exception:
1424+ time.sleep(1)
1425+ else:
1426+ self.fail('can ssh for ipv6 address from public network')
1427+
1428+ def test_999_tearDown(self):
1429+ self.delete_key_pair(self.conn, TEST_KEY)
1430+ security_group_name = self.data['security_group_name']
1431+ group = self.data['group']
1432+ if group:
1433+ group.revoke('tcp', 22, 22, '0.0.0.0/0')
1434+ if FLAGS.use_ipv6:
1435+ group.revoke('tcp', 22, 22, '::/0')
1436+ self.conn.delete_security_group(security_group_name)
1437+ if 'instance_id' in self.data:
1438+ self.conn.terminate_instances([self.data['instance_id']])
1439+
1440+if __name__ == "__main__":
1441+ suites = {'instance': unittest.makeSuite(InstanceTestsFromPublic)}
1442+ sys.exit(base.run_tests(suites))
1443
1444=== modified file 'smoketests/user_smoketests.py'
1445--- smoketests/user_smoketests.py 2011-01-06 01:51:05 +0000
1446+++ smoketests/user_smoketests.py 2011-01-14 02:54:27 +0000
1447@@ -45,7 +45,7 @@
1448 flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image',
1449 'Local image file to use for bundling tests')
1450
1451-TEST_PREFIX = 'test%s' % int (random.random()*1000000)
1452+TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
1453 TEST_BUCKET = '%s_bucket' % TEST_PREFIX
1454 TEST_KEY = '%s_key' % TEST_PREFIX
1455 TEST_GROUP = '%s_group' % TEST_PREFIX
1456@@ -80,7 +80,7 @@
1457
1458 def test_006_can_register_kernel(self):
1459 kernel_id = self.conn.register_image('%s/%s.manifest.xml' %
1460- (TEST_BUCKET, FLAGS.bundle_kernel))
1461+ (TEST_BUCKET, FLAGS.bundle_kernel))
1462 self.assert_(kernel_id is not None)
1463 self.data['kernel_id'] = kernel_id
1464
1465@@ -92,7 +92,7 @@
1466 time.sleep(1)
1467 else:
1468 print image.state
1469- self.assert_(False) # wasn't available within 10 seconds
1470+ self.assert_(False) # wasn't available within 10 seconds
1471 self.assert_(image.type == 'machine')
1472
1473 for i in xrange(10):
1474@@ -101,7 +101,7 @@
1475 break
1476 time.sleep(1)
1477 else:
1478- self.assert_(False) # wasn't available within 10 seconds
1479+ self.assert_(False) # wasn't available within 10 seconds
1480 self.assert_(kernel.type == 'kernel')
1481
1482 def test_008_can_describe_image_attribute(self):
1483@@ -152,14 +152,17 @@
1484 for x in xrange(60):
1485 instance.update()
1486 if instance.state == u'running':
1487- break
1488+ break
1489 time.sleep(1)
1490 else:
1491 self.fail('instance failed to start')
1492 ip = reservations[0].instances[0].private_dns_name
1493 self.failIf(ip == '0.0.0.0')
1494 self.data['private_ip'] = ip
1495- print self.data['private_ip']
1496+ if FLAGS.use_ipv6:
1497+ ipv6 = reservations[0].instances[0].dns_name_v6
1498+ self.failIf(ipv6 is None)
1499+ self.data['ip_v6'] = ipv6
1500
1501 def test_004_can_ping_private_ip(self):
1502 for x in xrange(120):
1503@@ -171,6 +174,16 @@
1504 else:
1505 self.fail('could not ping instance')
1506
1507+ if FLAGS.use_ipv6:
1508+ for x in xrange(120):
1509+ # ping waits for 1 second
1510+ status, output = commands.getstatusoutput(
1511+ 'ping6 -c1 %s' % self.data['ip_v6'])
1512+ if status == 0:
1513+ break
1514+ else:
1515+ self.fail('could not ping instance')
1516+
1517 def test_005_can_ssh_to_private_ip(self):
1518 for x in xrange(30):
1519 try:
1520@@ -183,6 +196,19 @@
1521 else:
1522 self.fail('could not ssh to instance')
1523
1524+ if FLAGS.use_ipv6:
1525+ for x in xrange(30):
1526+ try:
1527+ conn = self.connect_ssh(
1528+ self.data['ip_v6'], TEST_KEY)
1529+ conn.close()
1530+ except Exception:
1531+ time.sleep(1)
1532+ else:
1533+ break
1534+ else:
1535+ self.fail('could not ssh to instance v6')
1536+
1537 def test_006_can_allocate_elastic_ip(self):
1538 result = self.conn.allocate_address()
1539 self.assertTrue(hasattr(result, 'public_ip'))
1540@@ -388,7 +414,6 @@
1541 raise Exception("Timeout")
1542 time.sleep(1)
1543
1544-
1545 def test_999_tearDown(self):
1546 self.conn.delete_key_pair(TEST_KEY)
1547 self.conn.delete_security_group(TEST_GROUP)
1548
1549=== modified file 'tools/pip-requires'
1550--- tools/pip-requires 2010-12-28 00:10:26 +0000
1551+++ tools/pip-requires 2011-01-14 02:54:27 +0000
1552@@ -25,3 +25,4 @@
1553 Twisted>=10.1.0
1554 PasteDeploy
1555 paste
1556+netaddr