Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Add the option to minimize ovs l2 polling
This change adds the ability to monitor the local ovsdb for interface changes so that the l2 agent can avoid unnecessary polling. Minimal changes are made to the agent so the risk of breakage should be low. Future efforts to make the agent entirely event-based may be able to use OvsdbMonitor as a starting point. By default polling minimization is not done, and can only be enabled by setting 'minimize_polling = True' in the ovs section of the l2 agent's config file. Closes-Bug: #1177973 Change-Id: I26c035b48a74df2148696869c5a9affae5ab3d27
- Loading branch information
Showing
13 changed files
with
660 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
|
||
# Copyright 2013 Red Hat, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
# not use this file except in compliance with the License. You may obtain | ||
# a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations | ||
# under the License. | ||
|
||
import eventlet | ||
|
||
from neutron.agent.linux import async_process | ||
from neutron.openstack.common import log as logging | ||
|
||
|
||
LOG = logging.getLogger(__name__) | ||
|
||
|
||
class OvsdbMonitor(async_process.AsyncProcess): | ||
"""Manages an invocation of 'ovsdb-client monitor'.""" | ||
|
||
def __init__(self, table_name, columns=None, format=None, | ||
root_helper=None, respawn_interval=None): | ||
|
||
cmd = ['ovsdb-client', 'monitor', table_name] | ||
if columns: | ||
cmd.append(','.join(columns)) | ||
if format: | ||
cmd.append('--format=%s' % format) | ||
super(OvsdbMonitor, self).__init__(cmd, | ||
root_helper=root_helper, | ||
respawn_interval=respawn_interval) | ||
|
||
def _read_stdout(self): | ||
data = self._process.stdout.readline() | ||
if not data: | ||
return | ||
#TODO(marun) The default root helper outputs exit errors to | ||
# stdout due to bug #1219530. This check can be moved to | ||
# _read_stderr once the error is correctly output to stderr. | ||
if self.root_helper and self.root_helper in data: | ||
self._stderr_lines.put(data) | ||
LOG.error(_('Error received from ovsdb monitor: %s') % data) | ||
else: | ||
self._stdout_lines.put(data) | ||
LOG.debug(_('Output received from ovsdb monitor: %s') % data) | ||
return data | ||
|
||
def _read_stderr(self): | ||
data = super(OvsdbMonitor, self)._read_stderr() | ||
if data: | ||
LOG.error(_('Error received from ovsdb monitor: %s') % data) | ||
# Do not return value to ensure that stderr output will | ||
# stop the monitor. | ||
|
||
|
||
class SimpleInterfaceMonitor(OvsdbMonitor): | ||
"""Monitors the Interface table of the local host's ovsdb for changes. | ||
The has_updates() method indicates whether changes to the ovsdb | ||
Interface table have been detected since the monitor started or | ||
since the previous access. | ||
""" | ||
|
||
def __init__(self, root_helper=None, respawn_interval=None): | ||
super(SimpleInterfaceMonitor, self).__init__( | ||
'Interface', | ||
columns=['name'], | ||
format='json', | ||
root_helper=root_helper, | ||
respawn_interval=respawn_interval, | ||
) | ||
self.data_received = False | ||
|
||
@property | ||
def is_active(self): | ||
return (self.data_received and | ||
self._kill_event and | ||
not self._kill_event.ready()) | ||
|
||
@property | ||
def has_updates(self): | ||
"""Indicate whether the ovsdb Interface table has been updated. | ||
True will be returned if the monitor process is not active. | ||
This 'failing open' minimizes the risk of falsely indicating | ||
the absense of updates at the expense of potential false | ||
positives. | ||
""" | ||
return bool(list(self.iter_stdout())) or not self.is_active | ||
|
||
def start(self, block=False, timeout=5): | ||
super(SimpleInterfaceMonitor, self).start() | ||
if block: | ||
eventlet.timeout.Timeout(timeout) | ||
while not self.is_active: | ||
eventlet.sleep() | ||
|
||
def _kill(self, *args, **kwargs): | ||
self.data_received = False | ||
super(SimpleInterfaceMonitor, self)._kill(*args, **kwargs) | ||
|
||
def _read_stdout(self): | ||
data = super(SimpleInterfaceMonitor, self)._read_stdout() | ||
if data and not self.data_received: | ||
self.data_received = True | ||
return data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# vim: tabstop=4 shiftwidth=4 softtabstop=4 | ||
|
||
# Copyright 2013 Red Hat, Inc. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); you may | ||
# not use this file except in compliance with the License. You may obtain | ||
# a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | ||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | ||
# License for the specific language governing permissions and limitations | ||
# under the License. | ||
|
||
import contextlib | ||
|
||
import eventlet | ||
|
||
from neutron.agent.linux import ovsdb_monitor | ||
|
||
|
||
@contextlib.contextmanager | ||
def get_polling_manager(minimize_polling=False, root_helper=None): | ||
if minimize_polling: | ||
pm = InterfacePollingMinimizer(root_helper=root_helper) | ||
pm.start() | ||
else: | ||
pm = AlwaysPoll() | ||
try: | ||
yield pm | ||
finally: | ||
if minimize_polling: | ||
pm.stop() | ||
|
||
|
||
class BasePollingManager(object): | ||
|
||
def __init__(self): | ||
self._force_polling = False | ||
self._polling_completed = True | ||
|
||
def force_polling(self): | ||
self._force_polling = True | ||
|
||
def polling_completed(self): | ||
self._polling_completed = True | ||
|
||
def _is_polling_required(self): | ||
raise NotImplemented | ||
|
||
@property | ||
def is_polling_required(self): | ||
# Always consume the updates to minimize polling. | ||
polling_required = self._is_polling_required() | ||
|
||
# Polling is required regardless of whether updates have been | ||
# detected. | ||
if self._force_polling: | ||
self._force_polling = False | ||
polling_required = True | ||
|
||
# Polling is required if not yet done for previously detected | ||
# updates. | ||
if not self._polling_completed: | ||
polling_required = True | ||
|
||
if polling_required: | ||
# Track whether polling has been completed to ensure that | ||
# polling can be required until the caller indicates via a | ||
# call to polling_completed() that polling has been | ||
# successfully performed. | ||
self._polling_completed = False | ||
|
||
return polling_required | ||
|
||
|
||
class AlwaysPoll(BasePollingManager): | ||
|
||
@property | ||
def is_polling_required(self): | ||
return True | ||
|
||
|
||
class InterfacePollingMinimizer(BasePollingManager): | ||
"""Monitors ovsdb to determine when polling is required.""" | ||
|
||
def __init__(self, root_helper=None): | ||
super(InterfacePollingMinimizer, self).__init__() | ||
self._monitor = ovsdb_monitor.SimpleInterfaceMonitor( | ||
root_helper=root_helper) | ||
|
||
def start(self): | ||
self._monitor.start() | ||
|
||
def stop(self): | ||
self._monitor.stop() | ||
|
||
def _is_polling_required(self): | ||
# Maximize the chances of update detection having a chance to | ||
# collect output. | ||
eventlet.sleep() | ||
return self._monitor.has_updates |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.