Support physical network mappings

Add a new configure option named `physical_vswitch_mappings`.
The example format is <physical_network>:<physical_vswitch>.
It maps physical_network to appropriate node-specific physical
vswitches on each agent.

Code changes:
- New configure option added: physical_vswitch_mappings.
  The usage of flat_networks in ml2_conf.ini is removed.
- When init agent, check the physical_vswitch_mappings first.
- When binding port, mapping physical_network to
  physical_vswitch.
- Fix vswitch_create parameter vid to type int
- neutron_zvm_plugin.ini.sample updated
- UT cases updated

Change-Id: Ic61197432d1f5fa559d08aabba65f0959a319620
Signed-off-by: QianXi <qianxii@cn.ibm.com>
changes/16/710916/2
QianXi 3 years ago committed by jichenjc
parent de4a996811
commit e87d0282f6
  1. 28
      etc/neutron/plugins/zvm/neutron_zvm_plugin.ini.sample
  2. 43
      neutron/plugins/zvm/agent/zvm_network.py
  3. 44
      neutron/plugins/zvm/agent/zvm_neutron_agent.py
  4. 13
      neutron/plugins/zvm/common/config.py
  5. 12
      neutron/tests/unit/plugins/zvm/test_zvm_network.py
  6. 19
      neutron/tests/unit/plugins/zvm/test_zvm_neutron_agent.py

@ -15,6 +15,14 @@
# cloud_connector_url = https://10.10.10.1:8080
# Example: cloud_connector_url = https://10.10.10.1:8080
# (ListOpt) Comma-separated list of <physical_network>:<physical_vswitch>
# tuples mapping physical network names to the agent's node-specific physical
# vswitch to be used for flat and VLAN networks. All physical networks listed
# in network_vlan_ranges on the server should have mappings to appropriate
# vswitches on each agent. This property is required.
# Example: flatnet:vsw1,vlannet1:vsw2,vlannet2:vsw3
# physical_vswitch_mappings = flatnet:vsw1,vlannet1:vsw2,vlannet2:vsw3
# (StrOpt ) rdev_list for each vswitch's uplink real device(s), seperated by ','.
# If a vswitch does not connect an OSA, which means it does not connect to
# external network, rdev_list can be ignored.
@ -29,29 +37,30 @@
#-----------------------------------------------------------------------------
# 1. Single FLAT Mode
# Physical network names should be identical to z/VM vswitch names. In
# this sample, xcatvsw2 is used for physical network, it should be a
# Layer 2, VLAN UNAWARE vswitch in z/VM. When create neutron network, set
# xcatvsw2 provider network_type to flat
# In this sample, xcatvsw2 is used for physical vswitch, and flatnet is used for
# physical network name. xcatvsw2 should be a Layer 2, VLAN UNAWARE vswitch in
# z/VM. When create neutron network, set provider:physical_network to flatnet.
# Neutron server:
#
# [database]
# connection = mysql://root:nova@127.0.0.1:3306/neutron
# [ml2_type_flat]
# flat_networks = xcatvsw2
# flat_networks = flatnet
# Neutron z/VM Agent:
#
# [AGENT]
# polling_interval = 2
# cloud_connector_url = https://10.10.10.1:8080
# physical_vswitch_mappings = flatnet:xcatvsw2
# 2. FLAT, VLAN mixed Mode
# Physical network names should be identical to z/VM vswitch names. In
# this sample, xcatvsw2 is used for physical network it should be a
# In this sample, xcatvsw2 is used for physical vswitch, it should be a
# Layer 2, VLAN UNAWARE vswitch in z/VM, xcatvsw3 is used for OpenStack
# Compute/Data Network, it is a Layer 2, VLAN AWARE vswitch in z/VM.
# flatnet is used for physical network name as flat type and vlannet
# is used for physical network name as vlan type.
# This configuration also can used for single FLAT network mode if you
# only create only one FLAT network with xcatvsw2
@ -59,14 +68,17 @@
#
# [database]
# connection = mysql://root:nova@127.0.0.1:3306/neutron
# [ml2_type_flat]
# flat_networks = flatnet
# [ml2_type_vlan]
# network_vlan_ranges = xcatvsw3:10:100
# network_vlan_ranges = vlannet:10:100
#
# Neutron z/VM Agent:
#
# [AGENT]
# polling_interval = 2
# cloud_connector_url = https://10.10.10.1:8080
# physical_vswitch_mappings = flatnet:xcatvsw2,vlannet:xcatvsw3
#
# [xcatvsw3]
# rdev_list=6243

@ -27,8 +27,6 @@ vswitch_opts = [
help='RDev list for vswitch uplink port')]
CONF = cfg.CONF
CONF.import_opt('flat_networks', "neutron.plugins.ml2.drivers.type_flat",
'ml2_type_flat')
CONF.import_opt('network_vlan_ranges', "neutron.plugins.ml2.drivers.type_vlan",
'ml2_type_vlan')
@ -40,14 +38,17 @@ class zvmVswitch(object):
if not len(vlan):
vlan = 'UNAWARE'
else:
vlan = str(vlan[0][0])
# When create vlan awared vswitch, the vid
# should be int in `vswitch_create` schema
vlan = int(vlan[0][0])
self._requesthandler.call('vswitch_create', name,
rdev=getattr(CONF.get(name), "rdev_list"),
vid=vlan, network_type='ETHERNET')
class zvmNetwork(object):
def __init__(self):
def __init__(self, vswitch_mappings):
self.vswitch_mappings = vswitch_mappings
self._requesthandler = utils.zVMConnectorRequestHandler()
self._vsws = []
self._maps = {}
@ -55,17 +56,43 @@ class zvmNetwork(object):
def _creat_networks(self):
admin_vsw = self._requesthandler.call('vswitch_get_list')
# This dict maps physical network to its vlan range.
# Networks which are not in network_vlan_ranges are
# considered as flat.
self._maps = plugin_utils.parse_network_vlan_ranges(
CONF.ml2_type_vlan.network_vlan_ranges +
CONF.ml2_type_flat.flat_networks)
list(self.vswitch_mappings.keys()))
self._vsws = []
for vsw in self._maps.keys():
unmapped_physical_nets = []
for physical_net in self._maps.keys():
if physical_net not in self.vswitch_mappings:
# No vswitch mapping for this physical network, skip it
unmapped_physical_nets.append(physical_net)
continue
# Get the physical vswitch name
vsw = self.vswitch_mappings[physical_net]
CONF.register_opts(vswitch_opts, vsw)
if vsw.upper() in admin_vsw:
LOG.info('Vswitch %s is pre-created by admin or system, '
'neutron-zvm-agent will not handle it' % vsw)
'neutron-zvm-agent will not handle it' % vsw)
else:
self._vsws.append(zvmVswitch(vsw, self._maps[vsw]))
self._vsws.append(zvmVswitch(vsw,
self._maps[physical_net]))
# Log the physical networks which do not map to physical vswitches
# and continue the running.
if unmapped_physical_nets:
LOG.error("Unknown physical vswitch names for "
"physical networks %(unmapped_physical_nets)s. "
"Supported physical networks: "
"%(mapped_physical_nets)s. "
"Please check physical_vswitch_mappings "
"and network_vlan_ranges in conf files. "
"All physical networks listed in "
"network_vlan_ranges on the server should "
"have mappings to appropriate vswitches.", {
'unmapped_physical_nets': unmapped_physical_nets,
'mapped_physical_nets': list(
self.vswitch_mappings.keys())})
def get_network_maps(self):
return self._maps

@ -21,6 +21,7 @@ import time
from neutron_lib.agent import topics
from neutron_lib import constants as q_const
from neutron_lib import context
from neutron_lib.utils import helpers
from oslo_log import log as logging
from oslo_service import loopingcall
@ -47,8 +48,9 @@ def restart_wrapper(func):
class zvmNeutronAgent(object):
RPC_API_VERSION = '1.1'
def __init__(self):
def __init__(self, vswitch_mappings):
super(zvmNeutronAgent, self).__init__()
self.vswitch_mappings = vswitch_mappings
self._requesthandler = utils.zVMConnectorRequestHandler()
self._utils = utils.zvmUtils()
self._polling_interval = CONF.AGENT.polling_interval
@ -56,7 +58,7 @@ class zvmNeutronAgent(object):
'zvm_host') or CONF.host
self._port_map = {}
zvm_net = zvm_network.zvmNetwork()
zvm_net = zvm_network.zvmNetwork(self.vswitch_mappings)
self.agent_state = {
'binary': 'neutron-zvm-agent',
'host': self._host,
@ -231,6 +233,21 @@ class zvmNeutronAgent(object):
LOG.info("Port %(device)s updated. "
"Details: %(details)s",
{'device': device, 'details': details})
if (details['physical_network'] not in
self.vswitch_mappings):
errmsg = ("Unknown physical vswitch name for "
"physical network %(physical_net)s. "
"Please check physical_vswitch_mappings. "
"Supported physical networks: "
"%(mapped_physical_nets)s.") % {
'physical_net': details['physical_network'],
'mapped_physical_nets': list(
self.vswitch_mappings.keys())}
raise exception.zVMInvalidDataError(msg=errmsg)
# Update physical_network to physical vswitch
details['physical_network'] = self.vswitch_mappings[
details['physical_network']]
userid = self._treat_vif_port(
details['port_id'],
details['network_id'],
@ -253,8 +270,8 @@ class zvmNeutronAgent(object):
nics_info[userid] = []
nics_info[userid].append(
{'port_id': details['port_id'],
'vswitch': details['physical_network'],
'mac': mac})
'vswitch': details['physical_network'],
'mac': mac})
LOG.debug("Adding NICs for %(userid)s, info: %(nic)s",
{'userid': userid, 'nic': nics_info[userid]})
@ -372,7 +389,24 @@ def main():
common_config.init(sys.argv[1:])
common_config.setup_logging()
agent = zvmNeutronAgent()
try:
# Example for vswitch_mappings is
# {'provider': 'vswitch01'}
vswitch_mappings = helpers.parse_mappings(
cfg.CONF.AGENT.physical_vswitch_mappings)
except ValueError as e:
LOG.error("Parsing physical_vswitch_mappings failed: %s. "
"Agent terminated!", e)
sys.exit(1)
if not vswitch_mappings:
LOG.error("Invalid physical_vswitch_mappings: can NOT be "
"empty. Agent terminated!")
sys.exit(1)
LOG.info("Vswitch mappings: %s", vswitch_mappings)
agent = zvmNeutronAgent(vswitch_mappings)
# Start to query ZVMSDK DB
LOG.info("z/VM agent initialized, now running... ")

@ -19,13 +19,14 @@ from oslo_config import cfg
from neutron._i18n import _
from neutron.conf.agent import common as config
DEFAULT_VSWITCH_MAPPINGS = []
agent_opts = [
cfg.IntOpt(
'polling_interval',
default=2,
help=_("The number of seconds the agent will wait between "
"polling for local device changes.")),
"polling for local device changes.")),
cfg.URIOpt('cloud_connector_url',
schemes=['http', 'https'],
sample_default='http://zvm.example.org:8080/',
@ -43,6 +44,16 @@ A string, it must be a path to a CA bundle to use.
help="""
Token file that contains admin-token to access sdk http server.
"""),
cfg.ListOpt('physical_vswitch_mappings',
default=DEFAULT_VSWITCH_MAPPINGS,
help=_("Comma-separated list of "
"<physical_network>:<physical_vswitch> tuples "
"mapping physical network names to the agent's "
"node-specific physical vswitch to be used "
"for flat and VLAN networks. All physical networks "
"listed in network_vlan_ranges on the server should "
"have mappings to appropriate vswitches on each "
"agent.")),
]
CONF = cfg.CONF

@ -22,11 +22,14 @@ from oslo_config import cfg
from neutron.plugins.zvm.agent import zvm_network
from neutron.plugins.zvm.common import utils as zvmutils
from neutron.tests import base
from neutron_lib.utils import helpers
SDK_URL = 'https://10.10.10.1:8080'
FLAT_NETWORKS = ['flat_net1', '9dotvsw']
VLAN_NETWORKS = ['vlan_net1:100:500', '10dotvsw:10:100']
PHYSICAL_VSWITCH_MAPPINGS = ("flat_net1:flat_net1,9dotvsw:9dotvsw,"
"vlan_net1:vlan_net1,10dotvsw:10dotvsw")
NETWORK_MAPS = {'vlan_net1': [(100, 500)], 'flat_net1': [],
'9dotvsw': [], '10dotvsw': [(10, 100)]}
@ -38,14 +41,17 @@ class TestZVMNetwork(base.BaseTestCase):
@mock.patch.object(zvmutils.zVMConnectorRequestHandler, 'call')
def setUp(self, call):
super(TestZVMNetwork, self).setUp()
cfg.CONF.set_override('flat_networks', FLAT_NETWORKS,
group='ml2_type_flat')
cfg.CONF.set_override('physical_vswitch_mappings',
PHYSICAL_VSWITCH_MAPPINGS,
group='AGENT')
cfg.CONF.set_override('network_vlan_ranges', VLAN_NETWORKS,
group='ml2_type_vlan')
cfg.CONF.set_override('cloud_connector_url', SDK_URL,
group='AGENT')
call.return_value = []
self._zvm_network = zvm_network.zvmNetwork()
vswitch_mappings = helpers.parse_mappings(
cfg.CONF.AGENT.physical_vswitch_mappings)
self._zvm_network = zvm_network.zvmNetwork(vswitch_mappings)
def test_init_driver(self):
self.assertIsInstance(self._zvm_network._requesthandler,

@ -21,10 +21,12 @@ from oslo_config import cfg
from neutron.plugins.zvm.agent import zvm_neutron_agent
from neutron.tests import base
from neutron_lib.utils import helpers
SDK_URL = 'https://10.10.10.1:8080'
FLAT_NETWORKS = ['flat_net1']
VLAN_NETWORKS = ['vlan_net1:100:500']
PHYSICAL_VSWITCH_MAPPINGS = 'flat_net1:flat_net1,vlan_net1:vlan_net1'
NET_UUID = 'zvm-net-uuid'
PORT_UUID = 'zvm-port-uuid'
@ -44,8 +46,9 @@ class TestZVMNeutronAgent(base.BaseTestCase):
self.addCleanup(cfg.CONF.reset)
cfg.CONF.set_override('cloud_connector_url', SDK_URL,
group='AGENT')
cfg.CONF.set_override('flat_networks', FLAT_NETWORKS,
group='ml2_type_flat')
cfg.CONF.set_override('physical_vswitch_mappings',
PHYSICAL_VSWITCH_MAPPINGS,
group='AGENT')
cfg.CONF.set_override('network_vlan_ranges', VLAN_NETWORKS,
group='ml2_type_vlan')
@ -69,7 +72,11 @@ class TestZVMNeutronAgent(base.BaseTestCase):
utils_ins.get_port_map = mock.MagicMock(
return_value=net_attrs)
self.agent = zvm_neutron_agent.zvmNeutronAgent()
vswitch_mappings = helpers.parse_mappings(
cfg.CONF.AGENT.physical_vswitch_mappings)
self.agent = zvm_neutron_agent.zvmNeutronAgent(
vswitch_mappings)
self.agent.plugin_rpc = mock.Mock()
self.agent.context = mock.Mock()
self.agent.agent_id = mock.Mock()
@ -117,7 +124,8 @@ class TestZVMNeutronAgent(base.BaseTestCase):
self.agent._treat_devices_added([])
def test_treat_devices_added_down_port(self):
details = dict(port_id='added_port_down', physical_network='vsw',
details = dict(port_id='added_port_down',
physical_network='flat_net1',
segmentation_id='10', network_id='fake_net',
mac_address='00:11:22:33:44:55',
network_type='flat', admin_state_up=False)
@ -129,7 +137,8 @@ class TestZVMNeutronAgent(base.BaseTestCase):
self.assertTrue(self.agent.plugin_rpc.update_device_down.called)
def test_treat_devices_added_up_port(self):
details = dict(port_id='added_port', physical_network='vsw',
details = dict(port_id='added_port',
physical_network='flat_net1',
segmentation_id='10', network_id='fake_net',
mac_address='00:11:22:33:44:55',
network_type='flat', admin_state_up=True)

Loading…
Cancel
Save