Added unit tests
This commit is contained in:
parent
753896ba44
commit
c72636d3c1
@ -1,15 +1,39 @@
|
|||||||
# vim: set ts=4:et
|
# vim: set ts=4:et
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
import socket
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
config,
|
config,
|
||||||
relation_ids,
|
relation_ids,
|
||||||
related_units,
|
related_units,
|
||||||
relation_get,
|
relation_get,
|
||||||
|
unit_get,
|
||||||
|
cached,
|
||||||
|
)
|
||||||
|
from charmhelpers.core.host import (
|
||||||
|
apt_install,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.openstack.context import (
|
from charmhelpers.contrib.openstack.context import (
|
||||||
OSContextGenerator,
|
OSContextGenerator,
|
||||||
context_complete
|
context_complete
|
||||||
)
|
)
|
||||||
import quantum_utils as qutils
|
|
||||||
|
DB_USER = "quantum"
|
||||||
|
QUANTUM_DB = "quantum"
|
||||||
|
NOVA_DB_USER = "nova"
|
||||||
|
NOVA_DB = "nova"
|
||||||
|
|
||||||
|
OVS = "ovs"
|
||||||
|
NVP = "nvp"
|
||||||
|
|
||||||
|
OVS_PLUGIN = \
|
||||||
|
"quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2"
|
||||||
|
NVP_PLUGIN = \
|
||||||
|
"quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2"
|
||||||
|
CORE_PLUGIN = {
|
||||||
|
OVS: OVS_PLUGIN,
|
||||||
|
NVP: NVP_PLUGIN
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class NetworkServiceContext(OSContextGenerator):
|
class NetworkServiceContext(OSContextGenerator):
|
||||||
@ -58,9 +82,9 @@ class ExternalPortContext(OSContextGenerator):
|
|||||||
class QuantumGatewayContext(OSContextGenerator):
|
class QuantumGatewayContext(OSContextGenerator):
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
ctxt = {
|
ctxt = {
|
||||||
'shared_secret': qutils.get_shared_secret(),
|
'shared_secret': get_shared_secret(),
|
||||||
'local_ip': qutils.get_host_ip(),
|
'local_ip': get_host_ip(),
|
||||||
'core_plugin': qutils.CORE_PLUGIN[config('plugin')],
|
'core_plugin': CORE_PLUGIN[config('plugin')],
|
||||||
'plugin': config('plugin')
|
'plugin': config('plugin')
|
||||||
}
|
}
|
||||||
return ctxt
|
return ctxt
|
||||||
@ -75,15 +99,49 @@ class QuantumSharedDBContext(OSContextGenerator):
|
|||||||
ctxt = {
|
ctxt = {
|
||||||
'database_host': relation_get('db_host', rid=rid,
|
'database_host': relation_get('db_host', rid=rid,
|
||||||
unit=unit),
|
unit=unit),
|
||||||
'quantum_database': qutils.QUANTUM_DB,
|
'quantum_database': QUANTUM_DB,
|
||||||
'quantum_user': qutils.DB_USER,
|
'quantum_user': DB_USER,
|
||||||
'quantum_password': relation_get('quantum_password',
|
'quantum_password': relation_get('quantum_password',
|
||||||
rid=rid, unit=unit),
|
rid=rid, unit=unit),
|
||||||
'nova_database': qutils.NOVA_DB,
|
'nova_database': NOVA_DB,
|
||||||
'nova_user': qutils.NOVA_DB_USER,
|
'nova_user': NOVA_DB_USER,
|
||||||
'nova_password': relation_get('nova_password', rid=rid,
|
'nova_password': relation_get('nova_password', rid=rid,
|
||||||
unit=unit)
|
unit=unit)
|
||||||
}
|
}
|
||||||
|
print ctxt
|
||||||
if context_complete(ctxt):
|
if context_complete(ctxt):
|
||||||
return ctxt
|
return ctxt
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
@cached
|
||||||
|
def get_host_ip(hostname=None):
|
||||||
|
try:
|
||||||
|
import dns.resolver
|
||||||
|
except ImportError:
|
||||||
|
apt_install('python-dnspython', fatal=True)
|
||||||
|
import dns.resolver
|
||||||
|
hostname = hostname or unit_get('private-address')
|
||||||
|
try:
|
||||||
|
# Test to see if already an IPv4 address
|
||||||
|
socket.inet_aton(hostname)
|
||||||
|
return hostname
|
||||||
|
except socket.error:
|
||||||
|
answers = dns.resolver.query(hostname, 'A')
|
||||||
|
if answers:
|
||||||
|
return answers[0].address
|
||||||
|
|
||||||
|
|
||||||
|
SHARED_SECRET = "/etc/quantum/secret.txt"
|
||||||
|
|
||||||
|
|
||||||
|
def get_shared_secret():
|
||||||
|
secret = None
|
||||||
|
if not os.path.exists(SHARED_SECRET):
|
||||||
|
secret = str(uuid.uuid4())
|
||||||
|
with open(SHARED_SECRET, 'w') as secret_file:
|
||||||
|
secret_file.write(secret)
|
||||||
|
else:
|
||||||
|
with open(SHARED_SECRET, 'r') as secret_file:
|
||||||
|
secret = secret_file.read().strip()
|
||||||
|
return secret
|
||||||
|
@ -33,11 +33,13 @@ from quantum_utils import (
|
|||||||
get_packages,
|
get_packages,
|
||||||
get_early_packages,
|
get_early_packages,
|
||||||
valid_plugin,
|
valid_plugin,
|
||||||
DB_USER, QUANTUM_DB,
|
|
||||||
NOVA_DB_USER, NOVA_DB,
|
|
||||||
configure_ovs,
|
configure_ovs,
|
||||||
reassign_agent_resources,
|
reassign_agent_resources,
|
||||||
)
|
)
|
||||||
|
from quantum_contexts import (
|
||||||
|
DB_USER, QUANTUM_DB,
|
||||||
|
NOVA_DB_USER, NOVA_DB,
|
||||||
|
)
|
||||||
|
|
||||||
hooks = Hooks()
|
hooks = Hooks()
|
||||||
CONFIGS = register_configs()
|
CONFIGS = register_configs()
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
import os
|
|
||||||
import uuid
|
|
||||||
import socket
|
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
log,
|
log,
|
||||||
config,
|
config,
|
||||||
unit_get,
|
|
||||||
cached
|
|
||||||
)
|
)
|
||||||
from charmhelpers.core.host import (
|
from charmhelpers.core.host import (
|
||||||
apt_install,
|
apt_install,
|
||||||
@ -22,23 +17,19 @@ from charmhelpers.contrib.openstack.utils import (
|
|||||||
)
|
)
|
||||||
import charmhelpers.contrib.openstack.context as context
|
import charmhelpers.contrib.openstack.context as context
|
||||||
import charmhelpers.contrib.openstack.templating as templating
|
import charmhelpers.contrib.openstack.templating as templating
|
||||||
import quantum_contexts
|
from quantum_contexts import (
|
||||||
|
CORE_PLUGIN,
|
||||||
|
OVS, NVP,
|
||||||
|
QuantumGatewayContext,
|
||||||
|
NetworkServiceContext,
|
||||||
|
QuantumSharedDBContext,
|
||||||
|
ExternalPortContext,
|
||||||
|
)
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
OVS = "ovs"
|
|
||||||
NVP = "nvp"
|
|
||||||
|
|
||||||
OVS_PLUGIN = \
|
|
||||||
"quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2"
|
|
||||||
NVP_PLUGIN = \
|
|
||||||
"quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2"
|
|
||||||
CORE_PLUGIN = {
|
|
||||||
OVS: OVS_PLUGIN,
|
|
||||||
NVP: NVP_PLUGIN
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def valid_plugin():
|
def valid_plugin():
|
||||||
|
print config('plugin')
|
||||||
return config('plugin') in CORE_PLUGIN
|
return config('plugin') in CORE_PLUGIN
|
||||||
|
|
||||||
OVS_PLUGIN_CONF = \
|
OVS_PLUGIN_CONF = \
|
||||||
@ -94,18 +85,18 @@ NOVA_CONF = "/etc/nova/nova.conf"
|
|||||||
|
|
||||||
SHARED_CONFIG_FILES = {
|
SHARED_CONFIG_FILES = {
|
||||||
DHCP_AGENT_CONF: {
|
DHCP_AGENT_CONF: {
|
||||||
'hook_contexts': [quantum_contexts.QuantumGatewayContext()],
|
'hook_contexts': [QuantumGatewayContext()],
|
||||||
'services': ['quantum-dhcp-agent']
|
'services': ['quantum-dhcp-agent']
|
||||||
},
|
},
|
||||||
METADATA_AGENT_CONF: {
|
METADATA_AGENT_CONF: {
|
||||||
'hook_contexts': [quantum_contexts.NetworkServiceContext()],
|
'hook_contexts': [NetworkServiceContext()],
|
||||||
'services': ['quantum-metadata-agent']
|
'services': ['quantum-metadata-agent']
|
||||||
},
|
},
|
||||||
NOVA_CONF: {
|
NOVA_CONF: {
|
||||||
'hook_contexts': [context.AMQPContext(),
|
'hook_contexts': [context.AMQPContext(),
|
||||||
quantum_contexts.QuantumSharedDBContext(),
|
QuantumSharedDBContext(),
|
||||||
quantum_contexts.NetworkServiceContext(),
|
NetworkServiceContext(),
|
||||||
quantum_contexts.QuantumGatewayContext()],
|
QuantumGatewayContext()],
|
||||||
'services': ['nova-api-metadata']
|
'services': ['nova-api-metadata']
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -113,24 +104,24 @@ SHARED_CONFIG_FILES = {
|
|||||||
OVS_CONFIG_FILES = {
|
OVS_CONFIG_FILES = {
|
||||||
QUANTUM_CONF: {
|
QUANTUM_CONF: {
|
||||||
'hook_contexts': [context.AMQPContext(),
|
'hook_contexts': [context.AMQPContext(),
|
||||||
quantum_contexts.QuantumGatewayContext()],
|
QuantumGatewayContext()],
|
||||||
'services': ['quantum-l3-agent',
|
'services': ['quantum-l3-agent',
|
||||||
'quantum-dhcp-agent',
|
'quantum-dhcp-agent',
|
||||||
'quantum-metadata-agent',
|
'quantum-metadata-agent',
|
||||||
'quantum-plugin-openvswitch-agent']
|
'quantum-plugin-openvswitch-agent']
|
||||||
},
|
},
|
||||||
L3_AGENT_CONF: {
|
L3_AGENT_CONF: {
|
||||||
'hook_contexts': [quantum_contexts.NetworkServiceContext()],
|
'hook_contexts': [NetworkServiceContext()],
|
||||||
'services': ['quantum-l3-agent']
|
'services': ['quantum-l3-agent']
|
||||||
},
|
},
|
||||||
# TODO: Check to see if this is actually required
|
# TODO: Check to see if this is actually required
|
||||||
OVS_PLUGIN_CONF: {
|
OVS_PLUGIN_CONF: {
|
||||||
'hook_contexts': [quantum_contexts.QuantumSharedDBContext(),
|
'hook_contexts': [QuantumSharedDBContext(),
|
||||||
quantum_contexts.QuantumGatewayContext()],
|
QuantumGatewayContext()],
|
||||||
'services': ['quantum-plugin-openvswitch-agent']
|
'services': ['quantum-plugin-openvswitch-agent']
|
||||||
},
|
},
|
||||||
EXT_PORT_CONF: {
|
EXT_PORT_CONF: {
|
||||||
'hook_contexts': [quantum_contexts.ExternalPortContext()],
|
'hook_contexts': [ExternalPortContext()],
|
||||||
'services': []
|
'services': []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,39 +173,16 @@ def restart_map():
|
|||||||
return OrderedDict(_map)
|
return OrderedDict(_map)
|
||||||
|
|
||||||
|
|
||||||
DB_USER = "quantum"
|
|
||||||
QUANTUM_DB = "quantum"
|
|
||||||
KEYSTONE_SERVICE = "quantum"
|
|
||||||
NOVA_DB_USER = "nova"
|
|
||||||
NOVA_DB = "nova"
|
|
||||||
|
|
||||||
RABBIT_USER = "nova"
|
|
||||||
RABBIT_VHOST = "nova"
|
|
||||||
|
|
||||||
INT_BRIDGE = "br-int"
|
INT_BRIDGE = "br-int"
|
||||||
EXT_BRIDGE = "br-ex"
|
EXT_BRIDGE = "br-ex"
|
||||||
|
|
||||||
SHARED_SECRET = "/etc/quantum/secret.txt"
|
|
||||||
|
|
||||||
|
|
||||||
def get_shared_secret():
|
|
||||||
secret = None
|
|
||||||
if not os.path.exists(SHARED_SECRET):
|
|
||||||
secret = str(uuid.uuid4())
|
|
||||||
with open(SHARED_SECRET, 'w') as secret_file:
|
|
||||||
secret_file.write(secret)
|
|
||||||
else:
|
|
||||||
with open(SHARED_SECRET, 'r') as secret_file:
|
|
||||||
secret = secret_file.read().strip()
|
|
||||||
return secret
|
|
||||||
|
|
||||||
DHCP_AGENT = "DHCP Agent"
|
DHCP_AGENT = "DHCP Agent"
|
||||||
L3_AGENT = "L3 Agent"
|
L3_AGENT = "L3 Agent"
|
||||||
|
|
||||||
|
|
||||||
def reassign_agent_resources():
|
def reassign_agent_resources():
|
||||||
''' Use agent scheduler API to detect down agents and re-schedule '''
|
''' Use agent scheduler API to detect down agents and re-schedule '''
|
||||||
env = quantum_contexts.NetworkServiceContext()()
|
env = NetworkServiceContext()()
|
||||||
if not env:
|
if not env:
|
||||||
log('Unable to re-assign resources at this time')
|
log('Unable to re-assign resources at this time')
|
||||||
return
|
return
|
||||||
@ -305,24 +273,6 @@ def do_openstack_upgrade(configs):
|
|||||||
configs.set_release(openstack_release=new_os_rel)
|
configs.set_release(openstack_release=new_os_rel)
|
||||||
|
|
||||||
|
|
||||||
@cached
|
|
||||||
def get_host_ip(hostname=None):
|
|
||||||
try:
|
|
||||||
import dns.resolver
|
|
||||||
except ImportError:
|
|
||||||
apt_install('python-dnspython', fatal=True)
|
|
||||||
import dns.resolver
|
|
||||||
hostname = hostname or unit_get('private-address')
|
|
||||||
try:
|
|
||||||
# Test to see if already an IPv4 address
|
|
||||||
socket.inet_aton(hostname)
|
|
||||||
return hostname
|
|
||||||
except socket.error:
|
|
||||||
answers = dns.resolver.query(hostname, 'A')
|
|
||||||
if answers:
|
|
||||||
return answers[0].address
|
|
||||||
|
|
||||||
|
|
||||||
def configure_ovs():
|
def configure_ovs():
|
||||||
if config('plugin') == OVS:
|
if config('plugin') == OVS:
|
||||||
add_bridge(INT_BRIDGE)
|
add_bridge(INT_BRIDGE)
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
import sys
|
||||||
|
sys.path.append('hooks')
|
218
unit_tests/test_quantum_contexts.py
Normal file
218
unit_tests/test_quantum_contexts.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
from mock import MagicMock, patch
|
||||||
|
import quantum_contexts
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
from test_utils import (
|
||||||
|
CharmTestCase
|
||||||
|
)
|
||||||
|
|
||||||
|
TO_PATCH = [
|
||||||
|
'config',
|
||||||
|
'relation_get',
|
||||||
|
'relation_ids',
|
||||||
|
'related_units',
|
||||||
|
'context_complete',
|
||||||
|
'unit_get',
|
||||||
|
'apt_install',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def patch_open():
|
||||||
|
'''Patch open() to allow mocking both open() itself and the file that is
|
||||||
|
yielded.
|
||||||
|
|
||||||
|
Yields the mock for "open" and "file", respectively.'''
|
||||||
|
mock_open = MagicMock(spec=open)
|
||||||
|
mock_file = MagicMock(spec=file)
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def stub_open(*args, **kwargs):
|
||||||
|
mock_open(*args, **kwargs)
|
||||||
|
yield mock_file
|
||||||
|
|
||||||
|
with patch('__builtin__.open', stub_open):
|
||||||
|
yield mock_open, mock_file
|
||||||
|
|
||||||
|
|
||||||
|
class _TestQuantumContext(CharmTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(_TestQuantumContext, self).setUp(quantum_contexts, TO_PATCH)
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
|
||||||
|
def test_not_related(self):
|
||||||
|
self.relation_ids.return_value = []
|
||||||
|
self.assertEquals(self.context(), {})
|
||||||
|
|
||||||
|
def test_no_units(self):
|
||||||
|
self.relation_ids.return_value = []
|
||||||
|
self.relation_ids.return_value = ['foo']
|
||||||
|
self.related_units.return_value = []
|
||||||
|
self.assertEquals(self.context(), {})
|
||||||
|
|
||||||
|
def test_no_data(self):
|
||||||
|
self.relation_ids.return_value = ['foo']
|
||||||
|
self.related_units.return_value = ['bar']
|
||||||
|
self.relation_get.side_effect = self.test_relation.get
|
||||||
|
self.context_complete.return_value = False
|
||||||
|
self.assertEquals(self.context(), {})
|
||||||
|
|
||||||
|
def test_data_multi_unit(self):
|
||||||
|
self.relation_ids.return_value = ['foo']
|
||||||
|
self.related_units.return_value = ['bar', 'baz']
|
||||||
|
self.context_complete.return_value = True
|
||||||
|
self.relation_get.side_effect = self.test_relation.get
|
||||||
|
self.assertEquals(self.context(), self.data_result)
|
||||||
|
|
||||||
|
def test_data_single_unit(self):
|
||||||
|
self.relation_ids.return_value = ['foo']
|
||||||
|
self.related_units.return_value = ['bar']
|
||||||
|
self.context_complete.return_value = True
|
||||||
|
self.relation_get.side_effect = self.test_relation.get
|
||||||
|
self.assertEquals(self.context(), self.data_result)
|
||||||
|
|
||||||
|
|
||||||
|
class TestQuantumSharedDBContext(_TestQuantumContext):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestQuantumSharedDBContext, self).setUp()
|
||||||
|
self.context = quantum_contexts.QuantumSharedDBContext()
|
||||||
|
self.test_relation.set(
|
||||||
|
{'db_host': '10.5.0.1',
|
||||||
|
'nova_password': 'novapass',
|
||||||
|
'quantum_password': 'quantumpass'}
|
||||||
|
)
|
||||||
|
self.data_result = {
|
||||||
|
'database_host': '10.5.0.1',
|
||||||
|
'nova_user': 'nova',
|
||||||
|
'nova_password': 'novapass',
|
||||||
|
'nova_database': 'nova',
|
||||||
|
'quantum_user': 'quantum',
|
||||||
|
'quantum_password': 'quantumpass',
|
||||||
|
'quantum_database': 'quantum'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestNetworkServiceContext(_TestQuantumContext):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestNetworkServiceContext, self).setUp()
|
||||||
|
self.context = quantum_contexts.NetworkServiceContext()
|
||||||
|
self.test_relation.set(
|
||||||
|
{'keystone_host': '10.5.0.1',
|
||||||
|
'service_port': '5000',
|
||||||
|
'auth_port': '20000',
|
||||||
|
'service_tenant': 'tenant',
|
||||||
|
'service_username': 'username',
|
||||||
|
'service_password': 'password',
|
||||||
|
'quantum_host': '10.5.0.2',
|
||||||
|
'quantum_port': '9696',
|
||||||
|
'quantum_url': 'http://10.5.0.2:9696/v2',
|
||||||
|
'region': 'aregion'}
|
||||||
|
)
|
||||||
|
self.data_result = {
|
||||||
|
'keystone_host': '10.5.0.1',
|
||||||
|
'service_port': '5000',
|
||||||
|
'auth_port': '20000',
|
||||||
|
'service_tenant': 'tenant',
|
||||||
|
'service_username': 'username',
|
||||||
|
'service_password': 'password',
|
||||||
|
'quantum_host': '10.5.0.2',
|
||||||
|
'quantum_port': '9696',
|
||||||
|
'quantum_url': 'http://10.5.0.2:9696/v2',
|
||||||
|
'region': 'aregion',
|
||||||
|
'service_protocol': 'http',
|
||||||
|
'auth_protocol': 'http',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestExternalPortContext(CharmTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestExternalPortContext, self).setUp(quantum_contexts,
|
||||||
|
TO_PATCH)
|
||||||
|
|
||||||
|
def test_no_ext_port(self):
|
||||||
|
self.config.return_value = None
|
||||||
|
self.assertEquals(quantum_contexts.ExternalPortContext()(),
|
||||||
|
None)
|
||||||
|
|
||||||
|
def test_ext_port(self):
|
||||||
|
self.config.return_value = 'eth1010'
|
||||||
|
self.assertEquals(quantum_contexts.ExternalPortContext()(),
|
||||||
|
{'ext_port': 'eth1010'})
|
||||||
|
|
||||||
|
|
||||||
|
class TestQuantumGatewayContext(CharmTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestQuantumGatewayContext, self).setUp(quantum_contexts,
|
||||||
|
TO_PATCH)
|
||||||
|
|
||||||
|
@patch.object(quantum_contexts, 'get_shared_secret')
|
||||||
|
@patch.object(quantum_contexts, 'get_host_ip')
|
||||||
|
def test_all(self, _host_ip, _secret):
|
||||||
|
self.config.return_value = 'ovs'
|
||||||
|
_host_ip.return_value = '10.5.0.1'
|
||||||
|
_secret.return_value = 'testsecret'
|
||||||
|
self.assertEquals(quantum_contexts.QuantumGatewayContext()(),
|
||||||
|
{'shared_secret': 'testsecret',
|
||||||
|
'local_ip': '10.5.0.1',
|
||||||
|
'core_plugin':
|
||||||
|
"quantum.plugins.openvswitch.ovs_quantum_plugin."
|
||||||
|
"OVSQuantumPluginV2",
|
||||||
|
'plugin': 'ovs'})
|
||||||
|
|
||||||
|
|
||||||
|
class TestSharedSecret(CharmTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestSharedSecret, self).setUp(quantum_contexts,
|
||||||
|
TO_PATCH)
|
||||||
|
|
||||||
|
@patch('os.path')
|
||||||
|
@patch('uuid.uuid4')
|
||||||
|
def test_secret_created_stored(self, _uuid4, _path):
|
||||||
|
_path.exists.return_value = False
|
||||||
|
_uuid4.return_value = 'secret_thing'
|
||||||
|
with patch_open() as (_open, _file):
|
||||||
|
self.assertEquals(quantum_contexts.get_shared_secret(),
|
||||||
|
'secret_thing')
|
||||||
|
_open.assert_called_with(quantum_contexts.SHARED_SECRET, 'w')
|
||||||
|
_file.write.assert_called_with('secret_thing')
|
||||||
|
|
||||||
|
@patch('os.path')
|
||||||
|
def test_secret_retrieved(self, _path):
|
||||||
|
_path.exists.return_value = True
|
||||||
|
with patch_open() as (_open, _file):
|
||||||
|
_file.read.return_value = 'secret_thing\n'
|
||||||
|
self.assertEquals(quantum_contexts.get_shared_secret(),
|
||||||
|
'secret_thing')
|
||||||
|
_open.assert_called_with(quantum_contexts.SHARED_SECRET, 'r')
|
||||||
|
|
||||||
|
|
||||||
|
class TestHostIP(CharmTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestHostIP, self).setUp(quantum_contexts,
|
||||||
|
TO_PATCH)
|
||||||
|
|
||||||
|
def test_get_host_ip_already_ip(self):
|
||||||
|
self.assertEquals(quantum_contexts.get_host_ip('10.5.0.1'),
|
||||||
|
'10.5.0.1')
|
||||||
|
|
||||||
|
def test_get_host_ip_noarg(self):
|
||||||
|
self.unit_get.return_value = "10.5.0.1"
|
||||||
|
self.assertEquals(quantum_contexts.get_host_ip(),
|
||||||
|
'10.5.0.1')
|
||||||
|
|
||||||
|
@patch('dns.resolver.query')
|
||||||
|
def test_get_host_ip_hostname_unresolvable(self, _query):
|
||||||
|
class NXDOMAIN(Exception):
|
||||||
|
pass
|
||||||
|
_query.side_effect = NXDOMAIN()
|
||||||
|
self.assertRaises(NXDOMAIN, quantum_contexts.get_host_ip,
|
||||||
|
'missing.example.com')
|
||||||
|
|
||||||
|
@patch('dns.resolver.query')
|
||||||
|
def test_get_host_ip_hostname_resolvable(self, _query):
|
||||||
|
data = MagicMock()
|
||||||
|
data.address = '10.5.0.1'
|
||||||
|
_query.return_value = [data]
|
||||||
|
self.assertEquals(quantum_contexts.get_host_ip('myhost.example.com'),
|
||||||
|
'10.5.0.1')
|
||||||
|
_query.assert_called_with('myhost.example.com', 'A')
|
143
unit_tests/test_quantum_hooks.py
Normal file
143
unit_tests/test_quantum_hooks.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
from mock import MagicMock, patch, call
|
||||||
|
import quantum_utils as utils
|
||||||
|
_register_configs = utils.register_configs
|
||||||
|
_restart_map = utils.restart_map
|
||||||
|
utils.register_configs = MagicMock()
|
||||||
|
utils.restart_map = MagicMock()
|
||||||
|
import quantum_hooks as hooks
|
||||||
|
utils.register_configs = _register_configs
|
||||||
|
utils.restart_map = _restart_map
|
||||||
|
|
||||||
|
from charmhelpers.contrib.hahelpers.cluster import HAIncompleteConfig
|
||||||
|
from test_utils import CharmTestCase
|
||||||
|
|
||||||
|
|
||||||
|
TO_PATCH = [
|
||||||
|
'config',
|
||||||
|
'configure_installation_source',
|
||||||
|
'valid_plugin',
|
||||||
|
'apt_update',
|
||||||
|
'apt_install',
|
||||||
|
'filter_installed_packages',
|
||||||
|
'get_early_packages',
|
||||||
|
'get_packages',
|
||||||
|
'log',
|
||||||
|
'do_openstack_upgrade',
|
||||||
|
'openstack_upgrade_available',
|
||||||
|
'CONFIGS',
|
||||||
|
'configure_ovs',
|
||||||
|
'relation_set',
|
||||||
|
'unit_get',
|
||||||
|
'relation_get',
|
||||||
|
'install_ca_cert',
|
||||||
|
'eligible_leader',
|
||||||
|
'reassign_agent_resources',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TestQuantumHooks(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestQuantumHooks, self).setUp(hooks, TO_PATCH)
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
self.test_config.set('openstack-origin', 'cloud:precise-havana')
|
||||||
|
self.test_config.set('plugin', 'ovs')
|
||||||
|
|
||||||
|
def _call_hook(self, hookname):
|
||||||
|
hooks.hooks.execute([
|
||||||
|
'hooks/{}'.format(hookname)])
|
||||||
|
|
||||||
|
def test_install_hook(self):
|
||||||
|
self.valid_plugin.return_value = True
|
||||||
|
_pkgs = ['foo', 'bar']
|
||||||
|
self.filter_installed_packages.return_value = _pkgs
|
||||||
|
self._call_hook('install')
|
||||||
|
self.configure_installation_source.assert_called_with(
|
||||||
|
'cloud:precise-havana'
|
||||||
|
)
|
||||||
|
self.apt_update.assert_called_with(fatal=True)
|
||||||
|
self.apt_install.assert_has_calls([
|
||||||
|
call(_pkgs, fatal=True),
|
||||||
|
call(_pkgs, fatal=True),
|
||||||
|
])
|
||||||
|
self.get_early_packages.assert_called()
|
||||||
|
self.get_packages.assert_called()
|
||||||
|
|
||||||
|
@patch('sys.exit')
|
||||||
|
def test_install_hook_invalid_plugin(self, _exit):
|
||||||
|
self.valid_plugin.return_value = False
|
||||||
|
self._call_hook('install')
|
||||||
|
self.log.assert_called()
|
||||||
|
_exit.assert_called_with(1)
|
||||||
|
|
||||||
|
def test_config_changed_upgrade(self):
|
||||||
|
self.openstack_upgrade_available.return_value = True
|
||||||
|
self.valid_plugin.return_value = True
|
||||||
|
self._call_hook('config-changed')
|
||||||
|
self.do_openstack_upgrade.assert_called_with(self.CONFIGS)
|
||||||
|
self.CONFIGS.write_all.assert_called()
|
||||||
|
self.configure_ovs.assert_called()
|
||||||
|
|
||||||
|
@patch('sys.exit')
|
||||||
|
def test_config_changed_invalid_plugin(self, _exit):
|
||||||
|
self.valid_plugin.return_value = False
|
||||||
|
self._call_hook('config-changed')
|
||||||
|
self.log.assert_called()
|
||||||
|
_exit.assert_called_with(1)
|
||||||
|
|
||||||
|
def test_upgrade_charm(self):
|
||||||
|
_install = self.patch('install')
|
||||||
|
_config_changed = self.patch('config_changed')
|
||||||
|
self._call_hook('upgrade-charm')
|
||||||
|
_install.assert_called()
|
||||||
|
_config_changed.assert_called()
|
||||||
|
|
||||||
|
def test_db_joined(self):
|
||||||
|
self.unit_get.return_value = 'myhostname'
|
||||||
|
self._call_hook('shared-db-relation-joined')
|
||||||
|
self.relation_set.assert_called_with(
|
||||||
|
quantum_username='quantum',
|
||||||
|
quantum_database='quantum',
|
||||||
|
quantum_hostname='myhostname',
|
||||||
|
nova_username='nova',
|
||||||
|
nova_database='nova',
|
||||||
|
nova_hostname='myhostname',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_amqp_joined(self):
|
||||||
|
self._call_hook('amqp-relation-joined')
|
||||||
|
self.relation_set.assert_called_with(
|
||||||
|
username='nova',
|
||||||
|
vhost='nova',
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_amqp_changed(self):
|
||||||
|
self._call_hook('amqp-relation-changed')
|
||||||
|
self.CONFIGS.write_all.assert_called()
|
||||||
|
|
||||||
|
def test_shared_db_changed(self):
|
||||||
|
self._call_hook('shared-db-relation-changed')
|
||||||
|
self.CONFIGS.write_all.assert_called()
|
||||||
|
|
||||||
|
def test_nm_changed(self):
|
||||||
|
self.relation_get.return_value = "cert"
|
||||||
|
self._call_hook('quantum-network-service-relation-changed')
|
||||||
|
self.CONFIGS.write_all.assert_called()
|
||||||
|
self.install_ca_cert.assert_called_with('cert')
|
||||||
|
|
||||||
|
def test_cluster_departed_nvp(self):
|
||||||
|
self.test_config.set('plugin', 'nvp')
|
||||||
|
self._call_hook('cluster-relation-departed')
|
||||||
|
self.log.assert_called()
|
||||||
|
self.eligible_leader.assert_not_called()
|
||||||
|
self.reassign_agent_resources.assert_not_called()
|
||||||
|
|
||||||
|
def test_cluster_departed_ovs_not_leader(self):
|
||||||
|
self.eligible_leader.return_value = False
|
||||||
|
self._call_hook('cluster-relation-departed')
|
||||||
|
self.reassign_agent_resources.assert_not_called()
|
||||||
|
|
||||||
|
def test_cluster_departed_ovs_leader(self):
|
||||||
|
self.eligible_leader.return_value = True
|
||||||
|
self._call_hook('cluster-relation-departed')
|
||||||
|
self.reassign_agent_resources.assert_called()
|
169
unit_tests/test_quantum_utils.py
Normal file
169
unit_tests/test_quantum_utils.py
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
from mock import MagicMock, patch, call
|
||||||
|
import charmhelpers.contrib.openstack.templating as templating
|
||||||
|
templating.OSConfigRenderer = MagicMock()
|
||||||
|
import quantum_utils
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from test_utils import (
|
||||||
|
CharmTestCase
|
||||||
|
)
|
||||||
|
|
||||||
|
TO_PATCH = [
|
||||||
|
'get_os_codename_package',
|
||||||
|
'config',
|
||||||
|
'get_os_codename_install_source',
|
||||||
|
'apt_update',
|
||||||
|
'apt_install',
|
||||||
|
'configure_installation_source',
|
||||||
|
'log',
|
||||||
|
'add_bridge',
|
||||||
|
'add_bridge_port'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TestQuantumUtils(CharmTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestQuantumUtils, self).setUp(quantum_utils, TO_PATCH)
|
||||||
|
|
||||||
|
def test_valid_plugin(self):
|
||||||
|
self.config.return_value = 'ovs'
|
||||||
|
self.assertTrue(quantum_utils.valid_plugin())
|
||||||
|
self.config.return_value = 'nvp'
|
||||||
|
self.assertTrue(quantum_utils.valid_plugin())
|
||||||
|
|
||||||
|
def test_invalid_plugin(self):
|
||||||
|
self.config.return_value = 'invalid'
|
||||||
|
self.assertFalse(quantum_utils.valid_plugin())
|
||||||
|
|
||||||
|
def test_get_early_packages_ovs(self):
|
||||||
|
self.config.return_value = 'ovs'
|
||||||
|
self.assertEquals(quantum_utils.get_early_packages(),
|
||||||
|
['openvswitch-datapath-dkms'])
|
||||||
|
|
||||||
|
def test_get_early_packages_nvp(self):
|
||||||
|
self.config.return_value = 'nvp'
|
||||||
|
self.assertEquals(quantum_utils.get_early_packages(),
|
||||||
|
[])
|
||||||
|
|
||||||
|
def test_get_packages_ovs(self):
|
||||||
|
self.config.return_value = 'ovs'
|
||||||
|
self.assertNotEqual(quantum_utils.get_packages(), [])
|
||||||
|
|
||||||
|
def test_configure_ovs_ovs_ext_port(self):
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
self.test_config.set('plugin', 'ovs')
|
||||||
|
self.test_config.set('ext-port', 'eth0')
|
||||||
|
quantum_utils.configure_ovs()
|
||||||
|
self.add_bridge.assert_has_calls([
|
||||||
|
call('br-int'),
|
||||||
|
call('br-ex')
|
||||||
|
])
|
||||||
|
self.add_bridge_port.assert_called_with('br-ex', 'eth0')
|
||||||
|
|
||||||
|
def test_configure_ovs_nvp(self):
|
||||||
|
self.config.return_value = 'nvp'
|
||||||
|
quantum_utils.configure_ovs()
|
||||||
|
self.add_bridge.assert_called_with('br-int')
|
||||||
|
|
||||||
|
def test_do_openstack_upgrade(self):
|
||||||
|
self.config.side_effect = self.test_config.get
|
||||||
|
self.test_config.set('openstack-origin', 'cloud:precise-havana')
|
||||||
|
self.test_config.set('plugin', 'ovs')
|
||||||
|
self.config.return_value = 'cloud:precise-havana'
|
||||||
|
self.get_os_codename_install_source.return_value = 'havana'
|
||||||
|
configs = MagicMock()
|
||||||
|
quantum_utils.do_openstack_upgrade(configs)
|
||||||
|
configs.set_release.assert_called_with(openstack_release='havana')
|
||||||
|
self.log.assert_called()
|
||||||
|
self.apt_update.assert_called_with(fatal=True)
|
||||||
|
dpkg_opts = [
|
||||||
|
'--option', 'Dpkg::Options::=--force-confnew',
|
||||||
|
'--option', 'Dpkg::Options::=--force-confdef',
|
||||||
|
]
|
||||||
|
self.apt_install.assert_called_with(
|
||||||
|
packages=quantum_utils.GATEWAY_PKGS['ovs'],
|
||||||
|
options=dpkg_opts, fatal=True
|
||||||
|
)
|
||||||
|
self.configure_installation_source.assert_called_with(
|
||||||
|
'cloud:precise-havana'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_register_configs_ovs(self):
|
||||||
|
self.config.return_value = 'ovs'
|
||||||
|
self.get_os_codename_package.return_value = 'havana'
|
||||||
|
configs = quantum_utils.register_configs()
|
||||||
|
confs = [quantum_utils.DHCP_AGENT_CONF,
|
||||||
|
quantum_utils.METADATA_AGENT_CONF,
|
||||||
|
quantum_utils.NOVA_CONF,
|
||||||
|
quantum_utils.QUANTUM_CONF,
|
||||||
|
quantum_utils.L3_AGENT_CONF,
|
||||||
|
quantum_utils.OVS_PLUGIN_CONF,
|
||||||
|
quantum_utils.EXT_PORT_CONF]
|
||||||
|
for conf in confs:
|
||||||
|
configs.register.assert_any_call(
|
||||||
|
conf,
|
||||||
|
quantum_utils.CONFIG_FILES[quantum_utils.OVS][conf]
|
||||||
|
['hook_contexts']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_restart_map_ovs(self):
|
||||||
|
self.config.return_value = 'ovs'
|
||||||
|
ex_map = OrderedDict([
|
||||||
|
(quantum_utils.L3_AGENT_CONF, ['quantum-l3-agent']),
|
||||||
|
(quantum_utils.OVS_PLUGIN_CONF,
|
||||||
|
['quantum-plugin-openvswitch-agent']),
|
||||||
|
(quantum_utils.NOVA_CONF, ['nova-api-metadata']),
|
||||||
|
(quantum_utils.METADATA_AGENT_CONF, ['quantum-metadata-agent']),
|
||||||
|
(quantum_utils.DHCP_AGENT_CONF, ['quantum-dhcp-agent']),
|
||||||
|
(quantum_utils.QUANTUM_CONF, ['quantum-l3-agent',
|
||||||
|
'quantum-dhcp-agent',
|
||||||
|
'quantum-metadata-agent',
|
||||||
|
'quantum-plugin-openvswitch-agent'])
|
||||||
|
])
|
||||||
|
self.assertEquals(quantum_utils.restart_map(), ex_map)
|
||||||
|
|
||||||
|
def test_register_configs_nvp(self):
|
||||||
|
self.config.return_value = 'nvp'
|
||||||
|
self.get_os_codename_package.return_value = 'havana'
|
||||||
|
configs = quantum_utils.register_configs()
|
||||||
|
confs = [quantum_utils.DHCP_AGENT_CONF,
|
||||||
|
quantum_utils.METADATA_AGENT_CONF,
|
||||||
|
quantum_utils.NOVA_CONF,
|
||||||
|
quantum_utils.QUANTUM_CONF]
|
||||||
|
for conf in confs:
|
||||||
|
configs.register.assert_any_call(
|
||||||
|
conf,
|
||||||
|
quantum_utils.CONFIG_FILES[quantum_utils.NVP][conf]
|
||||||
|
['hook_contexts']
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_restart_map_nvp(self):
|
||||||
|
self.config.return_value = 'nvp'
|
||||||
|
ex_map = OrderedDict([
|
||||||
|
(quantum_utils.DHCP_AGENT_CONF, ['quantum-dhcp-agent']),
|
||||||
|
(quantum_utils.NOVA_CONF, ['nova-api-metadata']),
|
||||||
|
(quantum_utils.QUANTUM_CONF, ['quantum-dhcp-agent',
|
||||||
|
'quantum-metadata-agent']),
|
||||||
|
(quantum_utils.METADATA_AGENT_CONF, ['quantum-metadata-agent']),
|
||||||
|
])
|
||||||
|
self.assertEquals(quantum_utils.restart_map(), ex_map)
|
||||||
|
|
||||||
|
def test_register_configs_pre_install(self):
|
||||||
|
self.config.return_value = 'ovs'
|
||||||
|
self.get_os_codename_package.return_value = None
|
||||||
|
configs = quantum_utils.register_configs()
|
||||||
|
confs = [quantum_utils.DHCP_AGENT_CONF,
|
||||||
|
quantum_utils.METADATA_AGENT_CONF,
|
||||||
|
quantum_utils.NOVA_CONF,
|
||||||
|
quantum_utils.QUANTUM_CONF,
|
||||||
|
quantum_utils.L3_AGENT_CONF,
|
||||||
|
quantum_utils.OVS_PLUGIN_CONF,
|
||||||
|
quantum_utils.EXT_PORT_CONF]
|
||||||
|
for conf in confs:
|
||||||
|
configs.register.assert_any_call(
|
||||||
|
conf,
|
||||||
|
quantum_utils.CONFIG_FILES[quantum_utils.OVS][conf]
|
||||||
|
['hook_contexts']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
97
unit_tests/test_utils.py
Normal file
97
unit_tests/test_utils.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import logging
|
||||||
|
import unittest
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
|
|
||||||
|
def load_config():
|
||||||
|
'''
|
||||||
|
Walk backwords from __file__ looking for config.yaml, load and return the
|
||||||
|
'options' section'
|
||||||
|
'''
|
||||||
|
config = None
|
||||||
|
f = __file__
|
||||||
|
while config is None:
|
||||||
|
d = os.path.dirname(f)
|
||||||
|
if os.path.isfile(os.path.join(d, 'config.yaml')):
|
||||||
|
config = os.path.join(d, 'config.yaml')
|
||||||
|
break
|
||||||
|
f = d
|
||||||
|
|
||||||
|
if not config:
|
||||||
|
logging.error('Could not find config.yaml in any parent directory '
|
||||||
|
'of %s. ' % file)
|
||||||
|
raise Exception
|
||||||
|
|
||||||
|
return yaml.safe_load(open(config).read())['options']
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_config():
|
||||||
|
'''
|
||||||
|
Load default charm config from config.yaml return as a dict.
|
||||||
|
If no default is set in config.yaml, its value is None.
|
||||||
|
'''
|
||||||
|
default_config = {}
|
||||||
|
config = load_config()
|
||||||
|
for k, v in config.iteritems():
|
||||||
|
if 'default' in v:
|
||||||
|
default_config[k] = v['default']
|
||||||
|
else:
|
||||||
|
default_config[k] = None
|
||||||
|
return default_config
|
||||||
|
|
||||||
|
|
||||||
|
class CharmTestCase(unittest.TestCase):
|
||||||
|
def setUp(self, obj, patches):
|
||||||
|
super(CharmTestCase, self).setUp()
|
||||||
|
self.patches = patches
|
||||||
|
self.obj = obj
|
||||||
|
self.test_config = TestConfig()
|
||||||
|
self.test_relation = TestRelation()
|
||||||
|
self.patch_all()
|
||||||
|
|
||||||
|
def patch(self, method):
|
||||||
|
_m = patch.object(self.obj, method)
|
||||||
|
mock = _m.start()
|
||||||
|
self.addCleanup(_m.stop)
|
||||||
|
return mock
|
||||||
|
|
||||||
|
def patch_all(self):
|
||||||
|
for method in self.patches:
|
||||||
|
setattr(self, method, self.patch(method))
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfig(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.config = get_default_config()
|
||||||
|
|
||||||
|
def get(self, attr):
|
||||||
|
try:
|
||||||
|
return self.config[attr]
|
||||||
|
except KeyError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_all(self):
|
||||||
|
return self.config
|
||||||
|
|
||||||
|
def set(self, attr, value):
|
||||||
|
if attr not in self.config:
|
||||||
|
raise KeyError
|
||||||
|
self.config[attr] = value
|
||||||
|
|
||||||
|
|
||||||
|
class TestRelation(object):
|
||||||
|
def __init__(self, relation_data={}):
|
||||||
|
self.relation_data = relation_data
|
||||||
|
|
||||||
|
def set(self, relation_data):
|
||||||
|
self.relation_data = relation_data
|
||||||
|
|
||||||
|
def get(self, attr=None, unit=None, rid=None):
|
||||||
|
if attr is None:
|
||||||
|
return self.relation_data
|
||||||
|
elif attr in self.relation_data:
|
||||||
|
return self.relation_data[attr]
|
||||||
|
return None
|
Loading…
Reference in New Issue
Block a user