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 neutron.agent.common import config
from neutron.agent import rpc as agent_rpc
from neutron.common import config as logging_config
from neutron.common import constants as n_const
from neutron.common import topics
from neutron import context
from neutron.openstack.common import log as logging
from neutron.openstack.common import loopingcall
from neutron.openstack.common.rpc import dispatcher
from neutron.plugins.hyperv.agent import utils
from neutron.plugins.hyperv.agent import utilsfactory
@ -63,6 +66,7 @@ agent_opts = [
CONF = cfg.CONF
CONF.register_opts(agent_opts, "AGENT")
config.register_agent_state_opts_helper(cfg.CONF)
class HyperVNeutronAgent(object):
@ -74,13 +78,34 @@ class HyperVNeutronAgent(object):
self._polling_interval = CONF.AGENT.polling_interval
self._load_physical_network_mappings()
self._network_vswitch_map = {}
self._set_agent_state()
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):
self.agent_id = 'hyperv_%s' % platform.node()
self.topic = topics.AGENT
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN)
# RPC network init
self.context = context.get_admin_context_without_session()
# Handle updates from service
@ -93,6 +118,10 @@ class HyperVNeutronAgent(object):
self.connection = agent_rpc.create_consumers(self.dispatcher,
self.topic,
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):
self._physical_network_mappings = {}
@ -103,14 +132,14 @@ class HyperVNeutronAgent(object):
else:
pattern = re.escape(parts[0].strip()).replace('\\*', '.*')
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):
for compre in self._physical_network_mappings:
for pattern in self._physical_network_mappings:
if phys_network_name is None:
phys_network_name = ''
if compre.match(phys_network_name):
return self._physical_network_mappings[compre]
if re.match(pattern, phys_network_name):
return self._physical_network_mappings[pattern]
# Not found in the mappings, the vswitch has the same name
return phys_network_name

View File

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

View File

@ -18,6 +18,7 @@
from neutron.common import constants as q_const
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 l3_rpc_base
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
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):
"""Agent requests device details."""

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import re
from neutron.common import constants
from neutron.extensions import portbindings
from neutron.openstack.common import log
@ -46,6 +48,10 @@ class HypervMechanismDriver(mech_agent.AgentMechanismDriverBase):
if network_type == 'local':
return True
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:
return False

View File

@ -39,11 +39,32 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
utilsfactory._get_windows_version = mock.MagicMock(
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.plugin_rpc = mock.Mock()
self.agent.context = 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):
port = mock.Mock()
net_uuid = 'my-net-uuid'
@ -112,6 +133,14 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
def test_treat_devices_removed_ignores_missing_port(self):
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):
with mock.patch.object(hyperv_neutron_agent,
'HyperVNeutronAgent') as plugin: