Merge trunk
This commit is contained in:
@@ -69,3 +69,150 @@ table.docutils {
|
|||||||
.tweet_list li .tweet_avatar {
|
.tweet_list li .tweet_avatar {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------
|
||||||
|
PURE CSS SPEECH BUBBLES
|
||||||
|
by Nicolas Gallagher
|
||||||
|
- http://nicolasgallagher.com/pure-css-speech-bubbles/
|
||||||
|
|
||||||
|
http://nicolasgallagher.com
|
||||||
|
http://twitter.com/necolas
|
||||||
|
|
||||||
|
Created: 02 March 2010
|
||||||
|
Version: 1.1 (21 October 2010)
|
||||||
|
|
||||||
|
Dual licensed under MIT and GNU GPLv2 © Nicolas Gallagher
|
||||||
|
------------------------------------------ */
|
||||||
|
/* THE SPEECH BUBBLE
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* THE SPEECH BUBBLE
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.triangle-border {
|
||||||
|
position:relative;
|
||||||
|
padding:15px;
|
||||||
|
margin:1em 0 3em;
|
||||||
|
border:5px solid #BC1518;
|
||||||
|
color:#333;
|
||||||
|
background:#fff;
|
||||||
|
|
||||||
|
/* css3 */
|
||||||
|
-moz-border-radius:10px;
|
||||||
|
-webkit-border-radius:10px;
|
||||||
|
border-radius:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variant : for left positioned triangle
|
||||||
|
------------------------------------------ */
|
||||||
|
|
||||||
|
.triangle-border.left {
|
||||||
|
margin-left:30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variant : for right positioned triangle
|
||||||
|
------------------------------------------ */
|
||||||
|
|
||||||
|
.triangle-border.right {
|
||||||
|
margin-right:30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* THE TRIANGLE
|
||||||
|
------------------------------------------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
.triangle-border:before {
|
||||||
|
content:"";
|
||||||
|
display:block; /* reduce the damage in FF3.0 */
|
||||||
|
position:absolute;
|
||||||
|
bottom:-40px; /* value = - border-top-width - border-bottom-width */
|
||||||
|
left:40px; /* controls horizontal position */
|
||||||
|
width:0;
|
||||||
|
height:0;
|
||||||
|
border:20px solid transparent;
|
||||||
|
border-top-color:#BC1518;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates the smaller triangle */
|
||||||
|
.triangle-border:after {
|
||||||
|
content:"";
|
||||||
|
display:block; /* reduce the damage in FF3.0 */
|
||||||
|
position:absolute;
|
||||||
|
bottom:-26px; /* value = - border-top-width - border-bottom-width */
|
||||||
|
left:47px; /* value = (:before left) + (:before border-left) - (:after border-left) */
|
||||||
|
width:0;
|
||||||
|
height:0;
|
||||||
|
border:13px solid transparent;
|
||||||
|
border-top-color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variant : top
|
||||||
|
------------------------------------------ */
|
||||||
|
|
||||||
|
/* creates the larger triangle */
|
||||||
|
.triangle-border.top:before {
|
||||||
|
top:-40px; /* value = - border-top-width - border-bottom-width */
|
||||||
|
right:40px; /* controls horizontal position */
|
||||||
|
bottom:auto;
|
||||||
|
left:auto;
|
||||||
|
border:20px solid transparent;
|
||||||
|
border-bottom-color:#BC1518;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates the smaller triangle */
|
||||||
|
.triangle-border.top:after {
|
||||||
|
top:-26px; /* value = - border-top-width - border-bottom-width */
|
||||||
|
right:47px; /* value = (:before right) + (:before border-right) - (:after border-right) */
|
||||||
|
bottom:auto;
|
||||||
|
left:auto;
|
||||||
|
border:13px solid transparent;
|
||||||
|
border-bottom-color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variant : left
|
||||||
|
------------------------------------------ */
|
||||||
|
|
||||||
|
/* creates the larger triangle */
|
||||||
|
.triangle-border.left:before {
|
||||||
|
top:10px; /* controls vertical position */
|
||||||
|
left:-30px; /* value = - border-left-width - border-right-width */
|
||||||
|
bottom:auto;
|
||||||
|
border-width:15px 30px 15px 0;
|
||||||
|
border-style:solid;
|
||||||
|
border-color:transparent #BC1518;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates the smaller triangle */
|
||||||
|
.triangle-border.left:after {
|
||||||
|
top:16px; /* value = (:before top) + (:before border-top) - (:after border-top) */
|
||||||
|
left:-21px; /* value = - border-left-width - border-right-width */
|
||||||
|
bottom:auto;
|
||||||
|
border-width:9px 21px 9px 0;
|
||||||
|
border-style:solid;
|
||||||
|
border-color:transparent #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variant : right
|
||||||
|
------------------------------------------ */
|
||||||
|
|
||||||
|
/* creates the larger triangle */
|
||||||
|
.triangle-border.right:before {
|
||||||
|
top:10px; /* controls vertical position */
|
||||||
|
right:-30px; /* value = - border-left-width - border-right-width */
|
||||||
|
bottom:auto;
|
||||||
|
left:auto;
|
||||||
|
border-width:15px 0 15px 30px;
|
||||||
|
border-style:solid;
|
||||||
|
border-color:transparent #BC1518;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates the smaller triangle */
|
||||||
|
.triangle-border.right:after {
|
||||||
|
top:16px; /* value = (:before top) + (:before border-top) - (:after border-top) */
|
||||||
|
right:-21px; /* value = - border-left-width - border-right-width */
|
||||||
|
bottom:auto;
|
||||||
|
left:auto;
|
||||||
|
border-width:9px 0 9px 21px;
|
||||||
|
border-style:solid;
|
||||||
|
border-color:transparent #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,12 +71,21 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">$('#searchbox').show(0);</script>
|
<script type="text/javascript">$('#searchbox').show(0);</script>
|
||||||
|
|
||||||
|
<p class="triangle-border right">
|
||||||
|
Psst... hey. You're reading the latest content, but it might be out of sync with code. You can read <a href="http://nova.openstack.org/2011.1">Nova 2011.1 docs</a> or <a href="http://docs.openstack.org">all OpenStack docs</a> too.
|
||||||
|
</p>
|
||||||
|
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{%- if pagename == "index" %}
|
{%- if pagename == "index" %}
|
||||||
<h3>{{ _('Twitter Feed') }}</h3>
|
|
||||||
|
|
||||||
|
<h3>{{ _('Twitter Feed') }}</h3>
|
||||||
<div id="twitter_feed" class='twitter_feed'></div>
|
<div id="twitter_feed" class='twitter_feed'></div>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
|
|||||||
@@ -90,6 +90,21 @@ class ComputeTestCase(test.TestCase):
|
|||||||
inst.update(params)
|
inst.update(params)
|
||||||
return db.instance_create(self.context, inst)['id']
|
return db.instance_create(self.context, inst)['id']
|
||||||
|
|
||||||
|
def _create_instance_type(self, params={}):
|
||||||
|
"""Create a test instance"""
|
||||||
|
context = self.context.elevated()
|
||||||
|
inst = {}
|
||||||
|
inst['name'] = 'm1.small'
|
||||||
|
inst['memory_mb'] = '1024'
|
||||||
|
inst['vcpus'] = '1'
|
||||||
|
inst['local_gb'] = '20'
|
||||||
|
inst['flavorid'] = '1'
|
||||||
|
inst['swap'] = '2048'
|
||||||
|
inst['rxtx_quota'] = 100
|
||||||
|
inst['rxtx_cap'] = 200
|
||||||
|
inst.update(params)
|
||||||
|
return db.instance_type_create(context, inst)['id']
|
||||||
|
|
||||||
def _create_group(self):
|
def _create_group(self):
|
||||||
values = {'name': 'testgroup',
|
values = {'name': 'testgroup',
|
||||||
'description': 'testgroup',
|
'description': 'testgroup',
|
||||||
@@ -307,15 +322,53 @@ class ComputeTestCase(test.TestCase):
|
|||||||
"""Ensure instance can be migrated/resized"""
|
"""Ensure instance can be migrated/resized"""
|
||||||
instance_id = self._create_instance()
|
instance_id = self._create_instance()
|
||||||
context = self.context.elevated()
|
context = self.context.elevated()
|
||||||
|
|
||||||
self.compute.run_instance(self.context, instance_id)
|
self.compute.run_instance(self.context, instance_id)
|
||||||
db.instance_update(self.context, instance_id, {'host': 'foo'})
|
db.instance_update(self.context, instance_id, {'host': 'foo'})
|
||||||
self.compute.prep_resize(context, instance_id)
|
self.compute.prep_resize(context, instance_id, 1)
|
||||||
migration_ref = db.migration_get_by_instance_and_status(context,
|
migration_ref = db.migration_get_by_instance_and_status(context,
|
||||||
instance_id, 'pre-migrating')
|
instance_id, 'pre-migrating')
|
||||||
self.compute.resize_instance(context, instance_id,
|
self.compute.resize_instance(context, instance_id,
|
||||||
migration_ref['id'])
|
migration_ref['id'])
|
||||||
self.compute.terminate_instance(context, instance_id)
|
self.compute.terminate_instance(context, instance_id)
|
||||||
|
|
||||||
|
def test_resize_invalid_flavor_fails(self):
|
||||||
|
"""Ensure invalid flavors raise"""
|
||||||
|
instance_id = self._create_instance()
|
||||||
|
context = self.context.elevated()
|
||||||
|
self.compute.run_instance(self.context, instance_id)
|
||||||
|
|
||||||
|
self.assertRaises(exception.NotFound, self.compute_api.resize,
|
||||||
|
context, instance_id, 200)
|
||||||
|
|
||||||
|
self.compute.terminate_instance(context, instance_id)
|
||||||
|
|
||||||
|
def test_resize_down_fails(self):
|
||||||
|
"""Ensure resizing down raises and fails"""
|
||||||
|
context = self.context.elevated()
|
||||||
|
instance_id = self._create_instance()
|
||||||
|
|
||||||
|
self.compute.run_instance(self.context, instance_id)
|
||||||
|
db.instance_update(self.context, instance_id,
|
||||||
|
{'instance_type': 'm1.xlarge'})
|
||||||
|
|
||||||
|
self.assertRaises(exception.ApiError, self.compute_api.resize,
|
||||||
|
context, instance_id, 1)
|
||||||
|
|
||||||
|
self.compute.terminate_instance(context, instance_id)
|
||||||
|
|
||||||
|
def test_resize_same_size_fails(self):
|
||||||
|
"""Ensure invalid flavors raise"""
|
||||||
|
context = self.context.elevated()
|
||||||
|
instance_id = self._create_instance()
|
||||||
|
|
||||||
|
self.compute.run_instance(self.context, instance_id)
|
||||||
|
|
||||||
|
self.assertRaises(exception.ApiError, self.compute_api.resize,
|
||||||
|
context, instance_id, 1)
|
||||||
|
|
||||||
|
self.compute.terminate_instance(context, instance_id)
|
||||||
|
|
||||||
def test_get_by_flavor_id(self):
|
def test_get_by_flavor_id(self):
|
||||||
type = instance_types.get_by_flavor_id(1)
|
type = instance_types.get_by_flavor_id(1)
|
||||||
self.assertEqual(type, 'm1.tiny')
|
self.assertEqual(type, 'm1.tiny')
|
||||||
@@ -326,10 +379,8 @@ class ComputeTestCase(test.TestCase):
|
|||||||
instance_id = self._create_instance()
|
instance_id = self._create_instance()
|
||||||
self.compute.run_instance(self.context, instance_id)
|
self.compute.run_instance(self.context, instance_id)
|
||||||
self.assertRaises(exception.Error, self.compute.prep_resize,
|
self.assertRaises(exception.Error, self.compute.prep_resize,
|
||||||
self.context, instance_id)
|
self.context, instance_id, 1)
|
||||||
self.compute.terminate_instance(self.context, instance_id)
|
self.compute.terminate_instance(self.context, instance_id)
|
||||||
type = instance_types.get_by_flavor_id("1")
|
|
||||||
self.assertEqual(type, 'm1.tiny')
|
|
||||||
|
|
||||||
def _setup_other_managers(self):
|
def _setup_other_managers(self):
|
||||||
self.volume_manager = utils.import_object(FLAGS.volume_manager)
|
self.volume_manager = utils.import_object(FLAGS.volume_manager)
|
||||||
|
|||||||
161
nova/tests/test_flat_network.py
Normal file
161
nova/tests/test_flat_network.py
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
Unit Tests for flat network code
|
||||||
|
"""
|
||||||
|
import IPy
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from nova import context
|
||||||
|
from nova import db
|
||||||
|
from nova import exception
|
||||||
|
from nova import flags
|
||||||
|
from nova import log as logging
|
||||||
|
from nova import test
|
||||||
|
from nova import utils
|
||||||
|
from nova.auth import manager
|
||||||
|
from nova.tests.network import base
|
||||||
|
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
LOG = logging.getLogger('nova.tests.network')
|
||||||
|
|
||||||
|
|
||||||
|
class FlatNetworkTestCase(base.NetworkTestCase):
|
||||||
|
"""Test cases for network code"""
|
||||||
|
def test_public_network_association(self):
|
||||||
|
"""Makes sure that we can allocate a public ip"""
|
||||||
|
# TODO(vish): better way of adding floating ips
|
||||||
|
|
||||||
|
self.context._project = self.projects[0]
|
||||||
|
self.context.project_id = self.projects[0].id
|
||||||
|
pubnet = IPy.IP(flags.FLAGS.floating_range)
|
||||||
|
address = str(pubnet[0])
|
||||||
|
try:
|
||||||
|
db.floating_ip_get_by_address(context.get_admin_context(), address)
|
||||||
|
except exception.NotFound:
|
||||||
|
db.floating_ip_create(context.get_admin_context(),
|
||||||
|
{'address': address,
|
||||||
|
'host': FLAGS.host})
|
||||||
|
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.network.allocate_floating_ip,
|
||||||
|
self.context, self.projects[0].id)
|
||||||
|
|
||||||
|
fix_addr = self._create_address(0)
|
||||||
|
float_addr = address
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.network.associate_floating_ip,
|
||||||
|
self.context, float_addr, fix_addr)
|
||||||
|
|
||||||
|
address = db.instance_get_floating_address(context.get_admin_context(),
|
||||||
|
self.instance_id)
|
||||||
|
self.assertEqual(address, None)
|
||||||
|
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.network.disassociate_floating_ip,
|
||||||
|
self.context, float_addr)
|
||||||
|
|
||||||
|
address = db.instance_get_floating_address(context.get_admin_context(),
|
||||||
|
self.instance_id)
|
||||||
|
self.assertEqual(address, None)
|
||||||
|
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.network.deallocate_floating_ip,
|
||||||
|
self.context, float_addr)
|
||||||
|
|
||||||
|
self.network.deallocate_fixed_ip(self.context, fix_addr)
|
||||||
|
db.floating_ip_destroy(context.get_admin_context(), float_addr)
|
||||||
|
|
||||||
|
def test_allocate_deallocate_fixed_ip(self):
|
||||||
|
"""Makes sure that we can allocate and deallocate a fixed ip"""
|
||||||
|
address = self._create_address(0)
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
self._deallocate_address(0, address)
|
||||||
|
|
||||||
|
# check if the fixed ip address is really deallocated
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
|
||||||
|
def test_side_effects(self):
|
||||||
|
"""Ensures allocating and releasing has no side effects"""
|
||||||
|
address = self._create_address(0)
|
||||||
|
address2 = self._create_address(1, self.instance2_id)
|
||||||
|
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address2,
|
||||||
|
self.projects[1].id))
|
||||||
|
|
||||||
|
self._deallocate_address(0, address)
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
|
||||||
|
# First address release shouldn't affect the second
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address2,
|
||||||
|
self.projects[0].id))
|
||||||
|
|
||||||
|
self._deallocate_address(1, address2)
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address2,
|
||||||
|
self.projects[1].id))
|
||||||
|
|
||||||
|
def test_ips_are_reused(self):
|
||||||
|
"""Makes sure that ip addresses that are deallocated get reused"""
|
||||||
|
address = self._create_address(0)
|
||||||
|
self.network.deallocate_fixed_ip(self.context, address)
|
||||||
|
|
||||||
|
address2 = self._create_address(0)
|
||||||
|
self.assertEqual(address, address2)
|
||||||
|
|
||||||
|
self.network.deallocate_fixed_ip(self.context, address2)
|
||||||
|
|
||||||
|
def test_too_many_addresses(self):
|
||||||
|
"""Test for a NoMoreAddresses exception when all fixed ips are used.
|
||||||
|
"""
|
||||||
|
admin_context = context.get_admin_context()
|
||||||
|
network = db.project_get_network(admin_context, self.projects[0].id)
|
||||||
|
num_available_ips = db.network_count_available_ips(admin_context,
|
||||||
|
network['id'])
|
||||||
|
addresses = []
|
||||||
|
instance_ids = []
|
||||||
|
for i in range(num_available_ips):
|
||||||
|
instance_ref = self._create_instance(0)
|
||||||
|
instance_ids.append(instance_ref['id'])
|
||||||
|
address = self._create_address(0, instance_ref['id'])
|
||||||
|
addresses.append(address)
|
||||||
|
|
||||||
|
ip_count = db.network_count_available_ips(context.get_admin_context(),
|
||||||
|
network['id'])
|
||||||
|
self.assertEqual(ip_count, 0)
|
||||||
|
self.assertRaises(db.NoMoreAddresses,
|
||||||
|
self.network.allocate_fixed_ip,
|
||||||
|
self.context,
|
||||||
|
'foo')
|
||||||
|
|
||||||
|
for i in range(num_available_ips):
|
||||||
|
self.network.deallocate_fixed_ip(self.context, addresses[i])
|
||||||
|
db.instance_destroy(context.get_admin_context(), instance_ids[i])
|
||||||
|
ip_count = db.network_count_available_ips(context.get_admin_context(),
|
||||||
|
network['id'])
|
||||||
|
self.assertEqual(ip_count, num_available_ips)
|
||||||
|
|
||||||
|
def run(self, result=None):
|
||||||
|
if(FLAGS.network_manager == 'nova.network.manager.FlatManager'):
|
||||||
|
super(FlatNetworkTestCase, self).run(result)
|
||||||
@@ -21,9 +21,10 @@ import sys
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import nova
|
import nova
|
||||||
|
from nova import test
|
||||||
|
|
||||||
|
|
||||||
class LocalizationTestCase(unittest.TestCase):
|
class LocalizationTestCase(test.TestCase):
|
||||||
def test_multiple_positional_format_placeholders(self):
|
def test_multiple_positional_format_placeholders(self):
|
||||||
pat = re.compile("\W_\(")
|
pat = re.compile("\W_\(")
|
||||||
single_pat = re.compile("\W%\W")
|
single_pat = re.compile("\W%\W")
|
||||||
|
|||||||
@@ -18,8 +18,12 @@ import errno
|
|||||||
import os
|
import os
|
||||||
import select
|
import select
|
||||||
|
|
||||||
|
from eventlet import greenpool
|
||||||
|
from eventlet import greenthread
|
||||||
|
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.utils import parse_mailmap, str_dict_replace, synchronized
|
from nova import utils
|
||||||
|
from nova.utils import parse_mailmap, str_dict_replace
|
||||||
|
|
||||||
|
|
||||||
class ProjectTestCase(test.TestCase):
|
class ProjectTestCase(test.TestCase):
|
||||||
@@ -63,7 +67,7 @@ class ProjectTestCase(test.TestCase):
|
|||||||
|
|
||||||
class LockTestCase(test.TestCase):
|
class LockTestCase(test.TestCase):
|
||||||
def test_synchronized_wrapped_function_metadata(self):
|
def test_synchronized_wrapped_function_metadata(self):
|
||||||
@synchronized('whatever')
|
@utils.synchronized('whatever')
|
||||||
def foo():
|
def foo():
|
||||||
"""Bar"""
|
"""Bar"""
|
||||||
pass
|
pass
|
||||||
@@ -72,11 +76,42 @@ class LockTestCase(test.TestCase):
|
|||||||
self.assertEquals(foo.__name__, 'foo', "Wrapped function's name "
|
self.assertEquals(foo.__name__, 'foo', "Wrapped function's name "
|
||||||
"got mangled")
|
"got mangled")
|
||||||
|
|
||||||
def test_synchronized(self):
|
def test_synchronized_internally(self):
|
||||||
|
"""We can lock across multiple green threads"""
|
||||||
|
saved_sem_num = len(utils._semaphores)
|
||||||
|
seen_threads = list()
|
||||||
|
|
||||||
|
@utils.synchronized('testlock2', external=False)
|
||||||
|
def f(id):
|
||||||
|
for x in range(10):
|
||||||
|
seen_threads.append(id)
|
||||||
|
greenthread.sleep(0)
|
||||||
|
|
||||||
|
threads = []
|
||||||
|
pool = greenpool.GreenPool(10)
|
||||||
|
for i in range(10):
|
||||||
|
threads.append(pool.spawn(f, i))
|
||||||
|
|
||||||
|
for thread in threads:
|
||||||
|
thread.wait()
|
||||||
|
|
||||||
|
self.assertEquals(len(seen_threads), 100)
|
||||||
|
# Looking at the seen threads, split it into chunks of 10, and verify
|
||||||
|
# that the last 9 match the first in each chunk.
|
||||||
|
for i in range(10):
|
||||||
|
for j in range(9):
|
||||||
|
self.assertEquals(seen_threads[i * 10],
|
||||||
|
seen_threads[i * 10 + 1 + j])
|
||||||
|
|
||||||
|
self.assertEqual(saved_sem_num, len(utils._semaphores),
|
||||||
|
"Semaphore leak detected")
|
||||||
|
|
||||||
|
def test_synchronized_externally(self):
|
||||||
|
"""We can lock across multiple processes"""
|
||||||
rpipe1, wpipe1 = os.pipe()
|
rpipe1, wpipe1 = os.pipe()
|
||||||
rpipe2, wpipe2 = os.pipe()
|
rpipe2, wpipe2 = os.pipe()
|
||||||
|
|
||||||
@synchronized('testlock')
|
@utils.synchronized('testlock1', external=True)
|
||||||
def f(rpipe, wpipe):
|
def f(rpipe, wpipe):
|
||||||
try:
|
try:
|
||||||
os.write(wpipe, "foo")
|
os.write(wpipe, "foo")
|
||||||
|
|||||||
@@ -20,21 +20,10 @@ Unit Tests for network code
|
|||||||
"""
|
"""
|
||||||
import IPy
|
import IPy
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
|
|
||||||
from nova import context
|
|
||||||
from nova import db
|
|
||||||
from nova import exception
|
|
||||||
from nova import flags
|
|
||||||
from nova import log as logging
|
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova import utils
|
|
||||||
from nova.auth import manager
|
|
||||||
from nova.network import linux_net
|
from nova.network import linux_net
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
LOG = logging.getLogger('nova.tests.network')
|
|
||||||
|
|
||||||
|
|
||||||
class IptablesManagerTestCase(test.TestCase):
|
class IptablesManagerTestCase(test.TestCase):
|
||||||
sample_filter = ['#Generated by iptables-save on Fri Feb 18 15:17:05 2011',
|
sample_filter = ['#Generated by iptables-save on Fri Feb 18 15:17:05 2011',
|
||||||
@@ -175,363 +164,3 @@ class IptablesManagerTestCase(test.TestCase):
|
|||||||
self.assertTrue('-A %s -j run_tests.py-%s' \
|
self.assertTrue('-A %s -j run_tests.py-%s' \
|
||||||
% (chain, chain) in new_lines,
|
% (chain, chain) in new_lines,
|
||||||
"Built-in chain %s not wrapped" % (chain,))
|
"Built-in chain %s not wrapped" % (chain,))
|
||||||
|
|
||||||
|
|
||||||
class NetworkTestCase(test.TestCase):
|
|
||||||
"""Test cases for network code"""
|
|
||||||
def setUp(self):
|
|
||||||
super(NetworkTestCase, self).setUp()
|
|
||||||
# NOTE(vish): if you change these flags, make sure to change the
|
|
||||||
# flags in the corresponding section in nova-dhcpbridge
|
|
||||||
self.flags(connection_type='fake',
|
|
||||||
fake_call=True,
|
|
||||||
fake_network=True)
|
|
||||||
self.manager = manager.AuthManager()
|
|
||||||
self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
|
|
||||||
self.projects = []
|
|
||||||
self.network = utils.import_object(FLAGS.network_manager)
|
|
||||||
self.context = context.RequestContext(project=None, user=self.user)
|
|
||||||
for i in range(FLAGS.num_networks):
|
|
||||||
name = 'project%s' % i
|
|
||||||
project = self.manager.create_project(name, 'netuser', name)
|
|
||||||
self.projects.append(project)
|
|
||||||
# create the necessary network data for the project
|
|
||||||
user_context = context.RequestContext(project=self.projects[i],
|
|
||||||
user=self.user)
|
|
||||||
host = self.network.get_network_host(user_context.elevated())
|
|
||||||
instance_ref = self._create_instance(0)
|
|
||||||
self.instance_id = instance_ref['id']
|
|
||||||
instance_ref = self._create_instance(1)
|
|
||||||
self.instance2_id = instance_ref['id']
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
# TODO(termie): this should really be instantiating clean datastores
|
|
||||||
# in between runs, one failure kills all the tests
|
|
||||||
db.instance_destroy(context.get_admin_context(), self.instance_id)
|
|
||||||
db.instance_destroy(context.get_admin_context(), self.instance2_id)
|
|
||||||
for project in self.projects:
|
|
||||||
self.manager.delete_project(project)
|
|
||||||
self.manager.delete_user(self.user)
|
|
||||||
super(NetworkTestCase, self).tearDown()
|
|
||||||
|
|
||||||
def _create_instance(self, project_num, mac=None):
|
|
||||||
if not mac:
|
|
||||||
mac = utils.generate_mac()
|
|
||||||
project = self.projects[project_num]
|
|
||||||
self.context._project = project
|
|
||||||
self.context.project_id = project.id
|
|
||||||
return db.instance_create(self.context,
|
|
||||||
{'project_id': project.id,
|
|
||||||
'mac_address': mac})
|
|
||||||
|
|
||||||
def _create_address(self, project_num, instance_id=None):
|
|
||||||
"""Create an address in given project num"""
|
|
||||||
if instance_id is None:
|
|
||||||
instance_id = self.instance_id
|
|
||||||
self.context._project = self.projects[project_num]
|
|
||||||
self.context.project_id = self.projects[project_num].id
|
|
||||||
return self.network.allocate_fixed_ip(self.context, instance_id)
|
|
||||||
|
|
||||||
def _deallocate_address(self, project_num, address):
|
|
||||||
self.context._project = self.projects[project_num]
|
|
||||||
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']))
|
|
||||||
self._deallocate_address(0, address)
|
|
||||||
db.instance_destroy(context.get_admin_context(),
|
|
||||||
instance_ref['id'])
|
|
||||||
|
|
||||||
def test_public_network_association(self):
|
|
||||||
"""Makes sure that we can allocaate a public ip"""
|
|
||||||
# TODO(vish): better way of adding floating ips
|
|
||||||
self.context._project = self.projects[0]
|
|
||||||
self.context.project_id = self.projects[0].id
|
|
||||||
pubnet = IPy.IP(flags.FLAGS.floating_range)
|
|
||||||
address = str(pubnet[0])
|
|
||||||
try:
|
|
||||||
db.floating_ip_get_by_address(context.get_admin_context(), address)
|
|
||||||
except exception.NotFound:
|
|
||||||
db.floating_ip_create(context.get_admin_context(),
|
|
||||||
{'address': address,
|
|
||||||
'host': FLAGS.host})
|
|
||||||
float_addr = self.network.allocate_floating_ip(self.context,
|
|
||||||
self.projects[0].id)
|
|
||||||
fix_addr = self._create_address(0)
|
|
||||||
lease_ip(fix_addr)
|
|
||||||
self.assertEqual(float_addr, str(pubnet[0]))
|
|
||||||
self.network.associate_floating_ip(self.context, float_addr, fix_addr)
|
|
||||||
address = db.instance_get_floating_address(context.get_admin_context(),
|
|
||||||
self.instance_id)
|
|
||||||
self.assertEqual(address, float_addr)
|
|
||||||
self.network.disassociate_floating_ip(self.context, float_addr)
|
|
||||||
address = db.instance_get_floating_address(context.get_admin_context(),
|
|
||||||
self.instance_id)
|
|
||||||
self.assertEqual(address, None)
|
|
||||||
self.network.deallocate_floating_ip(self.context, float_addr)
|
|
||||||
self.network.deallocate_fixed_ip(self.context, fix_addr)
|
|
||||||
release_ip(fix_addr)
|
|
||||||
db.floating_ip_destroy(context.get_admin_context(), float_addr)
|
|
||||||
|
|
||||||
def test_allocate_deallocate_fixed_ip(self):
|
|
||||||
"""Makes sure that we can allocate and deallocate a fixed ip"""
|
|
||||||
address = self._create_address(0)
|
|
||||||
self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
|
|
||||||
lease_ip(address)
|
|
||||||
self._deallocate_address(0, address)
|
|
||||||
|
|
||||||
# Doesn't go away until it's dhcp released
|
|
||||||
self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
|
|
||||||
|
|
||||||
release_ip(address)
|
|
||||||
self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
|
|
||||||
|
|
||||||
def test_side_effects(self):
|
|
||||||
"""Ensures allocating and releasing has no side effects"""
|
|
||||||
address = self._create_address(0)
|
|
||||||
address2 = self._create_address(1, self.instance2_id)
|
|
||||||
|
|
||||||
self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
|
|
||||||
self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
|
|
||||||
self.assertFalse(is_allocated_in_project(address, self.projects[1].id))
|
|
||||||
|
|
||||||
# Addresses are allocated before they're issued
|
|
||||||
lease_ip(address)
|
|
||||||
lease_ip(address2)
|
|
||||||
|
|
||||||
self._deallocate_address(0, address)
|
|
||||||
release_ip(address)
|
|
||||||
self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
|
|
||||||
|
|
||||||
# First address release shouldn't affect the second
|
|
||||||
self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
|
|
||||||
|
|
||||||
self._deallocate_address(1, address2)
|
|
||||||
release_ip(address2)
|
|
||||||
self.assertFalse(is_allocated_in_project(address2,
|
|
||||||
self.projects[1].id))
|
|
||||||
|
|
||||||
def test_subnet_edge(self):
|
|
||||||
"""Makes sure that private ips don't overlap"""
|
|
||||||
first = self._create_address(0)
|
|
||||||
lease_ip(first)
|
|
||||||
instance_ids = []
|
|
||||||
for i in range(1, FLAGS.num_networks):
|
|
||||||
instance_ref = self._create_instance(i, mac=utils.generate_mac())
|
|
||||||
instance_ids.append(instance_ref['id'])
|
|
||||||
address = self._create_address(i, instance_ref['id'])
|
|
||||||
instance_ref = self._create_instance(i, mac=utils.generate_mac())
|
|
||||||
instance_ids.append(instance_ref['id'])
|
|
||||||
address2 = self._create_address(i, instance_ref['id'])
|
|
||||||
instance_ref = self._create_instance(i, mac=utils.generate_mac())
|
|
||||||
instance_ids.append(instance_ref['id'])
|
|
||||||
address3 = self._create_address(i, instance_ref['id'])
|
|
||||||
lease_ip(address)
|
|
||||||
lease_ip(address2)
|
|
||||||
lease_ip(address3)
|
|
||||||
self.context._project = self.projects[i]
|
|
||||||
self.context.project_id = self.projects[i].id
|
|
||||||
self.assertFalse(is_allocated_in_project(address,
|
|
||||||
self.projects[0].id))
|
|
||||||
self.assertFalse(is_allocated_in_project(address2,
|
|
||||||
self.projects[0].id))
|
|
||||||
self.assertFalse(is_allocated_in_project(address3,
|
|
||||||
self.projects[0].id))
|
|
||||||
self.network.deallocate_fixed_ip(self.context, address)
|
|
||||||
self.network.deallocate_fixed_ip(self.context, address2)
|
|
||||||
self.network.deallocate_fixed_ip(self.context, address3)
|
|
||||||
release_ip(address)
|
|
||||||
release_ip(address2)
|
|
||||||
release_ip(address3)
|
|
||||||
for instance_id in instance_ids:
|
|
||||||
db.instance_destroy(context.get_admin_context(), instance_id)
|
|
||||||
self.context._project = self.projects[0]
|
|
||||||
self.context.project_id = self.projects[0].id
|
|
||||||
self.network.deallocate_fixed_ip(self.context, first)
|
|
||||||
self._deallocate_address(0, first)
|
|
||||||
release_ip(first)
|
|
||||||
|
|
||||||
def test_vpn_ip_and_port_looks_valid(self):
|
|
||||||
"""Ensure the vpn ip and port are reasonable"""
|
|
||||||
self.assert_(self.projects[0].vpn_ip)
|
|
||||||
self.assert_(self.projects[0].vpn_port >= FLAGS.vpn_start)
|
|
||||||
self.assert_(self.projects[0].vpn_port <= FLAGS.vpn_start +
|
|
||||||
FLAGS.num_networks)
|
|
||||||
|
|
||||||
def test_too_many_networks(self):
|
|
||||||
"""Ensure error is raised if we run out of networks"""
|
|
||||||
projects = []
|
|
||||||
networks_left = (FLAGS.num_networks -
|
|
||||||
db.network_count(context.get_admin_context()))
|
|
||||||
for i in range(networks_left):
|
|
||||||
project = self.manager.create_project('many%s' % i, self.user)
|
|
||||||
projects.append(project)
|
|
||||||
db.project_get_network(context.get_admin_context(), project.id)
|
|
||||||
project = self.manager.create_project('last', self.user)
|
|
||||||
projects.append(project)
|
|
||||||
self.assertRaises(db.NoMoreNetworks,
|
|
||||||
db.project_get_network,
|
|
||||||
context.get_admin_context(),
|
|
||||||
project.id)
|
|
||||||
for project in projects:
|
|
||||||
self.manager.delete_project(project)
|
|
||||||
|
|
||||||
def test_ips_are_reused(self):
|
|
||||||
"""Makes sure that ip addresses that are deallocated get reused"""
|
|
||||||
address = self._create_address(0)
|
|
||||||
lease_ip(address)
|
|
||||||
self.network.deallocate_fixed_ip(self.context, address)
|
|
||||||
release_ip(address)
|
|
||||||
|
|
||||||
address2 = self._create_address(0)
|
|
||||||
self.assertEqual(address, address2)
|
|
||||||
lease_ip(address)
|
|
||||||
self.network.deallocate_fixed_ip(self.context, address2)
|
|
||||||
release_ip(address)
|
|
||||||
|
|
||||||
def test_available_ips(self):
|
|
||||||
"""Make sure the number of available ips for the network is correct
|
|
||||||
|
|
||||||
The number of available IP addresses depends on the test
|
|
||||||
environment's setup.
|
|
||||||
|
|
||||||
Network size is set in test fixture's setUp method.
|
|
||||||
|
|
||||||
There are ips reserved at the bottom and top of the range.
|
|
||||||
services (network, gateway, CloudPipe, broadcast)
|
|
||||||
"""
|
|
||||||
network = db.project_get_network(context.get_admin_context(),
|
|
||||||
self.projects[0].id)
|
|
||||||
net_size = flags.FLAGS.network_size
|
|
||||||
admin_context = context.get_admin_context()
|
|
||||||
total_ips = (db.network_count_available_ips(admin_context,
|
|
||||||
network['id']) +
|
|
||||||
db.network_count_reserved_ips(admin_context,
|
|
||||||
network['id']) +
|
|
||||||
db.network_count_allocated_ips(admin_context,
|
|
||||||
network['id']))
|
|
||||||
self.assertEqual(total_ips, net_size)
|
|
||||||
|
|
||||||
def test_too_many_addresses(self):
|
|
||||||
"""Test for a NoMoreAddresses exception when all fixed ips are used.
|
|
||||||
"""
|
|
||||||
admin_context = context.get_admin_context()
|
|
||||||
network = db.project_get_network(admin_context, self.projects[0].id)
|
|
||||||
num_available_ips = db.network_count_available_ips(admin_context,
|
|
||||||
network['id'])
|
|
||||||
addresses = []
|
|
||||||
instance_ids = []
|
|
||||||
for i in range(num_available_ips):
|
|
||||||
instance_ref = self._create_instance(0)
|
|
||||||
instance_ids.append(instance_ref['id'])
|
|
||||||
address = self._create_address(0, instance_ref['id'])
|
|
||||||
addresses.append(address)
|
|
||||||
lease_ip(address)
|
|
||||||
|
|
||||||
ip_count = db.network_count_available_ips(context.get_admin_context(),
|
|
||||||
network['id'])
|
|
||||||
self.assertEqual(ip_count, 0)
|
|
||||||
self.assertRaises(db.NoMoreAddresses,
|
|
||||||
self.network.allocate_fixed_ip,
|
|
||||||
self.context,
|
|
||||||
'foo')
|
|
||||||
|
|
||||||
for i in range(num_available_ips):
|
|
||||||
self.network.deallocate_fixed_ip(self.context, addresses[i])
|
|
||||||
release_ip(addresses[i])
|
|
||||||
db.instance_destroy(context.get_admin_context(), instance_ids[i])
|
|
||||||
ip_count = db.network_count_available_ips(context.get_admin_context(),
|
|
||||||
network['id'])
|
|
||||||
self.assertEqual(ip_count, num_available_ips)
|
|
||||||
|
|
||||||
def test_dhcp_lease_output(self):
|
|
||||||
admin_ctxt = context.get_admin_context()
|
|
||||||
address = self._create_address(0, self.instance_id)
|
|
||||||
lease_ip(address)
|
|
||||||
network_ref = db.network_get_by_instance(admin_ctxt, self.instance_id)
|
|
||||||
leases = linux_net.get_dhcp_leases(context.get_admin_context(),
|
|
||||||
network_ref['id'])
|
|
||||||
for line in leases.split('\n'):
|
|
||||||
seconds, mac, ip, hostname, client_id = line.split(' ')
|
|
||||||
self.assertTrue(int(seconds) > time.time(), 'Lease expires in '
|
|
||||||
'the past')
|
|
||||||
octets = mac.split(':')
|
|
||||||
self.assertEqual(len(octets), 6, "Wrong number of octets "
|
|
||||||
"in %s" % (max,))
|
|
||||||
for octet in octets:
|
|
||||||
self.assertEqual(len(octet), 2, "Oddly sized octet: %s"
|
|
||||||
% (octet,))
|
|
||||||
# This will throw an exception if the octet is invalid
|
|
||||||
int(octet, 16)
|
|
||||||
|
|
||||||
# And this will raise an exception in case of an invalid IP
|
|
||||||
IPy.IP(ip)
|
|
||||||
|
|
||||||
release_ip(address)
|
|
||||||
|
|
||||||
|
|
||||||
def is_allocated_in_project(address, project_id):
|
|
||||||
"""Returns true if address is in specified project"""
|
|
||||||
project_net = db.project_get_network(context.get_admin_context(),
|
|
||||||
project_id)
|
|
||||||
network = db.fixed_ip_get_network(context.get_admin_context(), address)
|
|
||||||
instance = db.fixed_ip_get_instance(context.get_admin_context(), address)
|
|
||||||
# instance exists until release
|
|
||||||
return instance is not None and network['id'] == project_net['id']
|
|
||||||
|
|
||||||
|
|
||||||
def binpath(script):
|
|
||||||
"""Returns the absolute path to a script in bin"""
|
|
||||||
return os.path.abspath(os.path.join(__file__, "../../../bin", script))
|
|
||||||
|
|
||||||
|
|
||||||
def lease_ip(private_ip):
|
|
||||||
"""Run add command on dhcpbridge"""
|
|
||||||
network_ref = db.fixed_ip_get_network(context.get_admin_context(),
|
|
||||||
private_ip)
|
|
||||||
instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
|
|
||||||
private_ip)
|
|
||||||
cmd = (binpath('nova-dhcpbridge'), 'add',
|
|
||||||
instance_ref['mac_address'],
|
|
||||||
private_ip, 'fake')
|
|
||||||
env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
|
|
||||||
'TESTING': '1',
|
|
||||||
'FLAGFILE': FLAGS.dhcpbridge_flagfile}
|
|
||||||
(out, err) = utils.execute(*cmd, addl_env=env)
|
|
||||||
LOG.debug("ISSUE_IP: %s, %s ", out, err)
|
|
||||||
|
|
||||||
|
|
||||||
def release_ip(private_ip):
|
|
||||||
"""Run del command on dhcpbridge"""
|
|
||||||
network_ref = db.fixed_ip_get_network(context.get_admin_context(),
|
|
||||||
private_ip)
|
|
||||||
instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
|
|
||||||
private_ip)
|
|
||||||
cmd = (binpath('nova-dhcpbridge'), 'del',
|
|
||||||
instance_ref['mac_address'],
|
|
||||||
private_ip, 'fake')
|
|
||||||
env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
|
|
||||||
'TESTING': '1',
|
|
||||||
'FLAGFILE': FLAGS.dhcpbridge_flagfile}
|
|
||||||
(out, err) = utils.execute(*cmd, addl_env=env)
|
|
||||||
LOG.debug("RELEASE_IP: %s, %s ", out, err)
|
|
||||||
|
|||||||
@@ -77,13 +77,11 @@ class CacheConcurrencyTestCase(test.TestCase):
|
|||||||
eventlet.sleep(0)
|
eventlet.sleep(0)
|
||||||
try:
|
try:
|
||||||
self.assertFalse(done2.ready())
|
self.assertFalse(done2.ready())
|
||||||
self.assertTrue('fname' in conn._image_sems)
|
|
||||||
finally:
|
finally:
|
||||||
wait1.send()
|
wait1.send()
|
||||||
done1.wait()
|
done1.wait()
|
||||||
eventlet.sleep(0)
|
eventlet.sleep(0)
|
||||||
self.assertTrue(done2.ready())
|
self.assertTrue(done2.ready())
|
||||||
self.assertFalse('fname' in conn._image_sems)
|
|
||||||
|
|
||||||
def test_different_fname_concurrency(self):
|
def test_different_fname_concurrency(self):
|
||||||
"""Ensures that two different fname caches are concurrent"""
|
"""Ensures that two different fname caches are concurrent"""
|
||||||
|
|||||||
242
nova/tests/test_vlan_network.py
Normal file
242
nova/tests/test_vlan_network.py
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
Unit Tests for vlan network code
|
||||||
|
"""
|
||||||
|
import IPy
|
||||||
|
import os
|
||||||
|
|
||||||
|
from nova import context
|
||||||
|
from nova import db
|
||||||
|
from nova import exception
|
||||||
|
from nova import flags
|
||||||
|
from nova import log as logging
|
||||||
|
from nova import test
|
||||||
|
from nova import utils
|
||||||
|
from nova.auth import manager
|
||||||
|
from nova.tests.network import base
|
||||||
|
from nova.tests.network import binpath,\
|
||||||
|
lease_ip, release_ip
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
LOG = logging.getLogger('nova.tests.network')
|
||||||
|
|
||||||
|
|
||||||
|
class VlanNetworkTestCase(base.NetworkTestCase):
|
||||||
|
"""Test cases for network code"""
|
||||||
|
def test_public_network_association(self):
|
||||||
|
"""Makes sure that we can allocaate a public ip"""
|
||||||
|
# TODO(vish): better way of adding floating ips
|
||||||
|
self.context._project = self.projects[0]
|
||||||
|
self.context.project_id = self.projects[0].id
|
||||||
|
pubnet = IPy.IP(flags.FLAGS.floating_range)
|
||||||
|
address = str(pubnet[0])
|
||||||
|
try:
|
||||||
|
db.floating_ip_get_by_address(context.get_admin_context(), address)
|
||||||
|
except exception.NotFound:
|
||||||
|
db.floating_ip_create(context.get_admin_context(),
|
||||||
|
{'address': address,
|
||||||
|
'host': FLAGS.host})
|
||||||
|
float_addr = self.network.allocate_floating_ip(self.context,
|
||||||
|
self.projects[0].id)
|
||||||
|
fix_addr = self._create_address(0)
|
||||||
|
lease_ip(fix_addr)
|
||||||
|
self.assertEqual(float_addr, str(pubnet[0]))
|
||||||
|
self.network.associate_floating_ip(self.context, float_addr, fix_addr)
|
||||||
|
address = db.instance_get_floating_address(context.get_admin_context(),
|
||||||
|
self.instance_id)
|
||||||
|
self.assertEqual(address, float_addr)
|
||||||
|
self.network.disassociate_floating_ip(self.context, float_addr)
|
||||||
|
address = db.instance_get_floating_address(context.get_admin_context(),
|
||||||
|
self.instance_id)
|
||||||
|
self.assertEqual(address, None)
|
||||||
|
self.network.deallocate_floating_ip(self.context, float_addr)
|
||||||
|
self.network.deallocate_fixed_ip(self.context, fix_addr)
|
||||||
|
release_ip(fix_addr)
|
||||||
|
db.floating_ip_destroy(context.get_admin_context(), float_addr)
|
||||||
|
|
||||||
|
def test_allocate_deallocate_fixed_ip(self):
|
||||||
|
"""Makes sure that we can allocate and deallocate a fixed ip"""
|
||||||
|
address = self._create_address(0)
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
lease_ip(address)
|
||||||
|
self._deallocate_address(0, address)
|
||||||
|
|
||||||
|
# Doesn't go away until it's dhcp released
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
|
||||||
|
release_ip(address)
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
|
||||||
|
def test_side_effects(self):
|
||||||
|
"""Ensures allocating and releasing has no side effects"""
|
||||||
|
address = self._create_address(0)
|
||||||
|
address2 = self._create_address(1, self.instance2_id)
|
||||||
|
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address2,
|
||||||
|
self.projects[1].id))
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address,
|
||||||
|
self.projects[1].id))
|
||||||
|
|
||||||
|
# Addresses are allocated before they're issued
|
||||||
|
lease_ip(address)
|
||||||
|
lease_ip(address2)
|
||||||
|
|
||||||
|
self._deallocate_address(0, address)
|
||||||
|
release_ip(address)
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
|
||||||
|
# First address release shouldn't affect the second
|
||||||
|
self.assertTrue(self._is_allocated_in_project(address2,
|
||||||
|
self.projects[1].id))
|
||||||
|
|
||||||
|
self._deallocate_address(1, address2)
|
||||||
|
release_ip(address2)
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address2,
|
||||||
|
self.projects[1].id))
|
||||||
|
|
||||||
|
def test_subnet_edge(self):
|
||||||
|
"""Makes sure that private ips don't overlap"""
|
||||||
|
first = self._create_address(0)
|
||||||
|
lease_ip(first)
|
||||||
|
instance_ids = []
|
||||||
|
for i in range(1, FLAGS.num_networks):
|
||||||
|
instance_ref = self._create_instance(i, mac=utils.generate_mac())
|
||||||
|
instance_ids.append(instance_ref['id'])
|
||||||
|
address = self._create_address(i, instance_ref['id'])
|
||||||
|
instance_ref = self._create_instance(i, mac=utils.generate_mac())
|
||||||
|
instance_ids.append(instance_ref['id'])
|
||||||
|
address2 = self._create_address(i, instance_ref['id'])
|
||||||
|
instance_ref = self._create_instance(i, mac=utils.generate_mac())
|
||||||
|
instance_ids.append(instance_ref['id'])
|
||||||
|
address3 = self._create_address(i, instance_ref['id'])
|
||||||
|
lease_ip(address)
|
||||||
|
lease_ip(address2)
|
||||||
|
lease_ip(address3)
|
||||||
|
self.context._project = self.projects[i]
|
||||||
|
self.context.project_id = self.projects[i].id
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address,
|
||||||
|
self.projects[0].id))
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address2,
|
||||||
|
self.projects[0].id))
|
||||||
|
self.assertFalse(self._is_allocated_in_project(address3,
|
||||||
|
self.projects[0].id))
|
||||||
|
self.network.deallocate_fixed_ip(self.context, address)
|
||||||
|
self.network.deallocate_fixed_ip(self.context, address2)
|
||||||
|
self.network.deallocate_fixed_ip(self.context, address3)
|
||||||
|
release_ip(address)
|
||||||
|
release_ip(address2)
|
||||||
|
release_ip(address3)
|
||||||
|
for instance_id in instance_ids:
|
||||||
|
db.instance_destroy(context.get_admin_context(), instance_id)
|
||||||
|
self.context._project = self.projects[0]
|
||||||
|
self.context.project_id = self.projects[0].id
|
||||||
|
self.network.deallocate_fixed_ip(self.context, first)
|
||||||
|
self._deallocate_address(0, first)
|
||||||
|
release_ip(first)
|
||||||
|
|
||||||
|
def test_vpn_ip_and_port_looks_valid(self):
|
||||||
|
"""Ensure the vpn ip and port are reasonable"""
|
||||||
|
self.assert_(self.projects[0].vpn_ip)
|
||||||
|
self.assert_(self.projects[0].vpn_port >= FLAGS.vpn_start)
|
||||||
|
self.assert_(self.projects[0].vpn_port <= FLAGS.vpn_start +
|
||||||
|
FLAGS.num_networks)
|
||||||
|
|
||||||
|
def test_too_many_networks(self):
|
||||||
|
"""Ensure error is raised if we run out of networks"""
|
||||||
|
projects = []
|
||||||
|
networks_left = (FLAGS.num_networks -
|
||||||
|
db.network_count(context.get_admin_context()))
|
||||||
|
for i in range(networks_left):
|
||||||
|
project = self.manager.create_project('many%s' % i, self.user)
|
||||||
|
projects.append(project)
|
||||||
|
db.project_get_network(context.get_admin_context(), project.id)
|
||||||
|
project = self.manager.create_project('last', self.user)
|
||||||
|
projects.append(project)
|
||||||
|
self.assertRaises(db.NoMoreNetworks,
|
||||||
|
db.project_get_network,
|
||||||
|
context.get_admin_context(),
|
||||||
|
project.id)
|
||||||
|
for project in projects:
|
||||||
|
self.manager.delete_project(project)
|
||||||
|
|
||||||
|
def test_ips_are_reused(self):
|
||||||
|
"""Makes sure that ip addresses that are deallocated get reused"""
|
||||||
|
address = self._create_address(0)
|
||||||
|
lease_ip(address)
|
||||||
|
self.network.deallocate_fixed_ip(self.context, address)
|
||||||
|
release_ip(address)
|
||||||
|
|
||||||
|
address2 = self._create_address(0)
|
||||||
|
self.assertEqual(address, address2)
|
||||||
|
lease_ip(address)
|
||||||
|
self.network.deallocate_fixed_ip(self.context, address2)
|
||||||
|
release_ip(address)
|
||||||
|
|
||||||
|
def test_too_many_addresses(self):
|
||||||
|
"""Test for a NoMoreAddresses exception when all fixed ips are used.
|
||||||
|
"""
|
||||||
|
admin_context = context.get_admin_context()
|
||||||
|
network = db.project_get_network(admin_context, self.projects[0].id)
|
||||||
|
num_available_ips = db.network_count_available_ips(admin_context,
|
||||||
|
network['id'])
|
||||||
|
addresses = []
|
||||||
|
instance_ids = []
|
||||||
|
for i in range(num_available_ips):
|
||||||
|
instance_ref = self._create_instance(0)
|
||||||
|
instance_ids.append(instance_ref['id'])
|
||||||
|
address = self._create_address(0, instance_ref['id'])
|
||||||
|
addresses.append(address)
|
||||||
|
lease_ip(address)
|
||||||
|
|
||||||
|
ip_count = db.network_count_available_ips(context.get_admin_context(),
|
||||||
|
network['id'])
|
||||||
|
self.assertEqual(ip_count, 0)
|
||||||
|
self.assertRaises(db.NoMoreAddresses,
|
||||||
|
self.network.allocate_fixed_ip,
|
||||||
|
self.context,
|
||||||
|
'foo')
|
||||||
|
|
||||||
|
for i in range(num_available_ips):
|
||||||
|
self.network.deallocate_fixed_ip(self.context, addresses[i])
|
||||||
|
release_ip(addresses[i])
|
||||||
|
db.instance_destroy(context.get_admin_context(), instance_ids[i])
|
||||||
|
ip_count = db.network_count_available_ips(context.get_admin_context(),
|
||||||
|
network['id'])
|
||||||
|
self.assertEqual(ip_count, num_available_ips)
|
||||||
|
|
||||||
|
def _is_allocated_in_project(self, address, project_id):
|
||||||
|
"""Returns true if address is in specified project"""
|
||||||
|
project_net = db.project_get_network(context.get_admin_context(),
|
||||||
|
project_id)
|
||||||
|
network = db.fixed_ip_get_network(context.get_admin_context(),
|
||||||
|
address)
|
||||||
|
instance = db.fixed_ip_get_instance(context.get_admin_context(),
|
||||||
|
address)
|
||||||
|
# instance exists until release
|
||||||
|
return instance is not None and network['id'] == project_net['id']
|
||||||
|
|
||||||
|
def run(self, result=None):
|
||||||
|
if(FLAGS.network_manager == 'nova.network.manager.VlanManager'):
|
||||||
|
super(VlanNetworkTestCase, self).run(result)
|
||||||
@@ -228,6 +228,9 @@ class FakeSessionForMigrationTests(fake.SessionBase):
|
|||||||
def VDI_get_by_uuid(*args):
|
def VDI_get_by_uuid(*args):
|
||||||
return 'hurr'
|
return 'hurr'
|
||||||
|
|
||||||
|
def VDI_resize_online(*args):
|
||||||
|
pass
|
||||||
|
|
||||||
def VM_start(self, _1, ref, _2, _3):
|
def VM_start(self, _1, ref, _2, _3):
|
||||||
vm = fake.get_record('VM', ref)
|
vm = fake.get_record('VM', ref)
|
||||||
if vm['power_state'] != 'Halted':
|
if vm['power_state'] != 'Halted':
|
||||||
@@ -240,7 +243,7 @@ class FakeSessionForMigrationTests(fake.SessionBase):
|
|||||||
|
|
||||||
def stub_out_migration_methods(stubs):
|
def stub_out_migration_methods(stubs):
|
||||||
def fake_get_snapshot(self, instance):
|
def fake_get_snapshot(self, instance):
|
||||||
return 'foo', 'bar'
|
return 'vm_ref', dict(image='foo', snap='bar')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fake_get_vdi(cls, session, vm_ref):
|
def fake_get_vdi(cls, session, vm_ref):
|
||||||
@@ -249,7 +252,7 @@ def stub_out_migration_methods(stubs):
|
|||||||
vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
|
vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
|
||||||
return vdi_ref, {'uuid': vdi_rec['uuid'], }
|
return vdi_ref, {'uuid': vdi_rec['uuid'], }
|
||||||
|
|
||||||
def fake_shutdown(self, inst, vm, method='clean'):
|
def fake_shutdown(self, inst, vm, hard=True):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ from xml.sax import saxutils
|
|||||||
|
|
||||||
from eventlet import event
|
from eventlet import event
|
||||||
from eventlet import greenthread
|
from eventlet import greenthread
|
||||||
|
from eventlet import semaphore
|
||||||
from eventlet.green import subprocess
|
from eventlet.green import subprocess
|
||||||
None
|
None
|
||||||
from nova import exception
|
from nova import exception
|
||||||
@@ -334,6 +335,14 @@ def utcnow():
|
|||||||
utcnow.override_time = None
|
utcnow.override_time = None
|
||||||
|
|
||||||
|
|
||||||
|
def is_older_than(before, seconds):
|
||||||
|
"""Return True if before is older than 'seconds'"""
|
||||||
|
if utcnow() - before > datetime.timedelta(seconds=seconds):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def utcnow_ts():
|
def utcnow_ts():
|
||||||
"""Timestamp version of our utcnow function."""
|
"""Timestamp version of our utcnow function."""
|
||||||
return time.mktime(utcnow().timetuple())
|
return time.mktime(utcnow().timetuple())
|
||||||
@@ -531,17 +540,76 @@ def loads(s):
|
|||||||
return json.loads(s)
|
return json.loads(s)
|
||||||
|
|
||||||
|
|
||||||
def synchronized(name):
|
_semaphores = {}
|
||||||
|
|
||||||
|
|
||||||
|
class _NoopContextManager(object):
|
||||||
|
def __enter__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def synchronized(name, external=False):
|
||||||
|
"""Synchronization decorator
|
||||||
|
|
||||||
|
Decorating a method like so:
|
||||||
|
@synchronized('mylock')
|
||||||
|
def foo(self, *args):
|
||||||
|
...
|
||||||
|
|
||||||
|
ensures that only one thread will execute the bar method at a time.
|
||||||
|
|
||||||
|
Different methods can share the same lock:
|
||||||
|
@synchronized('mylock')
|
||||||
|
def foo(self, *args):
|
||||||
|
...
|
||||||
|
|
||||||
|
@synchronized('mylock')
|
||||||
|
def bar(self, *args):
|
||||||
|
...
|
||||||
|
|
||||||
|
This way only one of either foo or bar can be executing at a time.
|
||||||
|
|
||||||
|
The external keyword argument denotes whether this lock should work across
|
||||||
|
multiple processes. This means that if two different workers both run a
|
||||||
|
a method decorated with @synchronized('mylock', external=True), only one
|
||||||
|
of them will execute at a time.
|
||||||
|
"""
|
||||||
|
|
||||||
def wrap(f):
|
def wrap(f):
|
||||||
@functools.wraps(f)
|
@functools.wraps(f)
|
||||||
def inner(*args, **kwargs):
|
def inner(*args, **kwargs):
|
||||||
LOG.debug(_("Attempting to grab %(lock)s for method "
|
# NOTE(soren): If we ever go natively threaded, this will be racy.
|
||||||
"%(method)s..." % {"lock": name,
|
# See http://stackoverflow.com/questions/5390569/dyn\
|
||||||
|
# amically-allocating-and-destroying-mutexes
|
||||||
|
if name not in _semaphores:
|
||||||
|
_semaphores[name] = semaphore.Semaphore()
|
||||||
|
sem = _semaphores[name]
|
||||||
|
LOG.debug(_('Attempting to grab semaphore "%(lock)s" for method '
|
||||||
|
'"%(method)s"...' % {"lock": name,
|
||||||
"method": f.__name__}))
|
"method": f.__name__}))
|
||||||
lock = lockfile.FileLock(os.path.join(FLAGS.lock_path,
|
with sem:
|
||||||
'nova-%s.lock' % name))
|
if external:
|
||||||
with lock:
|
LOG.debug(_('Attempting to grab file lock "%(lock)s" for '
|
||||||
return f(*args, **kwargs)
|
'method "%(method)s"...' %
|
||||||
|
{"lock": name, "method": f.__name__}))
|
||||||
|
lock_file_path = os.path.join(FLAGS.lock_path,
|
||||||
|
'nova-%s.lock' % name)
|
||||||
|
lock = lockfile.FileLock(lock_file_path)
|
||||||
|
else:
|
||||||
|
lock = _NoopContextManager()
|
||||||
|
|
||||||
|
with lock:
|
||||||
|
retval = f(*args, **kwargs)
|
||||||
|
|
||||||
|
# If no-one else is waiting for it, delete it.
|
||||||
|
# See note about possible raciness above.
|
||||||
|
if not sem.balance < 1:
|
||||||
|
del _semaphores[name]
|
||||||
|
|
||||||
|
return retval
|
||||||
return inner
|
return inner
|
||||||
return wrap
|
return wrap
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user