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: 1536 lines (+808/-53)
22 files modified
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 (+67/-0)
nova/tests/test_network.py (+21/-0)
nova/utils.py (+49/-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
Devin Carlen (community) Needs Information
Soren Hansen (community) Needs Fixing
Review via email: mp+45228@code.launchpad.net

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

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

To post a comment you must log in.
lp:~ntt-pf-lab/nova/ipv6-support updated
474. By Nachi Ueno

Fixed:Create instance fails when use_ipv6=False

475. By Nachi Ueno

Fixed for pep8

476. By Nachi Ueno

missing _()

Revision history for this message
Soren Hansen (soren) wrote :
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) :
review: Needs Fixing
Revision history for this message
Devin Carlen (devcamcar) wrote :

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 :

>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 :).

lp:~ntt-pf-lab/nova/ipv6-support updated
477. By Nachi Ueno

changed exception class

478. By Hisaharu Ishii

Fixed for pep8
Remove temporary debugging

479. By Nachi Ueno

Add DescribeInstanceV6 for backward compatibility

480. By Nachi Ueno

Fixed bug

481. By Nachi Ueno

Merged with r548

482. By Hisaharu Ishii

Change command to get link local address
Remove superfluous code

483. By Nachi Ueno

Merged with 549

484. By Nachi Ueno

Fixed syntax errors

485. By Hisaharu Ishii

Support IPv6 firewall with IptablesFirewallDriver

486. By Hisaharu Ishii

Merged with r551

487. By Nachi Ueno

fixed method signature of modify_rules
fixed unit_test for ipv6

488. By Nachi Ueno

merged with r555

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 'bin/nova-manage'
--- bin/nova-manage 2011-01-10 15:13:30 +0000
+++ bin/nova-manage 2011-01-12 13:09:31 +0000
@@ -89,6 +89,7 @@
89flags.DECLARE('network_size', 'nova.network.manager')89flags.DECLARE('network_size', 'nova.network.manager')
90flags.DECLARE('vlan_start', 'nova.network.manager')90flags.DECLARE('vlan_start', 'nova.network.manager')
91flags.DECLARE('vpn_start', 'nova.network.manager')91flags.DECLARE('vpn_start', 'nova.network.manager')
92flags.DECLARE('fixed_range_v6', 'nova.network.manager')
9293
9394
94class VpnCommands(object):95class VpnCommands(object):
@@ -437,11 +438,12 @@
437 """Class for managing networks."""438 """Class for managing networks."""
438439
439 def create(self, fixed_range=None, num_networks=None,440 def create(self, fixed_range=None, num_networks=None,
440 network_size=None, vlan_start=None, vpn_start=None):441 network_size=None, vlan_start=None, vpn_start=None,
442 fixed_range_v6=None):
441 """Creates fixed ips for host by range443 """Creates fixed ips for host by range
442 arguments: [fixed_range=FLAG], [num_networks=FLAG],444 arguments: [fixed_range=FLAG], [num_networks=FLAG],
443 [network_size=FLAG], [vlan_start=FLAG],445 [network_size=FLAG], [vlan_start=FLAG],
444 [vpn_start=FLAG]"""446 [vpn_start=FLAG], [fixed_range_v6=FLAG]"""
445 if not fixed_range:447 if not fixed_range:
446 fixed_range = FLAGS.fixed_range448 fixed_range = FLAGS.fixed_range
447 if not num_networks:449 if not num_networks:
@@ -452,11 +454,13 @@
452 vlan_start = FLAGS.vlan_start454 vlan_start = FLAGS.vlan_start
453 if not vpn_start:455 if not vpn_start:
454 vpn_start = FLAGS.vpn_start456 vpn_start = FLAGS.vpn_start
457 if not fixed_range_v6:
458 fixed_range_v6 = FLAGS.fixed_range_v6
455 net_manager = utils.import_object(FLAGS.network_manager)459 net_manager = utils.import_object(FLAGS.network_manager)
456 net_manager.create_networks(context.get_admin_context(),460 net_manager.create_networks(context.get_admin_context(),
457 fixed_range, int(num_networks),461 fixed_range, int(num_networks),
458 int(network_size), int(vlan_start),462 int(network_size), int(vlan_start),
459 int(vpn_start))463 int(vpn_start), fixed_range_v6)
460464
461465
462class ServiceCommands(object):466class ServiceCommands(object):
463467
=== 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-12 13:09:31 +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-12 13:09:31 +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-12 13:09:31 +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-12 13:09:31 +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-12 11:34:16 +0000
+++ nova/api/ec2/cloud.py 2011-01-12 13:09:31 +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-12 13:09:31 +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 11:34:16 +0000
+++ nova/db/sqlalchemy/api.py 2011-01-12 13:09:31 +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)
@@ -792,6 +803,17 @@
792803
793804
794@require_context805@require_context
806def instance_get_fixed_address_v6(context, instance_id):
807 session = get_session()
808 with session.begin():
809 instance_ref = instance_get(context, instance_id, session=session)
810 network_ref = network_get_by_instance(context, instance_id)
811 prefix = network_ref.cidr_v6
812 mac = instance_ref.mac_address
813 return utils.to_global_ipv6(prefix, mac)
814
815
816@require_context
795def instance_get_floating_address(context, instance_id):817def instance_get_floating_address(context, instance_id):
796 session = get_session()818 session = get_session()
797 with session.begin():819 with session.begin():
@@ -1128,6 +1150,11 @@
1128 return result1150 return result
11291151
11301152
1153@require_context
1154def project_get_network_v6(context, project_id):
1155 return project_get_network(context, project_id)
1156
1157
1131###################1158###################
11321159
11331160
11341161
=== 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-12 13:09:31 +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-12 13:09:31 +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 dnsmasq 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 dnsmasq 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 logging.debug("Hupping radvd threw %s", exc)
310 else:
311 logging.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 dnsmasq 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, leases 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 dnsmasq 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-12 13:09:31 +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-12 13:09:31 +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-12 13:09:31 +0000
@@ -24,6 +24,7 @@
24import random24import random
25import StringIO25import StringIO
26import webob26import webob
27import logging
2728
28from nova import context29from nova import context
29from nova import flags30from nova import flags
@@ -265,6 +266,72 @@
265266
266 return267 return
267268
269 def test_authorize_revoke_security_group_cidr_v6(self):
270 """
271 Test that we can add and remove CIDR based rules
272 to a security group for IPv6
273 """
274 self.expect_http()
275 self.mox.ReplayAll()
276 user = self.manager.create_user('fake', 'fake', 'fake')
277 project = self.manager.create_project('fake', 'fake', 'fake')
278
279 # At the moment, you need both of these to actually be netadmin
280 self.manager.add_role('fake', 'netadmin')
281 project.add_role('fake', 'netadmin')
282
283 security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
284 for x in range(random.randint(4, 8)))
285
286 group = self.ec2.create_security_group(security_group_name,
287 'test group')
288
289 self.expect_http()
290 self.mox.ReplayAll()
291 group.connection = self.ec2
292
293 group.authorize('tcp', 80, 81, '::/0')
294
295 self.expect_http()
296 self.mox.ReplayAll()
297
298 rv = self.ec2.get_all_security_groups()
299 # I don't bother checkng that we actually find it here,
300 # because the create/delete unit test further up should
301 # be good enough for that.
302 for group in rv:
303 if group.name == security_group_name:
304 self.assertEquals(len(group.rules), 1)
305 self.assertEquals(int(group.rules[0].from_port), 80)
306 self.assertEquals(int(group.rules[0].to_port), 81)
307 self.assertEquals(len(group.rules[0].grants), 1)
308 self.assertEquals(str(group.rules[0].grants[0]), '::/0')
309
310 self.expect_http()
311 self.mox.ReplayAll()
312 group.connection = self.ec2
313
314 group.revoke('tcp', 80, 81, '::/0')
315
316 self.expect_http()
317 self.mox.ReplayAll()
318
319 self.ec2.delete_security_group(security_group_name)
320
321 self.expect_http()
322 self.mox.ReplayAll()
323 group.connection = self.ec2
324
325 rv = self.ec2.get_all_security_groups()
326
327 self.assertEqual(len(rv), 1)
328 self.assertEqual(rv[0].name, 'default')
329
330 self.manager.delete_project(project)
331 self.manager.delete_user(user)
332
333 return
334
268 def test_authorize_revoke_security_group_foreign_group(self):335 def test_authorize_revoke_security_group_foreign_group(self):
269 """336 """
270 Test that we can grant and revoke another security group access337 Test that we can grant and revoke another security group access
271338
=== 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-12 13:09:31 +0000
@@ -96,6 +96,27 @@
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(1)
103 network_ref = db.project_get_network(
104 self.context,
105 self.context.project_id)
106 address_v6 = db.instance_get_fixed_address_v6(
107 self.context,
108 instance_ref['id'])
109 self.assertEqual(instance_ref['mac_address'],
110 utils.to_mac(address_v6))
111 instance_ref2 = db.fixed_ip_get_instance_v6(
112 self.context,
113 address_v6)
114 self.assertEqual(instance_ref['id'], instance_ref2['id'])
115 self.assertEqual(address_v6,
116 utils.to_global_ipv6(
117 network_ref['cidr_v6'],
118 instance_ref['mac_address']))
119
99 def test_public_network_association(self):120 def test_public_network_association(self):
100 """Makes sure that we can allocaate a public ip"""121 """Makes sure that we can allocaate a public ip"""
101 # TODO(vish): better way of adding floating ips122 # TODO(vish): better way of adding floating ips
102123
=== modified file 'nova/utils.py'
--- nova/utils.py 2011-01-12 01:32:12 +0000
+++ nova/utils.py 2011-01-12 13:09:31 +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,53 @@
200 return int(address.split(".")[-1])202 return int(address.split(".")[-1])
201203
202204
205def get_my_ip():
206 """Returns the actual ip of the local machine."""
207 try:
208 csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
209 csock.connect(('8.8.8.8', 80))
210 (addr, port) = csock.getsockname()
211 csock.close()
212 return addr
213 except socket.gaierror as ex:
214 logging.warn(_("Couldn't get IP, using 127.0.0.1 %s"), ex)
215 return "127.0.0.1"
216
217
218def get_my_linklocal(interface):
219 try:
220 if_str = execute("ip -f inet6 -o addr show %s" % interface)
221 condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope\s+link"
222 links = [re.search(condition, x) for x in if_str[0].split('\n')]
223 address = [w.group(1) for w in links if w is not None]
224 if address[0] is not None:
225 return address[0]
226 else:
227 return 'fe00::'
228 except IndexError as ex:
229 logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex)
230 except ProcessExecutionError as ex:
231 logging.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex)
232 except:
233 return 'fe00::'
234
235
236def to_global_ipv6(prefix, mac):
237 mac64 = netaddr.EUI(mac).eui64().words
238 int_addr = int(''.join(['%02x' % i for i in mac64]), 16)
239 mac64_addr = netaddr.IPAddress(int_addr)
240 maskIP = netaddr.IPNetwork(prefix).ip
241 return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).format()
242
243
244def to_mac(ipv6_address):
245 address = netaddr.IPAddress(ipv6_address)
246 mask1 = netaddr.IPAddress("::ffff:ffff:ffff:ffff")
247 mask2 = netaddr.IPAddress("::0200:0:0:0")
248 mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words
249 return ":".join(["%02x" % i for i in mac64[0:3] + mac64[5:8]])
250
251
203def utcnow():252def utcnow():
204 """Overridable version of datetime.datetime.utcnow."""253 """Overridable version of datetime.datetime.utcnow."""
205 if utcnow.override_time:254 if utcnow.override_time:
206255
=== 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-12 13:09:31 +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 01:32:12 +0000
+++ nova/virt/libvirt_conn.py 2011-01-12 13:09:31 +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):
@@ -370,7 +380,6 @@
370 instance['id'],380 instance['id'],
371 power_state.NOSTATE,381 power_state.NOSTATE,
372 'launching')382 'launching')
373
374 self.nwfilter.setup_basic_filtering(instance)383 self.nwfilter.setup_basic_filtering(instance)
375 self.firewall_driver.prepare_instance_filter(instance)384 self.firewall_driver.prepare_instance_filter(instance)
376 self._create_image(instance, xml)385 self._create_image(instance, xml)
@@ -539,12 +548,16 @@
539 if network_ref['injected']:548 if network_ref['injected']:
540 admin_context = context.get_admin_context()549 admin_context = context.get_admin_context()
541 address = db.instance_get_fixed_address(admin_context, inst['id'])550 address = db.instance_get_fixed_address(admin_context, inst['id'])
551 ra_server = network_ref['ra_server']
552 if not ra_server:
553 ra_server = "fd00::"
542 with open(FLAGS.injected_network_template) as f:554 with open(FLAGS.injected_network_template) as f:
543 net = f.read() % {'address': address,555 net = f.read() % {'address': address,
544 'netmask': network_ref['netmask'],556 'netmask': network_ref['netmask'],
545 'gateway': network_ref['gateway'],557 'gateway': network_ref['gateway'],
546 'broadcast': network_ref['broadcast'],558 'broadcast': network_ref['broadcast'],
547 'dns': network_ref['dns']}559 'dns': network_ref['dns'],
560 'ra_server': ra_server}
548 if key or net:561 if key or net:
549 if key:562 if key:
550 LOG.info(_('instance %s: injecting key into image %s'),563 LOG.info(_('instance %s: injecting key into image %s'),
@@ -599,13 +612,30 @@
599 instance['id'])612 instance['id'])
600 # Assume that the gateway also acts as the dhcp server.613 # Assume that the gateway also acts as the dhcp server.
601 dhcp_server = network['gateway']614 dhcp_server = network['gateway']
602615 ra_server = network['ra_server']
616 if not ra_server:
617 ra_server = 'fd00::'
603 if FLAGS.allow_project_net_traffic:618 if FLAGS.allow_project_net_traffic:
604 net, mask = _get_net_and_mask(network['cidr'])619 if FLAGS.use_ipv6:
605 extra_params = ("<parameter name=\"PROJNET\" "620 net, mask = _get_net_and_mask(network['cidr'])
606 "value=\"%s\" />\n"621 net_v6, prefixlen_v6 = _get_net_and_prefixlen(
607 "<parameter name=\"PROJMASK\" "622 network['cidr_v6'])
608 "value=\"%s\" />\n") % (net, mask)623 extra_params = ("<parameter name=\"PROJNET\" "
624 "value=\"%s\" />\n"
625 "<parameter name=\"PROJMASK\" "
626 "value=\"%s\" />\n"
627 "<parameter name=\"PROJNETV6\" "
628 "value=\"%s\" />\n"
629 "<parameter name=\"PROJMASKV6\" "
630 "value=\"%s\" />\n") % \
631 (net, mask, net_v6, prefixlen_v6)
632 else:
633 net, mask = _get_net_and_mask(network['cidr'])
634 extra_params = ("<parameter name=\"PROJNET\" "
635 "value=\"%s\" />\n"
636 "<parameter name=\"PROJMASK\" "
637 "value=\"%s\" />\n") % \
638 (net, mask)
609 else:639 else:
610 extra_params = "\n"640 extra_params = "\n"
611641
@@ -619,6 +649,7 @@
619 'mac_address': instance['mac_address'],649 'mac_address': instance['mac_address'],
620 'ip_address': ip_address,650 'ip_address': ip_address,
621 'dhcp_server': dhcp_server,651 'dhcp_server': dhcp_server,
652 'ra_server': ra_server,
622 'extra_params': extra_params,653 'extra_params': extra_params,
623 'rescue': rescue}654 'rescue': rescue}
624 if not rescue:655 if not rescue:
@@ -876,6 +907,15 @@
876 </rule>907 </rule>
877 </filter>'''908 </filter>'''
878909
910 def nova_ra_filter(self):
911 return '''<filter name='nova-allow-ra-server' chain='root'>
912 <uuid>d707fa71-4fb5-4b27-9ab7-ba5ca19c8804</uuid>
913 <rule action='accept' direction='inout'
914 priority='100'>
915 <icmpv6 srcipaddr='$RASERVER'/>
916 </rule>
917 </filter>'''
918
879 def setup_basic_filtering(self, instance):919 def setup_basic_filtering(self, instance):
880 """Set up basic filtering (MAC, IP, and ARP spoofing protection)"""920 """Set up basic filtering (MAC, IP, and ARP spoofing protection)"""
881 logging.info('called setup_basic_filtering in nwfilter')921 logging.info('called setup_basic_filtering in nwfilter')
@@ -900,13 +940,17 @@
900 ['no-mac-spoofing',940 ['no-mac-spoofing',
901 'no-ip-spoofing',941 'no-ip-spoofing',
902 'no-arp-spoofing',942 'no-arp-spoofing',
903 'allow-dhcp-server']))943 'allow-dhcp-server'
944 ]))
904 self._define_filter(self.nova_base_ipv4_filter)945 self._define_filter(self.nova_base_ipv4_filter)
905 self._define_filter(self.nova_base_ipv6_filter)946 self._define_filter(self.nova_base_ipv6_filter)
906 self._define_filter(self.nova_dhcp_filter)947 self._define_filter(self.nova_dhcp_filter)
948 self._define_filter(self.nova_ra_filter)
907 self._define_filter(self.nova_vpn_filter)949 self._define_filter(self.nova_vpn_filter)
908 if FLAGS.allow_project_net_traffic:950 if FLAGS.allow_project_net_traffic:
909 self._define_filter(self.nova_project_filter)951 self._define_filter(self.nova_project_filter)
952 if FLAGS.use_ipv6:
953 self._define_filter(self.nova_project_filter_v6)
910954
911 self.static_filters_configured = True955 self.static_filters_configured = True
912956
@@ -938,13 +982,13 @@
938982
939 def nova_base_ipv6_filter(self):983 def nova_base_ipv6_filter(self):
940 retval = "<filter name='nova-base-ipv6' chain='ipv6'>"984 retval = "<filter name='nova-base-ipv6' chain='ipv6'>"
941 for protocol in ['tcp', 'udp', 'icmp']:985 for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
942 for direction, action, priority in [('out', 'accept', 399),986 for direction, action, priority in [('out', 'accept', 399),
943 ('in', 'drop', 400)]:987 ('in', 'drop', 400)]:
944 retval += """<rule action='%s' direction='%s' priority='%d'>988 retval += """<rule action='%s' direction='%s' priority='%d'>
945 <%s-ipv6 />989 <%s />
946 </rule>""" % (action, direction,990 </rule>""" % (action, direction,
947 priority, protocol)991 priority, protocol)
948 retval += '</filter>'992 retval += '</filter>'
949 return retval993 return retval
950994
@@ -957,10 +1001,20 @@
957 retval += '</filter>'1001 retval += '</filter>'
958 return retval1002 return retval
9591003
1004 def nova_project_filter_v6(self):
1005 retval = "<filter name='nova-project-v6' chain='ipv6'>"
1006 for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
1007 retval += """<rule action='accept' direction='inout'
1008 priority='200'>
1009 <%s srcipaddr='$PROJNETV6'
1010 srcipmask='$PROJMASKV6' />
1011 </rule>""" % (protocol)
1012 retval += '</filter>'
1013 return retval
1014
960 def _define_filter(self, xml):1015 def _define_filter(self, xml):
961 if callable(xml):1016 if callable(xml):
962 xml = xml()1017 xml = xml()
963
964 # execute in a native thread and block current greenthread until done1018 # execute in a native thread and block current greenthread until done
965 tpool.execute(self._conn.nwfilterDefineXML, xml)1019 tpool.execute(self._conn.nwfilterDefineXML, xml)
9661020
@@ -970,7 +1024,6 @@
970 it makes sure the filters for the security groups as well as1024 it makes sure the filters for the security groups as well as
971 the base filter are all in place.1025 the base filter are all in place.
972 """1026 """
973
974 if instance['image_id'] == FLAGS.vpn_image_id:1027 if instance['image_id'] == FLAGS.vpn_image_id:
975 base_filter = 'nova-vpn'1028 base_filter = 'nova-vpn'
976 else:1029 else:
@@ -982,11 +1035,15 @@
982 instance_secgroup_filter_children = ['nova-base-ipv4',1035 instance_secgroup_filter_children = ['nova-base-ipv4',
983 'nova-base-ipv6',1036 'nova-base-ipv6',
984 'nova-allow-dhcp-server']1037 'nova-allow-dhcp-server']
1038 if FLAGS.use_ipv6:
1039 instance_secgroup_filter_children += ['nova-allow-ra-server']
9851040
986 ctxt = context.get_admin_context()1041 ctxt = context.get_admin_context()
9871042
988 if FLAGS.allow_project_net_traffic:1043 if FLAGS.allow_project_net_traffic:
989 instance_filter_children += ['nova-project']1044 instance_filter_children += ['nova-project']
1045 if FLAGS.use_ipv6:
1046 instance_filter_children += ['nova-project-v6']
9901047
991 for security_group in db.security_group_get_by_instance(ctxt,1048 for security_group in db.security_group_get_by_instance(ctxt,
992 instance['id']):1049 instance['id']):
@@ -1014,12 +1071,19 @@
1014 security_group = db.security_group_get(context.get_admin_context(),1071 security_group = db.security_group_get(context.get_admin_context(),
1015 security_group_id)1072 security_group_id)
1016 rule_xml = ""1073 rule_xml = ""
1074 v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'}
1017 for rule in security_group.rules:1075 for rule in security_group.rules:
1018 rule_xml += "<rule action='accept' direction='in' priority='300'>"1076 rule_xml += "<rule action='accept' direction='in' priority='300'>"
1019 if rule.cidr:1077 if rule.cidr:
1020 net, mask = _get_net_and_mask(rule.cidr)1078 version = _get_ip_version(rule.cidr)
1021 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \1079 if(FLAGS.use_ipv6 and version == 6):
1022 (rule.protocol, net, mask)1080 net, prefixlen = _get_net_and_prefixlen(rule.cidr)
1081 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1082 (v6protocol[rule.protocol], net, prefixlen)
1083 else:
1084 net, mask = _get_net_and_mask(rule.cidr)
1085 rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
1086 (rule.protocol, net, mask)
1023 if rule.protocol in ['tcp', 'udp']:1087 if rule.protocol in ['tcp', 'udp']:
1024 rule_xml += "dstportstart='%s' dstportend='%s' " % \1088 rule_xml += "dstportstart='%s' dstportend='%s' " % \
1025 (rule.from_port, rule.to_port)1089 (rule.from_port, rule.to_port)
@@ -1034,8 +1098,11 @@
10341098
1035 rule_xml += '/>\n'1099 rule_xml += '/>\n'
1036 rule_xml += "</rule>\n"1100 rule_xml += "</rule>\n"
1037 xml = "<filter name='nova-secgroup-%s' chain='ipv4'>%s</filter>" % \1101 xml = "<filter name='nova-secgroup-%s' " % security_group_id
1038 (security_group_id, rule_xml,)1102 if(FLAGS.use_ipv6):
1103 xml += "chain='root'>%s</filter>" % rule_xml
1104 else:
1105 xml += "chain='ipv4'>%s</filter>" % rule_xml
1039 return xml1106 return xml
10401107
1041 def _instance_filter_name(self, instance):1108 def _instance_filter_name(self, instance):
@@ -1064,11 +1131,17 @@
1064 def apply_ruleset(self):1131 def apply_ruleset(self):
1065 current_filter, _ = self.execute('sudo iptables-save -t filter')1132 current_filter, _ = self.execute('sudo iptables-save -t filter')
1066 current_lines = current_filter.split('\n')1133 current_lines = current_filter.split('\n')
1067 new_filter = self.modify_rules(current_lines)1134 new_filter = self.modify_rules(current_lines, 4)
1068 self.execute('sudo iptables-restore',1135 self.execute('sudo iptables-restore',
1069 process_input='\n'.join(new_filter))1136 process_input='\n'.join(new_filter))
1137 if(FLAGS.use_ipv6):
1138 current_filter, _ = self.execute('sudo ip6tables-save -t filter')
1139 current_lines = current_filter.split('\n')
1140 new_filter = self.modify_rules(current_lines, 6)
1141 self.execute('sudo ip6tables-restore',
1142 process_input='\n'.join(new_filter))
10701143
1071 def modify_rules(self, current_lines):1144 def modify_rules(self, current_lines, ip_version):
1072 ctxt = context.get_admin_context()1145 ctxt = context.get_admin_context()
1073 # Remove any trace of nova rules.1146 # Remove any trace of nova rules.
1074 new_filter = filter(lambda l: 'nova-' not in l, current_lines)1147 new_filter = filter(lambda l: 'nova-' not in l, current_lines)
@@ -1082,8 +1155,8 @@
1082 if not new_filter[rules_index].startswith(':'):1155 if not new_filter[rules_index].startswith(':'):
1083 break1156 break
10841157
1085 our_chains = [':nova-ipv4-fallback - [0:0]']1158 our_chains = [':nova-fallback - [0:0]']
1086 our_rules = ['-A nova-ipv4-fallback -j DROP']1159 our_rules = ['-A nova-fallback -j DROP']
10871160
1088 our_chains += [':nova-local - [0:0]']1161 our_chains += [':nova-local - [0:0]']
1089 our_rules += ['-A FORWARD -j nova-local']1162 our_rules += ['-A FORWARD -j nova-local']
@@ -1093,7 +1166,10 @@
1093 # First, we add instance chains and rules1166 # First, we add instance chains and rules
1094 for instance in self.instances:1167 for instance in self.instances:
1095 chain_name = self._instance_chain_name(instance)1168 chain_name = self._instance_chain_name(instance)
1096 ip_address = self._ip_for_instance(instance)1169 if(ip_version == 4):
1170 ip_address = self._ip_for_instance(instance)
1171 elif(ip_version == 6):
1172 ip_address = self._ip_for_instance_v6(instance)
10971173
1098 our_chains += [':%s - [0:0]' % chain_name]1174 our_chains += [':%s - [0:0]' % chain_name]
10991175
@@ -1119,13 +1195,19 @@
11191195
1120 our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]1196 our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]
11211197
1122 # Allow DHCP responses1198 if(ip_version == 4):
1123 dhcp_server = self._dhcp_server_for_instance(instance)1199 # Allow DHCP responses
1124 our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %1200 dhcp_server = self._dhcp_server_for_instance(instance)
1125 (chain_name, dhcp_server)]1201 our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %
1202 (chain_name, dhcp_server)]
1203 elif(ip_version == 6):
1204 # Allow RA responses
1205 ra_server = self._ra_server_for_instance(instance)
1206 our_rules += ['-A %s -s %s -p icmpv6' %
1207 (chain_name, ra_server)]
11261208
1127 # If nothing matches, jump to the fallback chain1209 # If nothing matches, jump to the fallback chain
1128 our_rules += ['-A %s -j nova-ipv4-fallback' % (chain_name,)]1210 our_rules += ['-A %s -j nova-fallback' % (chain_name,)]
11291211
1130 # then, security group chains and rules1212 # then, security group chains and rules
1131 for security_group in security_groups:1213 for security_group in security_groups:
@@ -1138,15 +1220,22 @@
11381220
1139 for rule in rules:1221 for rule in rules:
1140 logging.info('%r', rule)1222 logging.info('%r', rule)
1141 args = ['-A', chain_name, '-p', rule.protocol]
11421223
1143 if rule.cidr:1224 if not rule.cidr:
1144 args += ['-s', rule.cidr]
1145 else:
1146 # Eventually, a mechanism to grant access for security1225 # Eventually, a mechanism to grant access for security
1147 # groups will turn up here. It'll use ipsets.1226 # groups will turn up here. It'll use ipsets.
1148 continue1227 continue
11491228
1229 version = _get_ip_version(rule.cidr)
1230 if version != ip_version:
1231 continue
1232
1233 protocol = rule.protocol
1234 if version == 6 and rule.protocol == 'icmp':
1235 protocol = 'icmpv6'
1236
1237 args = ['-A', chain_name, '-p', protocol, '-s', rule.cidr]
1238
1150 if rule.protocol in ['udp', 'tcp']:1239 if rule.protocol in ['udp', 'tcp']:
1151 if rule.from_port == rule.to_port:1240 if rule.from_port == rule.to_port:
1152 args += ['--dport', '%s' % (rule.from_port,)]1241 args += ['--dport', '%s' % (rule.from_port,)]
@@ -1166,7 +1255,12 @@
1166 icmp_type_arg += '/%s' % icmp_code1255 icmp_type_arg += '/%s' % icmp_code
11671256
1168 if icmp_type_arg:1257 if icmp_type_arg:
1169 args += ['-m', 'icmp', '--icmp-type', icmp_type_arg]1258 if(ip_version == 4):
1259 args += ['-m', 'icmp', '--icmp-type',
1260 icmp_type_arg]
1261 elif(ip_version == 6):
1262 args += ['-m', 'icmp6', '--icmpv6-type',
1263 icmp_type_arg]
11701264
1171 args += ['-j ACCEPT']1265 args += ['-j ACCEPT']
1172 our_rules += [' '.join(args)]1266 our_rules += [' '.join(args)]
@@ -1192,7 +1286,16 @@
1192 return db.instance_get_fixed_address(context.get_admin_context(),1286 return db.instance_get_fixed_address(context.get_admin_context(),
1193 instance['id'])1287 instance['id'])
11941288
1289 def _ip_for_instance_v6(self, instance):
1290 return db.instance_get_fixed_address_v6(context.get_admin_context(),
1291 instance['id'])
1292
1195 def _dhcp_server_for_instance(self, instance):1293 def _dhcp_server_for_instance(self, instance):
1196 network = db.project_get_network(context.get_admin_context(),1294 network = db.project_get_network(context.get_admin_context(),
1197 instance['project_id'])1295 instance['project_id'])
1198 return network['gateway']1296 return network['gateway']
1297
1298 def _ra_server_for_instance(self, instance):
1299 network = db.project_get_network(context.get_admin_context(),
1300 instance['project_id'])
1301 return network['ra_server']
11991302
=== modified file 'smoketests/admin_smoketests.py'
--- smoketests/admin_smoketests.py 2011-01-06 01:51:05 +0000
+++ smoketests/admin_smoketests.py 2011-01-12 13:09:31 +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-12 13:09:31 +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-12 13:09:31 +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-12 13:09:31 +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-12 13:09:31 +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)