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

Proposed by Hisaharu Ishii
Status: Superseded
Proposed branch: lp:~ntt-pf-lab/nova/ipv6-support
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 1528 lines (+798/-53)
24 files modified
Authors (+4/-0)
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
Devin Carlen Pending
Soren Hansen Pending
Review via email: mp+46103@code.launchpad.net

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

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

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 557.
Fixed comment by Soren

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 :

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
lp:~ntt-pf-lab/nova/ipv6-support updated
492. By Nachi Ueno

Added netaddr for pip-requires

493. By Nachi Ueno

Moved commands which needs sudo to nova.sh

494. By Nachi Ueno

Merged with r561

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

lgtm

review: Approve
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
=== modified file 'Authors'
--- Authors 2011-01-12 19:39:25 +0000
+++ Authors 2011-01-13 21:53:11 +0000
@@ -45,3 +45,7 @@
45Vishvananda Ishaya <vishvananda@gmail.com>45Vishvananda Ishaya <vishvananda@gmail.com>
46Youcef Laribi <Youcef.Laribi@eu.citrix.com>46Youcef Laribi <Youcef.Laribi@eu.citrix.com>
47Zhixue Wu <Zhixue.Wu@citrix.com>47Zhixue Wu <Zhixue.Wu@citrix.com>
48Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp>
49Koji Iida <iida.koji@lab.ntt.co.jp>
50Nachi Ueno <ueno.nachi@lab.ntt.co.jp> <openstack@lab.ntt.co.jp> <nati.ueno@gmail.com> <nova@u4>
51
4852
=== modified file 'bin/nova-manage'
--- bin/nova-manage 2011-01-12 20:12:08 +0000
+++ bin/nova-manage 2011-01-13 21:53:11 +0000
@@ -91,6 +91,7 @@
91flags.DECLARE('network_size', 'nova.network.manager')91flags.DECLARE('network_size', 'nova.network.manager')
92flags.DECLARE('vlan_start', 'nova.network.manager')92flags.DECLARE('vlan_start', 'nova.network.manager')
93flags.DECLARE('vpn_start', 'nova.network.manager')93flags.DECLARE('vpn_start', 'nova.network.manager')
94flags.DECLARE('fixed_range_v6', 'nova.network.manager')
9495
9596
96class VpnCommands(object):97class VpnCommands(object):
@@ -439,11 +440,12 @@
439 """Class for managing networks."""440 """Class for managing networks."""
440441
441 def create(self, fixed_range=None, num_networks=None,442 def create(self, fixed_range=None, num_networks=None,
442 network_size=None, vlan_start=None, vpn_start=None):443 network_size=None, vlan_start=None, vpn_start=None,
444 fixed_range_v6=None):
443 """Creates fixed ips for host by range445 """Creates fixed ips for host by range
444 arguments: [fixed_range=FLAG], [num_networks=FLAG],446 arguments: [fixed_range=FLAG], [num_networks=FLAG],
445 [network_size=FLAG], [vlan_start=FLAG],447 [network_size=FLAG], [vlan_start=FLAG],
446 [vpn_start=FLAG]"""448 [vpn_start=FLAG], [fixed_range_v6=FLAG]"""
447 if not fixed_range:449 if not fixed_range:
448 fixed_range = FLAGS.fixed_range450 fixed_range = FLAGS.fixed_range
449 if not num_networks:451 if not num_networks:
@@ -454,11 +456,13 @@
454 vlan_start = FLAGS.vlan_start456 vlan_start = FLAGS.vlan_start
455 if not vpn_start:457 if not vpn_start:
456 vpn_start = FLAGS.vpn_start458 vpn_start = FLAGS.vpn_start
459 if not fixed_range_v6:
460 fixed_range_v6 = FLAGS.fixed_range_v6
457 net_manager = utils.import_object(FLAGS.network_manager)461 net_manager = utils.import_object(FLAGS.network_manager)
458 net_manager.create_networks(context.get_admin_context(),462 net_manager.create_networks(context.get_admin_context(),
459 fixed_range, int(num_networks),463 fixed_range, int(num_networks),
460 int(network_size), int(vlan_start),464 int(network_size), int(vlan_start),
461 int(vpn_start))465 int(vpn_start), fixed_range_v6)
462466
463467
464class ServiceCommands(object):468class ServiceCommands(object):
465469
=== added directory 'contrib/boto_v6'
=== added file 'contrib/boto_v6/__init__.py'
--- contrib/boto_v6/__init__.py 1970-01-01 00:00:00 +0000
+++ contrib/boto_v6/__init__.py 2011-01-13 21:53:11 +0000
@@ -0,0 +1,37 @@
1# Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/
2# Copyright (c) 2010, Eucalyptus Systems, Inc.
3# All rights reserved.
4#
5# Permission is hereby granted, free of charge, to any person obtaining a
6# copy of this software and associated documentation files (the
7# "Software"), to deal in the Software without restriction, including
8# without limitation the rights to use, copy, modify, merge, publish, dis-
9# tribute, sublicense, and/or sell copies of the Software, and to permit
10# persons to whom the Software is furnished to do so, subject to the fol-
11# lowing conditions:
12#
13# The above copyright notice and this permission notice shall be included
14# in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
18# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22# IN THE SOFTWARE.
23
24
25def connect_ec2(aws_access_key_id=None, aws_secret_access_key=None, **kwargs):
26 """
27 :type aws_access_key_id: string
28 :param aws_access_key_id: Your AWS Access Key ID
29
30 :type aws_secret_access_key: string
31 :param aws_secret_access_key: Your AWS Secret Access Key
32
33 :rtype: :class:`boto.ec2.connection.EC2Connection`
34 :return: A connection to Amazon's EC2
35 """
36 from boto_v6.ec2.connection import EC2ConnectionV6
37 return EC2ConnectionV6(aws_access_key_id, aws_secret_access_key, **kwargs)
038
=== added directory 'contrib/boto_v6/ec2'
=== added file 'contrib/boto_v6/ec2/__init__.py'
=== added file 'contrib/boto_v6/ec2/connection.py'
--- contrib/boto_v6/ec2/connection.py 1970-01-01 00:00:00 +0000
+++ contrib/boto_v6/ec2/connection.py 2011-01-13 21:53:11 +0000
@@ -0,0 +1,41 @@
1'''
2Created on 2010/12/20
3
4@author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp>
5'''
6import boto
7import boto.ec2
8from boto_v6.ec2.instance import ReservationV6
9
10
11class EC2ConnectionV6(boto.ec2.EC2Connection):
12 '''
13 EC2Connection for OpenStack IPV6 mode
14 '''
15 def get_all_instances(self, instance_ids=None, filters=None):
16 """
17 Retrieve all the instances associated with your account.
18
19 :type instance_ids: list
20 :param instance_ids: A list of strings of instance IDs
21
22 :type filters: dict
23 :param filters: Optional filters that can be used to limit
24 the results returned. Filters are provided
25 in the form of a dictionary consisting of
26 filter names as the key and filter values
27 as the value. The set of allowable filter
28 names/values is dependent on the request
29 being performed. Check the EC2 API guide
30 for details.
31
32 :rtype: list
33 :return: A list of :class:`boto.ec2.instance.Reservation`
34 """
35 params = {}
36 if instance_ids:
37 self.build_list_params(params, instance_ids, 'InstanceId')
38 if filters:
39 self.build_filter_params(params, filters)
40 return self.get_list('DescribeInstancesV6', params,
41 [('item', ReservationV6)])
042
=== added file 'contrib/boto_v6/ec2/instance.py'
--- contrib/boto_v6/ec2/instance.py 1970-01-01 00:00:00 +0000
+++ contrib/boto_v6/ec2/instance.py 2011-01-13 21:53:11 +0000
@@ -0,0 +1,37 @@
1'''
2Created on 2010/12/20
3
4@author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp>
5'''
6import boto
7from boto.resultset import ResultSet
8from boto.ec2.instance import Reservation
9from boto.ec2.instance import Group
10from boto.ec2.instance import Instance
11
12
13class ReservationV6(Reservation):
14 def startElement(self, name, attrs, connection):
15 if name == 'instancesSet':
16 self.instances = ResultSet([('item', InstanceV6)])
17 return self.instances
18 elif name == 'groupSet':
19 self.groups = ResultSet([('item', Group)])
20 return self.groups
21 else:
22 return None
23
24
25class InstanceV6(Instance):
26 def __init__(self, connection=None):
27 Instance.__init__(self, connection)
28 self.dns_name_v6 = None
29
30 def endElement(self, name, value, connection):
31 Instance.endElement(self, name, value, connection)
32 if name == 'dnsNameV6':
33 self.dns_name_v6 = value
34
35 def _update(self, updated):
36 self.__dict__.update(updated.__dict__)
37 self.dns_name_v6 = updated.dns_name_v6
038
=== modified file 'contrib/nova.sh'
--- contrib/nova.sh 2010-12-30 00:07:41 +0000
+++ contrib/nova.sh 2011-01-13 21:53:11 +0000
@@ -86,6 +86,13 @@
86 sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot86 sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot
87 sudo apt-get install -y python-daemon python-eventlet python-gflags python-tornado python-ipy87 sudo apt-get install -y python-daemon python-eventlet python-gflags python-tornado python-ipy
88 sudo apt-get install -y python-libvirt python-libxml2 python-routes88 sudo apt-get install -y python-libvirt python-libxml2 python-routes
89#For IPV6
90 sudo apt-get install -y python-netaddr
91 sudo apt-get install -y radvd
92#(Nati) Note that this configuration is only needed for nova-network node.
93 sudo bash -c "echo 1 > /proc/sys/net/ipv6/conf/all/forwarding"
94 sudo bash -c "echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"
95
89 if [ "$USE_MYSQL" == 1 ]; then96 if [ "$USE_MYSQL" == 1 ]; then
90 cat <<MYSQL_PRESEED | debconf-set-selections97 cat <<MYSQL_PRESEED | debconf-set-selections
91mysql-server-5.1 mysql-server/root_password password $MYSQL_PASS98mysql-server-5.1 mysql-server/root_password password $MYSQL_PASS
@@ -107,6 +114,8 @@
107114
108if [ "$CMD" == "run" ]; then115if [ "$CMD" == "run" ]; then
109 killall dnsmasq116 killall dnsmasq
117 #For IPv6
118 killall radvd
110 screen -d -m -S nova -t nova119 screen -d -m -S nova -t nova
111 sleep 1120 sleep 1
112 if [ "$USE_MYSQL" == 1 ]; then121 if [ "$USE_MYSQL" == 1 ]; then
113122
=== modified file 'nova/api/ec2/cloud.py'
--- nova/api/ec2/cloud.py 2011-01-13 18:24:06 +0000
+++ nova/api/ec2/cloud.py 2011-01-13 21:53:11 +0000
@@ -26,9 +26,11 @@
26import datetime26import datetime
27import IPy27import IPy
28import os28import os
29import urllib
2930
30from nova import compute31from nova import compute
31from nova import context32from nova import context
33
32from nova import crypto34from nova import crypto
33from nova import db35from nova import db
34from nova import exception36from nova import exception
@@ -374,6 +376,7 @@
374 values['group_id'] = source_security_group['id']376 values['group_id'] = source_security_group['id']
375 elif cidr_ip:377 elif cidr_ip:
376 # If this fails, it throws an exception. This is what we want.378 # If this fails, it throws an exception. This is what we want.
379 cidr_ip = urllib.unquote(cidr_ip).decode()
377 IPy.IP(cidr_ip)380 IPy.IP(cidr_ip)
378 values['cidr'] = cidr_ip381 values['cidr'] = cidr_ip
379 else:382 else:
@@ -643,6 +646,10 @@
643 def describe_instances(self, context, **kwargs):646 def describe_instances(self, context, **kwargs):
644 return self._format_describe_instances(context, **kwargs)647 return self._format_describe_instances(context, **kwargs)
645648
649 def describe_instances_v6(self, context, **kwargs):
650 kwargs['use_v6'] = True
651 return self._format_describe_instances(context, **kwargs)
652
646 def _format_describe_instances(self, context, **kwargs):653 def _format_describe_instances(self, context, **kwargs):
647 return {'reservationSet': self._format_instances(context, **kwargs)}654 return {'reservationSet': self._format_instances(context, **kwargs)}
648655
@@ -678,10 +685,16 @@
678 if instance['fixed_ip']['floating_ips']:685 if instance['fixed_ip']['floating_ips']:
679 fixed = instance['fixed_ip']686 fixed = instance['fixed_ip']
680 floating_addr = fixed['floating_ips'][0]['address']687 floating_addr = fixed['floating_ips'][0]['address']
688 if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
689 i['dnsNameV6'] = utils.to_global_ipv6(
690 instance['fixed_ip']['network']['cidr_v6'],
691 instance['mac_address'])
692
681 i['privateDnsName'] = fixed_addr693 i['privateDnsName'] = fixed_addr
682 i['publicDnsName'] = floating_addr694 i['publicDnsName'] = floating_addr
683 i['dnsName'] = i['publicDnsName'] or i['privateDnsName']695 i['dnsName'] = i['publicDnsName'] or i['privateDnsName']
684 i['keyName'] = instance['key_name']696 i['keyName'] = instance['key_name']
697
685 if context.user.is_admin():698 if context.user.is_admin():
686 i['keyName'] = '%s (%s, %s)' % (i['keyName'],699 i['keyName'] = '%s (%s, %s)' % (i['keyName'],
687 instance['project_id'],700 instance['project_id'],
688701
=== modified file 'nova/db/api.py'
--- nova/db/api.py 2011-01-12 23:03:08 +0000
+++ nova/db/api.py 2011-01-13 21:53:11 +0000
@@ -299,6 +299,10 @@
299 return IMPL.fixed_ip_get_instance(context, address)299 return IMPL.fixed_ip_get_instance(context, address)
300300
301301
302def fixed_ip_get_instance_v6(context, address):
303 return IMPL.fixed_ip_get_instance_v6(context, address)
304
305
302def fixed_ip_get_network(context, address):306def fixed_ip_get_network(context, address):
303 """Get a network for a fixed ip by address."""307 """Get a network for a fixed ip by address."""
304 return IMPL.fixed_ip_get_network(context, address)308 return IMPL.fixed_ip_get_network(context, address)
@@ -357,6 +361,10 @@
357 return IMPL.instance_get_fixed_address(context, instance_id)361 return IMPL.instance_get_fixed_address(context, instance_id)
358362
359363
364def instance_get_fixed_address_v6(context, instance_id):
365 return IMPL.instance_get_fixed_address_v6(context, instance_id)
366
367
360def instance_get_floating_address(context, instance_id):368def instance_get_floating_address(context, instance_id):
361 """Get the first floating ip address of an instance."""369 """Get the first floating ip address of an instance."""
362 return IMPL.instance_get_floating_address(context, instance_id)370 return IMPL.instance_get_floating_address(context, instance_id)
@@ -552,6 +560,10 @@
552 return IMPL.project_get_network(context, project_id)560 return IMPL.project_get_network(context, project_id)
553561
554562
563def project_get_network_v6(context, project_id):
564 return IMPL.project_get_network_v6(context, project_id)
565
566
555###################567###################
556568
557569
558570
=== modified file 'nova/db/sqlalchemy/api.py'
--- nova/db/sqlalchemy/api.py 2011-01-12 22:24:16 +0000
+++ nova/db/sqlalchemy/api.py 2011-01-13 21:53:11 +0000
@@ -606,6 +606,17 @@
606 return fixed_ip_ref.instance606 return fixed_ip_ref.instance
607607
608608
609@require_context
610def fixed_ip_get_instance_v6(context, address):
611 session = get_session()
612 mac = utils.to_mac(address)
613
614 result = session.query(models.Instance
615 ).filter_by(mac_address=mac
616 ).first()
617 return result
618
619
609@require_admin_context620@require_admin_context
610def fixed_ip_get_network(context, address):621def fixed_ip_get_network(context, address):
611 fixed_ip_ref = fixed_ip_get_by_address(context, address)622 fixed_ip_ref = fixed_ip_get_by_address(context, address)
@@ -794,6 +805,17 @@
794805
795806
796@require_context807@require_context
808def instance_get_fixed_address_v6(context, instance_id):
809 session = get_session()
810 with session.begin():
811 instance_ref = instance_get(context, instance_id, session=session)
812 network_ref = network_get_by_instance(context, instance_id)
813 prefix = network_ref.cidr_v6
814 mac = instance_ref.mac_address
815 return utils.to_global_ipv6(prefix, mac)
816
817
818@require_context
797def instance_get_floating_address(context, instance_id):819def instance_get_floating_address(context, instance_id):
798 session = get_session()820 session = get_session()
799 with session.begin():821 with session.begin():
@@ -1130,6 +1152,11 @@
1130 return result1152 return result
11311153
11321154
1155@require_context
1156def project_get_network_v6(context, project_id):
1157 return project_get_network(context, project_id)
1158
1159
1133###################1160###################
11341161
11351162
11361163
=== modified file 'nova/db/sqlalchemy/models.py'
--- nova/db/sqlalchemy/models.py 2011-01-12 23:03:08 +0000
+++ nova/db/sqlalchemy/models.py 2011-01-13 21:53:11 +0000
@@ -411,6 +411,10 @@
411411
412 injected = Column(Boolean, default=False)412 injected = Column(Boolean, default=False)
413 cidr = Column(String(255), unique=True)413 cidr = Column(String(255), unique=True)
414 cidr_v6 = Column(String(255), unique=True)
415
416 ra_server = Column(String(255))
417
414 netmask = Column(String(255))418 netmask = Column(String(255))
415 bridge = Column(String(255))419 bridge = Column(String(255))
416 gateway = Column(String(255))420 gateway = Column(String(255))
417421
=== modified file 'nova/network/linux_net.py'
--- nova/network/linux_net.py 2011-01-11 05:49:46 +0000
+++ nova/network/linux_net.py 2011-01-13 21:53:11 +0000
@@ -50,6 +50,7 @@
50 'Public IP of network host')50 'Public IP of network host')
51flags.DEFINE_bool('use_nova_chains', False,51flags.DEFINE_bool('use_nova_chains', False,
52 'use the nova_ routing chains instead of default')52 'use the nova_ routing chains instead of default')
53
53flags.DEFINE_string('dns_server', None,54flags.DEFINE_string('dns_server', None,
54 'if set, uses specific dns server for dnsmasq')55 'if set, uses specific dns server for dnsmasq')
55flags.DEFINE_string('dmz_cidr', '10.128.0.0/24',56flags.DEFINE_string('dmz_cidr', '10.128.0.0/24',
@@ -196,6 +197,10 @@
196 net_attrs['gateway'],197 net_attrs['gateway'],
197 net_attrs['broadcast'],198 net_attrs['broadcast'],
198 net_attrs['netmask']))199 net_attrs['netmask']))
200 if(FLAGS.use_ipv6):
201 _execute("sudo ifconfig %s add %s up" % \
202 (bridge,
203 net_attrs['cidr_v6']))
199 else:204 else:
200 _execute("sudo ifconfig %s up" % bridge)205 _execute("sudo ifconfig %s up" % bridge)
201 if FLAGS.use_nova_chains:206 if FLAGS.use_nova_chains:
@@ -262,6 +267,50 @@
262 _execute(command, addl_env=env)267 _execute(command, addl_env=env)
263268
264269
270def update_ra(context, network_id):
271 network_ref = db.network_get(context, network_id)
272
273 conffile = _ra_file(network_ref['bridge'], 'conf')
274 with open(conffile, 'w') as f:
275 conf_str = """
276interface %s
277{
278 AdvSendAdvert on;
279 MinRtrAdvInterval 3;
280 MaxRtrAdvInterval 10;
281 prefix %s
282 {
283 AdvOnLink on;
284 AdvAutonomous on;
285 };
286};
287""" % (network_ref['bridge'], network_ref['cidr_v6'])
288 f.write(conf_str)
289
290 # Make sure radvd can actually read it (it setuid()s to "nobody")
291 os.chmod(conffile, 0644)
292
293 pid = _ra_pid_for(network_ref['bridge'])
294
295 # if radvd is already running, then tell it to reload
296 if pid:
297 out, _err = _execute('cat /proc/%d/cmdline'
298 % pid, check_exit_code=False)
299 if conffile in out:
300 try:
301 _execute('sudo kill -HUP %d' % pid)
302 return
303 except Exception as exc: # pylint: disable-msg=W0703
304 LOG.debug(_("Hupping radvd threw %s"), exc)
305 else:
306 LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
307 command = _ra_cmd(network_ref)
308 _execute(command)
309 db.network_update(context, network_id,
310 {"ra_server":
311 utils.get_my_linklocal(network_ref['bridge'])})
312
313
265def _host_dhcp(fixed_ip_ref):314def _host_dhcp(fixed_ip_ref):
266 """Return a host string for an address"""315 """Return a host string for an address"""
267 instance_ref = fixed_ip_ref['instance']316 instance_ref = fixed_ip_ref['instance']
@@ -323,6 +372,15 @@
323 return ''.join(cmd)372 return ''.join(cmd)
324373
325374
375def _ra_cmd(net):
376 """Builds radvd command"""
377 cmd = ['sudo -E radvd',
378# ' -u nobody',
379 ' -C %s' % _ra_file(net['bridge'], 'conf'),
380 ' -p %s' % _ra_file(net['bridge'], 'pid')]
381 return ''.join(cmd)
382
383
326def _stop_dnsmasq(network):384def _stop_dnsmasq(network):
327 """Stops the dnsmasq instance for a given network"""385 """Stops the dnsmasq instance for a given network"""
328 pid = _dnsmasq_pid_for(network)386 pid = _dnsmasq_pid_for(network)
@@ -344,6 +402,16 @@
344 kind))402 kind))
345403
346404
405def _ra_file(bridge, kind):
406 """Return path to a pid or conf file for a bridge"""
407
408 if not os.path.exists(FLAGS.networks_path):
409 os.makedirs(FLAGS.networks_path)
410 return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path,
411 bridge,
412 kind))
413
414
347def _dnsmasq_pid_for(bridge):415def _dnsmasq_pid_for(bridge):
348 """Returns the pid for prior dnsmasq instance for a bridge416 """Returns the pid for prior dnsmasq instance for a bridge
349417
@@ -357,3 +425,18 @@
357 if os.path.exists(pid_file):425 if os.path.exists(pid_file):
358 with open(pid_file, 'r') as f:426 with open(pid_file, 'r') as f:
359 return int(f.read())427 return int(f.read())
428
429
430def _ra_pid_for(bridge):
431 """Returns the pid for prior radvd instance for a bridge
432
433 Returns None if no pid file exists
434
435 If machine has rebooted pid might be incorrect (caller should check)
436 """
437
438 pid_file = _ra_file(bridge, 'pid')
439
440 if os.path.exists(pid_file):
441 with open(pid_file, 'r') as f:
442 return int(f.read())
360443
=== modified file 'nova/network/manager.py'
--- nova/network/manager.py 2011-01-10 02:08:54 +0000
+++ nova/network/manager.py 2011-01-13 21:53:11 +0000
@@ -82,6 +82,7 @@
82flags.DEFINE_string('floating_range', '4.4.4.0/24',82flags.DEFINE_string('floating_range', '4.4.4.0/24',
83 'Floating IP address block')83 'Floating IP address block')
84flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')84flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')
85flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block')
85flags.DEFINE_integer('cnt_vpn_clients', 5,86flags.DEFINE_integer('cnt_vpn_clients', 5,
86 'Number of addresses reserved for vpn clients')87 'Number of addresses reserved for vpn clients')
87flags.DEFINE_string('network_driver', 'nova.network.linux_net',88flags.DEFINE_string('network_driver', 'nova.network.linux_net',
@@ -90,6 +91,9 @@
90 'Whether to update dhcp when fixed_ip is disassociated')91 'Whether to update dhcp when fixed_ip is disassociated')
91flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600,92flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600,
92 'Seconds after which a deallocated ip is disassociated')93 'Seconds after which a deallocated ip is disassociated')
94
95flags.DEFINE_bool('use_ipv6', True,
96 'use the ipv6')
93flags.DEFINE_string('network_host', socket.gethostname(),97flags.DEFINE_string('network_host', socket.gethostname(),
94 'Network host to use for ip allocation in flat modes')98 'Network host to use for ip allocation in flat modes')
95flags.DEFINE_bool('fake_call', False,99flags.DEFINE_bool('fake_call', False,
@@ -235,7 +239,7 @@
235 """Get the network host for the current context."""239 """Get the network host for the current context."""
236 raise NotImplementedError()240 raise NotImplementedError()
237241
238 def create_networks(self, context, num_networks, network_size,242 def create_networks(self, context, num_networks, network_size, cidr_v6,
239 *args, **kwargs):243 *args, **kwargs):
240 """Create networks based on parameters."""244 """Create networks based on parameters."""
241 raise NotImplementedError()245 raise NotImplementedError()
@@ -321,9 +325,11 @@
321 pass325 pass
322326
323 def create_networks(self, context, cidr, num_networks, network_size,327 def create_networks(self, context, cidr, num_networks, network_size,
324 *args, **kwargs):328 cidr_v6, *args, **kwargs):
325 """Create networks based on parameters."""329 """Create networks based on parameters."""
326 fixed_net = IPy.IP(cidr)330 fixed_net = IPy.IP(cidr)
331 fixed_net_v6 = IPy.IP(cidr_v6)
332 significant_bits_v6 = 64
327 for index in range(num_networks):333 for index in range(num_networks):
328 start = index * network_size334 start = index * network_size
329 significant_bits = 32 - int(math.log(network_size, 2))335 significant_bits = 32 - int(math.log(network_size, 2))
@@ -336,7 +342,13 @@
336 net['gateway'] = str(project_net[1])342 net['gateway'] = str(project_net[1])
337 net['broadcast'] = str(project_net.broadcast())343 net['broadcast'] = str(project_net.broadcast())
338 net['dhcp_start'] = str(project_net[2])344 net['dhcp_start'] = str(project_net[2])
345
346 if(FLAGS.use_ipv6):
347 cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6)
348 net['cidr_v6'] = cidr_v6
349
339 network_ref = self.db.network_create_safe(context, net)350 network_ref = self.db.network_create_safe(context, net)
351
340 if network_ref:352 if network_ref:
341 self._create_fixed_ips(context, network_ref['id'])353 self._create_fixed_ips(context, network_ref['id'])
342354
@@ -482,12 +494,16 @@
482 network_ref['bridge'])494 network_ref['bridge'])
483495
484 def create_networks(self, context, cidr, num_networks, network_size,496 def create_networks(self, context, cidr, num_networks, network_size,
485 vlan_start, vpn_start):497 vlan_start, vpn_start, cidr_v6):
486 """Create networks based on parameters."""498 """Create networks based on parameters."""
487 fixed_net = IPy.IP(cidr)499 fixed_net = IPy.IP(cidr)
500 fixed_net_v6 = IPy.IP(cidr_v6)
501 network_size_v6 = 1 << 64
502 significant_bits_v6 = 64
488 for index in range(num_networks):503 for index in range(num_networks):
489 vlan = vlan_start + index504 vlan = vlan_start + index
490 start = index * network_size505 start = index * network_size
506 start_v6 = index * network_size_v6
491 significant_bits = 32 - int(math.log(network_size, 2))507 significant_bits = 32 - int(math.log(network_size, 2))
492 cidr = "%s/%s" % (fixed_net[start], significant_bits)508 cidr = "%s/%s" % (fixed_net[start], significant_bits)
493 project_net = IPy.IP(cidr)509 project_net = IPy.IP(cidr)
@@ -500,6 +516,13 @@
500 net['dhcp_start'] = str(project_net[3])516 net['dhcp_start'] = str(project_net[3])
501 net['vlan'] = vlan517 net['vlan'] = vlan
502 net['bridge'] = 'br%s' % vlan518 net['bridge'] = 'br%s' % vlan
519 if(FLAGS.use_ipv6):
520 cidr_v6 = "%s/%s" % (
521 fixed_net_v6[start_v6],
522 significant_bits_v6
523 )
524 net['cidr_v6'] = cidr_v6
525
503 # NOTE(vish): This makes ports unique accross the cloud, a more526 # NOTE(vish): This makes ports unique accross the cloud, a more
504 # robust solution would be to make them unique per ip527 # robust solution would be to make them unique per ip
505 net['vpn_public_port'] = vpn_start + index528 net['vpn_public_port'] = vpn_start + index
@@ -538,6 +561,7 @@
538 self.driver.ensure_vlan_bridge(network_ref['vlan'],561 self.driver.ensure_vlan_bridge(network_ref['vlan'],
539 network_ref['bridge'],562 network_ref['bridge'],
540 network_ref)563 network_ref)
564
541 # NOTE(vish): only ensure this forward if the address hasn't been set565 # NOTE(vish): only ensure this forward if the address hasn't been set
542 # manually.566 # manually.
543 if address == FLAGS.vpn_ip:567 if address == FLAGS.vpn_ip:
@@ -546,6 +570,8 @@
546 network_ref['vpn_private_address'])570 network_ref['vpn_private_address'])
547 if not FLAGS.fake_network:571 if not FLAGS.fake_network:
548 self.driver.update_dhcp(context, network_id)572 self.driver.update_dhcp(context, network_id)
573 if(FLAGS.use_ipv6):
574 self.driver.update_ra(context, network_id)
549575
550 @property576 @property
551 def _bottom_reserved_ips(self):577 def _bottom_reserved_ips(self):
552578
=== modified file 'nova/test.py'
--- nova/test.py 2010-12-17 01:05:54 +0000
+++ nova/test.py 2011-01-13 21:53:11 +0000
@@ -156,7 +156,8 @@
156 FLAGS.fixed_range,156 FLAGS.fixed_range,
157 5, 16,157 5, 16,
158 FLAGS.vlan_start,158 FLAGS.vlan_start,
159 FLAGS.vpn_start)159 FLAGS.vpn_start,
160 FLAGS.fixed_range_v6)
160161
161 # emulate some of the mox stuff, we can't use the metaclass162 # emulate some of the mox stuff, we can't use the metaclass
162 # because it screws with our generators163 # because it screws with our generators
163164
=== modified file 'nova/tests/test_api.py'
--- nova/tests/test_api.py 2010-12-17 01:05:54 +0000
+++ nova/tests/test_api.py 2011-01-13 21:53:11 +0000
@@ -265,6 +265,72 @@
265265
266 return266 return
267267
268 def test_authorize_revoke_security_group_cidr_v6(self):
269 """
270 Test that we can add and remove CIDR based rules
271 to a security group for IPv6
272 """
273 self.expect_http()
274 self.mox.ReplayAll()
275 user = self.manager.create_user('fake', 'fake', 'fake')
276 project = self.manager.create_project('fake', 'fake', 'fake')
277
278 # At the moment, you need both of these to actually be netadmin
279 self.manager.add_role('fake', 'netadmin')
280 project.add_role('fake', 'netadmin')
281
282 security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
283 for x in range(random.randint(4, 8)))
284
285 group = self.ec2.create_security_group(security_group_name,
286 'test group')
287
288 self.expect_http()
289 self.mox.ReplayAll()
290 group.connection = self.ec2
291
292 group.authorize('tcp', 80, 81, '::/0')
293
294 self.expect_http()
295 self.mox.ReplayAll()
296
297 rv = self.ec2.get_all_security_groups()
298 # I don't bother checkng that we actually find it here,
299 # because the create/delete unit test further up should
300 # be good enough for that.
301 for group in rv:
302 if group.name == security_group_name:
303 self.assertEquals(len(group.rules), 1)
304 self.assertEquals(int(group.rules[0].from_port), 80)
305 self.assertEquals(int(group.rules[0].to_port), 81)
306 self.assertEquals(len(group.rules[0].grants), 1)
307 self.assertEquals(str(group.rules[0].grants[0]), '::/0')
308
309 self.expect_http()
310 self.mox.ReplayAll()
311 group.connection = self.ec2
312
313 group.revoke('tcp', 80, 81, '::/0')
314
315 self.expect_http()
316 self.mox.ReplayAll()
317
318 self.ec2.delete_security_group(security_group_name)
319
320 self.expect_http()
321 self.mox.ReplayAll()
322 group.connection = self.ec2
323
324 rv = self.ec2.get_all_security_groups()
325
326 self.assertEqual(len(rv), 1)
327 self.assertEqual(rv[0].name, 'default')
328
329 self.manager.delete_project(project)
330 self.manager.delete_user(user)
331
332 return
333
268 def test_authorize_revoke_security_group_foreign_group(self):334 def test_authorize_revoke_security_group_foreign_group(self):
269 """335 """
270 Test that we can grant and revoke another security group access336 Test that we can grant and revoke another security group access
271337
=== modified file 'nova/tests/test_network.py'
--- nova/tests/test_network.py 2011-01-04 05:23:35 +0000
+++ nova/tests/test_network.py 2011-01-13 21:53:11 +0000
@@ -96,6 +96,28 @@
96 self.context.project_id = self.projects[project_num].id96 self.context.project_id = self.projects[project_num].id
97 self.network.deallocate_fixed_ip(self.context, address)97 self.network.deallocate_fixed_ip(self.context, address)
9898
99 def test_private_ipv6(self):
100 """Make sure ipv6 is OK"""
101 if FLAGS.use_ipv6:
102 instance_ref = self._create_instance(0)
103 address = self._create_address(0, instance_ref['id'])
104 network_ref = db.project_get_network(
105 context.get_admin_context(),
106 self.context.project_id)
107 address_v6 = db.instance_get_fixed_address_v6(
108 context.get_admin_context(),
109 instance_ref['id'])
110 self.assertEqual(instance_ref['mac_address'],
111 utils.to_mac(address_v6))
112 instance_ref2 = db.fixed_ip_get_instance_v6(
113 context.get_admin_context(),
114 address_v6)
115 self.assertEqual(instance_ref['id'], instance_ref2['id'])
116 self.assertEqual(address_v6,
117 utils.to_global_ipv6(
118 network_ref['cidr_v6'],
119 instance_ref['mac_address']))
120
99 def test_public_network_association(self):121 def test_public_network_association(self):
100 """Makes sure that we can allocaate a public ip"""122 """Makes sure that we can allocaate a public ip"""
101 # TODO(vish): better way of adding floating ips123 # TODO(vish): better way of adding floating ips
102124
=== modified file 'nova/utils.py'
--- nova/utils.py 2011-01-12 01:32:12 +0000
+++ nova/utils.py 2011-01-13 21:53:11 +0000
@@ -30,6 +30,8 @@
30import sys30import sys
31import time31import time
32from xml.sax import saxutils32from xml.sax import saxutils
33import re
34import netaddr
3335
34from eventlet import event36from eventlet import event
35from eventlet import greenthread37from eventlet import greenthread
@@ -200,6 +202,40 @@
200 return int(address.split(".")[-1])202 return int(address.split(".")[-1])
201203
202204
205def get_my_linklocal(interface):
206 try:
207 if_str = execute("ip -f inet6 -o addr show %s" % interface)
208 condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope\s+link"
209 links = [re.search(condition, x) for x in if_str[0].split('\n')]
210 address = [w.group(1) for w in links if w is not None]
211 if address[0] is not None:
212 return address[0]
213 else:
214 return 'fe00::'
215 except IndexError as ex:
216 LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex)
217 except ProcessExecutionError as ex:
218 LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex)
219 except:
220 return 'fe00::'
221
222
223def to_global_ipv6(prefix, mac):
224 mac64 = netaddr.EUI(mac).eui64().words
225 int_addr = int(''.join(['%02x' % i for i in mac64]), 16)
226 mac64_addr = netaddr.IPAddress(int_addr)
227 maskIP = netaddr.IPNetwork(prefix).ip
228 return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).format()
229
230
231def to_mac(ipv6_address):
232 address = netaddr.IPAddress(ipv6_address)
233 mask1 = netaddr.IPAddress("::ffff:ffff:ffff:ffff")
234 mask2 = netaddr.IPAddress("::0200:0:0:0")
235 mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words
236 return ":".join(["%02x" % i for i in mac64[0:3] + mac64[5:8]])
237
238
203def utcnow():239def utcnow():
204 """Overridable version of datetime.datetime.utcnow."""240 """Overridable version of datetime.datetime.utcnow."""
205 if utcnow.override_time:241 if utcnow.override_time:
206242
=== modified file 'nova/virt/libvirt.xml.template'
--- nova/virt/libvirt.xml.template 2011-01-05 00:22:47 +0000
+++ nova/virt/libvirt.xml.template 2011-01-13 21:53:11 +0000
@@ -66,6 +66,7 @@
66 <filterref filter="nova-instance-${name}">66 <filterref filter="nova-instance-${name}">
67 <parameter name="IP" value="${ip_address}" />67 <parameter name="IP" value="${ip_address}" />
68 <parameter name="DHCPSERVER" value="${dhcp_server}" />68 <parameter name="DHCPSERVER" value="${dhcp_server}" />
69 <parameter name="RASERVER" value="${ra_server}" />
69#if $getVar('extra_params', False)70#if $getVar('extra_params', False)
70 ${extra_params}71 ${extra_params}
71#end if72#end if
7273
=== modified file 'nova/virt/libvirt_conn.py'
--- nova/virt/libvirt_conn.py 2011-01-13 17:08:53 +0000
+++ nova/virt/libvirt_conn.py 2011-01-13 21:53:11 +0000
@@ -127,6 +127,16 @@
127 return str(net.net()), str(net.netmask())127 return str(net.net()), str(net.netmask())
128128
129129
130def _get_net_and_prefixlen(cidr):
131 net = IPy.IP(cidr)
132 return str(net.net()), str(net.prefixlen())
133
134
135def _get_ip_version(cidr):
136 net = IPy.IP(cidr)
137 return int(net.version())
138
139
130class LibvirtConnection(object):140class LibvirtConnection(object):
131141
132 def __init__(self, read_only):142 def __init__(self, read_only):
@@ -372,7 +382,6 @@
372 instance['id'],382 instance['id'],
373 power_state.NOSTATE,383 power_state.NOSTATE,
374 'launching')384 'launching')
375
376 self.nwfilter.setup_basic_filtering(instance)385 self.nwfilter.setup_basic_filtering(instance)
377 self.firewall_driver.prepare_instance_filter(instance)386 self.firewall_driver.prepare_instance_filter(instance)
378 self._create_image(instance, xml)387 self._create_image(instance, xml)
@@ -541,12 +550,16 @@
541 if network_ref['injected']:550 if network_ref['injected']:
542 admin_context = context.get_admin_context()551 admin_context = context.get_admin_context()
543 address = db.instance_get_fixed_address(admin_context, inst['id'])552 address = db.instance_get_fixed_address(admin_context, inst['id'])
553 ra_server = network_ref['ra_server']
554 if not ra_server:
555 ra_server = "fd00::"
544 with open(FLAGS.injected_network_template) as f:556 with open(FLAGS.injected_network_template) as f:
545 net = f.read() % {'address': address,557 net = f.read() % {'address': address,
546 'netmask': network_ref['netmask'],558 'netmask': network_ref['netmask'],
547 'gateway': network_ref['gateway'],559 'gateway': network_ref['gateway'],
548 'broadcast': network_ref['broadcast'],560 'broadcast': network_ref['broadcast'],
549 'dns': network_ref['dns']}561 'dns': network_ref['dns'],
562 'ra_server': ra_server}
550 if key or net:563 if key or net:
551 if key:564 if key:
552 LOG.info(_('instance %s: injecting key into image %s'),565 LOG.info(_('instance %s: injecting key into image %s'),
@@ -601,13 +614,30 @@
601 instance['id'])614 instance['id'])
602 # Assume that the gateway also acts as the dhcp server.615 # Assume that the gateway also acts as the dhcp server.
603 dhcp_server = network['gateway']616 dhcp_server = network['gateway']
604617 ra_server = network['ra_server']
618 if not ra_server:
619 ra_server = 'fd00::'
605 if FLAGS.allow_project_net_traffic:620 if FLAGS.allow_project_net_traffic:
606 net, mask = _get_net_and_mask(network['cidr'])621 if FLAGS.use_ipv6:
607 extra_params = ("<parameter name=\"PROJNET\" "622 net, mask = _get_net_and_mask(network['cidr'])
608 "value=\"%s\" />\n"623 net_v6, prefixlen_v6 = _get_net_and_prefixlen(
609 "<parameter name=\"PROJMASK\" "624 network['cidr_v6'])
610 "value=\"%s\" />\n") % (net, mask)625 extra_params = ("<parameter name=\"PROJNET\" "
626 "value=\"%s\" />\n"
627 "<parameter name=\"PROJMASK\" "
628 "value=\"%s\" />\n"
629 "<parameter name=\"PROJNETV6\" "
630 "value=\"%s\" />\n"
631 "<parameter name=\"PROJMASKV6\" "
632 "value=\"%s\" />\n") % \
633 (net, mask, net_v6, prefixlen_v6)
634 else:
635 net, mask = _get_net_and_mask(network['cidr'])
636 extra_params = ("<parameter name=\"PROJNET\" "
637 "value=\"%s\" />\n"
638 "<parameter name=\"PROJMASK\" "
639 "value=\"%s\" />\n") % \
640 (net, mask)
611 else:641 else:
612 extra_params = "\n"642 extra_params = "\n"
613643
@@ -621,6 +651,7 @@
621 'mac_address': instance['mac_address'],651 'mac_address': instance['mac_address'],
622 'ip_address': ip_address,652 'ip_address': ip_address,
623 'dhcp_server': dhcp_server,653 'dhcp_server': dhcp_server,
654 'ra_server': ra_server,
624 'extra_params': extra_params,655 'extra_params': extra_params,
625 'rescue': rescue}656 'rescue': rescue}
626 if not rescue:657 if not rescue:
@@ -882,6 +913,15 @@
882 </rule>913 </rule>
883 </filter>'''914 </filter>'''
884915
916 def nova_ra_filter(self):
917 return '''<filter name='nova-allow-ra-server' chain='root'>
918 <uuid>d707fa71-4fb5-4b27-9ab7-ba5ca19c8804</uuid>
919 <rule action='accept' direction='inout'
920 priority='100'>
921 <icmpv6 srcipaddr='$RASERVER'/>
922 </rule>
923 </filter>'''
924
885 def setup_basic_filtering(self, instance):925 def setup_basic_filtering(self, instance):
886 """Set up basic filtering (MAC, IP, and ARP spoofing protection)"""926 """Set up basic filtering (MAC, IP, and ARP spoofing protection)"""
887 logging.info('called setup_basic_filtering in nwfilter')927 logging.info('called setup_basic_filtering in nwfilter')
@@ -906,13 +946,17 @@
906 ['no-mac-spoofing',946 ['no-mac-spoofing',
907 'no-ip-spoofing',947 'no-ip-spoofing',
908 'no-arp-spoofing',948 'no-arp-spoofing',
909 'allow-dhcp-server']))949 'allow-dhcp-server'
950 ]))
910 self._define_filter(self.nova_base_ipv4_filter)951 self._define_filter(self.nova_base_ipv4_filter)
911 self._define_filter(self.nova_base_ipv6_filter)952 self._define_filter(self.nova_base_ipv6_filter)
912 self._define_filter(self.nova_dhcp_filter)953 self._define_filter(self.nova_dhcp_filter)
954 self._define_filter(self.nova_ra_filter)
913 self._define_filter(self.nova_vpn_filter)955 self._define_filter(self.nova_vpn_filter)
914 if FLAGS.allow_project_net_traffic:956 if FLAGS.allow_project_net_traffic:
915 self._define_filter(self.nova_project_filter)957 self._define_filter(self.nova_project_filter)
958 if FLAGS.use_ipv6:
959 self._define_filter(self.nova_project_filter_v6)
916960
917 self.static_filters_configured = True961 self.static_filters_configured = True
918962
@@ -944,13 +988,13 @@
944988
945 def nova_base_ipv6_filter(self):989 def nova_base_ipv6_filter(self):
946 retval = "<filter name='nova-base-ipv6' chain='ipv6'>"990 retval = "<filter name='nova-base-ipv6' chain='ipv6'>"
947 for protocol in ['tcp', 'udp', 'icmp']:991 for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
948 for direction, action, priority in [('out', 'accept', 399),992 for direction, action, priority in [('out', 'accept', 399),
949 ('in', 'drop', 400)]:993 ('in', 'drop', 400)]:
950 retval += """<rule action='%s' direction='%s' priority='%d'>994 retval += """<rule action='%s' direction='%s' priority='%d'>
951 <%s-ipv6 />995 <%s />
952 </rule>""" % (action, direction,996 </rule>""" % (action, direction,
953 priority, protocol)997 priority, protocol)
954 retval += '</filter>'998 retval += '</filter>'
955 return retval999 return retval
9561000
@@ -963,10 +1007,20 @@
963 retval += '</filter>'1007 retval += '</filter>'
964 return retval1008 return retval
9651009
1010 def nova_project_filter_v6(self):
1011 retval = "<filter name='nova-project-v6' chain='ipv6'>"
1012 for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
1013 retval += """<rule action='accept' direction='inout'
1014 priority='200'>
1015 <%s srcipaddr='$PROJNETV6'
1016 srcipmask='$PROJMASKV6' />
1017 </rule>""" % (protocol)
1018 retval += '</filter>'
1019 return retval
1020
966 def _define_filter(self, xml):1021 def _define_filter(self, xml):
967 if callable(xml):1022 if callable(xml):
968 xml = xml()1023 xml = xml()
969
970 # execute in a native thread and block current greenthread until done1024 # execute in a native thread and block current greenthread until done
971 tpool.execute(self._conn.nwfilterDefineXML, xml)1025 tpool.execute(self._conn.nwfilterDefineXML, xml)
9721026
@@ -980,7 +1034,6 @@
980 it makes sure the filters for the security groups as well as1034 it makes sure the filters for the security groups as well as
981 the base filter are all in place.1035 the base filter are all in place.
982 """1036 """
983
984 if instance['image_id'] == FLAGS.vpn_image_id:1037 if instance['image_id'] == FLAGS.vpn_image_id:
985 base_filter = 'nova-vpn'1038 base_filter = 'nova-vpn'
986 else:1039 else:
@@ -992,11 +1045,15 @@
992 instance_secgroup_filter_children = ['nova-base-ipv4',1045 instance_secgroup_filter_children = ['nova-base-ipv4',
993 'nova-base-ipv6',1046 'nova-base-ipv6',
994 'nova-allow-dhcp-server']1047 'nova-allow-dhcp-server']
1048 if FLAGS.use_ipv6:
1049 instance_secgroup_filter_children += ['nova-allow-ra-server']
9951050
996 ctxt = context.get_admin_context()1051 ctxt = context.get_admin_context()
9971052
998 if FLAGS.allow_project_net_traffic:1053 if FLAGS.allow_project_net_traffic:
999 instance_filter_children += ['nova-project']1054 instance_filter_children += ['nova-project']
1055 if FLAGS.use_ipv6:
1056 instance_filter_children += ['nova-project-v6']
10001057
1001 for security_group in db.security_group_get_by_instance(ctxt,1058 for security_group in db.security_group_get_by_instance(ctxt,
1002 instance['id']):1059 instance['id']):
@@ -1024,12 +1081,19 @@
1024 security_group = db.security_group_get(context.get_admin_context(),1081 security_group = db.security_group_get(context.get_admin_context(),
1025 security_group_id)1082 security_group_id)
1026 rule_xml = ""1083 rule_xml = ""
1084 v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'}
1027 for rule in security_group.rules:1085 for rule in security_group.rules:
1028 rule_xml += "<rule action='accept' direction='in' priority='300'>"1086 rule_xml += "<rule action='accept' direction='in' priority='300'>"
1029 if rule.cidr:1087 if rule.cidr:
1030 net, mask = _get_net_and_mask(rule.cidr)1088 version = _get_ip_version(rule.cidr)
1031 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \1089 if(FLAGS.use_ipv6 and version == 6):
1032 (rule.protocol, net, mask)1090 net, prefixlen = _get_net_and_prefixlen(rule.cidr)
1091 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1092 (v6protocol[rule.protocol], net, prefixlen)
1093 else:
1094 net, mask = _get_net_and_mask(rule.cidr)
1095 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1096 (rule.protocol, net, mask)
1033 if rule.protocol in ['tcp', 'udp']:1097 if rule.protocol in ['tcp', 'udp']:
1034 rule_xml += "dstportstart='%s' dstportend='%s' " % \1098 rule_xml += "dstportstart='%s' dstportend='%s' " % \
1035 (rule.from_port, rule.to_port)1099 (rule.from_port, rule.to_port)
@@ -1044,8 +1108,11 @@
10441108
1045 rule_xml += '/>\n'1109 rule_xml += '/>\n'
1046 rule_xml += "</rule>\n"1110 rule_xml += "</rule>\n"
1047 xml = "<filter name='nova-secgroup-%s' chain='ipv4'>%s</filter>" % \1111 xml = "<filter name='nova-secgroup-%s' " % security_group_id
1048 (security_group_id, rule_xml,)1112 if(FLAGS.use_ipv6):
1113 xml += "chain='root'>%s</filter>" % rule_xml
1114 else:
1115 xml += "chain='ipv4'>%s</filter>" % rule_xml
1049 return xml1116 return xml
10501117
1051 def _instance_filter_name(self, instance):1118 def _instance_filter_name(self, instance):
@@ -1082,11 +1149,17 @@
1082 def apply_ruleset(self):1149 def apply_ruleset(self):
1083 current_filter, _ = self.execute('sudo iptables-save -t filter')1150 current_filter, _ = self.execute('sudo iptables-save -t filter')
1084 current_lines = current_filter.split('\n')1151 current_lines = current_filter.split('\n')
1085 new_filter = self.modify_rules(current_lines)1152 new_filter = self.modify_rules(current_lines, 4)
1086 self.execute('sudo iptables-restore',1153 self.execute('sudo iptables-restore',
1087 process_input='\n'.join(new_filter))1154 process_input='\n'.join(new_filter))
1155 if(FLAGS.use_ipv6):
1156 current_filter, _ = self.execute('sudo ip6tables-save -t filter')
1157 current_lines = current_filter.split('\n')
1158 new_filter = self.modify_rules(current_lines, 6)
1159 self.execute('sudo ip6tables-restore',
1160 process_input='\n'.join(new_filter))
10881161
1089 def modify_rules(self, current_lines):1162 def modify_rules(self, current_lines, ip_version=4):
1090 ctxt = context.get_admin_context()1163 ctxt = context.get_admin_context()
1091 # Remove any trace of nova rules.1164 # Remove any trace of nova rules.
1092 new_filter = filter(lambda l: 'nova-' not in l, current_lines)1165 new_filter = filter(lambda l: 'nova-' not in l, current_lines)
@@ -1100,8 +1173,8 @@
1100 if not new_filter[rules_index].startswith(':'):1173 if not new_filter[rules_index].startswith(':'):
1101 break1174 break
11021175
1103 our_chains = [':nova-ipv4-fallback - [0:0]']1176 our_chains = [':nova-fallback - [0:0]']
1104 our_rules = ['-A nova-ipv4-fallback -j DROP']1177 our_rules = ['-A nova-fallback -j DROP']
11051178
1106 our_chains += [':nova-local - [0:0]']1179 our_chains += [':nova-local - [0:0]']
1107 our_rules += ['-A FORWARD -j nova-local']1180 our_rules += ['-A FORWARD -j nova-local']
@@ -1112,7 +1185,10 @@
1112 for instance_id in self.instances:1185 for instance_id in self.instances:
1113 instance = self.instances[instance_id]1186 instance = self.instances[instance_id]
1114 chain_name = self._instance_chain_name(instance)1187 chain_name = self._instance_chain_name(instance)
1115 ip_address = self._ip_for_instance(instance)1188 if(ip_version == 4):
1189 ip_address = self._ip_for_instance(instance)
1190 elif(ip_version == 6):
1191 ip_address = self._ip_for_instance_v6(instance)
11161192
1117 our_chains += [':%s - [0:0]' % chain_name]1193 our_chains += [':%s - [0:0]' % chain_name]
11181194
@@ -1139,13 +1215,19 @@
11391215
1140 our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]1216 our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]
11411217
1142 # Allow DHCP responses1218 if(ip_version == 4):
1143 dhcp_server = self._dhcp_server_for_instance(instance)1219 # Allow DHCP responses
1144 our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %1220 dhcp_server = self._dhcp_server_for_instance(instance)
1145 (chain_name, dhcp_server)]1221 our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %
1222 (chain_name, dhcp_server)]
1223 elif(ip_version == 6):
1224 # Allow RA responses
1225 ra_server = self._ra_server_for_instance(instance)
1226 our_rules += ['-A %s -s %s -p icmpv6' %
1227 (chain_name, ra_server)]
11461228
1147 # If nothing matches, jump to the fallback chain1229 # If nothing matches, jump to the fallback chain
1148 our_rules += ['-A %s -j nova-ipv4-fallback' % (chain_name,)]1230 our_rules += ['-A %s -j nova-fallback' % (chain_name,)]
11491231
1150 # then, security group chains and rules1232 # then, security group chains and rules
1151 for security_group_id in security_groups:1233 for security_group_id in security_groups:
@@ -1158,15 +1240,22 @@
11581240
1159 for rule in rules:1241 for rule in rules:
1160 logging.info('%r', rule)1242 logging.info('%r', rule)
1161 args = ['-A', chain_name, '-p', rule.protocol]
11621243
1163 if rule.cidr:1244 if not rule.cidr:
1164 args += ['-s', rule.cidr]
1165 else:
1166 # Eventually, a mechanism to grant access for security1245 # Eventually, a mechanism to grant access for security
1167 # groups will turn up here. It'll use ipsets.1246 # groups will turn up here. It'll use ipsets.
1168 continue1247 continue
11691248
1249 version = _get_ip_version(rule.cidr)
1250 if version != ip_version:
1251 continue
1252
1253 protocol = rule.protocol
1254 if version == 6 and rule.protocol == 'icmp':
1255 protocol = 'icmpv6'
1256
1257 args = ['-A', chain_name, '-p', protocol, '-s', rule.cidr]
1258
1170 if rule.protocol in ['udp', 'tcp']:1259 if rule.protocol in ['udp', 'tcp']:
1171 if rule.from_port == rule.to_port:1260 if rule.from_port == rule.to_port:
1172 args += ['--dport', '%s' % (rule.from_port,)]1261 args += ['--dport', '%s' % (rule.from_port,)]
@@ -1186,7 +1275,12 @@
1186 icmp_type_arg += '/%s' % icmp_code1275 icmp_type_arg += '/%s' % icmp_code
11871276
1188 if icmp_type_arg:1277 if icmp_type_arg:
1189 args += ['-m', 'icmp', '--icmp-type', icmp_type_arg]1278 if(ip_version == 4):
1279 args += ['-m', 'icmp', '--icmp-type',
1280 icmp_type_arg]
1281 elif(ip_version == 6):
1282 args += ['-m', 'icmp6', '--icmpv6-type',
1283 icmp_type_arg]
11901284
1191 args += ['-j ACCEPT']1285 args += ['-j ACCEPT']
1192 our_rules += [' '.join(args)]1286 our_rules += [' '.join(args)]
@@ -1212,7 +1306,16 @@
1212 return db.instance_get_fixed_address(context.get_admin_context(),1306 return db.instance_get_fixed_address(context.get_admin_context(),
1213 instance['id'])1307 instance['id'])
12141308
1309 def _ip_for_instance_v6(self, instance):
1310 return db.instance_get_fixed_address_v6(context.get_admin_context(),
1311 instance['id'])
1312
1215 def _dhcp_server_for_instance(self, instance):1313 def _dhcp_server_for_instance(self, instance):
1216 network = db.project_get_network(context.get_admin_context(),1314 network = db.project_get_network(context.get_admin_context(),
1217 instance['project_id'])1315 instance['project_id'])
1218 return network['gateway']1316 return network['gateway']
1317
1318 def _ra_server_for_instance(self, instance):
1319 network = db.project_get_network(context.get_admin_context(),
1320 instance['project_id'])
1321 return network['ra_server']
12191322
=== modified file 'smoketests/admin_smoketests.py'
--- smoketests/admin_smoketests.py 2011-01-06 01:51:05 +0000
+++ smoketests/admin_smoketests.py 2011-01-13 21:53:11 +0000
@@ -43,7 +43,7 @@
43# TODO(devamcar): Use random tempfile43# TODO(devamcar): Use random tempfile
44ZIP_FILENAME = '/tmp/nova-me-x509.zip'44ZIP_FILENAME = '/tmp/nova-me-x509.zip'
4545
46TEST_PREFIX = 'test%s' % int(random.random()*1000000)46TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
47TEST_USERNAME = '%suser' % TEST_PREFIX47TEST_USERNAME = '%suser' % TEST_PREFIX
48TEST_PROJECTNAME = '%sproject' % TEST_PREFIX48TEST_PROJECTNAME = '%sproject' % TEST_PREFIX
4949
@@ -96,4 +96,3 @@
96if __name__ == "__main__":96if __name__ == "__main__":
97 suites = {'user': unittest.makeSuite(UserTests)}97 suites = {'user': unittest.makeSuite(UserTests)}
98 sys.exit(base.run_tests(suites))98 sys.exit(base.run_tests(suites))
99
10099
=== modified file 'smoketests/base.py'
--- smoketests/base.py 2010-11-04 22:50:23 +0000
+++ smoketests/base.py 2011-01-13 21:53:11 +0000
@@ -17,6 +17,7 @@
17# under the License.17# under the License.
1818
19import boto19import boto
20import boto_v6
20import commands21import commands
21import httplib22import httplib
22import os23import os
@@ -69,6 +70,17 @@
69 'test.')70 'test.')
7071
71 parts = self.split_clc_url(clc_url)72 parts = self.split_clc_url(clc_url)
73 if FLAGS.use_ipv6:
74 return boto_v6.connect_ec2(aws_access_key_id=access_key,
75 aws_secret_access_key=secret_key,
76 is_secure=parts['is_secure'],
77 region=RegionInfo(None,
78 'nova',
79 parts['ip']),
80 port=parts['port'],
81 path='/services/Cloud',
82 **kwargs)
83
72 return boto.connect_ec2(aws_access_key_id=access_key,84 return boto.connect_ec2(aws_access_key_id=access_key,
73 aws_secret_access_key=secret_key,85 aws_secret_access_key=secret_key,
74 is_secure=parts['is_secure'],86 is_secure=parts['is_secure'],
@@ -115,7 +127,8 @@
115 return True127 return True
116128
117 def upload_image(self, bucket_name, image):129 def upload_image(self, bucket_name, image):
118 cmd = 'euca-upload-bundle -b %s -m /tmp/%s.manifest.xml' % (bucket_name, image)130 cmd = 'euca-upload-bundle -b '
131 cmd += '%s -m /tmp/%s.manifest.xml' % (bucket_name, image)
119 status, output = commands.getstatusoutput(cmd)132 status, output = commands.getstatusoutput(cmd)
120 if status != 0:133 if status != 0:
121 print '%s -> \n %s' % (cmd, output)134 print '%s -> \n %s' % (cmd, output)
@@ -130,6 +143,7 @@
130 raise Exception(output)143 raise Exception(output)
131 return True144 return True
132145
146
133def run_tests(suites):147def run_tests(suites):
134 argv = FLAGS(sys.argv)148 argv = FLAGS(sys.argv)
135149
@@ -151,4 +165,3 @@
151 else:165 else:
152 for suite in suites.itervalues():166 for suite in suites.itervalues():
153 unittest.TextTestRunner(verbosity=2).run(suite)167 unittest.TextTestRunner(verbosity=2).run(suite)
154
155168
=== modified file 'smoketests/flags.py'
--- smoketests/flags.py 2010-11-04 22:50:23 +0000
+++ smoketests/flags.py 2011-01-13 21:53:11 +0000
@@ -33,6 +33,7 @@
33# __GLOBAL FLAGS ONLY__33# __GLOBAL FLAGS ONLY__
34# Define any app-specific flags in their own files, docs at:34# Define any app-specific flags in their own files, docs at:
35# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#3935# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39
36
36DEFINE_string('region', 'nova', 'Region to use')37DEFINE_string('region', 'nova', 'Region to use')
37DEFINE_string('test_image', 'ami-tiny', 'Image to use for launch tests')38DEFINE_string('test_image', 'ami-tiny', 'Image to use for launch tests')
3839DEFINE_string('use_ipv6', True, 'use the ipv6 or not')
3940
=== added file 'smoketests/public_network_smoketests.py'
--- smoketests/public_network_smoketests.py 1970-01-01 00:00:00 +0000
+++ smoketests/public_network_smoketests.py 2011-01-13 21:53:11 +0000
@@ -0,0 +1,180 @@
1# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2010 United States Government as represented by the
4# Administrator of the National Aeronautics and Space Administration.
5# All Rights Reserved.
6#
7# Licensed under the Apache License, Version 2.0 (the "License"); you may
8# not use this file except in compliance with the License. You may obtain
9# a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16# License for the specific language governing permissions and limitations
17# under the License.
18
19import commands
20import os
21import random
22import socket
23import sys
24import time
25import unittest
26
27from smoketests import flags
28from smoketests import base
29from smoketests import user_smoketests
30
31#Note that this test should run from
32#public network (outside of private network segments)
33#Please set EC2_URL correctly
34#You should use admin account in this test
35
36FLAGS = flags.FLAGS
37
38TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
39TEST_BUCKET = '%s_bucket' % TEST_PREFIX
40TEST_KEY = '%s_key' % TEST_PREFIX
41TEST_KEY2 = '%s_key2' % TEST_PREFIX
42TEST_DATA = {}
43
44
45class InstanceTestsFromPublic(user_smoketests.UserSmokeTestCase):
46 def test_001_can_create_keypair(self):
47 key = self.create_key_pair(self.conn, TEST_KEY)
48 self.assertEqual(key.name, TEST_KEY)
49
50 def test_002_security_group(self):
51 security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
52 for x in range(random.randint(4, 8)))
53 group = self.conn.create_security_group(security_group_name,
54 'test group')
55 group.connection = self.conn
56 group.authorize('tcp', 22, 22, '0.0.0.0/0')
57 if FLAGS.use_ipv6:
58 group.authorize('tcp', 22, 22, '::/0')
59
60 reservation = self.conn.run_instances(FLAGS.test_image,
61 key_name=TEST_KEY,
62 security_groups=[security_group_name],
63 instance_type='m1.tiny')
64 self.data['security_group_name'] = security_group_name
65 self.data['group'] = group
66 self.data['instance_id'] = reservation.instances[0].id
67
68 def test_003_instance_with_group_runs_within_60_seconds(self):
69 reservations = self.conn.get_all_instances([self.data['instance_id']])
70 instance = reservations[0].instances[0]
71 # allow 60 seconds to exit pending with IP
72 for x in xrange(60):
73 instance.update()
74 if instance.state == u'running':
75 break
76 time.sleep(1)
77 else:
78 self.fail('instance failed to start')
79 ip = reservations[0].instances[0].private_dns_name
80 self.failIf(ip == '0.0.0.0')
81 self.data['private_ip'] = ip
82 if FLAGS.use_ipv6:
83 ipv6 = reservations[0].instances[0].dns_name_v6
84 self.failIf(ipv6 is None)
85 self.data['ip_v6'] = ipv6
86
87 def test_004_can_ssh_to_ipv6(self):
88 if FLAGS.use_ipv6:
89 for x in xrange(20):
90 try:
91 conn = self.connect_ssh(
92 self.data['ip_v6'], TEST_KEY)
93 conn.close()
94 except Exception as ex:
95 print ex
96 time.sleep(1)
97 else:
98 break
99 else:
100 self.fail('could not ssh to instance')
101
102 def test_012_can_create_instance_with_keypair(self):
103 if 'instance_id' in self.data:
104 self.conn.terminate_instances([self.data['instance_id']])
105 reservation = self.conn.run_instances(FLAGS.test_image,
106 key_name=TEST_KEY,
107 instance_type='m1.tiny')
108 self.assertEqual(len(reservation.instances), 1)
109 self.data['instance_id'] = reservation.instances[0].id
110
111 def test_013_instance_runs_within_60_seconds(self):
112 reservations = self.conn.get_all_instances([self.data['instance_id']])
113 instance = reservations[0].instances[0]
114 # allow 60 seconds to exit pending with IP
115 for x in xrange(60):
116 instance.update()
117 if instance.state == u'running':
118 break
119 time.sleep(1)
120 else:
121 self.fail('instance failed to start')
122 ip = reservations[0].instances[0].private_dns_name
123 self.failIf(ip == '0.0.0.0')
124 self.data['private_ip'] = ip
125 if FLAGS.use_ipv6:
126 ipv6 = reservations[0].instances[0].dns_name_v6
127 self.failIf(ipv6 is None)
128 self.data['ip_v6'] = ipv6
129
130 def test_014_can_not_ping_private_ip(self):
131 for x in xrange(4):
132 # ping waits for 1 second
133 status, output = commands.getstatusoutput(
134 'ping -c1 %s' % self.data['private_ip'])
135 if status == 0:
136 self.fail('can ping private ip from public network')
137 if FLAGS.use_ipv6:
138 status, output = commands.getstatusoutput(
139 'ping6 -c1 %s' % self.data['ip_v6'])
140 if status == 0:
141 self.fail('can ping ipv6 from public network')
142 else:
143 pass
144
145 def test_015_can_not_ssh_to_private_ip(self):
146 for x in xrange(1):
147 try:
148 conn = self.connect_ssh(self.data['private_ip'], TEST_KEY)
149 conn.close()
150 except Exception:
151 time.sleep(1)
152 else:
153 self.fail('can ssh for ipv4 address from public network')
154
155 if FLAGS.use_ipv6:
156 for x in xrange(1):
157 try:
158 conn = self.connect_ssh(
159 self.data['ip_v6'], TEST_KEY)
160 conn.close()
161 except Exception:
162 time.sleep(1)
163 else:
164 self.fail('can ssh for ipv6 address from public network')
165
166 def test_999_tearDown(self):
167 self.delete_key_pair(self.conn, TEST_KEY)
168 security_group_name = self.data['security_group_name']
169 group = self.data['group']
170 if group:
171 group.revoke('tcp', 22, 22, '0.0.0.0/0')
172 if FLAGS.use_ipv6:
173 group.revoke('tcp', 22, 22, '::/0')
174 self.conn.delete_security_group(security_group_name)
175 if 'instance_id' in self.data:
176 self.conn.terminate_instances([self.data['instance_id']])
177
178if __name__ == "__main__":
179 suites = {'instance': unittest.makeSuite(InstanceTestsFromPublic)}
180 sys.exit(base.run_tests(suites))
0181
=== modified file 'smoketests/user_smoketests.py'
--- smoketests/user_smoketests.py 2011-01-06 01:51:05 +0000
+++ smoketests/user_smoketests.py 2011-01-13 21:53:11 +0000
@@ -45,7 +45,7 @@
45flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image',45flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image',
46 'Local image file to use for bundling tests')46 'Local image file to use for bundling tests')
4747
48TEST_PREFIX = 'test%s' % int (random.random()*1000000)48TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
49TEST_BUCKET = '%s_bucket' % TEST_PREFIX49TEST_BUCKET = '%s_bucket' % TEST_PREFIX
50TEST_KEY = '%s_key' % TEST_PREFIX50TEST_KEY = '%s_key' % TEST_PREFIX
51TEST_GROUP = '%s_group' % TEST_PREFIX51TEST_GROUP = '%s_group' % TEST_PREFIX
@@ -80,7 +80,7 @@
8080
81 def test_006_can_register_kernel(self):81 def test_006_can_register_kernel(self):
82 kernel_id = self.conn.register_image('%s/%s.manifest.xml' %82 kernel_id = self.conn.register_image('%s/%s.manifest.xml' %
83 (TEST_BUCKET, FLAGS.bundle_kernel))83 (TEST_BUCKET, FLAGS.bundle_kernel))
84 self.assert_(kernel_id is not None)84 self.assert_(kernel_id is not None)
85 self.data['kernel_id'] = kernel_id85 self.data['kernel_id'] = kernel_id
8686
@@ -92,7 +92,7 @@
92 time.sleep(1)92 time.sleep(1)
93 else:93 else:
94 print image.state94 print image.state
95 self.assert_(False) # wasn't available within 10 seconds95 self.assert_(False) # wasn't available within 10 seconds
96 self.assert_(image.type == 'machine')96 self.assert_(image.type == 'machine')
9797
98 for i in xrange(10):98 for i in xrange(10):
@@ -101,7 +101,7 @@
101 break101 break
102 time.sleep(1)102 time.sleep(1)
103 else:103 else:
104 self.assert_(False) # wasn't available within 10 seconds104 self.assert_(False) # wasn't available within 10 seconds
105 self.assert_(kernel.type == 'kernel')105 self.assert_(kernel.type == 'kernel')
106106
107 def test_008_can_describe_image_attribute(self):107 def test_008_can_describe_image_attribute(self):
@@ -152,14 +152,17 @@
152 for x in xrange(60):152 for x in xrange(60):
153 instance.update()153 instance.update()
154 if instance.state == u'running':154 if instance.state == u'running':
155 break155 break
156 time.sleep(1)156 time.sleep(1)
157 else:157 else:
158 self.fail('instance failed to start')158 self.fail('instance failed to start')
159 ip = reservations[0].instances[0].private_dns_name159 ip = reservations[0].instances[0].private_dns_name
160 self.failIf(ip == '0.0.0.0')160 self.failIf(ip == '0.0.0.0')
161 self.data['private_ip'] = ip161 self.data['private_ip'] = ip
162 print self.data['private_ip']162 if FLAGS.use_ipv6:
163 ipv6 = reservations[0].instances[0].dns_name_v6
164 self.failIf(ipv6 is None)
165 self.data['ip_v6'] = ipv6
163166
164 def test_004_can_ping_private_ip(self):167 def test_004_can_ping_private_ip(self):
165 for x in xrange(120):168 for x in xrange(120):
@@ -171,6 +174,16 @@
171 else:174 else:
172 self.fail('could not ping instance')175 self.fail('could not ping instance')
173176
177 if FLAGS.use_ipv6:
178 for x in xrange(120):
179 # ping waits for 1 second
180 status, output = commands.getstatusoutput(
181 'ping6 -c1 %s' % self.data['ip_v6'])
182 if status == 0:
183 break
184 else:
185 self.fail('could not ping instance')
186
174 def test_005_can_ssh_to_private_ip(self):187 def test_005_can_ssh_to_private_ip(self):
175 for x in xrange(30):188 for x in xrange(30):
176 try:189 try:
@@ -183,6 +196,19 @@
183 else:196 else:
184 self.fail('could not ssh to instance')197 self.fail('could not ssh to instance')
185198
199 if FLAGS.use_ipv6:
200 for x in xrange(30):
201 try:
202 conn = self.connect_ssh(
203 self.data['ip_v6'], TEST_KEY)
204 conn.close()
205 except Exception:
206 time.sleep(1)
207 else:
208 break
209 else:
210 self.fail('could not ssh to instance v6')
211
186 def test_006_can_allocate_elastic_ip(self):212 def test_006_can_allocate_elastic_ip(self):
187 result = self.conn.allocate_address()213 result = self.conn.allocate_address()
188 self.assertTrue(hasattr(result, 'public_ip'))214 self.assertTrue(hasattr(result, 'public_ip'))
@@ -388,7 +414,6 @@
388 raise Exception("Timeout")414 raise Exception("Timeout")
389 time.sleep(1)415 time.sleep(1)
390416
391
392 def test_999_tearDown(self):417 def test_999_tearDown(self):
393 self.conn.delete_key_pair(TEST_KEY)418 self.conn.delete_key_pair(TEST_KEY)
394 self.conn.delete_security_group(TEST_GROUP)419 self.conn.delete_security_group(TEST_GROUP)
395420
=== modified file 'tools/pip-requires'
--- tools/pip-requires 2010-12-28 00:10:26 +0000
+++ tools/pip-requires 2011-01-13 21:53:11 +0000
@@ -25,3 +25,4 @@
25Twisted>=10.1.025Twisted>=10.1.0
26PasteDeploy26PasteDeploy
27paste27paste
28netaddr