[dosaboy,r=james-page] Support mtu from neutron-api, add support for bridge mapping configuration.

This commit is contained in:
James Page 2015-03-19 17:00:09 +00:00
commit 9af23e724c
13 changed files with 349 additions and 64 deletions

View File

@ -25,8 +25,10 @@ options:
type: string type: string
default: default:
description: | description: |
The data port will be added to br-data and will allow usage of flat or VLAN Space-delimited list of bridge:port mappings. Ports will be added to
network types their corresponding bridge. The bridges will allow usage of flat or
VLAN network types with Neutron and should match this defined in
bridge-mappings.
disable-security-groups: disable-security-groups:
type: boolean type: boolean
default: false default: false
@ -36,6 +38,17 @@ options:
. .
BE CAREFUL - this option allows you to disable all port level security within BE CAREFUL - this option allows you to disable all port level security within
an OpenStack cloud. an OpenStack cloud.
bridge-mappings:
type: string
default: 'physnet1:br-data'
description: |
Space-delimited list of ML2 data bridge mappings with format
<provider>:<bridge>.
vlan-ranges:
type: string
default: "physnet1:1000:2000"
description: |
Space-delimited list of network provider vlan id ranges.
# Network configuration options # Network configuration options
# by default all access is over 'private-address' # by default all access is over 'private-address'
os-data-network: os-data-network:

View File

@ -16,6 +16,7 @@
import json import json
import os import os
import re
import time import time
from base64 import b64decode from base64 import b64decode
from subprocess import check_call from subprocess import check_call
@ -48,6 +49,8 @@ from charmhelpers.core.hookenv import (
from charmhelpers.core.sysctl import create as sysctl_create from charmhelpers.core.sysctl import create as sysctl_create
from charmhelpers.core.host import ( from charmhelpers.core.host import (
list_nics,
get_nic_hwaddr,
mkdir, mkdir,
write_file, write_file,
) )
@ -65,12 +68,18 @@ from charmhelpers.contrib.hahelpers.apache import (
from charmhelpers.contrib.openstack.neutron import ( from charmhelpers.contrib.openstack.neutron import (
neutron_plugin_attribute, neutron_plugin_attribute,
) )
from charmhelpers.contrib.openstack.ip import (
resolve_address,
INTERNAL,
)
from charmhelpers.contrib.network.ip import ( from charmhelpers.contrib.network.ip import (
get_address_in_network, get_address_in_network,
get_ipv4_addr,
get_ipv6_addr, get_ipv6_addr,
get_netmask_for_address, get_netmask_for_address,
format_ipv6_addr, format_ipv6_addr,
is_address_in_network, is_address_in_network,
is_bridge_member,
) )
from charmhelpers.contrib.openstack.utils import get_host_ip from charmhelpers.contrib.openstack.utils import get_host_ip
@ -727,7 +736,14 @@ class ApacheSSLContext(OSContextGenerator):
'endpoints': [], 'endpoints': [],
'ext_ports': []} 'ext_ports': []}
for cn in self.canonical_names(): cns = self.canonical_names()
if cns:
for cn in cns:
self.configure_cert(cn)
else:
# Expect cert/key provided in config (currently assumed that ca
# uses ip for cn)
cn = resolve_address(endpoint_type=INTERNAL)
self.configure_cert(cn) self.configure_cert(cn)
addresses = self.get_network_addresses() addresses = self.get_network_addresses()
@ -883,6 +899,48 @@ class NeutronContext(OSContextGenerator):
return ctxt return ctxt
class NeutronPortContext(OSContextGenerator):
NIC_PREFIXES = ['eth', 'bond']
def resolve_ports(self, ports):
"""Resolve NICs not yet bound to bridge(s)
If hwaddress provided then returns resolved hwaddress otherwise NIC.
"""
if not ports:
return None
hwaddr_to_nic = {}
hwaddr_to_ip = {}
for nic in list_nics(self.NIC_PREFIXES):
hwaddr = get_nic_hwaddr(nic)
hwaddr_to_nic[hwaddr] = nic
addresses = get_ipv4_addr(nic, fatal=False)
addresses += get_ipv6_addr(iface=nic, fatal=False)
hwaddr_to_ip[hwaddr] = addresses
resolved = []
mac_regex = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
for entry in ports:
if re.match(mac_regex, entry):
# NIC is in known NICs and does NOT hace an IP address
if entry in hwaddr_to_nic and not hwaddr_to_ip[entry]:
# If the nic is part of a bridge then don't use it
if is_bridge_member(hwaddr_to_nic[entry]):
continue
# Entry is a MAC address for a valid interface that doesn't
# have an IP address assigned yet.
resolved.append(hwaddr_to_nic[entry])
else:
# If the passed entry is not a MAC address, assume it's a valid
# interface, and that the user put it there on purpose (we can
# trust it to be the real external network).
resolved.append(entry)
return resolved
class OSConfigFlagContext(OSContextGenerator): class OSConfigFlagContext(OSContextGenerator):
"""Provides support for user-defined config flags. """Provides support for user-defined config flags.

View File

@ -16,6 +16,7 @@
# Various utilies for dealing with Neutron and the renaming from Quantum. # Various utilies for dealing with Neutron and the renaming from Quantum.
import six
from subprocess import check_output from subprocess import check_output
from charmhelpers.core.hookenv import ( from charmhelpers.core.hookenv import (
@ -237,3 +238,72 @@ def network_manager():
else: else:
# ensure accurate naming for all releases post-H # ensure accurate naming for all releases post-H
return 'neutron' return 'neutron'
def parse_mappings(mappings):
parsed = {}
if mappings:
mappings = mappings.split(' ')
for m in mappings:
p = m.partition(':')
if p[1] == ':':
parsed[p[0].strip()] = p[2].strip()
return parsed
def parse_bridge_mappings(mappings):
"""Parse bridge mappings.
Mappings must be a space-delimited list of provider:bridge mappings.
Returns dict of the form {provider:bridge}.
"""
return parse_mappings(mappings)
def parse_data_port_mappings(mappings, default_bridge='br-data'):
"""Parse data port mappings.
Mappings must be a space-delimited list of bridge:port mappings.
Returns dict of the form {bridge:port}.
"""
_mappings = parse_mappings(mappings)
if not _mappings:
if not mappings:
return {}
# For backwards-compatibility we need to support port-only provided in
# config.
_mappings = {default_bridge: mappings.split(' ')[0]}
bridges = _mappings.keys()
ports = _mappings.values()
if len(set(bridges)) != len(bridges):
raise Exception("It is not allowed to have more than one port "
"configured on the same bridge")
if len(set(ports)) != len(ports):
raise Exception("It is not allowed to have the same port configured "
"on more than one bridge")
return _mappings
def parse_vlan_range_mappings(mappings):
"""Parse vlan range mappings.
Mappings must be a space-delimited list of provider:start:end mappings.
Returns dict of the form {provider: (start, end)}.
"""
_mappings = parse_mappings(mappings)
if not _mappings:
return {}
mappings = {}
for p, r in six.iteritems(_mappings):
mappings[p] = tuple(r.split(':'))
return mappings

View File

@ -566,3 +566,29 @@ class Hooks(object):
def charm_dir(): def charm_dir():
"""Return the root directory of the current charm""" """Return the root directory of the current charm"""
return os.environ.get('CHARM_DIR') return os.environ.get('CHARM_DIR')
@cached
def action_get(key=None):
"""Gets the value of an action parameter, or all key/value param pairs"""
cmd = ['action-get']
if key is not None:
cmd.append(key)
cmd.append('--format=json')
action_data = json.loads(subprocess.check_output(cmd).decode('UTF-8'))
return action_data
def action_set(values):
"""Sets the values to be returned after the action finishes"""
cmd = ['action-set']
for k, v in list(values.items()):
cmd.append('{}={}'.format(k, v))
subprocess.check_call(cmd)
def action_fail(message):
"""Sets the action status to failed and sets the error message.
The results set by action_set are preserved."""
subprocess.check_call(['action-fail', message])

View File

@ -339,12 +339,16 @@ def lsb_release():
def pwgen(length=None): def pwgen(length=None):
"""Generate a random pasword.""" """Generate a random pasword."""
if length is None: if length is None:
# A random length is ok to use a weak PRNG
length = random.choice(range(35, 45)) length = random.choice(range(35, 45))
alphanumeric_chars = [ alphanumeric_chars = [
l for l in (string.ascii_letters + string.digits) l for l in (string.ascii_letters + string.digits)
if l not in 'l0QD1vAEIOUaeiou'] if l not in 'l0QD1vAEIOUaeiou']
# Use a crypto-friendly PRNG (e.g. /dev/urandom) for making the
# actual password
random_generator = random.SystemRandom()
random_chars = [ random_chars = [
random.choice(alphanumeric_chars) for _ in range(length)] random_generator.choice(alphanumeric_chars) for _ in range(length)]
return(''.join(random_chars)) return(''.join(random_chars))

View File

@ -139,7 +139,7 @@ class MysqlRelation(RelationContext):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.required_keys = ['host', 'user', 'password', 'database'] self.required_keys = ['host', 'user', 'password', 'database']
super(HttpRelation).__init__(self, *args, **kwargs) RelationContext.__init__(self, *args, **kwargs)
class HttpRelation(RelationContext): class HttpRelation(RelationContext):
@ -154,7 +154,7 @@ class HttpRelation(RelationContext):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.required_keys = ['host', 'port'] self.required_keys = ['host', 'port']
super(HttpRelation).__init__(self, *args, **kwargs) RelationContext.__init__(self, *args, **kwargs)
def provide_data(self): def provide_data(self):
return { return {

View File

@ -5,17 +5,25 @@ from charmhelpers.core.hookenv import (
config, config,
unit_get, unit_get,
) )
from charmhelpers.core.host import list_nics, get_nic_hwaddr
from charmhelpers.core.strutils import bool_from_string from charmhelpers.core.strutils import bool_from_string
from charmhelpers.contrib.openstack import context from charmhelpers.contrib.openstack import context
from charmhelpers.core.host import service_running, service_start from charmhelpers.core.host import (
service_running,
service_start,
service_restart,
)
from charmhelpers.contrib.network.ovs import add_bridge, add_bridge_port from charmhelpers.contrib.network.ovs import add_bridge, add_bridge_port
from charmhelpers.contrib.openstack.utils import get_host_ip from charmhelpers.contrib.openstack.utils import get_host_ip
from charmhelpers.contrib.network.ip import get_address_in_network from charmhelpers.contrib.network.ip import get_address_in_network
import re from charmhelpers.contrib.openstack.neutron import (
parse_bridge_mappings,
parse_data_port_mappings,
parse_vlan_range_mappings,
)
from charmhelpers.core.host import (
get_nic_hwaddr,
)
OVS_BRIDGE = 'br-int' OVS_BRIDGE = 'br-int'
DATA_BRIDGE = 'br-data'
def _neutron_api_settings(): def _neutron_api_settings():
@ -27,23 +35,46 @@ def _neutron_api_settings():
'l2_population': True, 'l2_population': True,
'overlay_network_type': 'gre', 'overlay_network_type': 'gre',
} }
for rid in relation_ids('neutron-plugin-api'): for rid in relation_ids('neutron-plugin-api'):
for unit in related_units(rid): for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit) rdata = relation_get(rid=rid, unit=unit)
if 'l2-population' not in rdata: if 'l2-population' in rdata:
continue neutron_settings.update({
neutron_settings = {
'l2_population': bool_from_string(rdata['l2-population']), 'l2_population': bool_from_string(rdata['l2-population']),
'overlay_network_type': rdata['overlay-network-type'], 'overlay_network_type': rdata['overlay-network-type'],
'neutron_security_groups': bool_from_string( 'neutron_security_groups':
rdata['neutron-security-groups'] bool_from_string(rdata['neutron-security-groups'])
), })
}
# Override with configuration if set to true # Override with configuration if set to true
if config('disable-security-groups'): if config('disable-security-groups'):
neutron_settings['neutron_security_groups'] = False neutron_settings['neutron_security_groups'] = False
net_dev_mtu = rdata.get('network-device-mtu')
if net_dev_mtu:
neutron_settings['network_device_mtu'] = net_dev_mtu
return neutron_settings return neutron_settings
return neutron_settings
class DataPortContext(context.NeutronPortContext):
def __call__(self):
ports = config('data-port')
if ports:
portmap = parse_data_port_mappings(ports)
ports = portmap.values()
resolved = self.resolve_ports(ports)
normalized = {get_nic_hwaddr(port): port for port in resolved
if port not in ports}
normalized.update({port: port for port in resolved
if port in ports})
if resolved:
return {bridge: normalized[port] for bridge, port in
portmap.iteritems() if port in normalized.keys()}
return None
class OVSPluginContext(context.NeutronContext): class OVSPluginContext(context.NeutronContext):
@ -62,31 +93,23 @@ class OVSPluginContext(context.NeutronContext):
neutron_api_settings = _neutron_api_settings() neutron_api_settings = _neutron_api_settings()
return neutron_api_settings['neutron_security_groups'] return neutron_api_settings['neutron_security_groups']
def get_data_port(self):
data_ports = config('data-port')
if not data_ports:
return None
hwaddrs = {}
for nic in list_nics(['eth', 'bond']):
hwaddrs[get_nic_hwaddr(nic).lower()] = nic
mac_regex = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
for entry in data_ports.split():
entry = entry.strip().lower()
if re.match(mac_regex, entry):
if entry in hwaddrs:
return hwaddrs[entry]
else:
return entry
return None
def _ensure_bridge(self): def _ensure_bridge(self):
if not service_running('openvswitch-switch'): if not service_running('openvswitch-switch'):
service_start('openvswitch-switch') service_start('openvswitch-switch')
add_bridge(OVS_BRIDGE) add_bridge(OVS_BRIDGE)
add_bridge(DATA_BRIDGE)
data_port = self.get_data_port() portmaps = DataPortContext()()
if data_port: bridgemaps = parse_bridge_mappings(config('bridge-mappings'))
add_bridge_port(DATA_BRIDGE, data_port, promisc=True) for provider, br in bridgemaps.iteritems():
add_bridge(br)
if not portmaps or br not in portmaps:
continue
add_bridge_port(br, portmaps[br], promisc=True)
service_restart('os-charm-phy-nic-mtu')
def ovs_ctxt(self): def ovs_ctxt(self):
# In addition to generating config context, ensure the OVS service # In addition to generating config context, ensure the OVS service
@ -112,4 +135,40 @@ class OVSPluginContext(context.NeutronContext):
ovs_ctxt['use_syslog'] = conf['use-syslog'] ovs_ctxt['use_syslog'] = conf['use-syslog']
ovs_ctxt['verbose'] = conf['verbose'] ovs_ctxt['verbose'] = conf['verbose']
ovs_ctxt['debug'] = conf['debug'] ovs_ctxt['debug'] = conf['debug']
net_dev_mtu = neutron_api_settings.get('network_device_mtu')
if net_dev_mtu:
# neutron.conf
ovs_ctxt['network_device_mtu'] = net_dev_mtu
# ml2 conf
ovs_ctxt['veth_mtu'] = net_dev_mtu
mappings = config('bridge-mappings')
if mappings:
ovs_ctxt['bridge_mappings'] = mappings
vlan_ranges = config('vlan-ranges')
vlan_range_mappings = parse_vlan_range_mappings(config('vlan-ranges'))
if vlan_ranges:
providers = vlan_range_mappings.keys()
ovs_ctxt['network_providers'] = ' '.join(providers)
ovs_ctxt['vlan_ranges'] = vlan_ranges
return ovs_ctxt return ovs_ctxt
class PhyNICMTUContext(DataPortContext):
"""Context used to apply settings to neutron data-port devices"""
def __call__(self):
ctxt = {}
mappings = super(PhyNICMTUContext, self).__call__()
if mappings and mappings.values():
ports = mappings.values()
neutron_api_settings = _neutron_api_settings()
mtu = neutron_api_settings.get('network_device_mtu')
if mtu:
ctxt['devs'] = '\\n'.join(ports)
ctxt['mtu'] = mtu
return ctxt

View File

@ -13,6 +13,8 @@ NEUTRON_CONF_DIR = "/etc/neutron"
NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR NEUTRON_CONF = '%s/neutron.conf' % NEUTRON_CONF_DIR
NEUTRON_DEFAULT = '/etc/default/neutron-server' NEUTRON_DEFAULT = '/etc/default/neutron-server'
ML2_CONF = '%s/plugins/ml2/ml2_conf.ini' % NEUTRON_CONF_DIR ML2_CONF = '%s/plugins/ml2/ml2_conf.ini' % NEUTRON_CONF_DIR
PHY_NIC_MTU_CONF = '/etc/init/os-charm-phy-nic-mtu.conf'
TEMPLATES = 'templates/'
BASE_RESOURCE_MAP = OrderedDict([ BASE_RESOURCE_MAP = OrderedDict([
(NEUTRON_CONF, { (NEUTRON_CONF, {
@ -24,8 +26,11 @@ BASE_RESOURCE_MAP = OrderedDict([
'services': ['neutron-plugin-openvswitch-agent'], 'services': ['neutron-plugin-openvswitch-agent'],
'contexts': [neutron_ovs_context.OVSPluginContext()], 'contexts': [neutron_ovs_context.OVSPluginContext()],
}), }),
(PHY_NIC_MTU_CONF, {
'services': ['os-charm-phy-nic-mtu'],
'contexts': [neutron_ovs_context.PhyNICMTUContext()],
}),
]) ])
TEMPLATES = 'templates/'
def determine_packages(): def determine_packages():

View File

@ -16,19 +16,22 @@ tunnel_id_ranges = 1:1000
vni_ranges = 1001:2000 vni_ranges = 1001:2000
[ml2_type_vlan] [ml2_type_vlan]
network_vlan_ranges = physnet1:1000:2000 network_vlan_ranges = {{ vlan_ranges }}
[ml2_type_flat] [ml2_type_flat]
flat_networks = physnet1 flat_networks = {{ network_providers }}
[ovs] [ovs]
enable_tunneling = True enable_tunneling = True
local_ip = {{ local_ip }} local_ip = {{ local_ip }}
bridge_mappings = physnet1:br-data bridge_mappings = {{ bridge_mappings }}
[agent] [agent]
tunnel_types = {{ overlay_network_type }} tunnel_types = {{ overlay_network_type }}
l2_population = {{ l2_population }} l2_population = {{ l2_population }}
{% if veth_mtu -%}
veth_mtu = {{ veth_mtu }}
{% endif %}
[securitygroup] [securitygroup]
{% if neutron_security_groups -%} {% if neutron_security_groups -%}

View File

@ -1,4 +1,4 @@
# grizzly # icehouse
############################################################################### ###############################################################################
# [ WARNING ] # [ WARNING ]
# Configuration file maintained by Juju. Local changes may be overwritten. # Configuration file maintained by Juju. Local changes may be overwritten.
@ -12,6 +12,7 @@ state_path = /var/lib/neutron
lock_path = $state_path/lock lock_path = $state_path/lock
bind_host = 0.0.0.0 bind_host = 0.0.0.0
bind_port = 9696 bind_port = 9696
network_device_mtu = {{ network_device_mtu }}
{% if core_plugin -%} {% if core_plugin -%}
core_plugin = {{ core_plugin }} core_plugin = {{ core_plugin }}

View File

@ -0,0 +1,22 @@
description "Enabling Quantum external networking port"
start on runlevel [2345]
task
script
devs="{{ devs }}"
mtu="{{ mtu }}"
tmpfile=`mktemp`
echo $devs > $tmpfile
if [ -n "$mtu" ]; then
while read -r dev; do
[ -n "$dev" ] || continue
rc=0
# Try all devices before exiting with error
ip link set $dev mtu $mtu || rc=$?
done < $tmpfile
rm $tmpfile
[ $rc = 0 ] || exit $rc
fi
end script

View File

@ -14,8 +14,6 @@ TO_PATCH = [
'service_running', 'service_running',
'service_start', 'service_start',
'get_host_ip', 'get_host_ip',
'get_nic_hwaddr',
'list_nics',
] ]
@ -32,34 +30,45 @@ class OVSPluginContextTest(CharmTestCase):
def tearDown(self): def tearDown(self):
super(OVSPluginContextTest, self).tearDown() super(OVSPluginContextTest, self).tearDown()
def test_data_port_name(self): @patch('charmhelpers.contrib.openstack.context.NeutronPortContext.'
self.test_config.set('data-port', 'em1') 'resolve_ports')
self.assertEquals(context.OVSPluginContext().get_data_port(), 'em1') def test_data_port_name(self, mock_resolve_ports):
self.test_config.set('data-port', 'br-data:em1')
mock_resolve_ports.side_effect = lambda ports: ports
self.assertEquals(context.DataPortContext()(),
{'br-data': 'em1'})
def test_data_port_mac(self): @patch.object(context, 'get_nic_hwaddr')
@patch('charmhelpers.contrib.openstack.context.get_nic_hwaddr')
@patch('charmhelpers.contrib.openstack.context.list_nics')
def test_data_port_mac(self, list_nics, get_nic_hwaddr, get_nic_hwaddr2):
machine_machs = { machine_machs = {
'em1': 'aa:aa:aa:aa:aa:aa', 'em1': 'aa:aa:aa:aa:aa:aa',
'eth0': 'bb:bb:bb:bb:bb:bb', 'eth0': 'bb:bb:bb:bb:bb:bb',
} }
get_nic_hwaddr2.side_effect = lambda nic: machine_machs[nic]
absent_mac = "cc:cc:cc:cc:cc:cc" absent_mac = "cc:cc:cc:cc:cc:cc"
config_macs = "%s %s" % (absent_mac, machine_machs['em1']) config_macs = ("br-d1:%s br-d2:%s" %
(absent_mac, machine_machs['em1']))
self.test_config.set('data-port', config_macs) self.test_config.set('data-port', config_macs)
list_nics.return_value = machine_machs.keys()
get_nic_hwaddr.side_effect = lambda nic: machine_machs[nic]
self.assertEquals(context.DataPortContext()(),
{'br-d2': 'em1'})
def get_hwaddr(eth): @patch('charmhelpers.contrib.openstack.context.NeutronPortContext.'
return machine_machs[eth] 'resolve_ports')
self.get_nic_hwaddr.side_effect = get_hwaddr def test_ensure_bridge_data_port_present(self, mock_resolve_ports):
self.list_nics.return_value = machine_machs.keys() self.test_config.set('data-port', 'br-data:em1')
self.assertEquals(context.OVSPluginContext().get_data_port(), 'em1') self.test_config.set('bridge-mappings', 'phybr1:br-data')
@patch.object(context.OVSPluginContext, 'get_data_port')
def test_ensure_bridge_data_port_present(self, get_data_port):
def add_port(bridge, port, promisc): def add_port(bridge, port, promisc):
if bridge == 'br-data' and port == 'em1' and promisc is True: if bridge == 'br-data' and port == 'em1' and promisc is True:
self.bridge_added = True self.bridge_added = True
return return
self.bridge_added = False self.bridge_added = False
get_data_port.return_value = 'em1' mock_resolve_ports.side_effect = lambda ports: ports
self.add_bridge_port.side_effect = add_port self.add_bridge_port.side_effect = add_port
context.OVSPluginContext()._ensure_bridge() context.OVSPluginContext()._ensure_bridge()
self.assertEquals(self.bridge_added, True) self.assertEquals(self.bridge_added, True)
@ -90,6 +99,7 @@ class OVSPluginContextTest(CharmTestCase):
self.relation_ids.return_value = ['rid2'] self.relation_ids.return_value = ['rid2']
self.test_relation.set({'neutron-security-groups': 'True', self.test_relation.set({'neutron-security-groups': 'True',
'l2-population': 'True', 'l2-population': 'True',
'network-device-mtu': 1500,
'overlay-network-type': 'gre', 'overlay-network-type': 'gre',
}) })
self.get_host_ip.return_value = '127.0.0.15' self.get_host_ip.return_value = '127.0.0.15'
@ -100,6 +110,8 @@ class OVSPluginContextTest(CharmTestCase):
'neutron_security_groups': True, 'neutron_security_groups': True,
'verbose': True, 'verbose': True,
'local_ip': '127.0.0.15', 'local_ip': '127.0.0.15',
'network_device_mtu': 1500,
'veth_mtu': 1500,
'config': 'neutron.randomconfig', 'config': 'neutron.randomconfig',
'use_syslog': True, 'use_syslog': True,
'network_manager': 'neutron', 'network_manager': 'neutron',
@ -109,6 +121,9 @@ class OVSPluginContextTest(CharmTestCase):
'neutron_url': 'https://127.0.0.13:9696', 'neutron_url': 'https://127.0.0.13:9696',
'l2_population': True, 'l2_population': True,
'overlay_network_type': 'gre', 'overlay_network_type': 'gre',
'network_providers': 'physnet1',
'bridge_mappings': 'physnet1:br-data',
'vlan_ranges': 'physnet1:1000:2000',
} }
self.assertEquals(expect, napi_ctxt()) self.assertEquals(expect, napi_ctxt())
self.service_start.assertCalled() self.service_start.assertCalled()
@ -133,6 +148,7 @@ class OVSPluginContextTest(CharmTestCase):
return "neutron.randomdriver" return "neutron.randomdriver"
if section == "config": if section == "config":
return "neutron.randomconfig" return "neutron.randomconfig"
_npa.side_effect = mock_npa _npa.side_effect = mock_npa
_config.return_value = 'ovs' _config.return_value = 'ovs'
_unit_get.return_value = '127.0.0.13' _unit_get.return_value = '127.0.0.13'
@ -143,6 +159,7 @@ class OVSPluginContextTest(CharmTestCase):
self.relation_ids.return_value = ['rid2'] self.relation_ids.return_value = ['rid2']
self.test_relation.set({'neutron-security-groups': 'True', self.test_relation.set({'neutron-security-groups': 'True',
'l2-population': 'True', 'l2-population': 'True',
'network-device-mtu': 1500,
'overlay-network-type': 'gre', 'overlay-network-type': 'gre',
}) })
self.get_host_ip.return_value = '127.0.0.15' self.get_host_ip.return_value = '127.0.0.15'
@ -153,6 +170,8 @@ class OVSPluginContextTest(CharmTestCase):
'neutron_security_groups': False, 'neutron_security_groups': False,
'verbose': True, 'verbose': True,
'local_ip': '127.0.0.15', 'local_ip': '127.0.0.15',
'veth_mtu': 1500,
'network_device_mtu': 1500,
'config': 'neutron.randomconfig', 'config': 'neutron.randomconfig',
'use_syslog': True, 'use_syslog': True,
'network_manager': 'neutron', 'network_manager': 'neutron',
@ -162,6 +181,9 @@ class OVSPluginContextTest(CharmTestCase):
'neutron_url': 'https://127.0.0.13:9696', 'neutron_url': 'https://127.0.0.13:9696',
'l2_population': True, 'l2_population': True,
'overlay_network_type': 'gre', 'overlay_network_type': 'gre',
'network_providers': 'physnet1',
'bridge_mappings': 'physnet1:br-data',
'vlan_ranges': 'physnet1:1000:2000',
} }
self.assertEquals(expect, napi_ctxt()) self.assertEquals(expect, napi_ctxt())
self.service_start.assertCalled() self.service_start.assertCalled()

View File

@ -71,7 +71,8 @@ class TestNeutronOVSUtils(CharmTestCase):
templating.OSConfigRenderer.side_effect = _mock_OSConfigRenderer templating.OSConfigRenderer.side_effect = _mock_OSConfigRenderer
_regconfs = nutils.register_configs() _regconfs = nutils.register_configs()
confs = ['/etc/neutron/neutron.conf', confs = ['/etc/neutron/neutron.conf',
'/etc/neutron/plugins/ml2/ml2_conf.ini'] '/etc/neutron/plugins/ml2/ml2_conf.ini',
'/etc/init/os-charm-phy-nic-mtu.conf']
self.assertItemsEqual(_regconfs.configs, confs) self.assertItemsEqual(_regconfs.configs, confs)
def test_resource_map(self): def test_resource_map(self):
@ -85,8 +86,9 @@ class TestNeutronOVSUtils(CharmTestCase):
expect = OrderedDict([ expect = OrderedDict([
(nutils.NEUTRON_CONF, ['neutron-plugin-openvswitch-agent']), (nutils.NEUTRON_CONF, ['neutron-plugin-openvswitch-agent']),
(ML2CONF, ['neutron-plugin-openvswitch-agent']), (ML2CONF, ['neutron-plugin-openvswitch-agent']),
(nutils.PHY_NIC_MTU_CONF, ['os-charm-phy-nic-mtu'])
]) ])
self.assertTrue(len(expect) == len(_restart_map)) self.assertEqual(expect, _restart_map)
for item in _restart_map: for item in _restart_map:
self.assertTrue(item in _restart_map) self.assertTrue(item in _restart_map)
self.assertTrue(expect[item] == _restart_map[item]) self.assertTrue(expect[item] == _restart_map[item])