Merged with trunk
Fixed testing instrastructure: - stubbed out LoopingCall.start - unstubbed db.instance_create - now using fake context in spawn tests - moved fake xenstore ops into fake driver to avoid code duplication Fixed pep8 errors
This commit is contained in:
@@ -356,3 +356,7 @@ DEFINE_string('host', socket.gethostname(),
|
|||||||
|
|
||||||
DEFINE_string('node_availability_zone', 'nova',
|
DEFINE_string('node_availability_zone', 'nova',
|
||||||
'availability zone of this node')
|
'availability zone of this node')
|
||||||
|
|
||||||
|
DEFINE_string('zone_name', 'nova', 'name of this zone')
|
||||||
|
DEFINE_string('zone_capabilities', 'kypervisor:xenserver;os:linux',
|
||||||
|
'Key/Value tags which represent capabilities of this zone')
|
||||||
|
49
nova/scheduler/api.py
Normal file
49
nova/scheduler/api.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Copyright (c) 2011 Openstack, LLC.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Handles all requests relating to schedulers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nova import flags
|
||||||
|
from nova import log as logging
|
||||||
|
from nova import rpc
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
LOG = logging.getLogger('nova.scheduler.api')
|
||||||
|
|
||||||
|
|
||||||
|
class API(object):
|
||||||
|
"""API for interacting with the scheduler."""
|
||||||
|
|
||||||
|
def _call_scheduler(self, method, context, params=None):
|
||||||
|
"""Generic handler for RPC calls to the scheduler.
|
||||||
|
|
||||||
|
:param params: Optional dictionary of arguments to be passed to the
|
||||||
|
scheduler worker
|
||||||
|
|
||||||
|
:retval: Result returned by scheduler worker
|
||||||
|
"""
|
||||||
|
if not params:
|
||||||
|
params = {}
|
||||||
|
queue = FLAGS.scheduler_topic
|
||||||
|
kwargs = {'method': method, 'args': params}
|
||||||
|
return rpc.call(context, queue, kwargs)
|
||||||
|
|
||||||
|
def get_zone_list(self, context):
|
||||||
|
items = self._call_scheduler('get_zone_list', context)
|
||||||
|
for item in items:
|
||||||
|
item['api_url'] = item['api_url'].replace('\\/', '/')
|
||||||
|
return items
|
143
nova/scheduler/zone_manager.py
Normal file
143
nova/scheduler/zone_manager.py
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# Copyright (c) 2011 Openstack, LLC.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
ZoneManager oversees all communications with child Zones.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import novaclient
|
||||||
|
import thread
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from eventlet import greenpool
|
||||||
|
|
||||||
|
from nova import db
|
||||||
|
from nova import flags
|
||||||
|
from nova import log as logging
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
flags.DEFINE_integer('zone_db_check_interval', 60,
|
||||||
|
'Seconds between getting fresh zone info from db.')
|
||||||
|
flags.DEFINE_integer('zone_failures_to_offline', 3,
|
||||||
|
'Number of consecutive errors before marking zone offline')
|
||||||
|
|
||||||
|
|
||||||
|
class ZoneState(object):
|
||||||
|
"""Holds the state of all connected child zones."""
|
||||||
|
def __init__(self):
|
||||||
|
self.is_active = True
|
||||||
|
self.name = None
|
||||||
|
self.capabilities = None
|
||||||
|
self.attempt = 0
|
||||||
|
self.last_seen = datetime.min
|
||||||
|
self.last_exception = None
|
||||||
|
self.last_exception_time = None
|
||||||
|
|
||||||
|
def update_credentials(self, zone):
|
||||||
|
"""Update zone credentials from db"""
|
||||||
|
self.zone_id = zone.id
|
||||||
|
self.api_url = zone.api_url
|
||||||
|
self.username = zone.username
|
||||||
|
self.password = zone.password
|
||||||
|
|
||||||
|
def update_metadata(self, zone_metadata):
|
||||||
|
"""Update zone metadata after successful communications with
|
||||||
|
child zone."""
|
||||||
|
self.last_seen = datetime.now()
|
||||||
|
self.attempt = 0
|
||||||
|
self.name = zone_metadata["name"]
|
||||||
|
self.capabilities = zone_metadata["capabilities"]
|
||||||
|
self.is_active = True
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return dict(name=self.name, capabilities=self.capabilities,
|
||||||
|
is_active=self.is_active, api_url=self.api_url,
|
||||||
|
id=self.zone_id)
|
||||||
|
|
||||||
|
def log_error(self, exception):
|
||||||
|
"""Something went wrong. Check to see if zone should be
|
||||||
|
marked as offline."""
|
||||||
|
self.last_exception = exception
|
||||||
|
self.last_exception_time = datetime.now()
|
||||||
|
api_url = self.api_url
|
||||||
|
logging.warning(_("'%(exception)s' error talking to "
|
||||||
|
"zone %(api_url)s") % locals())
|
||||||
|
|
||||||
|
max_errors = FLAGS.zone_failures_to_offline
|
||||||
|
self.attempt += 1
|
||||||
|
if self.attempt >= max_errors:
|
||||||
|
self.is_active = False
|
||||||
|
logging.error(_("No answer from zone %(api_url)s "
|
||||||
|
"after %(max_errors)d "
|
||||||
|
"attempts. Marking inactive.") % locals())
|
||||||
|
|
||||||
|
|
||||||
|
def _call_novaclient(zone):
|
||||||
|
"""Call novaclient. Broken out for testing purposes."""
|
||||||
|
client = novaclient.OpenStack(zone.username, zone.password, zone.api_url)
|
||||||
|
return client.zones.info()._info
|
||||||
|
|
||||||
|
|
||||||
|
def _poll_zone(zone):
|
||||||
|
"""Eventlet worker to poll a zone."""
|
||||||
|
logging.debug(_("Polling zone: %s") % zone.api_url)
|
||||||
|
try:
|
||||||
|
zone.update_metadata(_call_novaclient(zone))
|
||||||
|
except Exception, e:
|
||||||
|
zone.log_error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
class ZoneManager(object):
|
||||||
|
"""Keeps the zone states updated."""
|
||||||
|
def __init__(self):
|
||||||
|
self.last_zone_db_check = datetime.min
|
||||||
|
self.zone_states = {}
|
||||||
|
self.green_pool = greenpool.GreenPool()
|
||||||
|
|
||||||
|
def get_zone_list(self):
|
||||||
|
"""Return the list of zones we know about."""
|
||||||
|
return [zone.to_dict() for zone in self.zone_states.values()]
|
||||||
|
|
||||||
|
def _refresh_from_db(self, context):
|
||||||
|
"""Make our zone state map match the db."""
|
||||||
|
# Add/update existing zones ...
|
||||||
|
zones = db.zone_get_all(context)
|
||||||
|
existing = self.zone_states.keys()
|
||||||
|
db_keys = []
|
||||||
|
for zone in zones:
|
||||||
|
db_keys.append(zone.id)
|
||||||
|
if zone.id not in existing:
|
||||||
|
self.zone_states[zone.id] = ZoneState()
|
||||||
|
self.zone_states[zone.id].update_credentials(zone)
|
||||||
|
|
||||||
|
# Cleanup zones removed from db ...
|
||||||
|
keys = self.zone_states.keys() # since we're deleting
|
||||||
|
for zone_id in keys:
|
||||||
|
if zone_id not in db_keys:
|
||||||
|
del self.zone_states[zone_id]
|
||||||
|
|
||||||
|
def _poll_zones(self, context):
|
||||||
|
"""Try to connect to each child zone and get update."""
|
||||||
|
self.green_pool.imap(_poll_zone, self.zone_states.values())
|
||||||
|
|
||||||
|
def ping(self, context=None):
|
||||||
|
"""Ping should be called periodically to update zone status."""
|
||||||
|
diff = datetime.now() - self.last_zone_db_check
|
||||||
|
if diff.seconds >= FLAGS.zone_db_check_interval:
|
||||||
|
logging.debug(_("Updating zone cache from db."))
|
||||||
|
self.last_zone_db_check = datetime.now()
|
||||||
|
self._refresh_from_db(context)
|
||||||
|
self._poll_zones(context)
|
@@ -29,11 +29,153 @@ from nova import log as logging
|
|||||||
from nova import test
|
from nova import test
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.auth import manager
|
from nova.auth import manager
|
||||||
|
from nova.network import linux_net
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
LOG = logging.getLogger('nova.tests.network')
|
LOG = logging.getLogger('nova.tests.network')
|
||||||
|
|
||||||
|
|
||||||
|
class IptablesManagerTestCase(test.TestCase):
|
||||||
|
sample_filter = ['#Generated by iptables-save on Fri Feb 18 15:17:05 2011',
|
||||||
|
'*filter',
|
||||||
|
':INPUT ACCEPT [2223527:305688874]',
|
||||||
|
':FORWARD ACCEPT [0:0]',
|
||||||
|
':OUTPUT ACCEPT [2172501:140856656]',
|
||||||
|
':nova-compute-FORWARD - [0:0]',
|
||||||
|
':nova-compute-INPUT - [0:0]',
|
||||||
|
':nova-compute-local - [0:0]',
|
||||||
|
':nova-compute-OUTPUT - [0:0]',
|
||||||
|
':nova-filter-top - [0:0]',
|
||||||
|
'-A FORWARD -j nova-filter-top ',
|
||||||
|
'-A OUTPUT -j nova-filter-top ',
|
||||||
|
'-A nova-filter-top -j nova-compute-local ',
|
||||||
|
'-A INPUT -j nova-compute-INPUT ',
|
||||||
|
'-A OUTPUT -j nova-compute-OUTPUT ',
|
||||||
|
'-A FORWARD -j nova-compute-FORWARD ',
|
||||||
|
'-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT ',
|
||||||
|
'-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT ',
|
||||||
|
'-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT ',
|
||||||
|
'-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT ',
|
||||||
|
'-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT ',
|
||||||
|
'-A FORWARD -i virbr0 -o virbr0 -j ACCEPT ',
|
||||||
|
'-A FORWARD -o virbr0 -j REJECT --reject-with '
|
||||||
|
'icmp-port-unreachable ',
|
||||||
|
'-A FORWARD -i virbr0 -j REJECT --reject-with '
|
||||||
|
'icmp-port-unreachable ',
|
||||||
|
'COMMIT',
|
||||||
|
'# Completed on Fri Feb 18 15:17:05 2011']
|
||||||
|
|
||||||
|
sample_nat = ['# Generated by iptables-save on Fri Feb 18 15:17:05 2011',
|
||||||
|
'*nat',
|
||||||
|
':PREROUTING ACCEPT [3936:762355]',
|
||||||
|
':INPUT ACCEPT [2447:225266]',
|
||||||
|
':OUTPUT ACCEPT [63491:4191863]',
|
||||||
|
':POSTROUTING ACCEPT [63112:4108641]',
|
||||||
|
':nova-compute-OUTPUT - [0:0]',
|
||||||
|
':nova-compute-floating-ip-snat - [0:0]',
|
||||||
|
':nova-compute-SNATTING - [0:0]',
|
||||||
|
':nova-compute-PREROUTING - [0:0]',
|
||||||
|
':nova-compute-POSTROUTING - [0:0]',
|
||||||
|
':nova-postrouting-bottom - [0:0]',
|
||||||
|
'-A PREROUTING -j nova-compute-PREROUTING ',
|
||||||
|
'-A OUTPUT -j nova-compute-OUTPUT ',
|
||||||
|
'-A POSTROUTING -j nova-compute-POSTROUTING ',
|
||||||
|
'-A POSTROUTING -j nova-postrouting-bottom ',
|
||||||
|
'-A nova-postrouting-bottom -j nova-compute-SNATTING ',
|
||||||
|
'-A nova-compute-SNATTING -j nova-compute-floating-ip-snat ',
|
||||||
|
'COMMIT',
|
||||||
|
'# Completed on Fri Feb 18 15:17:05 2011']
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(IptablesManagerTestCase, self).setUp()
|
||||||
|
self.manager = linux_net.IptablesManager()
|
||||||
|
|
||||||
|
def test_filter_rules_are_wrapped(self):
|
||||||
|
current_lines = self.sample_filter
|
||||||
|
|
||||||
|
table = self.manager.ipv4['filter']
|
||||||
|
table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
||||||
|
new_lines = self.manager._modify_rules(current_lines, table)
|
||||||
|
self.assertTrue('-A run_tests.py-FORWARD '
|
||||||
|
'-s 1.2.3.4/5 -j DROP' in new_lines)
|
||||||
|
|
||||||
|
table.remove_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
||||||
|
new_lines = self.manager._modify_rules(current_lines, table)
|
||||||
|
self.assertTrue('-A run_tests.py-FORWARD '
|
||||||
|
'-s 1.2.3.4/5 -j DROP' not in new_lines)
|
||||||
|
|
||||||
|
def test_nat_rules(self):
|
||||||
|
current_lines = self.sample_nat
|
||||||
|
new_lines = self.manager._modify_rules(current_lines,
|
||||||
|
self.manager.ipv4['nat'])
|
||||||
|
|
||||||
|
for line in [':nova-compute-OUTPUT - [0:0]',
|
||||||
|
':nova-compute-floating-ip-snat - [0:0]',
|
||||||
|
':nova-compute-SNATTING - [0:0]',
|
||||||
|
':nova-compute-PREROUTING - [0:0]',
|
||||||
|
':nova-compute-POSTROUTING - [0:0]']:
|
||||||
|
self.assertTrue(line in new_lines, "One of nova-compute's chains "
|
||||||
|
"went missing.")
|
||||||
|
|
||||||
|
seen_lines = set()
|
||||||
|
for line in new_lines:
|
||||||
|
line = line.strip()
|
||||||
|
self.assertTrue(line not in seen_lines,
|
||||||
|
"Duplicate line: %s" % line)
|
||||||
|
seen_lines.add(line)
|
||||||
|
|
||||||
|
last_postrouting_line = ''
|
||||||
|
|
||||||
|
for line in new_lines:
|
||||||
|
if line.startswith('-A POSTROUTING'):
|
||||||
|
last_postrouting_line = line
|
||||||
|
|
||||||
|
self.assertTrue('-j nova-postrouting-bottom' in last_postrouting_line,
|
||||||
|
"Last POSTROUTING rule does not jump to "
|
||||||
|
"nova-postouting-bottom: %s" % last_postrouting_line)
|
||||||
|
|
||||||
|
for chain in ['POSTROUTING', 'PREROUTING', 'OUTPUT']:
|
||||||
|
self.assertTrue('-A %s -j run_tests.py-%s' \
|
||||||
|
% (chain, chain) in new_lines,
|
||||||
|
"Built-in chain %s not wrapped" % (chain,))
|
||||||
|
|
||||||
|
def test_filter_rules(self):
|
||||||
|
current_lines = self.sample_filter
|
||||||
|
new_lines = self.manager._modify_rules(current_lines,
|
||||||
|
self.manager.ipv4['filter'])
|
||||||
|
|
||||||
|
for line in [':nova-compute-FORWARD - [0:0]',
|
||||||
|
':nova-compute-INPUT - [0:0]',
|
||||||
|
':nova-compute-local - [0:0]',
|
||||||
|
':nova-compute-OUTPUT - [0:0]']:
|
||||||
|
self.assertTrue(line in new_lines, "One of nova-compute's chains"
|
||||||
|
" went missing.")
|
||||||
|
|
||||||
|
seen_lines = set()
|
||||||
|
for line in new_lines:
|
||||||
|
line = line.strip()
|
||||||
|
self.assertTrue(line not in seen_lines,
|
||||||
|
"Duplicate line: %s" % line)
|
||||||
|
seen_lines.add(line)
|
||||||
|
|
||||||
|
for chain in ['FORWARD', 'OUTPUT']:
|
||||||
|
for line in new_lines:
|
||||||
|
if line.startswith('-A %s' % chain):
|
||||||
|
self.assertTrue('-j nova-filter-top' in line,
|
||||||
|
"First %s rule does not "
|
||||||
|
"jump to nova-filter-top" % chain)
|
||||||
|
break
|
||||||
|
|
||||||
|
self.assertTrue('-A nova-filter-top '
|
||||||
|
'-j run_tests.py-local' in new_lines,
|
||||||
|
"nova-filter-top does not jump to wrapped local chain")
|
||||||
|
|
||||||
|
for chain in ['INPUT', 'OUTPUT', 'FORWARD']:
|
||||||
|
self.assertTrue('-A %s -j run_tests.py-%s' \
|
||||||
|
% (chain, chain) in new_lines,
|
||||||
|
"Built-in chain %s not wrapped" % (chain,))
|
||||||
|
|
||||||
|
|
||||||
class NetworkTestCase(test.TestCase):
|
class NetworkTestCase(test.TestCase):
|
||||||
"""Test cases for network code"""
|
"""Test cases for network code"""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import re
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import eventlet
|
import eventlet
|
||||||
@@ -301,16 +302,22 @@ class IptablesFirewallTestCase(test.TestCase):
|
|||||||
self.manager.delete_user(self.user)
|
self.manager.delete_user(self.user)
|
||||||
super(IptablesFirewallTestCase, self).tearDown()
|
super(IptablesFirewallTestCase, self).tearDown()
|
||||||
|
|
||||||
in_rules = [
|
in_nat_rules = [
|
||||||
|
'# Generated by iptables-save v1.4.10 on Sat Feb 19 00:03:19 2011',
|
||||||
|
'*nat',
|
||||||
|
':PREROUTING ACCEPT [1170:189210]',
|
||||||
|
':INPUT ACCEPT [844:71028]',
|
||||||
|
':OUTPUT ACCEPT [5149:405186]',
|
||||||
|
':POSTROUTING ACCEPT [5063:386098]',
|
||||||
|
]
|
||||||
|
|
||||||
|
in_filter_rules = [
|
||||||
'# Generated by iptables-save v1.4.4 on Mon Dec 6 11:54:13 2010',
|
'# Generated by iptables-save v1.4.4 on Mon Dec 6 11:54:13 2010',
|
||||||
'*filter',
|
'*filter',
|
||||||
':INPUT ACCEPT [969615:281627771]',
|
':INPUT ACCEPT [969615:281627771]',
|
||||||
':FORWARD ACCEPT [0:0]',
|
':FORWARD ACCEPT [0:0]',
|
||||||
':OUTPUT ACCEPT [915599:63811649]',
|
':OUTPUT ACCEPT [915599:63811649]',
|
||||||
':nova-block-ipv4 - [0:0]',
|
':nova-block-ipv4 - [0:0]',
|
||||||
'-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT ',
|
|
||||||
'-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT ',
|
|
||||||
'-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT ',
|
|
||||||
'-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT ',
|
'-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT ',
|
||||||
'-A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED'
|
'-A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED'
|
||||||
',ESTABLISHED -j ACCEPT ',
|
',ESTABLISHED -j ACCEPT ',
|
||||||
@@ -322,7 +329,7 @@ class IptablesFirewallTestCase(test.TestCase):
|
|||||||
'# Completed on Mon Dec 6 11:54:13 2010',
|
'# Completed on Mon Dec 6 11:54:13 2010',
|
||||||
]
|
]
|
||||||
|
|
||||||
in6_rules = [
|
in6_filter_rules = [
|
||||||
'# Generated by ip6tables-save v1.4.4 on Tue Jan 18 23:47:56 2011',
|
'# Generated by ip6tables-save v1.4.4 on Tue Jan 18 23:47:56 2011',
|
||||||
'*filter',
|
'*filter',
|
||||||
':INPUT ACCEPT [349155:75810423]',
|
':INPUT ACCEPT [349155:75810423]',
|
||||||
@@ -385,21 +392,31 @@ class IptablesFirewallTestCase(test.TestCase):
|
|||||||
def fake_iptables_execute(*cmd, **kwargs):
|
def fake_iptables_execute(*cmd, **kwargs):
|
||||||
process_input = kwargs.get('process_input', None)
|
process_input = kwargs.get('process_input', None)
|
||||||
if cmd == ('sudo', 'ip6tables-save', '-t', 'filter'):
|
if cmd == ('sudo', 'ip6tables-save', '-t', 'filter'):
|
||||||
return '\n'.join(self.in6_rules), None
|
return '\n'.join(self.in6_filter_rules), None
|
||||||
if cmd == ('sudo', 'iptables-save', '-t', 'filter'):
|
if cmd == ('sudo', 'iptables-save', '-t', 'filter'):
|
||||||
return '\n'.join(self.in_rules), None
|
return '\n'.join(self.in_filter_rules), None
|
||||||
|
if cmd == ('sudo', 'iptables-save', '-t', 'nat'):
|
||||||
|
return '\n'.join(self.in_nat_rules), None
|
||||||
if cmd == ('sudo', 'iptables-restore'):
|
if cmd == ('sudo', 'iptables-restore'):
|
||||||
self.out_rules = process_input.split('\n')
|
lines = process_input.split('\n')
|
||||||
|
if '*filter' in lines:
|
||||||
|
self.out_rules = lines
|
||||||
return '', ''
|
return '', ''
|
||||||
if cmd == ('sudo', 'ip6tables-restore'):
|
if cmd == ('sudo', 'ip6tables-restore'):
|
||||||
self.out6_rules = process_input.split('\n')
|
lines = process_input.split('\n')
|
||||||
|
if '*filter' in lines:
|
||||||
|
self.out6_rules = lines
|
||||||
return '', ''
|
return '', ''
|
||||||
self.fw.execute = fake_iptables_execute
|
print cmd, kwargs
|
||||||
|
|
||||||
|
from nova.network import linux_net
|
||||||
|
linux_net.iptables_manager.execute = fake_iptables_execute
|
||||||
|
|
||||||
self.fw.prepare_instance_filter(instance_ref)
|
self.fw.prepare_instance_filter(instance_ref)
|
||||||
self.fw.apply_instance_filter(instance_ref)
|
self.fw.apply_instance_filter(instance_ref)
|
||||||
|
|
||||||
in_rules = filter(lambda l: not l.startswith('#'), self.in_rules)
|
in_rules = filter(lambda l: not l.startswith('#'),
|
||||||
|
self.in_filter_rules)
|
||||||
for rule in in_rules:
|
for rule in in_rules:
|
||||||
if not 'nova' in rule:
|
if not 'nova' in rule:
|
||||||
self.assertTrue(rule in self.out_rules,
|
self.assertTrue(rule in self.out_rules,
|
||||||
@@ -422,17 +439,18 @@ class IptablesFirewallTestCase(test.TestCase):
|
|||||||
self.assertTrue(security_group_chain,
|
self.assertTrue(security_group_chain,
|
||||||
"The security group chain wasn't added")
|
"The security group chain wasn't added")
|
||||||
|
|
||||||
self.assertTrue('-A %s -p icmp -s 192.168.11.0/24 -j ACCEPT' % \
|
regex = re.compile('-A .* -p icmp -s 192.168.11.0/24 -j ACCEPT')
|
||||||
security_group_chain in self.out_rules,
|
self.assertTrue(len(filter(regex.match, self.out_rules)) > 0,
|
||||||
"ICMP acceptance rule wasn't added")
|
"ICMP acceptance rule wasn't added")
|
||||||
|
|
||||||
self.assertTrue('-A %s -p icmp -s 192.168.11.0/24 -m icmp --icmp-type '
|
regex = re.compile('-A .* -p icmp -s 192.168.11.0/24 -m icmp '
|
||||||
'8 -j ACCEPT' % security_group_chain in self.out_rules,
|
'--icmp-type 8 -j ACCEPT')
|
||||||
|
self.assertTrue(len(filter(regex.match, self.out_rules)) > 0,
|
||||||
"ICMP Echo Request acceptance rule wasn't added")
|
"ICMP Echo Request acceptance rule wasn't added")
|
||||||
|
|
||||||
self.assertTrue('-A %s -p tcp -s 192.168.10.0/24 -m multiport '
|
regex = re.compile('-A .* -p tcp -s 192.168.10.0/24 -m multiport '
|
||||||
'--dports 80:81 -j ACCEPT' % security_group_chain \
|
'--dports 80:81 -j ACCEPT')
|
||||||
in self.out_rules,
|
self.assertTrue(len(filter(regex.match, self.out_rules)) > 0,
|
||||||
"TCP port 80/81 acceptance rule wasn't added")
|
"TCP port 80/81 acceptance rule wasn't added")
|
||||||
db.instance_destroy(admin_ctxt, instance_ref['id'])
|
db.instance_destroy(admin_ctxt, instance_ref['id'])
|
||||||
|
|
||||||
|
@@ -55,6 +55,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(XenAPIVolumeTestCase, self).setUp()
|
super(XenAPIVolumeTestCase, self).setUp()
|
||||||
self.stubs = stubout.StubOutForTesting()
|
self.stubs = stubout.StubOutForTesting()
|
||||||
|
self.context = context.RequestContext('fake', 'fake', False)
|
||||||
FLAGS.target_host = '127.0.0.1'
|
FLAGS.target_host = '127.0.0.1'
|
||||||
FLAGS.xenapi_connection_url = 'test_url'
|
FLAGS.xenapi_connection_url = 'test_url'
|
||||||
FLAGS.xenapi_connection_password = 'test_pass'
|
FLAGS.xenapi_connection_password = 'test_pass'
|
||||||
@@ -62,7 +63,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
|||||||
#db_fakes.stub_out_db_network_api(self.stubs)
|
#db_fakes.stub_out_db_network_api(self.stubs)
|
||||||
stubs.stub_out_get_target(self.stubs)
|
stubs.stub_out_get_target(self.stubs)
|
||||||
xenapi_fake.reset()
|
xenapi_fake.reset()
|
||||||
self.values = {'name': 1, 'id': 1,
|
self.values = {'id': 1,
|
||||||
'project_id': 'fake',
|
'project_id': 'fake',
|
||||||
'user_id': 'fake',
|
'user_id': 'fake',
|
||||||
'image_id': 1,
|
'image_id': 1,
|
||||||
@@ -82,7 +83,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
|||||||
vol['availability_zone'] = FLAGS.storage_availability_zone
|
vol['availability_zone'] = FLAGS.storage_availability_zone
|
||||||
vol['status'] = "creating"
|
vol['status'] = "creating"
|
||||||
vol['attach_status'] = "detached"
|
vol['attach_status'] = "detached"
|
||||||
return db.volume_create(context.get_admin_context(), vol)
|
return db.volume_create(self.context, vol)
|
||||||
|
|
||||||
def test_create_iscsi_storage(self):
|
def test_create_iscsi_storage(self):
|
||||||
""" This shows how to test helper classes' methods """
|
""" This shows how to test helper classes' methods """
|
||||||
@@ -118,7 +119,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
|||||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests)
|
stubs.stubout_session(self.stubs, stubs.FakeSessionForVolumeTests)
|
||||||
conn = xenapi_conn.get_connection(False)
|
conn = xenapi_conn.get_connection(False)
|
||||||
volume = self._create_volume()
|
volume = self._create_volume()
|
||||||
instance = db.instance_create(self.values)
|
instance = db.instance_create(self.context, self.values)
|
||||||
vm = xenapi_fake.create_vm(instance.name, 'Running')
|
vm = xenapi_fake.create_vm(instance.name, 'Running')
|
||||||
result = conn.attach_volume(instance.name, volume['id'], '/dev/sdc')
|
result = conn.attach_volume(instance.name, volume['id'], '/dev/sdc')
|
||||||
|
|
||||||
@@ -138,7 +139,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
|||||||
stubs.FakeSessionForVolumeFailedTests)
|
stubs.FakeSessionForVolumeFailedTests)
|
||||||
conn = xenapi_conn.get_connection(False)
|
conn = xenapi_conn.get_connection(False)
|
||||||
volume = self._create_volume()
|
volume = self._create_volume()
|
||||||
instance = db.instance_create(self.values)
|
instance = db.instance_create(self.context, self.values)
|
||||||
xenapi_fake.create_vm(instance.name, 'Running')
|
xenapi_fake.create_vm(instance.name, 'Running')
|
||||||
self.assertRaises(Exception,
|
self.assertRaises(Exception,
|
||||||
conn.attach_volume,
|
conn.attach_volume,
|
||||||
@@ -167,12 +168,12 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||||
self.network = utils.import_object(FLAGS.network_manager)
|
self.network = utils.import_object(FLAGS.network_manager)
|
||||||
self.stubs = stubout.StubOutForTesting()
|
self.stubs = stubout.StubOutForTesting()
|
||||||
FLAGS.xenapi_connection_url = 'test_url'
|
self.flags(xenapi_connection_url='test_url',
|
||||||
FLAGS.xenapi_connection_password = 'test_pass'
|
xenapi_connection_password='test_pass',
|
||||||
|
instance_name_template='%d')
|
||||||
xenapi_fake.reset()
|
xenapi_fake.reset()
|
||||||
xenapi_fake.create_local_srs()
|
xenapi_fake.create_local_srs()
|
||||||
db_fakes.stub_out_db_instance_api(self.stubs)
|
db_fakes.stub_out_db_instance_api(self.stubs)
|
||||||
#db_fakes.stub_out_db_network_api(self.stubs)
|
|
||||||
xenapi_fake.create_network('fake', FLAGS.flat_network_bridge)
|
xenapi_fake.create_network('fake', FLAGS.flat_network_bridge)
|
||||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
||||||
stubs.stubout_get_this_vm_uuid(self.stubs)
|
stubs.stubout_get_this_vm_uuid(self.stubs)
|
||||||
@@ -182,6 +183,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
glance_stubs.stubout_glance_client(self.stubs,
|
glance_stubs.stubout_glance_client(self.stubs,
|
||||||
glance_stubs.FakeGlance)
|
glance_stubs.FakeGlance)
|
||||||
fake_utils.stub_out_utils_execute(self.stubs)
|
fake_utils.stub_out_utils_execute(self.stubs)
|
||||||
|
self.context = context.RequestContext('fake', 'fake', False)
|
||||||
self.conn = xenapi_conn.get_connection(False)
|
self.conn = xenapi_conn.get_connection(False)
|
||||||
|
|
||||||
def test_list_instances_0(self):
|
def test_list_instances_0(self):
|
||||||
@@ -235,8 +237,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
|
|
||||||
# Get Nova record for VM
|
# Get Nova record for VM
|
||||||
vm_info = conn.get_info('1')
|
vm_info = conn.get_info('1')
|
||||||
|
# Get XenAPI record for VM
|
||||||
# Get XenAPI record for VM
|
|
||||||
vms = [(ref, rec) for ref, rec
|
vms = [(ref, rec) for ref, rec
|
||||||
in xenapi_fake.get_all_records('VM').iteritems()
|
in xenapi_fake.get_all_records('VM').iteritems()
|
||||||
if not rec['is_control_domain']]
|
if not rec['is_control_domain']]
|
||||||
@@ -262,7 +263,8 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
self.assertEquals(vm['power_state'], 'Running')
|
self.assertEquals(vm['power_state'], 'Running')
|
||||||
|
|
||||||
if check_injection:
|
if check_injection:
|
||||||
xenstore_data = xenapi_fake.VM_get_xenstore_data(vm_ref)
|
session = xenapi_conn.XenAPISession('fake', 'fake', 'fake')
|
||||||
|
xenstore_data = session.call_xenapi('VM.get_xenstore_data', vm_ref)
|
||||||
key = 'vm-data/networking/aabbccddeeff'
|
key = 'vm-data/networking/aabbccddeeff'
|
||||||
xenstore_value = xenstore_data[key]
|
xenstore_value = xenstore_data[key]
|
||||||
tcpip_data = ast.literal_eval(xenstore_value)
|
tcpip_data = ast.literal_eval(xenstore_value)
|
||||||
@@ -282,10 +284,8 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
|
|
||||||
def _test_spawn(self, image_id, kernel_id, ramdisk_id,
|
def _test_spawn(self, image_id, kernel_id, ramdisk_id,
|
||||||
instance_type="m1.large", check_injection=False):
|
instance_type="m1.large", check_injection=False):
|
||||||
|
stubs.stubout_loopingcall_start(self.stubs)
|
||||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
values = {'id': 1,
|
||||||
values = {'name': "1",
|
|
||||||
'id': "1",
|
|
||||||
'project_id': self.project.id,
|
'project_id': self.project.id,
|
||||||
'user_id': self.user.id,
|
'user_id': self.user.id,
|
||||||
'image_id': image_id,
|
'image_id': image_id,
|
||||||
@@ -294,10 +294,9 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
'instance_type': instance_type,
|
'instance_type': instance_type,
|
||||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||||
}
|
}
|
||||||
conn = xenapi_conn.get_connection(False)
|
instance = db.instance_create(self.context, values)
|
||||||
instance = db.instance_create(values)
|
self.conn.spawn(instance)
|
||||||
conn.spawn(instance)
|
self.check_vm_record(self.conn, check_injection=check_injection)
|
||||||
self.check_vm_record(conn, check_injection=check_injection)
|
|
||||||
|
|
||||||
def test_spawn_not_enough_memory(self):
|
def test_spawn_not_enough_memory(self):
|
||||||
FLAGS.xenapi_image_service = 'glance'
|
FLAGS.xenapi_image_service = 'glance'
|
||||||
@@ -333,14 +332,12 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
|
|
||||||
self._tee_executed = False
|
self._tee_executed = False
|
||||||
|
|
||||||
def _tee_handler(cmd, input, *ignore_args):
|
def _tee_handler(cmd, **kwargs):
|
||||||
|
input = kwargs.get('process_input', None)
|
||||||
self.assertNotEqual(input, None)
|
self.assertNotEqual(input, None)
|
||||||
|
|
||||||
config = [line.strip() for line in input.split("\n")]
|
config = [line.strip() for line in input.split("\n")]
|
||||||
|
|
||||||
# Find the start of eth0 configuration and check it
|
# Find the start of eth0 configuration and check it
|
||||||
index = config.index('auto eth0')
|
index = config.index('auto eth0')
|
||||||
|
|
||||||
self.assertEquals(config[index + 1:index + 8], [
|
self.assertEquals(config[index + 1:index + 8], [
|
||||||
'iface eth0 inet static',
|
'iface eth0 inet static',
|
||||||
'address 10.0.0.3',
|
'address 10.0.0.3',
|
||||||
@@ -349,9 +346,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
'gateway 10.0.0.1',
|
'gateway 10.0.0.1',
|
||||||
'dns-nameservers 10.0.0.2',
|
'dns-nameservers 10.0.0.2',
|
||||||
''])
|
''])
|
||||||
|
|
||||||
self._tee_executed = True
|
self._tee_executed = True
|
||||||
|
|
||||||
return '', ''
|
return '', ''
|
||||||
|
|
||||||
fake_utils.fake_execute_set_repliers([
|
fake_utils.fake_execute_set_repliers([
|
||||||
@@ -371,12 +366,14 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
|
|
||||||
self._tee_executed = False
|
self._tee_executed = False
|
||||||
|
|
||||||
def _mount_handler(cmd, *ignore_args):
|
def _mount_handler(cmd, *ignore_args, **ignore_kwargs):
|
||||||
# When mounting, create real files under the mountpoint to simulate
|
# When mounting, create real files under the mountpoint to simulate
|
||||||
# files in the mounted filesystem
|
# files in the mounted filesystem
|
||||||
|
|
||||||
# RegExp extracts the path of the mountpoint
|
# RegExp extracts the path of the mountpoint
|
||||||
match = re.match(r'(sudo\s+)?mount[^"]*"[^"]*"\s+"([^"]*)"', cmd)
|
cmd_str = ' '.join(cmd)
|
||||||
|
match = re.match(r'(sudo\s+)?mount[^"]*"[^"]*"\s+"([^"]*)"',
|
||||||
|
cmd_str)
|
||||||
self._tmpdir = match.group(2)
|
self._tmpdir = match.group(2)
|
||||||
LOG.debug(_('Creating files in %s to simulate guest agent' %
|
LOG.debug(_('Creating files in %s to simulate guest agent' %
|
||||||
self._tmpdir))
|
self._tmpdir))
|
||||||
@@ -386,7 +383,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
'xe-update-networking'), 'w').close()
|
'xe-update-networking'), 'w').close()
|
||||||
return '', ''
|
return '', ''
|
||||||
|
|
||||||
def _umount_handler(cmd, *ignore_args):
|
def _umount_handler(cmd, *ignore_args, **ignore_kwargs):
|
||||||
# Umount would normall make files in the m,ounted filesystem
|
# Umount would normall make files in the m,ounted filesystem
|
||||||
# disappear, so do that here
|
# disappear, so do that here
|
||||||
LOG.debug(_('Removing simulated guest agent files in %s' %
|
LOG.debug(_('Removing simulated guest agent files in %s' %
|
||||||
@@ -397,7 +394,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
os.rmdir(os.path.join(self._tmpdir, 'usr'))
|
os.rmdir(os.path.join(self._tmpdir, 'usr'))
|
||||||
return '', ''
|
return '', ''
|
||||||
|
|
||||||
def _tee_handler(cmd, input, *ignore_args):
|
def _tee_handler(cmd, *ignore_args, **ignore_kwargs):
|
||||||
self._tee_executed = True
|
self._tee_executed = True
|
||||||
return '', ''
|
return '', ''
|
||||||
|
|
||||||
@@ -419,9 +416,9 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
|
|
||||||
def _create_instance(self):
|
def _create_instance(self):
|
||||||
"""Creates and spawns a test instance"""
|
"""Creates and spawns a test instance"""
|
||||||
|
stubs.stubout_loopingcall_start(self.stubs)
|
||||||
values = {
|
values = {
|
||||||
'name': '1',
|
'id': 1,
|
||||||
'id': '1',
|
|
||||||
'project_id': self.project.id,
|
'project_id': self.project.id,
|
||||||
'user_id': self.user.id,
|
'user_id': self.user.id,
|
||||||
'image_id': 1,
|
'image_id': 1,
|
||||||
@@ -429,7 +426,7 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
'ramdisk_id': 3,
|
'ramdisk_id': 3,
|
||||||
'instance_type': 'm1.large',
|
'instance_type': 'm1.large',
|
||||||
'mac_address': 'aa:bb:cc:dd:ee:ff'}
|
'mac_address': 'aa:bb:cc:dd:ee:ff'}
|
||||||
instance = db.instance_create(values)
|
instance = db.instance_create(self.context, values)
|
||||||
self.conn.spawn(instance)
|
self.conn.spawn(instance)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
@@ -474,11 +471,13 @@ class XenAPIMigrateInstance(test.TestCase):
|
|||||||
db_fakes.stub_out_db_instance_api(self.stubs)
|
db_fakes.stub_out_db_instance_api(self.stubs)
|
||||||
stubs.stub_out_get_target(self.stubs)
|
stubs.stub_out_get_target(self.stubs)
|
||||||
xenapi_fake.reset()
|
xenapi_fake.reset()
|
||||||
|
xenapi_fake.create_network('fake', FLAGS.flat_network_bridge)
|
||||||
self.manager = manager.AuthManager()
|
self.manager = manager.AuthManager()
|
||||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||||
admin=True)
|
admin=True)
|
||||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||||
self.values = {'name': 1, 'id': 1,
|
self.context = context.RequestContext('fake', 'fake', False)
|
||||||
|
self.values = {'id': 1,
|
||||||
'project_id': self.project.id,
|
'project_id': self.project.id,
|
||||||
'user_id': self.user.id,
|
'user_id': self.user.id,
|
||||||
'image_id': 1,
|
'image_id': 1,
|
||||||
@@ -488,6 +487,7 @@ class XenAPIMigrateInstance(test.TestCase):
|
|||||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||||
}
|
}
|
||||||
stubs.stub_out_migration_methods(self.stubs)
|
stubs.stub_out_migration_methods(self.stubs)
|
||||||
|
stubs.stubout_get_this_vm_uuid(self.stubs)
|
||||||
glance_stubs.stubout_glance_client(self.stubs,
|
glance_stubs.stubout_glance_client(self.stubs,
|
||||||
glance_stubs.FakeGlance)
|
glance_stubs.FakeGlance)
|
||||||
|
|
||||||
@@ -498,14 +498,15 @@ class XenAPIMigrateInstance(test.TestCase):
|
|||||||
self.stubs.UnsetAll()
|
self.stubs.UnsetAll()
|
||||||
|
|
||||||
def test_migrate_disk_and_power_off(self):
|
def test_migrate_disk_and_power_off(self):
|
||||||
instance = db.instance_create(self.values)
|
instance = db.instance_create(self.context, self.values)
|
||||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
|
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
|
||||||
conn = xenapi_conn.get_connection(False)
|
conn = xenapi_conn.get_connection(False)
|
||||||
conn.migrate_disk_and_power_off(instance, '127.0.0.1')
|
conn.migrate_disk_and_power_off(instance, '127.0.0.1')
|
||||||
|
|
||||||
def test_finish_resize(self):
|
def test_finish_resize(self):
|
||||||
instance = db.instance_create(self.values)
|
instance = db.instance_create(self.context, self.values)
|
||||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
|
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
|
||||||
|
stubs.stubout_loopingcall_start(self.stubs)
|
||||||
conn = xenapi_conn.get_connection(False)
|
conn = xenapi_conn.get_connection(False)
|
||||||
conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'))
|
conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'))
|
||||||
|
|
||||||
|
172
nova/tests/test_zones.py
Normal file
172
nova/tests/test_zones.py
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
# Copyright 2010 United States Government as represented by the
|
||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
Tests For ZoneManager
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import mox
|
||||||
|
import novaclient
|
||||||
|
|
||||||
|
from nova import context
|
||||||
|
from nova import db
|
||||||
|
from nova import flags
|
||||||
|
from nova import service
|
||||||
|
from nova import test
|
||||||
|
from nova import rpc
|
||||||
|
from nova import utils
|
||||||
|
from nova.auth import manager as auth_manager
|
||||||
|
from nova.scheduler import zone_manager
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
|
||||||
|
class FakeZone:
|
||||||
|
"""Represents a fake zone from the db"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
for k, v in kwargs.iteritems():
|
||||||
|
setattr(self, k, v)
|
||||||
|
|
||||||
|
|
||||||
|
def exploding_novaclient(zone):
|
||||||
|
"""Used when we want to simulate a novaclient call failing."""
|
||||||
|
raise Exception("kaboom")
|
||||||
|
|
||||||
|
|
||||||
|
class ZoneManagerTestCase(test.TestCase):
|
||||||
|
"""Test case for zone manager"""
|
||||||
|
def test_ping(self):
|
||||||
|
zm = zone_manager.ZoneManager()
|
||||||
|
self.mox.StubOutWithMock(zm, '_refresh_from_db')
|
||||||
|
self.mox.StubOutWithMock(zm, '_poll_zones')
|
||||||
|
zm._refresh_from_db(mox.IgnoreArg())
|
||||||
|
zm._poll_zones(mox.IgnoreArg())
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
zm.ping(None)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def test_refresh_from_db_new(self):
|
||||||
|
zm = zone_manager.ZoneManager()
|
||||||
|
|
||||||
|
self.mox.StubOutWithMock(db, 'zone_get_all')
|
||||||
|
db.zone_get_all(mox.IgnoreArg()).AndReturn([
|
||||||
|
FakeZone(id=1, api_url='http://foo.com', username='user1',
|
||||||
|
password='pass1'),
|
||||||
|
])
|
||||||
|
|
||||||
|
self.assertEquals(len(zm.zone_states), 0)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
zm._refresh_from_db(None)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
self.assertEquals(len(zm.zone_states), 1)
|
||||||
|
self.assertEquals(zm.zone_states[1].username, 'user1')
|
||||||
|
|
||||||
|
def test_refresh_from_db_replace_existing(self):
|
||||||
|
zm = zone_manager.ZoneManager()
|
||||||
|
zone_state = zone_manager.ZoneState()
|
||||||
|
zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com',
|
||||||
|
username='user1', password='pass1'))
|
||||||
|
zm.zone_states[1] = zone_state
|
||||||
|
|
||||||
|
self.mox.StubOutWithMock(db, 'zone_get_all')
|
||||||
|
db.zone_get_all(mox.IgnoreArg()).AndReturn([
|
||||||
|
FakeZone(id=1, api_url='http://foo.com', username='user2',
|
||||||
|
password='pass2'),
|
||||||
|
])
|
||||||
|
|
||||||
|
self.assertEquals(len(zm.zone_states), 1)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
zm._refresh_from_db(None)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
self.assertEquals(len(zm.zone_states), 1)
|
||||||
|
self.assertEquals(zm.zone_states[1].username, 'user2')
|
||||||
|
|
||||||
|
def test_refresh_from_db_missing(self):
|
||||||
|
zm = zone_manager.ZoneManager()
|
||||||
|
zone_state = zone_manager.ZoneState()
|
||||||
|
zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com',
|
||||||
|
username='user1', password='pass1'))
|
||||||
|
zm.zone_states[1] = zone_state
|
||||||
|
|
||||||
|
self.mox.StubOutWithMock(db, 'zone_get_all')
|
||||||
|
db.zone_get_all(mox.IgnoreArg()).AndReturn([])
|
||||||
|
|
||||||
|
self.assertEquals(len(zm.zone_states), 1)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
zm._refresh_from_db(None)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
self.assertEquals(len(zm.zone_states), 0)
|
||||||
|
|
||||||
|
def test_refresh_from_db_add_and_delete(self):
|
||||||
|
zm = zone_manager.ZoneManager()
|
||||||
|
zone_state = zone_manager.ZoneState()
|
||||||
|
zone_state.update_credentials(FakeZone(id=1, api_url='http://foo.com',
|
||||||
|
username='user1', password='pass1'))
|
||||||
|
zm.zone_states[1] = zone_state
|
||||||
|
|
||||||
|
self.mox.StubOutWithMock(db, 'zone_get_all')
|
||||||
|
|
||||||
|
db.zone_get_all(mox.IgnoreArg()).AndReturn([
|
||||||
|
FakeZone(id=2, api_url='http://foo.com', username='user2',
|
||||||
|
password='pass2'),
|
||||||
|
])
|
||||||
|
self.assertEquals(len(zm.zone_states), 1)
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
zm._refresh_from_db(None)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
self.assertEquals(len(zm.zone_states), 1)
|
||||||
|
self.assertEquals(zm.zone_states[2].username, 'user2')
|
||||||
|
|
||||||
|
def test_poll_zone(self):
|
||||||
|
self.mox.StubOutWithMock(zone_manager, '_call_novaclient')
|
||||||
|
zone_manager._call_novaclient(mox.IgnoreArg()).AndReturn(
|
||||||
|
dict(name='zohan', capabilities='hairdresser'))
|
||||||
|
|
||||||
|
zone_state = zone_manager.ZoneState()
|
||||||
|
zone_state.update_credentials(FakeZone(id=2,
|
||||||
|
api_url='http://foo.com', username='user2',
|
||||||
|
password='pass2'))
|
||||||
|
zone_state.attempt = 1
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
zone_manager._poll_zone(zone_state)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
self.assertEquals(zone_state.attempt, 0)
|
||||||
|
self.assertEquals(zone_state.name, 'zohan')
|
||||||
|
|
||||||
|
def test_poll_zone_fails(self):
|
||||||
|
self.stubs.Set(zone_manager, "_call_novaclient", exploding_novaclient)
|
||||||
|
|
||||||
|
zone_state = zone_manager.ZoneState()
|
||||||
|
zone_state.update_credentials(FakeZone(id=2,
|
||||||
|
api_url='http://foo.com', username='user2',
|
||||||
|
password='pass2'))
|
||||||
|
zone_state.attempt = FLAGS.zone_failures_to_offline - 1
|
||||||
|
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
zone_manager._poll_zone(zone_state)
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
self.assertEquals(zone_state.attempt, 3)
|
||||||
|
self.assertFalse(zone_state.is_active)
|
||||||
|
self.assertEquals(zone_state.name, None)
|
@@ -21,6 +21,7 @@ from nova.virt.xenapi import fake
|
|||||||
from nova.virt.xenapi import volume_utils
|
from nova.virt.xenapi import volume_utils
|
||||||
from nova.virt.xenapi import vm_utils
|
from nova.virt.xenapi import vm_utils
|
||||||
from nova.virt.xenapi import vmops
|
from nova.virt.xenapi import vmops
|
||||||
|
from nova import utils
|
||||||
|
|
||||||
|
|
||||||
def stubout_instance_snapshot(stubs):
|
def stubout_instance_snapshot(stubs):
|
||||||
@@ -137,14 +138,17 @@ def stubout_is_vdi_pv(stubs):
|
|||||||
stubs.Set(vm_utils, '_is_vdi_pv', f)
|
stubs.Set(vm_utils, '_is_vdi_pv', f)
|
||||||
|
|
||||||
|
|
||||||
|
def stubout_loopingcall_start(stubs):
|
||||||
|
def f_1(self, interval, now=True):
|
||||||
|
self.f(*self.args, **self.kw)
|
||||||
|
stubs.Set(utils.LoopingCall, 'start', f_1)
|
||||||
|
|
||||||
|
|
||||||
class FakeSessionForVMTests(fake.SessionBase):
|
class FakeSessionForVMTests(fake.SessionBase):
|
||||||
""" Stubs out a XenAPISession for VM tests """
|
""" Stubs out a XenAPISession for VM tests """
|
||||||
def __init__(self, uri):
|
def __init__(self, uri):
|
||||||
super(FakeSessionForVMTests, self).__init__(uri)
|
super(FakeSessionForVMTests, self).__init__(uri)
|
||||||
|
|
||||||
def network_get_all_records_where(self, _1, _2):
|
|
||||||
return self.xenapi.network.get_all_records()
|
|
||||||
|
|
||||||
def host_call_plugin(self, _1, _2, _3, _4, _5):
|
def host_call_plugin(self, _1, _2, _3, _4, _5):
|
||||||
sr_ref = fake.get_all('SR')[0]
|
sr_ref = fake.get_all('SR')[0]
|
||||||
vdi_ref = fake.create_vdi('', False, sr_ref, False)
|
vdi_ref = fake.create_vdi('', False, sr_ref, False)
|
||||||
@@ -178,12 +182,6 @@ class FakeSessionForVMTests(fake.SessionBase):
|
|||||||
def VM_destroy(self, session_ref, vm_ref):
|
def VM_destroy(self, session_ref, vm_ref):
|
||||||
fake.destroy_vm(vm_ref)
|
fake.destroy_vm(vm_ref)
|
||||||
|
|
||||||
def VM_add_to_xenstore_data(self, session_ref, vm_ref, key, value):
|
|
||||||
fake.VM_add_to_xenstore_data(vm_ref, key, value)
|
|
||||||
|
|
||||||
def VM_remove_from_xenstore_data(self, session_ref, vm_ref, key):
|
|
||||||
fake.VM_remove_from_xenstore_data(vm_ref, key)
|
|
||||||
|
|
||||||
def SR_scan(self, session_ref, sr_ref):
|
def SR_scan(self, session_ref, sr_ref):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user