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: 1528 lines (+799/-53)
23 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 (+6/-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 (+88/-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)
To merge this branch: bzr merge lp:~ntt-pf-lab/nova/ipv6-support
Reviewer Review Type Date Requested Status
Soren Hansen Pending
Devin Carlen Pending
Review via email: mp+46067@code.launchpad.net

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

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 555.
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

lp:~ntt-pf-lab/nova/ipv6-support updated
489. By Koji Iida

Fixed missing _().
Fixed to follow logging to LOG changes.
Fixed merge miss (get_fixed_ip was moved away).
Update some missing comments.

490. By Nachi Ueno

Merged with 557

491. By Hisaharu Ishii

Fixed Authors

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

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 08:08:15 +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 08:08:15 +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 08:08:15 +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 08:08:15 +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 08:08:15 +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 08:08:15 +0000
@@ -86,6 +86,10 @@
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
89 if [ "$USE_MYSQL" == 1 ]; then93 if [ "$USE_MYSQL" == 1 ]; then
90 cat <<MYSQL_PRESEED | debconf-set-selections94 cat <<MYSQL_PRESEED | debconf-set-selections
91mysql-server-5.1 mysql-server/root_password password $MYSQL_PASS95mysql-server-5.1 mysql-server/root_password password $MYSQL_PASS
@@ -107,6 +111,8 @@
107111
108if [ "$CMD" == "run" ]; then112if [ "$CMD" == "run" ]; then
109 killall dnsmasq113 killall dnsmasq
114 #For IPv6
115 killall radvd
110 screen -d -m -S nova -t nova116 screen -d -m -S nova -t nova
111 sleep 1117 sleep 1
112 if [ "$USE_MYSQL" == 1 ]; then118 if [ "$USE_MYSQL" == 1 ]; then
113119
=== modified file 'nova/api/ec2/cloud.py'
--- nova/api/ec2/cloud.py 2011-01-13 01:12:20 +0000
+++ nova/api/ec2/cloud.py 2011-01-13 08:08:15 +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
@@ -378,6 +380,7 @@
378 values['group_id'] = source_security_group['id']380 values['group_id'] = source_security_group['id']
379 elif cidr_ip:381 elif cidr_ip:
380 # If this fails, it throws an exception. This is what we want.382 # If this fails, it throws an exception. This is what we want.
383 cidr_ip = urllib.unquote(cidr_ip).decode()
381 IPy.IP(cidr_ip)384 IPy.IP(cidr_ip)
382 values['cidr'] = cidr_ip385 values['cidr'] = cidr_ip
383 else:386 else:
@@ -639,6 +642,10 @@
639 def describe_instances(self, context, **kwargs):642 def describe_instances(self, context, **kwargs):
640 return self._format_describe_instances(context, **kwargs)643 return self._format_describe_instances(context, **kwargs)
641644
645 def describe_instances_v6(self, context, **kwargs):
646 kwargs['use_v6'] = True
647 return self._format_describe_instances(context, **kwargs)
648
642 def _format_describe_instances(self, context, **kwargs):649 def _format_describe_instances(self, context, **kwargs):
643 return {'reservationSet': self._format_instances(context, **kwargs)}650 return {'reservationSet': self._format_instances(context, **kwargs)}
644651
@@ -674,10 +681,16 @@
674 if instance['fixed_ip']['floating_ips']:681 if instance['fixed_ip']['floating_ips']:
675 fixed = instance['fixed_ip']682 fixed = instance['fixed_ip']
676 floating_addr = fixed['floating_ips'][0]['address']683 floating_addr = fixed['floating_ips'][0]['address']
684 if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
685 i['dnsNameV6'] = utils.to_global_ipv6(
686 instance['fixed_ip']['network']['cidr_v6'],
687 instance['mac_address'])
688
677 i['privateDnsName'] = fixed_addr689 i['privateDnsName'] = fixed_addr
678 i['publicDnsName'] = floating_addr690 i['publicDnsName'] = floating_addr
679 i['dnsName'] = i['publicDnsName'] or i['privateDnsName']691 i['dnsName'] = i['publicDnsName'] or i['privateDnsName']
680 i['keyName'] = instance['key_name']692 i['keyName'] = instance['key_name']
693
681 if context.user.is_admin():694 if context.user.is_admin():
682 i['keyName'] = '%s (%s, %s)' % (i['keyName'],695 i['keyName'] = '%s (%s, %s)' % (i['keyName'],
683 instance['project_id'],696 instance['project_id'],
684697
=== modified file 'nova/db/api.py'
--- nova/db/api.py 2011-01-12 11:34:16 +0000
+++ nova/db/api.py 2011-01-13 08:08:15 +0000
@@ -295,6 +295,10 @@
295 return IMPL.fixed_ip_get_instance(context, address)295 return IMPL.fixed_ip_get_instance(context, address)
296296
297297
298def fixed_ip_get_instance_v6(context, address):
299 return IMPL.fixed_ip_get_instance_v6(context, address)
300
301
298def fixed_ip_get_network(context, address):302def fixed_ip_get_network(context, address):
299 """Get a network for a fixed ip by address."""303 """Get a network for a fixed ip by address."""
300 return IMPL.fixed_ip_get_network(context, address)304 return IMPL.fixed_ip_get_network(context, address)
@@ -353,6 +357,10 @@
353 return IMPL.instance_get_fixed_address(context, instance_id)357 return IMPL.instance_get_fixed_address(context, instance_id)
354358
355359
360def instance_get_fixed_address_v6(context, instance_id):
361 return IMPL.instance_get_fixed_address_v6(context, instance_id)
362
363
356def instance_get_floating_address(context, instance_id):364def instance_get_floating_address(context, instance_id):
357 """Get the first floating ip address of an instance."""365 """Get the first floating ip address of an instance."""
358 return IMPL.instance_get_floating_address(context, instance_id)366 return IMPL.instance_get_floating_address(context, instance_id)
@@ -548,6 +556,10 @@
548 return IMPL.project_get_network(context, project_id)556 return IMPL.project_get_network(context, project_id)
549557
550558
559def project_get_network_v6(context, project_id):
560 return IMPL.project_get_network_v6(context, project_id)
561
562
551###################563###################
552564
553565
554566
=== 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 08:08:15 +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 11:34:16 +0000
+++ nova/db/sqlalchemy/models.py 2011-01-13 08:08:15 +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 08:08:15 +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',
@@ -123,6 +124,11 @@
123 (FLAGS.fixed_range, FLAGS.dmz_cidr))124 (FLAGS.fixed_range, FLAGS.dmz_cidr))
124 _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" %125 _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" %
125 {'range': FLAGS.fixed_range})126 {'range': FLAGS.fixed_range})
127 if(FLAGS.use_ipv6):
128 _execute('sudo bash -c ' +
129 '"echo 1 > /proc/sys/net/ipv6/conf/all/forwarding"')
130 _execute('sudo bash -c ' +
131 '"echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"')
126132
127133
128def bind_floating_ip(floating_ip, check_exit_code=True):134def bind_floating_ip(floating_ip, check_exit_code=True):
@@ -196,6 +202,10 @@
196 net_attrs['gateway'],202 net_attrs['gateway'],
197 net_attrs['broadcast'],203 net_attrs['broadcast'],
198 net_attrs['netmask']))204 net_attrs['netmask']))
205 if(FLAGS.use_ipv6):
206 _execute("sudo ifconfig %s add %s up" % \
207 (bridge,
208 net_attrs['cidr_v6']))
199 else:209 else:
200 _execute("sudo ifconfig %s up" % bridge)210 _execute("sudo ifconfig %s up" % bridge)
201 if FLAGS.use_nova_chains:211 if FLAGS.use_nova_chains:
@@ -262,6 +272,50 @@
262 _execute(command, addl_env=env)272 _execute(command, addl_env=env)
263273
264274
275def update_ra(context, network_id):
276 network_ref = db.network_get(context, network_id)
277
278 conffile = _ra_file(network_ref['bridge'], 'conf')
279 with open(conffile, 'w') as f:
280 conf_str = """
281interface %s
282{
283 AdvSendAdvert on;
284 MinRtrAdvInterval 3;
285 MaxRtrAdvInterval 10;
286 prefix %s
287 {
288 AdvOnLink on;
289 AdvAutonomous on;
290 };
291};
292""" % (network_ref['bridge'], network_ref['cidr_v6'])
293 f.write(conf_str)
294
295 # Make sure radvd can actually read it (it setuid()s to "nobody")
296 os.chmod(conffile, 0644)
297
298 pid = _ra_pid_for(network_ref['bridge'])
299
300 # if radvd is already running, then tell it to reload
301 if pid:
302 out, _err = _execute('cat /proc/%d/cmdline'
303 % pid, check_exit_code=False)
304 if conffile in out:
305 try:
306 _execute('sudo kill -HUP %d' % pid)
307 return
308 except Exception as exc: # pylint: disable-msg=W0703
309 LOG.debug(_("Hupping radvd threw %s"), exc)
310 else:
311 LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
312 command = _ra_cmd(network_ref)
313 _execute(command)
314 db.network_update(context, network_id,
315 {"ra_server":
316 utils.get_my_linklocal(network_ref['bridge'])})
317
318
265def _host_dhcp(fixed_ip_ref):319def _host_dhcp(fixed_ip_ref):
266 """Return a host string for an address"""320 """Return a host string for an address"""
267 instance_ref = fixed_ip_ref['instance']321 instance_ref = fixed_ip_ref['instance']
@@ -323,6 +377,15 @@
323 return ''.join(cmd)377 return ''.join(cmd)
324378
325379
380def _ra_cmd(net):
381 """Builds radvd command"""
382 cmd = ['sudo -E radvd',
383# ' -u nobody',
384 ' -C %s' % _ra_file(net['bridge'], 'conf'),
385 ' -p %s' % _ra_file(net['bridge'], 'pid')]
386 return ''.join(cmd)
387
388
326def _stop_dnsmasq(network):389def _stop_dnsmasq(network):
327 """Stops the dnsmasq instance for a given network"""390 """Stops the dnsmasq instance for a given network"""
328 pid = _dnsmasq_pid_for(network)391 pid = _dnsmasq_pid_for(network)
@@ -344,6 +407,16 @@
344 kind))407 kind))
345408
346409
410def _ra_file(bridge, kind):
411 """Return path to a pid or conf file for a bridge"""
412
413 if not os.path.exists(FLAGS.networks_path):
414 os.makedirs(FLAGS.networks_path)
415 return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path,
416 bridge,
417 kind))
418
419
347def _dnsmasq_pid_for(bridge):420def _dnsmasq_pid_for(bridge):
348 """Returns the pid for prior dnsmasq instance for a bridge421 """Returns the pid for prior dnsmasq instance for a bridge
349422
@@ -357,3 +430,18 @@
357 if os.path.exists(pid_file):430 if os.path.exists(pid_file):
358 with open(pid_file, 'r') as f:431 with open(pid_file, 'r') as f:
359 return int(f.read())432 return int(f.read())
433
434
435def _ra_pid_for(bridge):
436 """Returns the pid for prior radvd instance for a bridge
437
438 Returns None if no pid file exists
439
440 If machine has rebooted pid might be incorrect (caller should check)
441 """
442
443 pid_file = _ra_file(bridge, 'pid')
444
445 if os.path.exists(pid_file):
446 with open(pid_file, 'r') as f:
447 return int(f.read())
360448
=== modified file 'nova/network/manager.py'
--- nova/network/manager.py 2011-01-10 02:08:54 +0000
+++ nova/network/manager.py 2011-01-13 08:08:15 +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 08:08:15 +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 08:08:15 +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 08:08:15 +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 08:08:15 +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 08:08:15 +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-12 19:39:25 +0000
+++ nova/virt/libvirt_conn.py 2011-01-13 08:08:15 +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):
@@ -383,7 +393,6 @@
383 instance['id'],393 instance['id'],
384 power_state.NOSTATE,394 power_state.NOSTATE,
385 'launching')395 'launching')
386
387 self.nwfilter.setup_basic_filtering(instance)396 self.nwfilter.setup_basic_filtering(instance)
388 self.firewall_driver.prepare_instance_filter(instance)397 self.firewall_driver.prepare_instance_filter(instance)
389 self._create_image(instance, xml)398 self._create_image(instance, xml)
@@ -552,12 +561,16 @@
552 if network_ref['injected']:561 if network_ref['injected']:
553 admin_context = context.get_admin_context()562 admin_context = context.get_admin_context()
554 address = db.instance_get_fixed_address(admin_context, inst['id'])563 address = db.instance_get_fixed_address(admin_context, inst['id'])
564 ra_server = network_ref['ra_server']
565 if not ra_server:
566 ra_server = "fd00::"
555 with open(FLAGS.injected_network_template) as f:567 with open(FLAGS.injected_network_template) as f:
556 net = f.read() % {'address': address,568 net = f.read() % {'address': address,
557 'netmask': network_ref['netmask'],569 'netmask': network_ref['netmask'],
558 'gateway': network_ref['gateway'],570 'gateway': network_ref['gateway'],
559 'broadcast': network_ref['broadcast'],571 'broadcast': network_ref['broadcast'],
560 'dns': network_ref['dns']}572 'dns': network_ref['dns'],
573 'ra_server': ra_server}
561 if key or net:574 if key or net:
562 if key:575 if key:
563 LOG.info(_('instance %s: injecting key into image %s'),576 LOG.info(_('instance %s: injecting key into image %s'),
@@ -612,13 +625,30 @@
612 instance['id'])625 instance['id'])
613 # Assume that the gateway also acts as the dhcp server.626 # Assume that the gateway also acts as the dhcp server.
614 dhcp_server = network['gateway']627 dhcp_server = network['gateway']
615628 ra_server = network['ra_server']
629 if not ra_server:
630 ra_server = 'fd00::'
616 if FLAGS.allow_project_net_traffic:631 if FLAGS.allow_project_net_traffic:
617 net, mask = _get_net_and_mask(network['cidr'])632 if FLAGS.use_ipv6:
618 extra_params = ("<parameter name=\"PROJNET\" "633 net, mask = _get_net_and_mask(network['cidr'])
619 "value=\"%s\" />\n"634 net_v6, prefixlen_v6 = _get_net_and_prefixlen(
620 "<parameter name=\"PROJMASK\" "635 network['cidr_v6'])
621 "value=\"%s\" />\n") % (net, mask)636 extra_params = ("<parameter name=\"PROJNET\" "
637 "value=\"%s\" />\n"
638 "<parameter name=\"PROJMASK\" "
639 "value=\"%s\" />\n"
640 "<parameter name=\"PROJNETV6\" "
641 "value=\"%s\" />\n"
642 "<parameter name=\"PROJMASKV6\" "
643 "value=\"%s\" />\n") % \
644 (net, mask, net_v6, prefixlen_v6)
645 else:
646 net, mask = _get_net_and_mask(network['cidr'])
647 extra_params = ("<parameter name=\"PROJNET\" "
648 "value=\"%s\" />\n"
649 "<parameter name=\"PROJMASK\" "
650 "value=\"%s\" />\n") % \
651 (net, mask)
622 else:652 else:
623 extra_params = "\n"653 extra_params = "\n"
624654
@@ -632,6 +662,7 @@
632 'mac_address': instance['mac_address'],662 'mac_address': instance['mac_address'],
633 'ip_address': ip_address,663 'ip_address': ip_address,
634 'dhcp_server': dhcp_server,664 'dhcp_server': dhcp_server,
665 'ra_server': ra_server,
635 'extra_params': extra_params,666 'extra_params': extra_params,
636 'rescue': rescue}667 'rescue': rescue}
637 if not rescue:668 if not rescue:
@@ -889,6 +920,15 @@
889 </rule>920 </rule>
890 </filter>'''921 </filter>'''
891922
923 def nova_ra_filter(self):
924 return '''<filter name='nova-allow-ra-server' chain='root'>
925 <uuid>d707fa71-4fb5-4b27-9ab7-ba5ca19c8804</uuid>
926 <rule action='accept' direction='inout'
927 priority='100'>
928 <icmpv6 srcipaddr='$RASERVER'/>
929 </rule>
930 </filter>'''
931
892 def setup_basic_filtering(self, instance):932 def setup_basic_filtering(self, instance):
893 """Set up basic filtering (MAC, IP, and ARP spoofing protection)"""933 """Set up basic filtering (MAC, IP, and ARP spoofing protection)"""
894 logging.info('called setup_basic_filtering in nwfilter')934 logging.info('called setup_basic_filtering in nwfilter')
@@ -913,13 +953,17 @@
913 ['no-mac-spoofing',953 ['no-mac-spoofing',
914 'no-ip-spoofing',954 'no-ip-spoofing',
915 'no-arp-spoofing',955 'no-arp-spoofing',
916 'allow-dhcp-server']))956 'allow-dhcp-server'
957 ]))
917 self._define_filter(self.nova_base_ipv4_filter)958 self._define_filter(self.nova_base_ipv4_filter)
918 self._define_filter(self.nova_base_ipv6_filter)959 self._define_filter(self.nova_base_ipv6_filter)
919 self._define_filter(self.nova_dhcp_filter)960 self._define_filter(self.nova_dhcp_filter)
961 self._define_filter(self.nova_ra_filter)
920 self._define_filter(self.nova_vpn_filter)962 self._define_filter(self.nova_vpn_filter)
921 if FLAGS.allow_project_net_traffic:963 if FLAGS.allow_project_net_traffic:
922 self._define_filter(self.nova_project_filter)964 self._define_filter(self.nova_project_filter)
965 if FLAGS.use_ipv6:
966 self._define_filter(self.nova_project_filter_v6)
923967
924 self.static_filters_configured = True968 self.static_filters_configured = True
925969
@@ -951,13 +995,13 @@
951995
952 def nova_base_ipv6_filter(self):996 def nova_base_ipv6_filter(self):
953 retval = "<filter name='nova-base-ipv6' chain='ipv6'>"997 retval = "<filter name='nova-base-ipv6' chain='ipv6'>"
954 for protocol in ['tcp', 'udp', 'icmp']:998 for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
955 for direction, action, priority in [('out', 'accept', 399),999 for direction, action, priority in [('out', 'accept', 399),
956 ('in', 'drop', 400)]:1000 ('in', 'drop', 400)]:
957 retval += """<rule action='%s' direction='%s' priority='%d'>1001 retval += """<rule action='%s' direction='%s' priority='%d'>
958 <%s-ipv6 />1002 <%s />
959 </rule>""" % (action, direction,1003 </rule>""" % (action, direction,
960 priority, protocol)1004 priority, protocol)
961 retval += '</filter>'1005 retval += '</filter>'
962 return retval1006 return retval
9631007
@@ -970,10 +1014,20 @@
970 retval += '</filter>'1014 retval += '</filter>'
971 return retval1015 return retval
9721016
1017 def nova_project_filter_v6(self):
1018 retval = "<filter name='nova-project-v6' chain='ipv6'>"
1019 for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
1020 retval += """<rule action='accept' direction='inout'
1021 priority='200'>
1022 <%s srcipaddr='$PROJNETV6'
1023 srcipmask='$PROJMASKV6' />
1024 </rule>""" % (protocol)
1025 retval += '</filter>'
1026 return retval
1027
973 def _define_filter(self, xml):1028 def _define_filter(self, xml):
974 if callable(xml):1029 if callable(xml):
975 xml = xml()1030 xml = xml()
976
977 # execute in a native thread and block current greenthread until done1031 # execute in a native thread and block current greenthread until done
978 tpool.execute(self._conn.nwfilterDefineXML, xml)1032 tpool.execute(self._conn.nwfilterDefineXML, xml)
9791033
@@ -983,7 +1037,6 @@
983 it makes sure the filters for the security groups as well as1037 it makes sure the filters for the security groups as well as
984 the base filter are all in place.1038 the base filter are all in place.
985 """1039 """
986
987 if instance['image_id'] == FLAGS.vpn_image_id:1040 if instance['image_id'] == FLAGS.vpn_image_id:
988 base_filter = 'nova-vpn'1041 base_filter = 'nova-vpn'
989 else:1042 else:
@@ -995,11 +1048,15 @@
995 instance_secgroup_filter_children = ['nova-base-ipv4',1048 instance_secgroup_filter_children = ['nova-base-ipv4',
996 'nova-base-ipv6',1049 'nova-base-ipv6',
997 'nova-allow-dhcp-server']1050 'nova-allow-dhcp-server']
1051 if FLAGS.use_ipv6:
1052 instance_secgroup_filter_children += ['nova-allow-ra-server']
9981053
999 ctxt = context.get_admin_context()1054 ctxt = context.get_admin_context()
10001055
1001 if FLAGS.allow_project_net_traffic:1056 if FLAGS.allow_project_net_traffic:
1002 instance_filter_children += ['nova-project']1057 instance_filter_children += ['nova-project']
1058 if FLAGS.use_ipv6:
1059 instance_filter_children += ['nova-project-v6']
10031060
1004 for security_group in db.security_group_get_by_instance(ctxt,1061 for security_group in db.security_group_get_by_instance(ctxt,
1005 instance['id']):1062 instance['id']):
@@ -1027,12 +1084,19 @@
1027 security_group = db.security_group_get(context.get_admin_context(),1084 security_group = db.security_group_get(context.get_admin_context(),
1028 security_group_id)1085 security_group_id)
1029 rule_xml = ""1086 rule_xml = ""
1087 v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'}
1030 for rule in security_group.rules:1088 for rule in security_group.rules:
1031 rule_xml += "<rule action='accept' direction='in' priority='300'>"1089 rule_xml += "<rule action='accept' direction='in' priority='300'>"
1032 if rule.cidr:1090 if rule.cidr:
1033 net, mask = _get_net_and_mask(rule.cidr)1091 version = _get_ip_version(rule.cidr)
1034 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \1092 if(FLAGS.use_ipv6 and version == 6):
1035 (rule.protocol, net, mask)1093 net, prefixlen = _get_net_and_prefixlen(rule.cidr)
1094 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1095 (v6protocol[rule.protocol], net, prefixlen)
1096 else:
1097 net, mask = _get_net_and_mask(rule.cidr)
1098 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1099 (rule.protocol, net, mask)
1036 if rule.protocol in ['tcp', 'udp']:1100 if rule.protocol in ['tcp', 'udp']:
1037 rule_xml += "dstportstart='%s' dstportend='%s' " % \1101 rule_xml += "dstportstart='%s' dstportend='%s' " % \
1038 (rule.from_port, rule.to_port)1102 (rule.from_port, rule.to_port)
@@ -1047,8 +1111,11 @@
10471111
1048 rule_xml += '/>\n'1112 rule_xml += '/>\n'
1049 rule_xml += "</rule>\n"1113 rule_xml += "</rule>\n"
1050 xml = "<filter name='nova-secgroup-%s' chain='ipv4'>%s</filter>" % \1114 xml = "<filter name='nova-secgroup-%s' " % security_group_id
1051 (security_group_id, rule_xml,)1115 if(FLAGS.use_ipv6):
1116 xml += "chain='root'>%s</filter>" % rule_xml
1117 else:
1118 xml += "chain='ipv4'>%s</filter>" % rule_xml
1052 return xml1119 return xml
10531120
1054 def _instance_filter_name(self, instance):1121 def _instance_filter_name(self, instance):
@@ -1077,11 +1144,17 @@
1077 def apply_ruleset(self):1144 def apply_ruleset(self):
1078 current_filter, _ = self.execute('sudo iptables-save -t filter')1145 current_filter, _ = self.execute('sudo iptables-save -t filter')
1079 current_lines = current_filter.split('\n')1146 current_lines = current_filter.split('\n')
1080 new_filter = self.modify_rules(current_lines)1147 new_filter = self.modify_rules(current_lines, 4)
1081 self.execute('sudo iptables-restore',1148 self.execute('sudo iptables-restore',
1082 process_input='\n'.join(new_filter))1149 process_input='\n'.join(new_filter))
1150 if(FLAGS.use_ipv6):
1151 current_filter, _ = self.execute('sudo ip6tables-save -t filter')
1152 current_lines = current_filter.split('\n')
1153 new_filter = self.modify_rules(current_lines, 6)
1154 self.execute('sudo ip6tables-restore',
1155 process_input='\n'.join(new_filter))
10831156
1084 def modify_rules(self, current_lines):1157 def modify_rules(self, current_lines, ip_version=4):
1085 ctxt = context.get_admin_context()1158 ctxt = context.get_admin_context()
1086 # Remove any trace of nova rules.1159 # Remove any trace of nova rules.
1087 new_filter = filter(lambda l: 'nova-' not in l, current_lines)1160 new_filter = filter(lambda l: 'nova-' not in l, current_lines)
@@ -1095,8 +1168,8 @@
1095 if not new_filter[rules_index].startswith(':'):1168 if not new_filter[rules_index].startswith(':'):
1096 break1169 break
10971170
1098 our_chains = [':nova-ipv4-fallback - [0:0]']1171 our_chains = [':nova-fallback - [0:0]']
1099 our_rules = ['-A nova-ipv4-fallback -j DROP']1172 our_rules = ['-A nova-fallback -j DROP']
11001173
1101 our_chains += [':nova-local - [0:0]']1174 our_chains += [':nova-local - [0:0]']
1102 our_rules += ['-A FORWARD -j nova-local']1175 our_rules += ['-A FORWARD -j nova-local']
@@ -1106,7 +1179,10 @@
1106 # First, we add instance chains and rules1179 # First, we add instance chains and rules
1107 for instance in self.instances:1180 for instance in self.instances:
1108 chain_name = self._instance_chain_name(instance)1181 chain_name = self._instance_chain_name(instance)
1109 ip_address = self._ip_for_instance(instance)1182 if(ip_version == 4):
1183 ip_address = self._ip_for_instance(instance)
1184 elif(ip_version == 6):
1185 ip_address = self._ip_for_instance_v6(instance)
11101186
1111 our_chains += [':%s - [0:0]' % chain_name]1187 our_chains += [':%s - [0:0]' % chain_name]
11121188
@@ -1132,13 +1208,19 @@
11321208
1133 our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]1209 our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]
11341210
1135 # Allow DHCP responses1211 if(ip_version == 4):
1136 dhcp_server = self._dhcp_server_for_instance(instance)1212 # Allow DHCP responses
1137 our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %1213 dhcp_server = self._dhcp_server_for_instance(instance)
1138 (chain_name, dhcp_server)]1214 our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %
1215 (chain_name, dhcp_server)]
1216 elif(ip_version == 6):
1217 # Allow RA responses
1218 ra_server = self._ra_server_for_instance(instance)
1219 our_rules += ['-A %s -s %s -p icmpv6' %
1220 (chain_name, ra_server)]
11391221
1140 # If nothing matches, jump to the fallback chain1222 # If nothing matches, jump to the fallback chain
1141 our_rules += ['-A %s -j nova-ipv4-fallback' % (chain_name,)]1223 our_rules += ['-A %s -j nova-fallback' % (chain_name,)]
11421224
1143 # then, security group chains and rules1225 # then, security group chains and rules
1144 for security_group in security_groups:1226 for security_group in security_groups:
@@ -1151,15 +1233,22 @@
11511233
1152 for rule in rules:1234 for rule in rules:
1153 logging.info('%r', rule)1235 logging.info('%r', rule)
1154 args = ['-A', chain_name, '-p', rule.protocol]
11551236
1156 if rule.cidr:1237 if not rule.cidr:
1157 args += ['-s', rule.cidr]
1158 else:
1159 # Eventually, a mechanism to grant access for security1238 # Eventually, a mechanism to grant access for security
1160 # groups will turn up here. It'll use ipsets.1239 # groups will turn up here. It'll use ipsets.
1161 continue1240 continue
11621241
1242 version = _get_ip_version(rule.cidr)
1243 if version != ip_version:
1244 continue
1245
1246 protocol = rule.protocol
1247 if version == 6 and rule.protocol == 'icmp':
1248 protocol = 'icmpv6'
1249
1250 args = ['-A', chain_name, '-p', protocol, '-s', rule.cidr]
1251
1163 if rule.protocol in ['udp', 'tcp']:1252 if rule.protocol in ['udp', 'tcp']:
1164 if rule.from_port == rule.to_port:1253 if rule.from_port == rule.to_port:
1165 args += ['--dport', '%s' % (rule.from_port,)]1254 args += ['--dport', '%s' % (rule.from_port,)]
@@ -1179,7 +1268,12 @@
1179 icmp_type_arg += '/%s' % icmp_code1268 icmp_type_arg += '/%s' % icmp_code
11801269
1181 if icmp_type_arg:1270 if icmp_type_arg:
1182 args += ['-m', 'icmp', '--icmp-type', icmp_type_arg]1271 if(ip_version == 4):
1272 args += ['-m', 'icmp', '--icmp-type',
1273 icmp_type_arg]
1274 elif(ip_version == 6):
1275 args += ['-m', 'icmp6', '--icmpv6-type',
1276 icmp_type_arg]
11831277
1184 args += ['-j ACCEPT']1278 args += ['-j ACCEPT']
1185 our_rules += [' '.join(args)]1279 our_rules += [' '.join(args)]
@@ -1205,7 +1299,16 @@
1205 return db.instance_get_fixed_address(context.get_admin_context(),1299 return db.instance_get_fixed_address(context.get_admin_context(),
1206 instance['id'])1300 instance['id'])
12071301
1302 def _ip_for_instance_v6(self, instance):
1303 return db.instance_get_fixed_address_v6(context.get_admin_context(),
1304 instance['id'])
1305
1208 def _dhcp_server_for_instance(self, instance):1306 def _dhcp_server_for_instance(self, instance):
1209 network = db.project_get_network(context.get_admin_context(),1307 network = db.project_get_network(context.get_admin_context(),
1210 instance['project_id'])1308 instance['project_id'])
1211 return network['gateway']1309 return network['gateway']
1310
1311 def _ra_server_for_instance(self, instance):
1312 network = db.project_get_network(context.get_admin_context(),
1313 instance['project_id'])
1314 return network['ra_server']
12121315
=== modified file 'smoketests/admin_smoketests.py'
--- smoketests/admin_smoketests.py 2011-01-06 01:51:05 +0000
+++ smoketests/admin_smoketests.py 2011-01-13 08:08:15 +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 08:08:15 +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 08:08:15 +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 08:08:15 +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 08:08:15 +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)