Fixes Hyper-V agent RPC calls for ML2 support

Adds support for the RPC report status in the Hyper-V
agent and plugin.

Fixes bug: #1226654

Change-Id: Ie5eee875afc062c536856589d6a3fd0414190f6d
This commit is contained in:
Petrut Lucian 2013-09-17 19:15:41 +03:00
parent 151cff7554
commit 3e10546c25
5 changed files with 76 additions and 9 deletions

View File

@ -25,11 +25,14 @@ import time
from oslo.config import cfg from oslo.config import cfg
from neutron.agent.common import config
from neutron.agent import rpc as agent_rpc from neutron.agent import rpc as agent_rpc
from neutron.common import config as logging_config from neutron.common import config as logging_config
from neutron.common import constants as n_const
from neutron.common import topics from neutron.common import topics
from neutron import context from neutron import context
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
from neutron.openstack.common import loopingcall
from neutron.openstack.common.rpc import dispatcher from neutron.openstack.common.rpc import dispatcher
from neutron.plugins.hyperv.agent import utils from neutron.plugins.hyperv.agent import utils
from neutron.plugins.hyperv.agent import utilsfactory from neutron.plugins.hyperv.agent import utilsfactory
@ -63,6 +66,7 @@ agent_opts = [
CONF = cfg.CONF CONF = cfg.CONF
CONF.register_opts(agent_opts, "AGENT") CONF.register_opts(agent_opts, "AGENT")
config.register_agent_state_opts_helper(cfg.CONF)
class HyperVNeutronAgent(object): class HyperVNeutronAgent(object):
@ -74,13 +78,34 @@ class HyperVNeutronAgent(object):
self._polling_interval = CONF.AGENT.polling_interval self._polling_interval = CONF.AGENT.polling_interval
self._load_physical_network_mappings() self._load_physical_network_mappings()
self._network_vswitch_map = {} self._network_vswitch_map = {}
self._set_agent_state()
self._setup_rpc() self._setup_rpc()
def _set_agent_state(self):
self.agent_state = {
'binary': 'neutron-hyperv-agent',
'host': cfg.CONF.host,
'topic': n_const.L2_AGENT_TOPIC,
'configurations': {'vswitch_mappings':
self._physical_network_mappings},
'agent_type': n_const.AGENT_TYPE_HYPERV,
'start_flag': True}
def _report_state(self):
try:
self.state_rpc.report_state(self.context,
self.agent_state)
self.agent_state.pop('start_flag', None)
except Exception as ex:
LOG.exception(_("Failed reporting state! %s"), ex)
def _setup_rpc(self): def _setup_rpc(self):
self.agent_id = 'hyperv_%s' % platform.node() self.agent_id = 'hyperv_%s' % platform.node()
self.topic = topics.AGENT self.topic = topics.AGENT
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)
# RPC network init # RPC network init
self.context = context.get_admin_context_without_session() self.context = context.get_admin_context_without_session()
# Handle updates from service # Handle updates from service
@ -93,6 +118,10 @@ class HyperVNeutronAgent(object):
self.connection = agent_rpc.create_consumers(self.dispatcher, self.connection = agent_rpc.create_consumers(self.dispatcher,
self.topic, self.topic,
consumers) consumers)
report_interval = CONF.AGENT.report_interval
if report_interval:
heartbeat = loopingcall.LoopingCall(self._report_state)
heartbeat.start(interval=report_interval)
def _load_physical_network_mappings(self): def _load_physical_network_mappings(self):
self._physical_network_mappings = {} self._physical_network_mappings = {}
@ -103,14 +132,14 @@ class HyperVNeutronAgent(object):
else: else:
pattern = re.escape(parts[0].strip()).replace('\\*', '.*') pattern = re.escape(parts[0].strip()).replace('\\*', '.*')
vswitch = parts[1].strip() vswitch = parts[1].strip()
self._physical_network_mappings[re.compile(pattern)] = vswitch self._physical_network_mappings[pattern] = vswitch
def _get_vswitch_for_physical_network(self, phys_network_name): def _get_vswitch_for_physical_network(self, phys_network_name):
for compre in self._physical_network_mappings: for pattern in self._physical_network_mappings:
if phys_network_name is None: if phys_network_name is None:
phys_network_name = '' phys_network_name = ''
if compre.match(phys_network_name): if re.match(pattern, phys_network_name):
return self._physical_network_mappings[compre] return self._physical_network_mappings[pattern]
# Not found in the mappings, the vswitch has the same name # Not found in the mappings, the vswitch has the same name
return phys_network_name return phys_network_name

View File

@ -21,6 +21,7 @@ from oslo.config import cfg
from neutron.api.v2 import attributes from neutron.api.v2 import attributes
from neutron.common import exceptions as q_exc from neutron.common import exceptions as q_exc
from neutron.common import topics from neutron.common import topics
from neutron.db import agents_db
from neutron.db import db_base_plugin_v2 from neutron.db import db_base_plugin_v2
from neutron.db import external_net_db from neutron.db import external_net_db
from neutron.db import l3_gwmode_db from neutron.db import l3_gwmode_db
@ -143,7 +144,8 @@ class VlanNetworkProvider(BaseNetworkProvider):
network[provider.SEGMENTATION_ID] = binding.segmentation_id network[provider.SEGMENTATION_ID] = binding.segmentation_id
class HyperVNeutronPlugin(db_base_plugin_v2.NeutronDbPluginV2, class HyperVNeutronPlugin(agents_db.AgentDbMixin,
db_base_plugin_v2.NeutronDbPluginV2,
external_net_db.External_net_db_mixin, external_net_db.External_net_db_mixin,
l3_gwmode_db.L3_NAT_db_mixin, l3_gwmode_db.L3_NAT_db_mixin,
portbindings_base.PortBindingBaseMixin): portbindings_base.PortBindingBaseMixin):
@ -153,7 +155,7 @@ class HyperVNeutronPlugin(db_base_plugin_v2.NeutronDbPluginV2,
# is qualified by class # is qualified by class
__native_bulk_support = True __native_bulk_support = True
supported_extension_aliases = ["provider", "external-net", "router", supported_extension_aliases = ["provider", "external-net", "router",
"ext-gw-mode", "binding", "quotas"] "agent", "ext-gw-mode", "binding", "quotas"]
def __init__(self, configfile=None): def __init__(self, configfile=None):
self._db = hyperv_db.HyperVPluginDB() self._db = hyperv_db.HyperVPluginDB()
@ -165,7 +167,6 @@ class HyperVNeutronPlugin(db_base_plugin_v2.NeutronDbPluginV2,
self._parse_network_vlan_ranges() self._parse_network_vlan_ranges()
self._create_network_providers_map() self._create_network_providers_map()
self._db.sync_vlan_allocations(self._network_vlan_ranges) self._db.sync_vlan_allocations(self._network_vlan_ranges)
self._setup_rpc() self._setup_rpc()

View File

@ -18,6 +18,7 @@
from neutron.common import constants as q_const from neutron.common import constants as q_const
from neutron.common import rpc as q_rpc from neutron.common import rpc as q_rpc
from neutron.db import agents_db
from neutron.db import dhcp_rpc_base from neutron.db import dhcp_rpc_base
from neutron.db import l3_rpc_base from neutron.db import l3_rpc_base
from neutron.openstack.common import log as logging from neutron.openstack.common import log as logging
@ -44,7 +45,8 @@ class HyperVRpcCallbacks(
If a manager would like to set an rpc API version, or support more than If a manager would like to set an rpc API version, or support more than
one class as the target of rpc messages, override this method. one class as the target of rpc messages, override this method.
''' '''
return q_rpc.PluginRpcDispatcher([self]) return q_rpc.PluginRpcDispatcher([self,
agents_db.AgentExtRpcCallback()])
def get_device_details(self, rpc_context, **kwargs): def get_device_details(self, rpc_context, **kwargs):
"""Agent requests device details.""" """Agent requests device details."""

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import re
from neutron.common import constants from neutron.common import constants
from neutron.extensions import portbindings from neutron.extensions import portbindings
from neutron.openstack.common import log from neutron.openstack.common import log
@ -46,6 +48,10 @@ class HypervMechanismDriver(mech_agent.AgentMechanismDriverBase):
if network_type == 'local': if network_type == 'local':
return True return True
elif network_type in ['flat', 'vlan']: elif network_type in ['flat', 'vlan']:
return segment[api.PHYSICAL_NETWORK] in mappings for pattern in mappings:
if re.match(pattern, segment[api.PHYSICAL_NETWORK]):
return True
else:
return False
else: else:
return False return False

View File

@ -39,11 +39,32 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
utilsfactory._get_windows_version = mock.MagicMock( utilsfactory._get_windows_version = mock.MagicMock(
return_value='6.2.0') return_value='6.2.0')
class MockFixedIntervalLoopingCall(object):
def __init__(self, f):
self.f = f
def start(self, interval=0):
self.f()
mock.patch('neutron.openstack.common.loopingcall.'
'FixedIntervalLoopingCall',
new=MockFixedIntervalLoopingCall)
self.agent = hyperv_neutron_agent.HyperVNeutronAgent() self.agent = hyperv_neutron_agent.HyperVNeutronAgent()
self.agent.plugin_rpc = mock.Mock() self.agent.plugin_rpc = mock.Mock()
self.agent.context = mock.Mock() self.agent.context = mock.Mock()
self.agent.agent_id = mock.Mock() self.agent.agent_id = mock.Mock()
fake_agent_state = {
'binary': 'neutron-hyperv-agent',
'host': 'fake_host_name',
'topic': 'N/A',
'configurations': {'vswitch_mappings': ['*:MyVirtualSwitch']},
'agent_type': 'HyperV agent',
'start_flag': True}
self.agent_state = fake_agent_state
def test_port_bound(self): def test_port_bound(self):
port = mock.Mock() port = mock.Mock()
net_uuid = 'my-net-uuid' net_uuid = 'my-net-uuid'
@ -112,6 +133,14 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
def test_treat_devices_removed_ignores_missing_port(self): def test_treat_devices_removed_ignores_missing_port(self):
self.mock_treat_devices_removed(False) self.mock_treat_devices_removed(False)
def test_report_state(self):
with mock.patch.object(self.agent.state_rpc,
"report_state") as report_st:
self.agent._report_state()
report_st.assert_called_with(self.agent.context,
self.agent.agent_state)
self.assertNotIn("start_flag", self.agent.agent_state)
def test_main(self): def test_main(self):
with mock.patch.object(hyperv_neutron_agent, with mock.patch.object(hyperv_neutron_agent,
'HyperVNeutronAgent') as plugin: 'HyperVNeutronAgent') as plugin: