Merge next branch

This commit is contained in:
Corey Bryant 2015-03-21 10:32:53 +00:00
commit d368ec5edb
9 changed files with 201 additions and 60 deletions

View File

@ -41,8 +41,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
@ -52,6 +54,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

@ -0,0 +1,14 @@
{% if zmq_host -%}
# ZeroMQ configuration (restart-nonce: {{ zmq_nonce }})
rpc_backend = zmq
rpc_zmq_host = {{ zmq_host }}
{% if zmq_redis_address -%}
rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_redis.MatchMakerRedis
matchmaker_heartbeat_freq = 15
matchmaker_heartbeat_ttl = 30
[matchmaker_redis]
host = {{ zmq_redis_address }}
{% else -%}
rpc_zmq_matchmaker = oslo.messaging._drivers.matchmaker_ring.MatchMakerRing
{% endif -%}
{% endif -%}

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,25 +35,48 @@ 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':
'neutron_security_groups': bool_from_string( bool_from_string(rdata['neutron-security-groups'])
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
return neutron_settings
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
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):
interfaces = [] interfaces = []
@ -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

@ -39,6 +39,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, {
@ -50,8 +52,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])