merge from upstream

This commit is contained in:
Andy Smith 2011-01-14 17:57:42 -08:00
commit 9750e4ab3e
39 changed files with 1309 additions and 300 deletions

View File

@ -4,8 +4,8 @@ Anthony Young <sleepsonthefloor@gmail.com>
Antony Messerli <ant@openstack.org>
Armando Migliaccio <Armando.Migliaccio@eu.citrix.com>
Chiradeep Vittal <chiradeep@cloud.com>
Chris Behrens <cbehrens@codestud.com>
Chmouel Boudjnah <chmouel@chmouel.com>
Chris Behrens <cbehrens@codestud.com>
Cory Wright <corywright@gmail.com>
David Pravec <David.Pravec@danix.org>
Dean Troyer <dtroyer@gmail.com>
@ -14,6 +14,7 @@ Ed Leafe <ed@leafe.com>
Eldar Nugaev <enugaev@griddynamics.com>
Eric Day <eday@oddments.org>
Ewan Mellor <ewan.mellor@citrix.com>
Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp>
Hisaki Ohara <hisaki.ohara@intel.com>
Ilya Alekseyev <ialekseev@griddynamics.com>
Jay Pipes <jaypipes@gmail.com>
@ -26,11 +27,14 @@ Josh Kearney <josh.kearney@rackspace.com>
Joshua McKenty <jmckenty@gmail.com>
Justin Santa Barbara <justin@fathomdb.com>
Ken Pepple <ken.pepple@gmail.com>
Koji Iida <iida.koji@lab.ntt.co.jp>
Lorin Hochstein <lorin@isi.edu>
Matt Dietz <matt.dietz@rackspace.com>
Michael Gundlach <michael.gundlach@rackspace.com>
Monsyne Dragon <mdragon@rackspace.com>
Monty Taylor <mordred@inaugust.com>
MORITA Kazutaka <morita.kazutaka@gmail.com>
Nachi Ueno <ueno.nachi@lab.ntt.co.jp> <openstack@lab.ntt.co.jp> <nati.ueno@gmail.com> <nova@u4>
Paul Voccio <paul@openstack.org>
Rick Clark <rick@openstack.org>
Rick Harris <rconradharris@gmail.com>

View File

@ -92,6 +92,7 @@ flags.DECLARE('num_networks', 'nova.network.manager')
flags.DECLARE('network_size', 'nova.network.manager')
flags.DECLARE('vlan_start', 'nova.network.manager')
flags.DECLARE('vpn_start', 'nova.network.manager')
flags.DECLARE('fixed_range_v6', 'nova.network.manager')
class VpnCommands(object):
@ -440,11 +441,12 @@ class NetworkCommands(object):
"""Class for managing networks."""
def create(self, fixed_range=None, num_networks=None,
network_size=None, vlan_start=None, vpn_start=None):
network_size=None, vlan_start=None, vpn_start=None,
fixed_range_v6=None):
"""Creates fixed ips for host by range
arguments: [fixed_range=FLAG], [num_networks=FLAG],
[network_size=FLAG], [vlan_start=FLAG],
[vpn_start=FLAG]"""
[vpn_start=FLAG], [fixed_range_v6=FLAG]"""
if not fixed_range:
fixed_range = FLAGS.fixed_range
if not num_networks:
@ -455,11 +457,13 @@ class NetworkCommands(object):
vlan_start = FLAGS.vlan_start
if not vpn_start:
vpn_start = FLAGS.vpn_start
if not fixed_range_v6:
fixed_range_v6 = FLAGS.fixed_range_v6
net_manager = utils.import_object(FLAGS.network_manager)
net_manager.create_networks(context.get_admin_context(),
fixed_range, int(num_networks),
int(network_size), int(vlan_start),
int(vpn_start))
int(vpn_start), fixed_range_v6)
class ServiceCommands(object):

View File

@ -0,0 +1,37 @@
# Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/
# Copyright (c) 2010, Eucalyptus Systems, Inc.
# All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
def connect_ec2(aws_access_key_id=None, aws_secret_access_key=None, **kwargs):
"""
:type aws_access_key_id: string
:param aws_access_key_id: Your AWS Access Key ID
:type aws_secret_access_key: string
:param aws_secret_access_key: Your AWS Secret Access Key
:rtype: :class:`boto.ec2.connection.EC2Connection`
:return: A connection to Amazon's EC2
"""
from boto_v6.ec2.connection import EC2ConnectionV6
return EC2ConnectionV6(aws_access_key_id, aws_secret_access_key, **kwargs)

View File

View File

@ -0,0 +1,41 @@
'''
Created on 2010/12/20
@author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp>
'''
import boto
import boto.ec2
from boto_v6.ec2.instance import ReservationV6
class EC2ConnectionV6(boto.ec2.EC2Connection):
'''
EC2Connection for OpenStack IPV6 mode
'''
def get_all_instances(self, instance_ids=None, filters=None):
"""
Retrieve all the instances associated with your account.
:type instance_ids: list
:param instance_ids: A list of strings of instance IDs
:type filters: dict
:param filters: Optional filters that can be used to limit
the results returned. Filters are provided
in the form of a dictionary consisting of
filter names as the key and filter values
as the value. The set of allowable filter
names/values is dependent on the request
being performed. Check the EC2 API guide
for details.
:rtype: list
:return: A list of :class:`boto.ec2.instance.Reservation`
"""
params = {}
if instance_ids:
self.build_list_params(params, instance_ids, 'InstanceId')
if filters:
self.build_filter_params(params, filters)
return self.get_list('DescribeInstancesV6', params,
[('item', ReservationV6)])

View File

@ -0,0 +1,37 @@
'''
Created on 2010/12/20
@author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp>
'''
import boto
from boto.resultset import ResultSet
from boto.ec2.instance import Reservation
from boto.ec2.instance import Group
from boto.ec2.instance import Instance
class ReservationV6(Reservation):
def startElement(self, name, attrs, connection):
if name == 'instancesSet':
self.instances = ResultSet([('item', InstanceV6)])
return self.instances
elif name == 'groupSet':
self.groups = ResultSet([('item', Group)])
return self.groups
else:
return None
class InstanceV6(Instance):
def __init__(self, connection=None):
Instance.__init__(self, connection)
self.dns_name_v6 = None
def endElement(self, name, value, connection):
Instance.endElement(self, name, value, connection)
if name == 'dnsNameV6':
self.dns_name_v6 = value
def _update(self, updated):
self.__dict__.update(updated.__dict__)
self.dns_name_v6 = updated.dns_name_v6

View File

@ -87,6 +87,13 @@ if [ "$CMD" == "install" ]; then
sudo apt-get install -y python-twisted python-sqlalchemy python-mox python-greenlet python-carrot
sudo apt-get install -y python-daemon python-eventlet python-gflags python-ipy
sudo apt-get install -y python-libvirt python-libxml2 python-routes python-cheetah
#For IPV6
sudo apt-get install -y python-netaddr
sudo apt-get install -y radvd
#(Nati) Note that this configuration is only needed for nova-network node.
sudo bash -c "echo 1 > /proc/sys/net/ipv6/conf/all/forwarding"
sudo bash -c "echo 0 > /proc/sys/net/ipv6/conf/all/accept_ra"
if [ "$USE_MYSQL" == 1 ]; then
cat <<MYSQL_PRESEED | debconf-set-selections
mysql-server-5.1 mysql-server/root_password password $MYSQL_PASS
@ -108,6 +115,8 @@ function screen_it {
if [ "$CMD" == "run" ]; then
killall dnsmasq
#For IPv6
killall radvd
screen -d -m -S nova -t nova
sleep 1
if [ "$USE_MYSQL" == 1 ]; then

View File

@ -26,16 +26,17 @@ import base64
import datetime
import IPy
import os
import urllib
from nova import compute
from nova import context
from nova import crypto
from nova import db
from nova import exception
from nova import flags
from nova import log as logging
from nova import network
from nova import rpc
from nova import utils
from nova import volume
from nova.compute import instance_types
@ -128,15 +129,6 @@ class CloudController(object):
result[key] = [line]
return result
def _trigger_refresh_security_group(self, context, security_group):
nodes = set([instance['host'] for instance in security_group.instances
if instance['host'] is not None])
for node in nodes:
rpc.cast(context,
'%s.%s' % (FLAGS.compute_topic, node),
{"method": "refresh_security_group",
"args": {"security_group_id": security_group.id}})
def _get_availability_zone_by_host(self, context, host):
services = db.service_get_all_by_host(context, host)
if len(services) > 0:
@ -374,6 +366,7 @@ class CloudController(object):
values['group_id'] = source_security_group['id']
elif cidr_ip:
# If this fails, it throws an exception. This is what we want.
cidr_ip = urllib.unquote(cidr_ip).decode()
IPy.IP(cidr_ip)
values['cidr'] = cidr_ip
else:
@ -519,13 +512,7 @@ class CloudController(object):
# instance_id is passed in as a list of instances
ec2_id = instance_id[0]
instance_id = ec2_id_to_id(ec2_id)
instance_ref = self.compute_api.get(context, instance_id)
output = rpc.call(context,
'%s.%s' % (FLAGS.compute_topic,
instance_ref['host']),
{"method": "get_console_output",
"args": {"instance_id": instance_ref['id']}})
output = self.compute_api.get_console_output(context, instance_id)
now = datetime.datetime.utcnow()
return {"InstanceId": ec2_id,
"Timestamp": now,
@ -643,6 +630,10 @@ class CloudController(object):
def describe_instances(self, context, **kwargs):
return self._format_describe_instances(context, **kwargs)
def describe_instances_v6(self, context, **kwargs):
kwargs['use_v6'] = True
return self._format_describe_instances(context, **kwargs)
def _format_describe_instances(self, context, **kwargs):
return {'reservationSet': self._format_instances(context, **kwargs)}
@ -678,10 +669,16 @@ class CloudController(object):
if instance['fixed_ip']['floating_ips']:
fixed = instance['fixed_ip']
floating_addr = fixed['floating_ips'][0]['address']
if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
i['dnsNameV6'] = utils.to_global_ipv6(
instance['fixed_ip']['network']['cidr_v6'],
instance['mac_address'])
i['privateDnsName'] = fixed_addr
i['publicDnsName'] = floating_addr
i['dnsName'] = i['publicDnsName'] or i['privateDnsName']
i['keyName'] = instance['key_name']
if context.user.is_admin():
i['keyName'] = '%s (%s, %s)' % (i['keyName'],
instance['project_id'],

View File

@ -165,15 +165,18 @@ class Controller(wsgi.Controller):
if not inst_dict:
return faults.Fault(exc.HTTPUnprocessableEntity())
ctxt = req.environ['nova.context']
update_dict = {}
if 'adminPass' in inst_dict['server']:
update_dict['admin_pass'] = inst_dict['server']['adminPass']
try:
self.compute_api.set_admin_password(ctxt, id)
except exception.TimeoutException, e:
return exc.HTTPRequestTimeout()
if 'name' in inst_dict['server']:
update_dict['display_name'] = inst_dict['server']['name']
try:
self.compute_api.update(req.environ['nova.context'], id,
**update_dict)
self.compute_api.update(ctxt, id, **update_dict)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPNoContent()

View File

@ -21,6 +21,7 @@ Handles all requests relating to instances (guest vms).
"""
import datetime
import re
import time
from nova import db
@ -280,7 +281,7 @@ class API(base.Base):
return self.db.instance_update(context, instance_id, kwargs)
def delete(self, context, instance_id):
LOG.debug(_("Going to try and terminate %s"), instance_id)
LOG.debug(_("Going to try to terminate %s"), instance_id)
try:
instance = self.get(context, instance_id)
except exception.NotFound as e:
@ -301,10 +302,8 @@ class API(base.Base):
host = instance['host']
if host:
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "terminate_instance",
"args": {"instance_id": instance_id}})
self._cast_compute_message('terminate_instance', context,
instance_id, host)
else:
self.db.instance_destroy(context, instance_id)
@ -332,50 +331,46 @@ class API(base.Base):
project_id)
return self.db.instance_get_all(context)
def _cast_compute_message(self, method, context, instance_id, host=None):
"""Generic handler for RPC casts to compute."""
if not host:
instance = self.get(context, instance_id)
host = instance['host']
queue = self.db.queue_get_for(context, FLAGS.compute_topic, host)
kwargs = {'method': method, 'args': {'instance_id': instance_id}}
rpc.cast(context, queue, kwargs)
def _call_compute_message(self, method, context, instance_id, host=None):
"""Generic handler for RPC calls to compute."""
if not host:
instance = self.get(context, instance_id)
host = instance["host"]
queue = self.db.queue_get_for(context, FLAGS.compute_topic, host)
kwargs = {"method": method, "args": {"instance_id": instance_id}}
return rpc.call(context, queue, kwargs)
def snapshot(self, context, instance_id, name):
"""Snapshot the given instance."""
instance = self.get(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "snapshot_instance",
"args": {"instance_id": instance_id, "name": name}})
self._cast_compute_message('snapshot_instance', context, instance_id)
def reboot(self, context, instance_id):
"""Reboot the given instance."""
instance = self.get(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "reboot_instance",
"args": {"instance_id": instance_id}})
self._cast_compute_message('reboot_instance', context, instance_id)
def pause(self, context, instance_id):
"""Pause the given instance."""
instance = self.get(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "pause_instance",
"args": {"instance_id": instance_id}})
self._cast_compute_message('pause_instance', context, instance_id)
def unpause(self, context, instance_id):
"""Unpause the given instance."""
instance = self.get(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "unpause_instance",
"args": {"instance_id": instance_id}})
self._cast_compute_message('unpause_instance', context, instance_id)
def get_diagnostics(self, context, instance_id):
"""Retrieve diagnostics for the given instance."""
instance = self.get(context, instance_id)
host = instance["host"]
return rpc.call(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "get_diagnostics",
"args": {"instance_id": instance_id}})
return self._call_compute_message(
"get_diagnostics",
context,
instance_id)
def get_actions(self, context, instance_id):
"""Retrieve actions for the given instance."""
@ -383,89 +378,54 @@ class API(base.Base):
def suspend(self, context, instance_id):
"""suspend the instance with instance_id"""
instance = self.get(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "suspend_instance",
"args": {"instance_id": instance_id}})
self._cast_compute_message('suspend_instance', context, instance_id)
def resume(self, context, instance_id):
"""resume the instance with instance_id"""
instance = self.get(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "resume_instance",
"args": {"instance_id": instance_id}})
self._cast_compute_message('resume_instance', context, instance_id)
def rescue(self, context, instance_id):
"""Rescue the given instance."""
instance = self.get(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "rescue_instance",
"args": {"instance_id": instance_id}})
self._cast_compute_message('rescue_instance', context, instance_id)
def unrescue(self, context, instance_id):
"""Unrescue the given instance."""
instance = self.get(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "unrescue_instance",
"args": {"instance_id": instance['id']}})
self._cast_compute_message('unrescue_instance', context, instance_id)
def set_admin_password(self, context, instance_id):
"""Set the root/admin password for the given instance."""
self._cast_compute_message('set_admin_password', context, instance_id)
def get_ajax_console(self, context, instance_id):
"""Get a url to an AJAX Console"""
instance = self.get(context, instance_id)
output = rpc.call(context,
'%s.%s' % (FLAGS.compute_topic,
instance['host']),
{'method': 'get_ajax_console',
'args': {'instance_id': instance['id']}})
output = self._call_compute_message('get_ajax_console',
context,
instance_id)
rpc.cast(context, '%s' % FLAGS.ajax_console_proxy_topic,
{'method': 'authorize_ajax_console',
'args': {'token': output['token'], 'host': output['host'],
'port': output['port']}})
return {'url': '%s?token=%s' % (FLAGS.ajax_console_proxy_url,
output['token'])}
def lock(self, context, instance_id):
"""
lock the instance with instance_id
def get_console_output(self, context, instance_id):
"""Get console output for an an instance"""
return self._call_compute_message('get_console_output',
context,
instance_id)
"""
instance = self.get_instance(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "lock_instance",
"args": {"instance_id": instance['id']}})
def lock(self, context, instance_id):
"""lock the instance with instance_id"""
self._cast_compute_message('lock_instance', context, instance_id)
def unlock(self, context, instance_id):
"""
unlock the instance with instance_id
"""
instance = self.get_instance(context, instance_id)
host = instance['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.compute_topic, host),
{"method": "unlock_instance",
"args": {"instance_id": instance['id']}})
"""unlock the instance with instance_id"""
self._cast_compute_message('unlock_instance', context, instance_id)
def get_lock(self, context, instance_id):
"""
return the boolean state of (instance with instance_id)'s lock
"""
instance = self.get_instance(context, instance_id)
"""return the boolean state of (instance with instance_id)'s lock"""
instance = self.get(context, instance_id)
return instance['locked']
def attach_volume(self, context, instance_id, volume_id, device):

View File

@ -35,6 +35,8 @@ terminating it.
"""
import datetime
import random
import string
import logging
import socket
import functools
@ -54,6 +56,8 @@ flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection',
'Driver to use for controlling virtualization')
flags.DEFINE_string('stub_network', False,
'Stub network related code')
flags.DEFINE_integer('password_length', 12,
'Length of generated admin passwords')
flags.DEFINE_string('console_host', socket.gethostname(),
'Console proxy host to use to connect to instances on'
'this host.')
@ -309,6 +313,35 @@ class ComputeManager(manager.Manager):
self.driver.snapshot(instance_ref, name)
@exception.wrap_exception
@checks_instance_lock
def set_admin_password(self, context, instance_id, new_pass=None):
"""Set the root/admin password for an instance on this server."""
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
if instance_ref['state'] != power_state.RUNNING:
logging.warn('trying to reset the password on a non-running '
'instance: %s (state: %s expected: %s)',
instance_ref['id'],
instance_ref['state'],
power_state.RUNNING)
logging.debug('instance %s: setting admin password',
instance_ref['name'])
if new_pass is None:
# Generate a random password
new_pass = self._generate_password(FLAGS.password_length)
self.driver.set_admin_password(instance_ref, new_pass)
self._update_state(context, instance_id)
def _generate_password(self, length=20):
"""Generate a random sequence of letters and digits
to be used as a password.
"""
chrs = string.letters + string.digits
return "".join([random.choice(chrs) for i in xrange(length)])
@exception.wrap_exception
@checks_instance_lock
def rescue_instance(self, context, instance_id):

View File

@ -299,6 +299,10 @@ def fixed_ip_get_instance(context, address):
return IMPL.fixed_ip_get_instance(context, address)
def fixed_ip_get_instance_v6(context, address):
return IMPL.fixed_ip_get_instance_v6(context, address)
def fixed_ip_get_network(context, address):
"""Get a network for a fixed ip by address."""
return IMPL.fixed_ip_get_network(context, address)
@ -357,6 +361,10 @@ def instance_get_fixed_address(context, instance_id):
return IMPL.instance_get_fixed_address(context, instance_id)
def instance_get_fixed_address_v6(context, instance_id):
return IMPL.instance_get_fixed_address_v6(context, instance_id)
def instance_get_floating_address(context, instance_id):
"""Get the first floating ip address of an instance."""
return IMPL.instance_get_floating_address(context, instance_id)
@ -552,6 +560,10 @@ def project_get_network(context, project_id, associate=True):
return IMPL.project_get_network(context, project_id)
def project_get_network_v6(context, project_id):
return IMPL.project_get_network_v6(context, project_id)
###################

View File

@ -606,6 +606,17 @@ def fixed_ip_get_instance(context, address):
return fixed_ip_ref.instance
@require_context
def fixed_ip_get_instance_v6(context, address):
session = get_session()
mac = utils.to_mac(address)
result = session.query(models.Instance
).filter_by(mac_address=mac
).first()
return result
@require_admin_context
def fixed_ip_get_network(context, address):
fixed_ip_ref = fixed_ip_get_by_address(context, address)
@ -793,6 +804,17 @@ def instance_get_fixed_address(context, instance_id):
return instance_ref.fixed_ip['address']
@require_context
def instance_get_fixed_address_v6(context, instance_id):
session = get_session()
with session.begin():
instance_ref = instance_get(context, instance_id, session=session)
network_ref = network_get_by_instance(context, instance_id)
prefix = network_ref.cidr_v6
mac = instance_ref.mac_address
return utils.to_global_ipv6(prefix, mac)
@require_context
def instance_get_floating_address(context, instance_id):
session = get_session()
@ -1130,6 +1152,11 @@ def project_get_network(context, project_id, associate=True):
return result
@require_context
def project_get_network_v6(context, project_id):
return project_get_network(context, project_id)
###################

View File

@ -366,6 +366,10 @@ class Network(BASE, NovaBase):
injected = Column(Boolean, default=False)
cidr = Column(String(255), unique=True)
cidr_v6 = Column(String(255), unique=True)
ra_server = Column(String(255))
netmask = Column(String(255))
bridge = Column(String(255))
gateway = Column(String(255))

View File

@ -76,6 +76,10 @@ class InvalidInputException(Error):
pass
class TimeoutException(Error):
pass
def wrap_exception(f):
def _wrap(*args, **kw):
try:

View File

@ -50,6 +50,7 @@ flags.DEFINE_string('routing_source_ip', '$my_ip',
'Public IP of network host')
flags.DEFINE_bool('use_nova_chains', False,
'use the nova_ routing chains instead of default')
flags.DEFINE_string('dns_server', None,
'if set, uses specific dns server for dnsmasq')
flags.DEFINE_string('dmz_cidr', '10.128.0.0/24',
@ -196,6 +197,10 @@ def ensure_bridge(bridge, interface, net_attrs=None):
net_attrs['gateway'],
net_attrs['broadcast'],
net_attrs['netmask']))
if(FLAGS.use_ipv6):
_execute("sudo ifconfig %s add %s up" % \
(bridge,
net_attrs['cidr_v6']))
else:
_execute("sudo ifconfig %s up" % bridge)
if FLAGS.use_nova_chains:
@ -262,6 +267,50 @@ def update_dhcp(context, network_id):
_execute(command, addl_env=env)
def update_ra(context, network_id):
network_ref = db.network_get(context, network_id)
conffile = _ra_file(network_ref['bridge'], 'conf')
with open(conffile, 'w') as f:
conf_str = """
interface %s
{
AdvSendAdvert on;
MinRtrAdvInterval 3;
MaxRtrAdvInterval 10;
prefix %s
{
AdvOnLink on;
AdvAutonomous on;
};
};
""" % (network_ref['bridge'], network_ref['cidr_v6'])
f.write(conf_str)
# Make sure radvd can actually read it (it setuid()s to "nobody")
os.chmod(conffile, 0644)
pid = _ra_pid_for(network_ref['bridge'])
# if radvd is already running, then tell it to reload
if pid:
out, _err = _execute('cat /proc/%d/cmdline'
% pid, check_exit_code=False)
if conffile in out:
try:
_execute('sudo kill -HUP %d' % pid)
return
except Exception as exc: # pylint: disable-msg=W0703
LOG.debug(_("Hupping radvd threw %s"), exc)
else:
LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
command = _ra_cmd(network_ref)
_execute(command)
db.network_update(context, network_id,
{"ra_server":
utils.get_my_linklocal(network_ref['bridge'])})
def _host_dhcp(fixed_ip_ref):
"""Return a host string for an address"""
instance_ref = fixed_ip_ref['instance']
@ -323,6 +372,15 @@ def _dnsmasq_cmd(net):
return ''.join(cmd)
def _ra_cmd(net):
"""Builds radvd command"""
cmd = ['sudo -E radvd',
# ' -u nobody',
' -C %s' % _ra_file(net['bridge'], 'conf'),
' -p %s' % _ra_file(net['bridge'], 'pid')]
return ''.join(cmd)
def _stop_dnsmasq(network):
"""Stops the dnsmasq instance for a given network"""
pid = _dnsmasq_pid_for(network)
@ -344,6 +402,16 @@ def _dhcp_file(bridge, kind):
kind))
def _ra_file(bridge, kind):
"""Return path to a pid or conf file for a bridge"""
if not os.path.exists(FLAGS.networks_path):
os.makedirs(FLAGS.networks_path)
return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path,
bridge,
kind))
def _dnsmasq_pid_for(bridge):
"""Returns the pid for prior dnsmasq instance for a bridge
@ -357,3 +425,18 @@ def _dnsmasq_pid_for(bridge):
if os.path.exists(pid_file):
with open(pid_file, 'r') as f:
return int(f.read())
def _ra_pid_for(bridge):
"""Returns the pid for prior radvd instance for a bridge
Returns None if no pid file exists
If machine has rebooted pid might be incorrect (caller should check)
"""
pid_file = _ra_file(bridge, 'pid')
if os.path.exists(pid_file):
with open(pid_file, 'r') as f:
return int(f.read())

View File

@ -82,6 +82,7 @@ flags.DEFINE_integer('network_size', 256,
flags.DEFINE_string('floating_range', '4.4.4.0/24',
'Floating IP address block')
flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')
flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block')
flags.DEFINE_integer('cnt_vpn_clients', 5,
'Number of addresses reserved for vpn clients')
flags.DEFINE_string('network_driver', 'nova.network.linux_net',
@ -90,6 +91,9 @@ flags.DEFINE_bool('update_dhcp_on_disassociate', False,
'Whether to update dhcp when fixed_ip is disassociated')
flags.DEFINE_integer('fixed_ip_disassociate_timeout', 600,
'Seconds after which a deallocated ip is disassociated')
flags.DEFINE_bool('use_ipv6', True,
'use the ipv6')
flags.DEFINE_string('network_host', socket.gethostname(),
'Network host to use for ip allocation in flat modes')
flags.DEFINE_bool('fake_call', False,
@ -235,8 +239,8 @@ class NetworkManager(manager.Manager):
"""Get the network host for the current context."""
raise NotImplementedError()
def create_networks(self, context, num_networks, network_size,
*args, **kwargs):
def create_networks(self, context, cidr, num_networks, network_size,
cidr_v6, *args, **kwargs):
"""Create networks based on parameters."""
raise NotImplementedError()
@ -321,9 +325,11 @@ class FlatManager(NetworkManager):
pass
def create_networks(self, context, cidr, num_networks, network_size,
*args, **kwargs):
cidr_v6, *args, **kwargs):
"""Create networks based on parameters."""
fixed_net = IPy.IP(cidr)
fixed_net_v6 = IPy.IP(cidr_v6)
significant_bits_v6 = 64
for index in range(num_networks):
start = index * network_size
significant_bits = 32 - int(math.log(network_size, 2))
@ -336,7 +342,13 @@ class FlatManager(NetworkManager):
net['gateway'] = str(project_net[1])
net['broadcast'] = str(project_net.broadcast())
net['dhcp_start'] = str(project_net[2])
if(FLAGS.use_ipv6):
cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6)
net['cidr_v6'] = cidr_v6
network_ref = self.db.network_create_safe(context, net)
if network_ref:
self._create_fixed_ips(context, network_ref['id'])
@ -482,12 +494,16 @@ class VlanManager(NetworkManager):
network_ref['bridge'])
def create_networks(self, context, cidr, num_networks, network_size,
vlan_start, vpn_start):
vlan_start, vpn_start, cidr_v6):
"""Create networks based on parameters."""
fixed_net = IPy.IP(cidr)
fixed_net_v6 = IPy.IP(cidr_v6)
network_size_v6 = 1 << 64
significant_bits_v6 = 64
for index in range(num_networks):
vlan = vlan_start + index
start = index * network_size
start_v6 = index * network_size_v6
significant_bits = 32 - int(math.log(network_size, 2))
cidr = "%s/%s" % (fixed_net[start], significant_bits)
project_net = IPy.IP(cidr)
@ -500,6 +516,13 @@ class VlanManager(NetworkManager):
net['dhcp_start'] = str(project_net[3])
net['vlan'] = vlan
net['bridge'] = 'br%s' % vlan
if(FLAGS.use_ipv6):
cidr_v6 = "%s/%s" % (
fixed_net_v6[start_v6],
significant_bits_v6
)
net['cidr_v6'] = cidr_v6
# NOTE(vish): This makes ports unique accross the cloud, a more
# robust solution would be to make them unique per ip
net['vpn_public_port'] = vpn_start + index
@ -538,6 +561,7 @@ class VlanManager(NetworkManager):
self.driver.ensure_vlan_bridge(network_ref['vlan'],
network_ref['bridge'],
network_ref)
# NOTE(vish): only ensure this forward if the address hasn't been set
# manually.
if address == FLAGS.vpn_ip:
@ -546,6 +570,8 @@ class VlanManager(NetworkManager):
network_ref['vpn_private_address'])
if not FLAGS.fake_network:
self.driver.update_dhcp(context, network_id)
if(FLAGS.use_ipv6):
self.driver.update_ra(context, network_id)
@property
def _bottom_reserved_ips(self):

View File

@ -23,14 +23,10 @@ and some black magic for inline callbacks.
"""
import datetime
import sys
import time
import unittest
import mox
import stubout
from twisted.internet import defer
from twisted.trial import unittest as trial_unittest
from nova import context
from nova import db
@ -74,7 +70,8 @@ class TestCase(unittest.TestCase):
FLAGS.fixed_range,
5, 16,
FLAGS.vlan_start,
FLAGS.vpn_start)
FLAGS.vpn_start,
FLAGS.fixed_range_v6)
# emulate some of the mox stuff, we can't use the metaclass
# because it screws with our generators
@ -139,95 +136,3 @@ class TestCase(unittest.TestCase):
_wrapped.func_name = self.originalAttach.func_name
rpc.Consumer.attach_to_eventlet = _wrapped
class TrialTestCase(trial_unittest.TestCase):
"""Test case base class for all unit tests"""
def setUp(self):
"""Run before each test method to initialize test environment"""
super(TrialTestCase, self).setUp()
# NOTE(vish): We need a better method for creating fixtures for tests
# now that we have some required db setup for the system
# to work properly.
self.start = datetime.datetime.utcnow()
ctxt = context.get_admin_context()
if db.network_count(ctxt) != 5:
network_manager.VlanManager().create_networks(ctxt,
FLAGS.fixed_range,
5, 16,
FLAGS.vlan_start,
FLAGS.vpn_start)
# emulate some of the mox stuff, we can't use the metaclass
# because it screws with our generators
self.mox = mox.Mox()
self.stubs = stubout.StubOutForTesting()
self.flag_overrides = {}
self.injected = []
self._original_flags = FLAGS.FlagValuesDict()
def tearDown(self):
"""Runs after each test method to finalize/tear down test
environment."""
try:
self.mox.UnsetStubs()
self.stubs.UnsetAll()
self.stubs.SmartUnsetAll()
self.mox.VerifyAll()
# NOTE(vish): Clean up any ips associated during the test.
ctxt = context.get_admin_context()
db.fixed_ip_disassociate_all_by_timeout(ctxt, FLAGS.host,
self.start)
db.network_disassociate_all(ctxt)
for x in self.injected:
try:
x.stop()
except AssertionError:
pass
if FLAGS.fake_rabbit:
fakerabbit.reset_all()
db.security_group_destroy_all(ctxt)
super(TrialTestCase, self).tearDown()
finally:
self.reset_flags()
def flags(self, **kw):
"""Override flag variables for a test"""
for k, v in kw.iteritems():
if k in self.flag_overrides:
self.reset_flags()
raise Exception(
'trying to override already overriden flag: %s' % k)
self.flag_overrides[k] = getattr(FLAGS, k)
setattr(FLAGS, k, v)
def reset_flags(self):
"""Resets all flag variables for the test. Runs after each test"""
FLAGS.Reset()
for k, v in self._original_flags.iteritems():
setattr(FLAGS, k, v)
def run(self, result=None):
test_method = getattr(self, self._testMethodName)
setattr(self,
self._testMethodName,
self._maybeInlineCallbacks(test_method, result))
rv = super(TrialTestCase, self).run(result)
setattr(self, self._testMethodName, test_method)
return rv
def _maybeInlineCallbacks(self, func, result):
def _wrapped():
g = func()
if isinstance(g, defer.Deferred):
return g
if not hasattr(g, 'send'):
return defer.succeed(g)
inlined = defer.inlineCallbacks(func)
d = inlined()
return d
_wrapped.func_name = func.func_name
return _wrapped

View File

@ -79,7 +79,7 @@ class FakeHttplibConnection(object):
pass
class XmlConversionTestCase(test.TrialTestCase):
class XmlConversionTestCase(test.TestCase):
"""Unit test api xml conversion"""
def test_number_conversion(self):
conv = apirequest._try_convert
@ -96,7 +96,7 @@ class XmlConversionTestCase(test.TrialTestCase):
self.assertEqual(conv('-0'), 0)
class ApiEc2TestCase(test.TrialTestCase):
class ApiEc2TestCase(test.TestCase):
"""Unit test for the cloud controller on an EC2 API"""
def setUp(self):
super(ApiEc2TestCase, self).setUp()
@ -265,6 +265,72 @@ class ApiEc2TestCase(test.TrialTestCase):
return
def test_authorize_revoke_security_group_cidr_v6(self):
"""
Test that we can add and remove CIDR based rules
to a security group for IPv6
"""
self.expect_http()
self.mox.ReplayAll()
user = self.manager.create_user('fake', 'fake', 'fake')
project = self.manager.create_project('fake', 'fake', 'fake')
# At the moment, you need both of these to actually be netadmin
self.manager.add_role('fake', 'netadmin')
project.add_role('fake', 'netadmin')
security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
for x in range(random.randint(4, 8)))
group = self.ec2.create_security_group(security_group_name,
'test group')
self.expect_http()
self.mox.ReplayAll()
group.connection = self.ec2
group.authorize('tcp', 80, 81, '::/0')
self.expect_http()
self.mox.ReplayAll()
rv = self.ec2.get_all_security_groups()
# I don't bother checkng that we actually find it here,
# because the create/delete unit test further up should
# be good enough for that.
for group in rv:
if group.name == security_group_name:
self.assertEquals(len(group.rules), 1)
self.assertEquals(int(group.rules[0].from_port), 80)
self.assertEquals(int(group.rules[0].to_port), 81)
self.assertEquals(len(group.rules[0].grants), 1)
self.assertEquals(str(group.rules[0].grants[0]), '::/0')
self.expect_http()
self.mox.ReplayAll()
group.connection = self.ec2
group.revoke('tcp', 80, 81, '::/0')
self.expect_http()
self.mox.ReplayAll()
self.ec2.delete_security_group(security_group_name)
self.expect_http()
self.mox.ReplayAll()
group.connection = self.ec2
rv = self.ec2.get_all_security_groups()
self.assertEqual(len(rv), 1)
self.assertEqual(rv[0].name, 'default')
self.manager.delete_project(project)
self.manager.delete_user(user)
return
def test_authorize_revoke_security_group_foreign_group(self):
"""
Test that we can grant and revoke another security group access

View File

@ -151,6 +151,13 @@ class ComputeTestCase(test.TestCase):
self.compute.reboot_instance(self.context, instance_id)
self.compute.terminate_instance(self.context, instance_id)
def test_set_admin_password(self):
"""Ensure instance can have its admin password set"""
instance_id = self._create_instance()
self.compute.run_instance(self.context, instance_id)
self.compute.set_admin_password(self.context, instance_id)
self.compute.terminate_instance(self.context, instance_id)
def test_snapshot(self):
"""Ensure instance can be snapshotted"""
instance_id = self._create_instance()

View File

@ -9,7 +9,7 @@ def _fake_context():
return context.RequestContext(1, 1)
class RootLoggerTestCase(test.TrialTestCase):
class RootLoggerTestCase(test.TestCase):
def setUp(self):
super(RootLoggerTestCase, self).setUp()
self.log = log.logging.root
@ -46,7 +46,7 @@ class RootLoggerTestCase(test.TrialTestCase):
self.assert_(True) # didn't raise exception
class NovaFormatterTestCase(test.TrialTestCase):
class NovaFormatterTestCase(test.TestCase):
def setUp(self):
super(NovaFormatterTestCase, self).setUp()
self.flags(logging_context_format_string="HAS CONTEXT "\
@ -78,7 +78,7 @@ class NovaFormatterTestCase(test.TrialTestCase):
self.assertEqual("NOCTXT: baz --DBG\n", self.stream.getvalue())
class NovaLoggerTestCase(test.TrialTestCase):
class NovaLoggerTestCase(test.TestCase):
def setUp(self):
super(NovaLoggerTestCase, self).setUp()
self.flags(default_log_levels=["nova-test=AUDIT"], verbose=False)
@ -96,7 +96,7 @@ class NovaLoggerTestCase(test.TrialTestCase):
self.assertEqual(log.AUDIT, l.level)
class VerboseLoggerTestCase(test.TrialTestCase):
class VerboseLoggerTestCase(test.TestCase):
def setUp(self):
super(VerboseLoggerTestCase, self).setUp()
self.flags(default_log_levels=["nova.test=AUDIT"], verbose=True)

View File

@ -38,7 +38,7 @@ def conditional_forbid(req):
return 'OK'
class LockoutTestCase(test.TrialTestCase):
class LockoutTestCase(test.TestCase):
"""Test case for the Lockout middleware."""
def setUp(self): # pylint: disable-msg=C0103
super(LockoutTestCase, self).setUp()

View File

@ -96,6 +96,28 @@ class NetworkTestCase(test.TestCase):
self.context.project_id = self.projects[project_num].id
self.network.deallocate_fixed_ip(self.context, address)
def test_private_ipv6(self):
"""Make sure ipv6 is OK"""
if FLAGS.use_ipv6:
instance_ref = self._create_instance(0)
address = self._create_address(0, instance_ref['id'])
network_ref = db.project_get_network(
context.get_admin_context(),
self.context.project_id)
address_v6 = db.instance_get_fixed_address_v6(
context.get_admin_context(),
instance_ref['id'])
self.assertEqual(instance_ref['mac_address'],
utils.to_mac(address_v6))
instance_ref2 = db.fixed_ip_get_instance_v6(
context.get_admin_context(),
address_v6)
self.assertEqual(instance_ref['id'], instance_ref2['id'])
self.assertEqual(address_v6,
utils.to_global_ipv6(
network_ref['cidr_v6'],
instance_ref['mac_address']))
def test_public_network_association(self):
"""Makes sure that we can allocaate a public ip"""
# TODO(vish): better way of adding floating ips

View File

@ -28,7 +28,7 @@ from nova import test
FLAGS = flags.FLAGS
class TwistdTestCase(test.TrialTestCase):
class TwistdTestCase(test.TestCase):
def setUp(self):
super(TwistdTestCase, self).setUp()
self.Options = twistd.WrapTwistedOptions(twistd.TwistdServerOptions)

View File

@ -31,6 +31,7 @@ from nova.compute import power_state
from nova.virt import xenapi_conn
from nova.virt.xenapi import fake as xenapi_fake
from nova.virt.xenapi import volume_utils
from nova.virt.xenapi.vmops import SimpleDH
from nova.tests.db import fakes as db_fakes
from nova.tests.xenapi import stubs
@ -262,3 +263,29 @@ class XenAPIVMTestCase(test.TestCase):
instance = db.instance_create(values)
self.conn.spawn(instance)
return instance
class XenAPIDiffieHellmanTestCase(test.TestCase):
"""
Unit tests for Diffie-Hellman code
"""
def setUp(self):
super(XenAPIDiffieHellmanTestCase, self).setUp()
self.alice = SimpleDH()
self.bob = SimpleDH()
def test_shared(self):
alice_pub = self.alice.get_public()
bob_pub = self.bob.get_public()
alice_shared = self.alice.compute_shared(bob_pub)
bob_shared = self.bob.compute_shared(alice_pub)
self.assertEquals(alice_shared, bob_shared)
def test_encryption(self):
msg = "This is a top-secret message"
enc = self.alice.encrypt(msg)
dec = self.bob.decrypt(enc)
self.assertEquals(dec, msg)
def tearDown(self):
super(XenAPIDiffieHellmanTestCase, self).tearDown()

View File

@ -30,6 +30,8 @@ import struct
import sys
import time
from xml.sax import saxutils
import re
import netaddr
from eventlet import event
from eventlet import greenthread
@ -200,6 +202,40 @@ def last_octet(address):
return int(address.split(".")[-1])
def get_my_linklocal(interface):
try:
if_str = execute("ip -f inet6 -o addr show %s" % interface)
condition = "\s+inet6\s+([0-9a-f:]+/\d+)\s+scope\s+link"
links = [re.search(condition, x) for x in if_str[0].split('\n')]
address = [w.group(1) for w in links if w is not None]
if address[0] is not None:
return address[0]
else:
return 'fe00::'
except IndexError as ex:
LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex)
except ProcessExecutionError as ex:
LOG.warn(_("Couldn't get Link Local IP of %s :%s"), interface, ex)
except:
return 'fe00::'
def to_global_ipv6(prefix, mac):
mac64 = netaddr.EUI(mac).eui64().words
int_addr = int(''.join(['%02x' % i for i in mac64]), 16)
mac64_addr = netaddr.IPAddress(int_addr)
maskIP = netaddr.IPNetwork(prefix).ip
return (mac64_addr ^ netaddr.IPAddress('::0200:0:0:0') | maskIP).format()
def to_mac(ipv6_address):
address = netaddr.IPAddress(ipv6_address)
mask1 = netaddr.IPAddress("::ffff:ffff:ffff:ffff")
mask2 = netaddr.IPAddress("::0200:0:0:0")
mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words
return ":".join(["%02x" % i for i in mac64[0:3] + mac64[5:8]])
def utcnow():
"""Overridable version of datetime.datetime.utcnow."""
if utcnow.override_time:

View File

@ -98,7 +98,7 @@ class FakeConnection(object):
the new instance.
The work will be done asynchronously. This function returns a
Deferred that allows the caller to detect when it is complete.
task that allows the caller to detect when it is complete.
Once this successfully completes, the instance should be
running (power_state.RUNNING).
@ -122,7 +122,7 @@ class FakeConnection(object):
The second parameter is the name of the snapshot.
The work will be done asynchronously. This function returns a
Deferred that allows the caller to detect when it is complete.
task that allows the caller to detect when it is complete.
"""
pass
@ -134,7 +134,20 @@ class FakeConnection(object):
and so the instance is being specified as instance.name.
The work will be done asynchronously. This function returns a
Deferred that allows the caller to detect when it is complete.
task that allows the caller to detect when it is complete.
"""
pass
def set_admin_password(self, instance, new_pass):
"""
Set the root password on the specified instance.
The first parameter is an instance of nova.compute.service.Instance,
and so the instance is being specified as instance.name. The second
parameter is the value of the new password.
The work will be done asynchronously. This function returns a
task that allows the caller to detect when it is complete.
"""
pass
@ -182,7 +195,7 @@ class FakeConnection(object):
and so the instance is being specified as instance.name.
The work will be done asynchronously. This function returns a
Deferred that allows the caller to detect when it is complete.
task that allows the caller to detect when it is complete.
"""
del self.instances[instance.name]

View File

@ -76,6 +76,7 @@
<filterref filter="nova-instance-${name}">
<parameter name="IP" value="${ip_address}" />
<parameter name="DHCPSERVER" value="${dhcp_server}" />
<parameter name="RASERVER" value="${ra_server}" />
#if $getVar('extra_params', False)
${extra_params}
#end if

View File

@ -130,6 +130,16 @@ def _get_net_and_mask(cidr):
return str(net.net()), str(net.netmask())
def _get_net_and_prefixlen(cidr):
net = IPy.IP(cidr)
return str(net.net()), str(net.prefixlen())
def _get_ip_version(cidr):
net = IPy.IP(cidr)
return int(net.version())
class LibvirtConnection(object):
def __init__(self, read_only):
@ -375,7 +385,6 @@ class LibvirtConnection(object):
instance['id'],
power_state.NOSTATE,
'launching')
self.nwfilter.setup_basic_filtering(instance)
self.firewall_driver.prepare_instance_filter(instance)
self._create_image(instance, xml)
@ -603,12 +612,16 @@ class LibvirtConnection(object):
if network_ref['injected']:
admin_context = context.get_admin_context()
address = db.instance_get_fixed_address(admin_context, inst['id'])
ra_server = network_ref['ra_server']
if not ra_server:
ra_server = "fd00::"
with open(FLAGS.injected_network_template) as f:
net = f.read() % {'address': address,
'netmask': network_ref['netmask'],
'gateway': network_ref['gateway'],
'broadcast': network_ref['broadcast'],
'dns': network_ref['dns']}
'dns': network_ref['dns'],
'ra_server': ra_server}
if key or net:
if key:
LOG.info(_('instance %s: injecting key into image %s'),
@ -644,13 +657,30 @@ class LibvirtConnection(object):
instance['id'])
# Assume that the gateway also acts as the dhcp server.
dhcp_server = network['gateway']
ra_server = network['ra_server']
if not ra_server:
ra_server = 'fd00::'
if FLAGS.allow_project_net_traffic:
net, mask = _get_net_and_mask(network['cidr'])
extra_params = ("<parameter name=\"PROJNET\" "
if FLAGS.use_ipv6:
net, mask = _get_net_and_mask(network['cidr'])
net_v6, prefixlen_v6 = _get_net_and_prefixlen(
network['cidr_v6'])
extra_params = ("<parameter name=\"PROJNET\" "
"value=\"%s\" />\n"
"<parameter name=\"PROJMASK\" "
"value=\"%s\" />\n") % (net, mask)
"value=\"%s\" />\n"
"<parameter name=\"PROJNETV6\" "
"value=\"%s\" />\n"
"<parameter name=\"PROJMASKV6\" "
"value=\"%s\" />\n") % \
(net, mask, net_v6, prefixlen_v6)
else:
net, mask = _get_net_and_mask(network['cidr'])
extra_params = ("<parameter name=\"PROJNET\" "
"value=\"%s\" />\n"
"<parameter name=\"PROJMASK\" "
"value=\"%s\" />\n") % \
(net, mask)
else:
extra_params = "\n"
if FLAGS.use_cow_images:
@ -668,6 +698,7 @@ class LibvirtConnection(object):
'mac_address': instance['mac_address'],
'ip_address': ip_address,
'dhcp_server': dhcp_server,
'ra_server': ra_server,
'extra_params': extra_params,
'rescue': rescue,
'local': instance_type['local_gb'],
@ -931,6 +962,15 @@ class NWFilterFirewall(FirewallDriver):
</rule>
</filter>'''
def nova_ra_filter(self):
return '''<filter name='nova-allow-ra-server' chain='root'>
<uuid>d707fa71-4fb5-4b27-9ab7-ba5ca19c8804</uuid>
<rule action='accept' direction='inout'
priority='100'>
<icmpv6 srcipaddr='$RASERVER'/>
</rule>
</filter>'''
def setup_basic_filtering(self, instance):
"""Set up basic filtering (MAC, IP, and ARP spoofing protection)"""
logging.info('called setup_basic_filtering in nwfilter')
@ -955,13 +995,17 @@ class NWFilterFirewall(FirewallDriver):
['no-mac-spoofing',
'no-ip-spoofing',
'no-arp-spoofing',
'allow-dhcp-server']))
'allow-dhcp-server'
]))
self._define_filter(self.nova_base_ipv4_filter)
self._define_filter(self.nova_base_ipv6_filter)
self._define_filter(self.nova_dhcp_filter)
self._define_filter(self.nova_ra_filter)
self._define_filter(self.nova_vpn_filter)
if FLAGS.allow_project_net_traffic:
self._define_filter(self.nova_project_filter)
if FLAGS.use_ipv6:
self._define_filter(self.nova_project_filter_v6)
self.static_filters_configured = True
@ -993,13 +1037,13 @@ class NWFilterFirewall(FirewallDriver):
def nova_base_ipv6_filter(self):
retval = "<filter name='nova-base-ipv6' chain='ipv6'>"
for protocol in ['tcp', 'udp', 'icmp']:
for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
for direction, action, priority in [('out', 'accept', 399),
('in', 'drop', 400)]:
retval += """<rule action='%s' direction='%s' priority='%d'>
<%s-ipv6 />
<%s />
</rule>""" % (action, direction,
priority, protocol)
priority, protocol)
retval += '</filter>'
return retval
@ -1012,10 +1056,20 @@ class NWFilterFirewall(FirewallDriver):
retval += '</filter>'
return retval
def nova_project_filter_v6(self):
retval = "<filter name='nova-project-v6' chain='ipv6'>"
for protocol in ['tcp-ipv6', 'udp-ipv6', 'icmpv6']:
retval += """<rule action='accept' direction='inout'
priority='200'>
<%s srcipaddr='$PROJNETV6'
srcipmask='$PROJMASKV6' />
</rule>""" % (protocol)
retval += '</filter>'
return retval
def _define_filter(self, xml):
if callable(xml):
xml = xml()
# execute in a native thread and block current greenthread until done
tpool.execute(self._conn.nwfilterDefineXML, xml)
@ -1029,7 +1083,6 @@ class NWFilterFirewall(FirewallDriver):
it makes sure the filters for the security groups as well as
the base filter are all in place.
"""
if instance['image_id'] == FLAGS.vpn_image_id:
base_filter = 'nova-vpn'
else:
@ -1041,11 +1094,15 @@ class NWFilterFirewall(FirewallDriver):
instance_secgroup_filter_children = ['nova-base-ipv4',
'nova-base-ipv6',
'nova-allow-dhcp-server']
if FLAGS.use_ipv6:
instance_secgroup_filter_children += ['nova-allow-ra-server']
ctxt = context.get_admin_context()
if FLAGS.allow_project_net_traffic:
instance_filter_children += ['nova-project']
if FLAGS.use_ipv6:
instance_filter_children += ['nova-project-v6']
for security_group in db.security_group_get_by_instance(ctxt,
instance['id']):
@ -1073,12 +1130,19 @@ class NWFilterFirewall(FirewallDriver):
security_group = db.security_group_get(context.get_admin_context(),
security_group_id)
rule_xml = ""
v6protocol = {'tcp': 'tcp-ipv6', 'udp': 'udp-ipv6', 'icmp': 'icmpv6'}
for rule in security_group.rules:
rule_xml += "<rule action='accept' direction='in' priority='300'>"
if rule.cidr:
net, mask = _get_net_and_mask(rule.cidr)
rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
(rule.protocol, net, mask)
version = _get_ip_version(rule.cidr)
if(FLAGS.use_ipv6 and version == 6):
net, prefixlen = _get_net_and_prefixlen(rule.cidr)
rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
(v6protocol[rule.protocol], net, prefixlen)
else:
net, mask = _get_net_and_mask(rule.cidr)
rule_xml += "<%s srcipaddr='%s' srcipmask='%s' " % \
(rule.protocol, net, mask)
if rule.protocol in ['tcp', 'udp']:
rule_xml += "dstportstart='%s' dstportend='%s' " % \
(rule.from_port, rule.to_port)
@ -1093,8 +1157,11 @@ class NWFilterFirewall(FirewallDriver):
rule_xml += '/>\n'
rule_xml += "</rule>\n"
xml = "<filter name='nova-secgroup-%s' chain='ipv4'>%s</filter>" % \
(security_group_id, rule_xml,)
xml = "<filter name='nova-secgroup-%s' " % security_group_id
if(FLAGS.use_ipv6):
xml += "chain='root'>%s</filter>" % rule_xml
else:
xml += "chain='ipv4'>%s</filter>" % rule_xml
return xml
def _instance_filter_name(self, instance):
@ -1131,11 +1198,17 @@ class IptablesFirewallDriver(FirewallDriver):
def apply_ruleset(self):
current_filter, _ = self.execute('sudo iptables-save -t filter')
current_lines = current_filter.split('\n')
new_filter = self.modify_rules(current_lines)
new_filter = self.modify_rules(current_lines, 4)
self.execute('sudo iptables-restore',
process_input='\n'.join(new_filter))
if(FLAGS.use_ipv6):
current_filter, _ = self.execute('sudo ip6tables-save -t filter')
current_lines = current_filter.split('\n')
new_filter = self.modify_rules(current_lines, 6)
self.execute('sudo ip6tables-restore',
process_input='\n'.join(new_filter))
def modify_rules(self, current_lines):
def modify_rules(self, current_lines, ip_version=4):
ctxt = context.get_admin_context()
# Remove any trace of nova rules.
new_filter = filter(lambda l: 'nova-' not in l, current_lines)
@ -1149,8 +1222,8 @@ class IptablesFirewallDriver(FirewallDriver):
if not new_filter[rules_index].startswith(':'):
break
our_chains = [':nova-ipv4-fallback - [0:0]']
our_rules = ['-A nova-ipv4-fallback -j DROP']
our_chains = [':nova-fallback - [0:0]']
our_rules = ['-A nova-fallback -j DROP']
our_chains += [':nova-local - [0:0]']
our_rules += ['-A FORWARD -j nova-local']
@ -1161,7 +1234,10 @@ class IptablesFirewallDriver(FirewallDriver):
for instance_id in self.instances:
instance = self.instances[instance_id]
chain_name = self._instance_chain_name(instance)
ip_address = self._ip_for_instance(instance)
if(ip_version == 4):
ip_address = self._ip_for_instance(instance)
elif(ip_version == 6):
ip_address = self._ip_for_instance_v6(instance)
our_chains += [':%s - [0:0]' % chain_name]
@ -1188,13 +1264,19 @@ class IptablesFirewallDriver(FirewallDriver):
our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]
# Allow DHCP responses
dhcp_server = self._dhcp_server_for_instance(instance)
our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %
(chain_name, dhcp_server)]
if(ip_version == 4):
# Allow DHCP responses
dhcp_server = self._dhcp_server_for_instance(instance)
our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %
(chain_name, dhcp_server)]
elif(ip_version == 6):
# Allow RA responses
ra_server = self._ra_server_for_instance(instance)
our_rules += ['-A %s -s %s -p icmpv6' %
(chain_name, ra_server)]
# If nothing matches, jump to the fallback chain
our_rules += ['-A %s -j nova-ipv4-fallback' % (chain_name,)]
our_rules += ['-A %s -j nova-fallback' % (chain_name,)]
# then, security group chains and rules
for security_group_id in security_groups:
@ -1207,15 +1289,22 @@ class IptablesFirewallDriver(FirewallDriver):
for rule in rules:
logging.info('%r', rule)
args = ['-A', chain_name, '-p', rule.protocol]
if rule.cidr:
args += ['-s', rule.cidr]
else:
if not rule.cidr:
# Eventually, a mechanism to grant access for security
# groups will turn up here. It'll use ipsets.
continue
version = _get_ip_version(rule.cidr)
if version != ip_version:
continue
protocol = rule.protocol
if version == 6 and rule.protocol == 'icmp':
protocol = 'icmpv6'
args = ['-A', chain_name, '-p', protocol, '-s', rule.cidr]
if rule.protocol in ['udp', 'tcp']:
if rule.from_port == rule.to_port:
args += ['--dport', '%s' % (rule.from_port,)]
@ -1235,7 +1324,12 @@ class IptablesFirewallDriver(FirewallDriver):
icmp_type_arg += '/%s' % icmp_code
if icmp_type_arg:
args += ['-m', 'icmp', '--icmp-type', icmp_type_arg]
if(ip_version == 4):
args += ['-m', 'icmp', '--icmp-type',
icmp_type_arg]
elif(ip_version == 6):
args += ['-m', 'icmp6', '--icmpv6-type',
icmp_type_arg]
args += ['-j ACCEPT']
our_rules += [' '.join(args)]
@ -1261,7 +1355,16 @@ class IptablesFirewallDriver(FirewallDriver):
return db.instance_get_fixed_address(context.get_admin_context(),
instance['id'])
def _ip_for_instance_v6(self, instance):
return db.instance_get_fixed_address_v6(context.get_admin_context(),
instance['id'])
def _dhcp_server_for_instance(self, instance):
network = db.project_get_network(context.get_admin_context(),
instance['project_id'])
return network['gateway']
def _ra_server_for_instance(self, instance):
network = db.project_get_network(context.get_admin_context(),
instance['project_id'])
return network['ra_server']

View File

@ -20,6 +20,11 @@ Management class for VM-related functions (spawn, reboot, etc).
"""
import json
import M2Crypto
import os
import subprocess
import tempfile
import uuid
from nova import db
from nova import context
@ -127,12 +132,31 @@ class VMOps(object):
"""Refactored out the common code of many methods that receive either
a vm name or a vm instance, and want a vm instance in return.
"""
vm = None
try:
instance_name = instance_or_vm.name
vm = VMHelper.lookup(self._session, instance_name)
except AttributeError:
# A vm opaque ref was passed
vm = instance_or_vm
if instance_or_vm.startswith("OpaqueRef:"):
# Got passed an opaque ref; return it
return instance_or_vm
else:
# Must be the instance name
instance_name = instance_or_vm
except (AttributeError, KeyError):
# Note the the KeyError will only happen with fakes.py
# Not a string; must be an ID or a vm instance
if isinstance(instance_or_vm, (int, long)):
ctx = context.get_admin_context()
try:
instance_obj = db.instance_get_by_id(ctx, instance_or_vm)
instance_name = instance_obj.name
except exception.NotFound:
# The unit tests screw this up, as they use an integer for
# the vm name. I'd fix that up, but that's a matter for
# another bug report. So for now, just try with the passed
# value
instance_name = instance_or_vm
else:
instance_name = instance_or_vm.name
vm = VMHelper.lookup(self._session, instance_name)
if vm is None:
raise Exception(_('Instance not present %s') % instance_name)
return vm
@ -189,6 +213,44 @@ class VMOps(object):
task = self._session.call_xenapi('Async.VM.clean_reboot', vm)
self._session.wait_for_task(instance.id, task)
def set_admin_password(self, instance, new_pass):
"""Set the root/admin password on the VM instance. This is done via
an agent running on the VM. Communication between nova and the agent
is done via writing xenstore records. Since communication is done over
the XenAPI RPC calls, we need to encrypt the password. We're using a
simple Diffie-Hellman class instead of the more advanced one in
M2Crypto for compatibility with the agent code.
"""
# Need to uniquely identify this request.
transaction_id = str(uuid.uuid4())
# The simple Diffie-Hellman class is used to manage key exchange.
dh = SimpleDH()
args = {'id': transaction_id, 'pub': str(dh.get_public())}
resp = self._make_agent_call('key_init', instance, '', args)
if resp is None:
# No response from the agent
return
resp_dict = json.loads(resp)
# Successful return code from key_init is 'D0'
if resp_dict['returncode'] != 'D0':
# There was some sort of error; the message will contain
# a description of the error.
raise RuntimeError(resp_dict['message'])
agent_pub = int(resp_dict['message'])
dh.compute_shared(agent_pub)
enc_pass = dh.encrypt(new_pass)
# Send the encrypted password
args['enc_pass'] = enc_pass
resp = self._make_agent_call('password', instance, '', args)
if resp is None:
# No response from the agent
return
resp_dict = json.loads(resp)
# Successful return code from password is '0'
if resp_dict['returncode'] != '0':
raise RuntimeError(resp_dict['message'])
return resp_dict['message']
def destroy(self, instance):
"""Destroy VM instance"""
vm = VMHelper.lookup(self._session, instance.name)
@ -246,30 +308,19 @@ class VMOps(object):
def suspend(self, instance, callback):
"""suspend the specified instance"""
instance_name = instance.name
vm = VMHelper.lookup(self._session, instance_name)
if vm is None:
raise Exception(_("suspend: instance not present %s") %
instance_name)
vm = self._get_vm_opaque_ref(instance)
task = self._session.call_xenapi('Async.VM.suspend', vm)
self._wait_with_callback(instance.id, task, callback)
def resume(self, instance, callback):
"""resume the specified instance"""
instance_name = instance.name
vm = VMHelper.lookup(self._session, instance_name)
if vm is None:
raise Exception(_("resume: instance not present %s") %
instance_name)
vm = self._get_vm_opaque_ref(instance)
task = self._session.call_xenapi('Async.VM.resume', vm, False, True)
self._wait_with_callback(instance.id, task, callback)
def get_info(self, instance_id):
def get_info(self, instance):
"""Return data about VM instance"""
vm = VMHelper.lookup(self._session, instance_id)
if vm is None:
raise exception.NotFound(_('Instance not'
' found %s') % instance_id)
vm = self._get_vm_opaque_ref(instance)
rec = self._session.get_xenapi().VM.get_record(vm)
return VMHelper.compile_info(rec)
@ -333,22 +384,34 @@ class VMOps(object):
return self._make_plugin_call('xenstore.py', method=method, vm=vm,
path=path, addl_args=addl_args)
def _make_agent_call(self, method, vm, path, addl_args={}):
"""Abstracts out the interaction with the agent xenapi plugin."""
return self._make_plugin_call('agent', method=method, vm=vm,
path=path, addl_args=addl_args)
def _make_plugin_call(self, plugin, method, vm, path, addl_args={}):
"""Abstracts out the process of calling a method of a xenapi plugin.
Any errors raised by the plugin will in turn raise a RuntimeError here.
"""
instance_id = vm.id
vm = self._get_vm_opaque_ref(vm)
rec = self._session.get_xenapi().VM.get_record(vm)
args = {'dom_id': rec['domid'], 'path': path}
args.update(addl_args)
# If the 'testing_mode' attribute is set, add that to the args.
if getattr(self, 'testing_mode', False):
args['testing_mode'] = 'true'
try:
task = self._session.async_call_plugin(plugin, method, args)
ret = self._session.wait_for_task(0, task)
ret = self._session.wait_for_task(instance_id, task)
except self.XenAPI.Failure, e:
raise RuntimeError("%s" % e.details[-1])
ret = None
err_trace = e.details[-1]
err_msg = err_trace.splitlines()[-1]
strargs = str(args)
if 'TIMEOUT:' in err_msg:
LOG.error(_('TIMEOUT: The call to %(method)s timed out. '
'VM id=%(instance_id)s; args=%(strargs)s') % locals())
else:
LOG.error(_('The call to %(method)s returned an error: %(e)s. '
'VM id=%(instance_id)s; args=%(strargs)s') % locals())
return ret
def add_to_xenstore(self, vm, path, key, value):
@ -460,3 +523,89 @@ class VMOps(object):
"""Removes all data from the xenstore parameter record for this VM."""
self.write_to_param_xenstore(instance_or_vm, {})
########################################################################
def _runproc(cmd):
pipe = subprocess.PIPE
return subprocess.Popen([cmd], shell=True, stdin=pipe, stdout=pipe,
stderr=pipe, close_fds=True)
class SimpleDH(object):
"""This class wraps all the functionality needed to implement
basic Diffie-Hellman-Merkle key exchange in Python. It features
intelligent defaults for the prime and base numbers needed for the
calculation, while allowing you to supply your own. It requires that
the openssl binary be installed on the system on which this is run,
as it uses that to handle the encryption and decryption. If openssl
is not available, a RuntimeError will be raised.
"""
def __init__(self, prime=None, base=None, secret=None):
"""You can specify the values for prime and base if you wish;
otherwise, reasonable default values will be used.
"""
if prime is None:
self._prime = 162259276829213363391578010288127
else:
self._prime = prime
if base is None:
self._base = 5
else:
self._base = base
self._shared = self._public = None
self._dh = M2Crypto.DH.set_params(
self.dec_to_mpi(self._prime),
self.dec_to_mpi(self._base))
self._dh.gen_key()
self._public = self.mpi_to_dec(self._dh.pub)
def get_public(self):
return self._public
def compute_shared(self, other):
self._shared = self.bin_to_dec(
self._dh.compute_key(self.dec_to_mpi(other)))
return self._shared
def mpi_to_dec(self, mpi):
bn = M2Crypto.m2.mpi_to_bn(mpi)
hexval = M2Crypto.m2.bn_to_hex(bn)
dec = int(hexval, 16)
return dec
def bin_to_dec(self, binval):
bn = M2Crypto.m2.bin_to_bn(binval)
hexval = M2Crypto.m2.bn_to_hex(bn)
dec = int(hexval, 16)
return dec
def dec_to_mpi(self, dec):
bn = M2Crypto.m2.dec_to_bn('%s' % dec)
mpi = M2Crypto.m2.bn_to_mpi(bn)
return mpi
def _run_ssl(self, text, which):
base_cmd = ('cat %(tmpfile)s | openssl enc -aes-128-cbc '
'-a -pass pass:%(shared)s -nosalt %(dec_flag)s')
if which.lower()[0] == 'd':
dec_flag = ' -d'
else:
dec_flag = ''
fd, tmpfile = tempfile.mkstemp()
os.close(fd)
file(tmpfile, 'w').write(text)
shared = self._shared
cmd = base_cmd % locals()
proc = _runproc(cmd)
proc.wait()
err = proc.stderr.read()
if err:
raise RuntimeError(_('OpenSSL error: %s') % err)
return proc.stdout.read()
def encrypt(self, text):
return self._run_ssl(text, 'enc')
def decrypt(self, text):
return self._run_ssl(text, 'dec')

View File

@ -149,6 +149,10 @@ class XenAPIConnection(object):
"""Reboot VM instance"""
self._vmops.reboot(instance)
def set_admin_password(self, instance, new_pass):
"""Set the root/admin password on the VM instance"""
self._vmops.set_admin_password(instance, new_pass)
def destroy(self, instance):
"""Destroy VM instance"""
self._vmops.destroy(instance)
@ -266,7 +270,8 @@ class XenAPISession(object):
def _poll_task(self, id, task, done):
"""Poll the given XenAPI task, and fire the given action if we
get a result."""
get a result.
"""
try:
name = self._session.xenapi.task.get_name_label(task)
status = self._session.xenapi.task.get_status(task)

View File

@ -371,3 +371,52 @@ class RBDDriver(VolumeDriver):
def undiscover_volume(self, volume):
"""Undiscover volume on a remote host"""
pass
class SheepdogDriver(VolumeDriver):
"""Executes commands relating to Sheepdog Volumes"""
def check_for_setup_error(self):
"""Returns an error if prerequisites aren't met"""
try:
(out, err) = self._execute("collie cluster info")
if not out.startswith('running'):
raise exception.Error(_("Sheepdog is not working: %s") % out)
except exception.ProcessExecutionError:
raise exception.Error(_("Sheepdog is not working"))
def create_volume(self, volume):
"""Creates a sheepdog volume"""
if int(volume['size']) == 0:
sizestr = '100M'
else:
sizestr = '%sG' % volume['size']
self._try_execute("qemu-img create sheepdog:%s %s" %
(volume['name'], sizestr))
def delete_volume(self, volume):
"""Deletes a logical volume"""
self._try_execute("collie vdi delete %s" % volume['name'])
def local_path(self, volume):
return "sheepdog:%s" % volume['name']
def ensure_export(self, context, volume):
"""Safely and synchronously recreates an export for a logical volume"""
pass
def create_export(self, context, volume):
"""Exports the volume"""
pass
def remove_export(self, context, volume):
"""Removes an export for a logical volume"""
pass
def discover_volume(self, volume):
"""Discover volume on a remote host"""
return "sheepdog:%s" % volume['name']
def undiscover_volume(self, volume):
"""Undiscover volume on a remote host"""
pass

View File

@ -0,0 +1,126 @@
#!/usr/bin/env python
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
# Copyright 2011 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# XenAPI plugin for reading/writing information to xenstore
#
try:
import json
except ImportError:
import simplejson as json
import os
import random
import subprocess
import tempfile
import time
import XenAPIPlugin
from pluginlib_nova import *
configure_logging("xenstore")
import xenstore
AGENT_TIMEOUT = 30
def jsonify(fnc):
def wrapper(*args, **kwargs):
return json.dumps(fnc(*args, **kwargs))
return wrapper
class TimeoutError(StandardError):
pass
@jsonify
def key_init(self, arg_dict):
"""Handles the Diffie-Hellman key exchange with the agent to
establish the shared secret key used to encrypt/decrypt sensitive
info to be passed, such as passwords. Returns the shared
secret key value.
"""
pub = int(arg_dict["pub"])
arg_dict["value"] = json.dumps({"name": "keyinit", "value": pub})
request_id = arg_dict["id"]
arg_dict["path"] = "data/host/%s" % request_id
xenstore.write_record(self, arg_dict)
try:
resp = _wait_for_agent(self, request_id, arg_dict)
except TimeoutError, e:
raise PluginError("%s" % e)
return resp
@jsonify
def password(self, arg_dict):
"""Writes a request to xenstore that tells the agent to set
the root password for the given VM. The password should be
encrypted using the shared secret key that was returned by a
previous call to key_init. The encrypted password value should
be passed as the value for the 'enc_pass' key in arg_dict.
"""
pub = int(arg_dict["pub"])
enc_pass = arg_dict["enc_pass"]
arg_dict["value"] = json.dumps({"name": "password", "value": enc_pass})
request_id = arg_dict["id"]
arg_dict["path"] = "data/host/%s" % request_id
xenstore.write_record(self, arg_dict)
try:
resp = _wait_for_agent(self, request_id, arg_dict)
except TimeoutError, e:
raise PluginError("%s" % e)
return resp
def _wait_for_agent(self, request_id, arg_dict):
"""Periodically checks xenstore for a response from the agent.
The request is always written to 'data/host/{id}', and
the agent's response for that request will be in 'data/guest/{id}'.
If no value appears from the agent within the time specified by
AGENT_TIMEOUT, the original request is deleted and a TimeoutError
is returned.
"""
arg_dict["path"] = "data/guest/%s" % request_id
arg_dict["ignore_missing_path"] = True
start = time.time()
while True:
if time.time() - start > AGENT_TIMEOUT:
# No response within the timeout period; bail out
# First, delete the request record
arg_dict["path"] = "data/host/%s" % request_id
xenstore.delete_record(self, arg_dict)
raise TimeoutError("TIMEOUT: No response from agent within %s seconds." %
AGENT_TIMEOUT)
ret = xenstore.read_record(self, arg_dict)
# Note: the response for None with be a string that includes
# double quotes.
if ret != '"None"':
# The agent responded
return ret
else:
time.sleep(3)
if __name__ == "__main__":
XenAPIPlugin.dispatch(
{"key_init": key_init,
"password": password})

View File

@ -43,7 +43,7 @@ flags.DEFINE_string('suite', None, 'Specific test suite to run ' + SUITE_NAMES)
# TODO(devamcar): Use random tempfile
ZIP_FILENAME = '/tmp/nova-me-x509.zip'
TEST_PREFIX = 'test%s' % int(random.random()*1000000)
TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
TEST_USERNAME = '%suser' % TEST_PREFIX
TEST_PROJECTNAME = '%sproject' % TEST_PREFIX
@ -96,4 +96,3 @@ class UserTests(AdminSmokeTestCase):
if __name__ == "__main__":
suites = {'user': unittest.makeSuite(UserTests)}
sys.exit(base.run_tests(suites))

View File

@ -17,6 +17,7 @@
# under the License.
import boto
import boto_v6
import commands
import httplib
import os
@ -69,6 +70,17 @@ class SmokeTestCase(unittest.TestCase):
'test.')
parts = self.split_clc_url(clc_url)
if FLAGS.use_ipv6:
return boto_v6.connect_ec2(aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
is_secure=parts['is_secure'],
region=RegionInfo(None,
'nova',
parts['ip']),
port=parts['port'],
path='/services/Cloud',
**kwargs)
return boto.connect_ec2(aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
is_secure=parts['is_secure'],
@ -115,7 +127,8 @@ class SmokeTestCase(unittest.TestCase):
return True
def upload_image(self, bucket_name, image):
cmd = 'euca-upload-bundle -b %s -m /tmp/%s.manifest.xml' % (bucket_name, image)
cmd = 'euca-upload-bundle -b '
cmd += '%s -m /tmp/%s.manifest.xml' % (bucket_name, image)
status, output = commands.getstatusoutput(cmd)
if status != 0:
print '%s -> \n %s' % (cmd, output)
@ -130,6 +143,7 @@ class SmokeTestCase(unittest.TestCase):
raise Exception(output)
return True
def run_tests(suites):
argv = FLAGS(sys.argv)
@ -151,4 +165,3 @@ def run_tests(suites):
else:
for suite in suites.itervalues():
unittest.TextTestRunner(verbosity=2).run(suite)

View File

@ -33,6 +33,7 @@ DEFINE_bool = DEFINE_bool
# __GLOBAL FLAGS ONLY__
# Define any app-specific flags in their own files, docs at:
# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39
DEFINE_string('region', 'nova', 'Region to use')
DEFINE_string('test_image', 'ami-tiny', 'Image to use for launch tests')
DEFINE_string('use_ipv6', True, 'use the ipv6 or not')

View File

@ -0,0 +1,180 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import commands
import os
import random
import socket
import sys
import time
import unittest
from smoketests import flags
from smoketests import base
from smoketests import user_smoketests
#Note that this test should run from
#public network (outside of private network segments)
#Please set EC2_URL correctly
#You should use admin account in this test
FLAGS = flags.FLAGS
TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
TEST_BUCKET = '%s_bucket' % TEST_PREFIX
TEST_KEY = '%s_key' % TEST_PREFIX
TEST_KEY2 = '%s_key2' % TEST_PREFIX
TEST_DATA = {}
class InstanceTestsFromPublic(user_smoketests.UserSmokeTestCase):
def test_001_can_create_keypair(self):
key = self.create_key_pair(self.conn, TEST_KEY)
self.assertEqual(key.name, TEST_KEY)
def test_002_security_group(self):
security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
for x in range(random.randint(4, 8)))
group = self.conn.create_security_group(security_group_name,
'test group')
group.connection = self.conn
group.authorize('tcp', 22, 22, '0.0.0.0/0')
if FLAGS.use_ipv6:
group.authorize('tcp', 22, 22, '::/0')
reservation = self.conn.run_instances(FLAGS.test_image,
key_name=TEST_KEY,
security_groups=[security_group_name],
instance_type='m1.tiny')
self.data['security_group_name'] = security_group_name
self.data['group'] = group
self.data['instance_id'] = reservation.instances[0].id
def test_003_instance_with_group_runs_within_60_seconds(self):
reservations = self.conn.get_all_instances([self.data['instance_id']])
instance = reservations[0].instances[0]
# allow 60 seconds to exit pending with IP
for x in xrange(60):
instance.update()
if instance.state == u'running':
break
time.sleep(1)
else:
self.fail('instance failed to start')
ip = reservations[0].instances[0].private_dns_name
self.failIf(ip == '0.0.0.0')
self.data['private_ip'] = ip
if FLAGS.use_ipv6:
ipv6 = reservations[0].instances[0].dns_name_v6
self.failIf(ipv6 is None)
self.data['ip_v6'] = ipv6
def test_004_can_ssh_to_ipv6(self):
if FLAGS.use_ipv6:
for x in xrange(20):
try:
conn = self.connect_ssh(
self.data['ip_v6'], TEST_KEY)
conn.close()
except Exception as ex:
print ex
time.sleep(1)
else:
break
else:
self.fail('could not ssh to instance')
def test_012_can_create_instance_with_keypair(self):
if 'instance_id' in self.data:
self.conn.terminate_instances([self.data['instance_id']])
reservation = self.conn.run_instances(FLAGS.test_image,
key_name=TEST_KEY,
instance_type='m1.tiny')
self.assertEqual(len(reservation.instances), 1)
self.data['instance_id'] = reservation.instances[0].id
def test_013_instance_runs_within_60_seconds(self):
reservations = self.conn.get_all_instances([self.data['instance_id']])
instance = reservations[0].instances[0]
# allow 60 seconds to exit pending with IP
for x in xrange(60):
instance.update()
if instance.state == u'running':
break
time.sleep(1)
else:
self.fail('instance failed to start')
ip = reservations[0].instances[0].private_dns_name
self.failIf(ip == '0.0.0.0')
self.data['private_ip'] = ip
if FLAGS.use_ipv6:
ipv6 = reservations[0].instances[0].dns_name_v6
self.failIf(ipv6 is None)
self.data['ip_v6'] = ipv6
def test_014_can_not_ping_private_ip(self):
for x in xrange(4):
# ping waits for 1 second
status, output = commands.getstatusoutput(
'ping -c1 %s' % self.data['private_ip'])
if status == 0:
self.fail('can ping private ip from public network')
if FLAGS.use_ipv6:
status, output = commands.getstatusoutput(
'ping6 -c1 %s' % self.data['ip_v6'])
if status == 0:
self.fail('can ping ipv6 from public network')
else:
pass
def test_015_can_not_ssh_to_private_ip(self):
for x in xrange(1):
try:
conn = self.connect_ssh(self.data['private_ip'], TEST_KEY)
conn.close()
except Exception:
time.sleep(1)
else:
self.fail('can ssh for ipv4 address from public network')
if FLAGS.use_ipv6:
for x in xrange(1):
try:
conn = self.connect_ssh(
self.data['ip_v6'], TEST_KEY)
conn.close()
except Exception:
time.sleep(1)
else:
self.fail('can ssh for ipv6 address from public network')
def test_999_tearDown(self):
self.delete_key_pair(self.conn, TEST_KEY)
security_group_name = self.data['security_group_name']
group = self.data['group']
if group:
group.revoke('tcp', 22, 22, '0.0.0.0/0')
if FLAGS.use_ipv6:
group.revoke('tcp', 22, 22, '::/0')
self.conn.delete_security_group(security_group_name)
if 'instance_id' in self.data:
self.conn.terminate_instances([self.data['instance_id']])
if __name__ == "__main__":
suites = {'instance': unittest.makeSuite(InstanceTestsFromPublic)}
sys.exit(base.run_tests(suites))

View File

@ -45,7 +45,7 @@ flags.DEFINE_string('bundle_kernel', 'openwrt-x86-vmlinuz',
flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image',
'Local image file to use for bundling tests')
TEST_PREFIX = 'test%s' % int (random.random()*1000000)
TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
TEST_BUCKET = '%s_bucket' % TEST_PREFIX
TEST_KEY = '%s_key' % TEST_PREFIX
TEST_GROUP = '%s_group' % TEST_PREFIX
@ -80,7 +80,7 @@ class ImageTests(UserSmokeTestCase):
def test_006_can_register_kernel(self):
kernel_id = self.conn.register_image('%s/%s.manifest.xml' %
(TEST_BUCKET, FLAGS.bundle_kernel))
(TEST_BUCKET, FLAGS.bundle_kernel))
self.assert_(kernel_id is not None)
self.data['kernel_id'] = kernel_id
@ -92,7 +92,7 @@ class ImageTests(UserSmokeTestCase):
time.sleep(1)
else:
print image.state
self.assert_(False) # wasn't available within 10 seconds
self.assert_(False) # wasn't available within 10 seconds
self.assert_(image.type == 'machine')
for i in xrange(10):
@ -101,7 +101,7 @@ class ImageTests(UserSmokeTestCase):
break
time.sleep(1)
else:
self.assert_(False) # wasn't available within 10 seconds
self.assert_(False) # wasn't available within 10 seconds
self.assert_(kernel.type == 'kernel')
def test_008_can_describe_image_attribute(self):
@ -152,14 +152,17 @@ class InstanceTests(UserSmokeTestCase):
for x in xrange(60):
instance.update()
if instance.state == u'running':
break
break
time.sleep(1)
else:
self.fail('instance failed to start')
ip = reservations[0].instances[0].private_dns_name
self.failIf(ip == '0.0.0.0')
self.data['private_ip'] = ip
print self.data['private_ip']
if FLAGS.use_ipv6:
ipv6 = reservations[0].instances[0].dns_name_v6
self.failIf(ipv6 is None)
self.data['ip_v6'] = ipv6
def test_004_can_ping_private_ip(self):
for x in xrange(120):
@ -171,6 +174,16 @@ class InstanceTests(UserSmokeTestCase):
else:
self.fail('could not ping instance')
if FLAGS.use_ipv6:
for x in xrange(120):
# ping waits for 1 second
status, output = commands.getstatusoutput(
'ping6 -c1 %s' % self.data['ip_v6'])
if status == 0:
break
else:
self.fail('could not ping instance')
def test_005_can_ssh_to_private_ip(self):
for x in xrange(30):
try:
@ -183,6 +196,19 @@ class InstanceTests(UserSmokeTestCase):
else:
self.fail('could not ssh to instance')
if FLAGS.use_ipv6:
for x in xrange(30):
try:
conn = self.connect_ssh(
self.data['ip_v6'], TEST_KEY)
conn.close()
except Exception:
time.sleep(1)
else:
break
else:
self.fail('could not ssh to instance v6')
def test_006_can_allocate_elastic_ip(self):
result = self.conn.allocate_address()
self.assertTrue(hasattr(result, 'public_ip'))
@ -388,7 +414,6 @@ class SecurityGroupTests(UserSmokeTestCase):
raise Exception("Timeout")
time.sleep(1)
def test_999_tearDown(self):
self.conn.delete_key_pair(TEST_KEY)
self.conn.delete_security_group(TEST_GROUP)

View File

@ -26,3 +26,4 @@ Twisted>=10.1.0
PasteDeploy
paste
sqlalchemy-migrate
netaddr