ovsdb: don't erase existing ovsdb managers
The current existing agent erases already set ovsdb managers entries. In some use cases, cloud admin sets ovsdb managers. eg, for SDN controllers or monitoring purpose. Neutron agent shouldn't unconditionally erase the existing ovsdb managers. This patch implements a new api add_manager (along with get_manager and remove_manager) to the ovsdb api which will allow us to configure a manager on a switch without overriding the existing managers. Closes-Bug: #1614766 Change-Id: Ibf9bd02fac3070d166546cac478ef984e4e43f28 Co-Authored-By: sridhargaddam <sgaddam@redhat.com> Co-Authored-By: Terry Wilson <twilson@redhat.com>
This commit is contained in:
parent
efe87156c5
commit
7d42176853
@ -106,6 +106,15 @@ class BaseOVS(object):
|
||||
self.vsctl_timeout = cfg.CONF.ovs_vsctl_timeout
|
||||
self.ovsdb = ovsdb.API.get(self)
|
||||
|
||||
def add_manager(self, connection_uri):
|
||||
self.ovsdb.add_manager(connection_uri).execute()
|
||||
|
||||
def get_manager(self):
|
||||
return self.ovsdb.get_manager().execute()
|
||||
|
||||
def remove_manager(self, connection_uri):
|
||||
self.ovsdb.remove_manager(connection_uri).execute()
|
||||
|
||||
def add_bridge(self, bridge_name,
|
||||
datapath_type=constants.OVS_DATAPATH_SYSTEM):
|
||||
|
||||
|
@ -100,6 +100,36 @@ class API(object):
|
||||
:rtype: :class:`Transaction`
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_manager(self, connection_uri):
|
||||
"""Create a command to add a Manager to the OVS switch
|
||||
|
||||
This API will add a new manager without overriding the existing ones.
|
||||
|
||||
:param connection_uri: target to which manager needs to be set
|
||||
:type connection_uri: string, see ovs-vsctl manpage for format
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_manager(self):
|
||||
"""Create a command to get Manager list from the OVS switch
|
||||
|
||||
:returns: :class:`Command` with list of Manager names result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def remove_manager(self, connection_uri):
|
||||
"""Create a command to remove a Manager from the OVS switch
|
||||
|
||||
This API will remove the manager configured on the OVS switch.
|
||||
|
||||
:param connection_uri: target identifying the manager uri that
|
||||
needs to be removed.
|
||||
:type connection_uri: string, see ovs-vsctl manpage for format
|
||||
:returns: :class:`Command` with no result
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_br(self, name, may_exist=True, datapath_type=None):
|
||||
"""Create a command to add an OVS bridge
|
||||
|
@ -211,6 +211,15 @@ class OvsdbIdl(api.API):
|
||||
self.context.vsctl_timeout,
|
||||
check_error, log_errors)
|
||||
|
||||
def add_manager(self, connection_uri):
|
||||
return cmd.AddManagerCommand(self, connection_uri)
|
||||
|
||||
def get_manager(self):
|
||||
return cmd.GetManagerCommand(self)
|
||||
|
||||
def remove_manager(self, connection_uri):
|
||||
return cmd.RemoveManagerCommand(self, connection_uri)
|
||||
|
||||
def add_br(self, name, may_exist=True, datapath_type=None):
|
||||
return cmd.AddBridgeCommand(self, name, may_exist, datapath_type)
|
||||
|
||||
|
@ -180,6 +180,21 @@ class OvsdbVsctl(ovsdb.API):
|
||||
def transaction(self, check_error=False, log_errors=True, **kwargs):
|
||||
return Transaction(self.context, check_error, log_errors, **kwargs)
|
||||
|
||||
def add_manager(self, connection_uri):
|
||||
# This will add a new manager without overriding existing ones.
|
||||
conn_uri = 'target="%s"' % connection_uri
|
||||
args = ['create', 'Manager', conn_uri, '--', 'add', 'Open_vSwitch',
|
||||
'.', 'manager_options', '@manager']
|
||||
return BaseCommand(self.context, '--id=@manager', args=args)
|
||||
|
||||
def get_manager(self):
|
||||
return MultiLineCommand(self.context, 'get-manager')
|
||||
|
||||
def remove_manager(self, connection_uri):
|
||||
args = ['get', 'Manager', connection_uri, '--', 'remove',
|
||||
'Open_vSwitch', '.', 'manager_options', '@manager']
|
||||
return BaseCommand(self.context, '--id=@manager', args=args)
|
||||
|
||||
def add_br(self, name, may_exist=True, datapath_type=None):
|
||||
opts = ['--may-exist'] if may_exist else None
|
||||
params = [name]
|
||||
|
@ -55,6 +55,47 @@ class BaseCommand(api.Command):
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
class AddManagerCommand(BaseCommand):
|
||||
def __init__(self, api, target):
|
||||
super(AddManagerCommand, self).__init__(api)
|
||||
self.target = target
|
||||
|
||||
def run_idl(self, txn):
|
||||
row = txn.insert(self.api._tables['Manager'])
|
||||
row.target = self.target
|
||||
self.api._ovs.verify('manager_options')
|
||||
self.api._ovs.manager_options = self.api._ovs.manager_options + [row]
|
||||
|
||||
|
||||
class GetManagerCommand(BaseCommand):
|
||||
def __init__(self, api):
|
||||
super(GetManagerCommand, self).__init__(api)
|
||||
|
||||
def run_idl(self, txn):
|
||||
self.result = [m.target for m in
|
||||
self.api._tables['Manager'].rows.values()]
|
||||
|
||||
|
||||
class RemoveManagerCommand(BaseCommand):
|
||||
def __init__(self, api, target):
|
||||
super(RemoveManagerCommand, self).__init__(api)
|
||||
self.target = target
|
||||
|
||||
def run_idl(self, txn):
|
||||
try:
|
||||
manager = idlutils.row_by_value(self.api.idl, 'Manager', 'target',
|
||||
self.target)
|
||||
except idlutils.RowNotFound:
|
||||
msg = _("Manager with target %s does not exist") % self.target
|
||||
LOG.error(msg)
|
||||
raise RuntimeError(msg)
|
||||
self.api._ovs.verify('manager_options')
|
||||
manager_list = self.api._ovs.manager_options
|
||||
manager_list.remove(manager)
|
||||
self.api._ovs.manager_options = manager_list
|
||||
self.api._tables['Manager'].rows[manager.uuid].delete()
|
||||
|
||||
|
||||
class AddBridgeCommand(BaseCommand):
|
||||
def __init__(self, api, name, may_exist, datapath_type):
|
||||
super(AddBridgeCommand, self).__init__(api)
|
||||
|
@ -12,7 +12,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.agent.common import utils
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron.agent.ovsdb import api as ovsdb
|
||||
|
||||
cfg.CONF.import_opt('ovs_vsctl_timeout', 'neutron.agent.common.ovs_lib')
|
||||
|
||||
|
||||
def _connection_to_manager_uri(conn_uri):
|
||||
@ -25,5 +29,9 @@ def _connection_to_manager_uri(conn_uri):
|
||||
|
||||
|
||||
def enable_connection_uri(conn_uri):
|
||||
class OvsdbVsctlContext(object):
|
||||
vsctl_timeout = cfg.CONF.ovs_vsctl_timeout
|
||||
|
||||
manager_uri = _connection_to_manager_uri(conn_uri)
|
||||
utils.execute(['ovs-vsctl', 'set-manager', manager_uri], run_as_root=True)
|
||||
api = ovsdb.API.get(OvsdbVsctlContext, 'vsctl')
|
||||
api.add_manager(manager_uri).execute(check_error=False, log_errors=True)
|
||||
|
@ -25,10 +25,11 @@ class ExclusivePort(resource_allocator.ExclusiveResource):
|
||||
:type port: int
|
||||
"""
|
||||
|
||||
def __init__(self, protocol):
|
||||
def __init__(self, protocol, start=1024, end=None):
|
||||
super(ExclusivePort, self).__init__(
|
||||
'ports',
|
||||
functools.partial(net_helpers.get_free_namespace_port, protocol))
|
||||
functools.partial(net_helpers.get_free_namespace_port, protocol,
|
||||
start=start, end=end))
|
||||
|
||||
def _setUp(self):
|
||||
super(ExclusivePort, self)._setUp()
|
||||
|
@ -74,6 +74,9 @@ CHILD_PROCESS_SLEEP = os.environ.get('OS_TEST_CHILD_PROCESS_SLEEP', 0.5)
|
||||
|
||||
TRANSPORT_PROTOCOLS = (n_const.PROTO_NAME_TCP, n_const.PROTO_NAME_UDP)
|
||||
|
||||
OVS_MANAGER_TEST_PORT_FIRST = 6610
|
||||
OVS_MANAGER_TEST_PORT_LAST = 6639
|
||||
|
||||
|
||||
def increment_ip_cidr(ip_cidr, offset=1):
|
||||
"""Increment ip_cidr offset times.
|
||||
@ -189,7 +192,7 @@ def get_unused_port(used, start=1024, end=None):
|
||||
return random.choice(list(candidates - used))
|
||||
|
||||
|
||||
def get_free_namespace_port(protocol, namespace=None):
|
||||
def get_free_namespace_port(protocol, namespace=None, start=1024, end=None):
|
||||
"""Return an unused port from given namespace
|
||||
|
||||
WARNING: This function returns a port that is free at the execution time of
|
||||
@ -199,6 +202,10 @@ def get_free_namespace_port(protocol, namespace=None):
|
||||
|
||||
:param protocol: Return free port for given protocol. Supported protocols
|
||||
are 'tcp' and 'udp'.
|
||||
:param namespace: Namespace in which free port has to be returned.
|
||||
:param start: The starting port number.
|
||||
:param end: The ending port number (free port that is returned would be
|
||||
between (start, end) values.
|
||||
"""
|
||||
if protocol == n_const.PROTO_NAME_TCP:
|
||||
param = '-tna'
|
||||
@ -211,7 +218,7 @@ def get_free_namespace_port(protocol, namespace=None):
|
||||
output = ip_wrapper.netns.execute(['ss', param])
|
||||
used_ports = _get_source_ports_from_ss_output(output)
|
||||
|
||||
return get_unused_port(used_ports)
|
||||
return get_unused_port(used_ports, start, end)
|
||||
|
||||
|
||||
def create_patch_ports(source, destination):
|
||||
|
@ -17,10 +17,12 @@ import collections
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
from neutron_lib import constants as const
|
||||
|
||||
from neutron.agent.common import ovs_lib
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.common import utils
|
||||
from neutron.tests.common.exclusive_resources import port
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.functional.agent.linux import base
|
||||
|
||||
@ -423,6 +425,38 @@ class OVSLibTestCase(base.BaseOVSLinuxTestCase):
|
||||
super(OVSLibTestCase, self).setUp()
|
||||
self.ovs = ovs_lib.BaseOVS()
|
||||
|
||||
def test_add_manager_appends(self):
|
||||
port1 = self.useFixture(port.ExclusivePort(const.PROTO_NAME_TCP,
|
||||
start=net_helpers.OVS_MANAGER_TEST_PORT_FIRST,
|
||||
end=net_helpers.OVS_MANAGER_TEST_PORT_LAST)).port
|
||||
port2 = self.useFixture(port.ExclusivePort(const.PROTO_NAME_TCP,
|
||||
start=net_helpers.OVS_MANAGER_TEST_PORT_FIRST,
|
||||
end=net_helpers.OVS_MANAGER_TEST_PORT_LAST)).port
|
||||
manager_list = ["ptcp:%s:127.0.0.1" % port1,
|
||||
"ptcp:%s:127.0.0.1" % port2]
|
||||
# Verify that add_manager does not override the existing manager
|
||||
expected_manager_list = list()
|
||||
for conn_uri in manager_list:
|
||||
self.ovs.add_manager(conn_uri)
|
||||
self.addCleanup(self.ovs.remove_manager, conn_uri)
|
||||
self.assertIn(conn_uri, self.ovs.get_manager())
|
||||
expected_manager_list.append(conn_uri)
|
||||
|
||||
# Verify that switch is configured with both the managers
|
||||
for manager_uri in expected_manager_list:
|
||||
self.assertIn(manager_uri, manager_list)
|
||||
|
||||
def test_add_manager_lifecycle_baseovs(self):
|
||||
port1 = self.useFixture(port.ExclusivePort(const.PROTO_NAME_TCP,
|
||||
start=net_helpers.OVS_MANAGER_TEST_PORT_FIRST,
|
||||
end=net_helpers.OVS_MANAGER_TEST_PORT_LAST)).port
|
||||
conn_uri = "ptcp:%s:127.0.0.1" % port1
|
||||
self.addCleanup(self.ovs.remove_manager, conn_uri)
|
||||
self.ovs.add_manager(conn_uri)
|
||||
self.assertIn(conn_uri, self.ovs.get_manager())
|
||||
self.ovs.remove_manager(conn_uri)
|
||||
self.assertNotIn(conn_uri, self.ovs.get_manager())
|
||||
|
||||
def test_bridge_lifecycle_baseovs(self):
|
||||
name = utils.get_rand_name(prefix=net_helpers.BR_PREFIX)
|
||||
self.addCleanup(self.ovs.delete_bridge, name)
|
||||
|
@ -34,10 +34,3 @@ class TestOVSNativeHelpers(base.BaseTestCase):
|
||||
for conn_uri, expected in CONNECTION_TO_MANAGER_URI_MAP:
|
||||
self.assertEqual(expected,
|
||||
helpers._connection_to_manager_uri(conn_uri))
|
||||
|
||||
def test_enable_connection_uri(self):
|
||||
for conn_uri, manager_uri in CONNECTION_TO_MANAGER_URI_MAP:
|
||||
helpers.enable_connection_uri(conn_uri)
|
||||
self.execute.assert_called_with(
|
||||
['ovs-vsctl', 'set-manager', manager_uri],
|
||||
run_as_root=True)
|
||||
|
Loading…
Reference in New Issue
Block a user