Skip to content

Commit 1183240

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "ovn: Use ovsdb-client to create neutron_pg_drop" into stable/yoga
2 parents 0681c36 + 0d0e6cd commit 1183240

File tree

9 files changed

+315
-291
lines changed

9 files changed

+315
-291
lines changed

neutron/common/ovn/utils.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from neutron_lib import exceptions as n_exc
2828
from neutron_lib.plugins import directory
2929
from neutron_lib.utils import net as n_utils
30+
from oslo_concurrency import processutils
3031
from oslo_config import cfg
3132
from oslo_log import log
3233
from oslo_serialization import jsonutils
@@ -56,6 +57,70 @@
5657
'PortExtraDHCPValidation', ['failed', 'invalid_ipv4', 'invalid_ipv6'])
5758

5859

60+
class OvsdbClientCommand(object):
61+
_CONNECTION = 0
62+
_PRIVATE_KEY = 1
63+
_CERTIFICATE = 2
64+
_CA_AUTHORITY = 3
65+
66+
OVN_Northbound = "OVN_Northbound"
67+
OVN_Southbound = "OVN_Southbound"
68+
69+
_db_settings = {
70+
OVN_Northbound: {
71+
_CONNECTION: ovn_conf.get_ovn_nb_connection,
72+
_PRIVATE_KEY: ovn_conf.get_ovn_nb_private_key,
73+
_CERTIFICATE: ovn_conf.get_ovn_nb_certificate,
74+
_CA_AUTHORITY: ovn_conf.get_ovn_nb_ca_cert,
75+
},
76+
OVN_Southbound: {
77+
_CONNECTION: ovn_conf.get_ovn_sb_connection,
78+
_PRIVATE_KEY: ovn_conf.get_ovn_sb_private_key,
79+
_CERTIFICATE: ovn_conf.get_ovn_sb_certificate,
80+
_CA_AUTHORITY: ovn_conf.get_ovn_sb_ca_cert,
81+
},
82+
}
83+
84+
@classmethod
85+
def run(cls, command):
86+
"""Run custom ovsdb protocol command.
87+
88+
:param command: JSON object of ovsdb protocol command
89+
"""
90+
try:
91+
db = command[0]
92+
except IndexError:
93+
raise KeyError(
94+
_("%s or %s schema must be specified in the command %s" % (
95+
cls.OVN_Northbound, cls.OVN_Southbound, command)))
96+
97+
if db not in (cls.OVN_Northbound, cls.OVN_Southbound):
98+
raise KeyError(
99+
_("%s or %s schema must be specified in the command %s" % (
100+
cls.OVN_Northbound, cls.OVN_Southbound, command)))
101+
102+
cmd = ['ovsdb-client',
103+
cls.COMMAND,
104+
cls._db_settings[db][cls._CONNECTION](),
105+
'--timeout',
106+
str(ovn_conf.get_ovn_ovsdb_timeout())]
107+
108+
if cls._db_settings[db][cls._PRIVATE_KEY]():
109+
cmd += ['-p', cls._db_settings[db][cls._PRIVATE_KEY](),
110+
'-c', cls._db_settings[db][cls._CERTIFICATE](),
111+
'-C', cls._db_settings[db][cls._CA_AUTHORITY]()]
112+
113+
cmd.append(jsonutils.dumps(command))
114+
115+
return processutils.execute(
116+
*cmd,
117+
log_errors=processutils.LOG_FINAL_ERROR)
118+
119+
120+
class OvsdbClientTransactCommand(OvsdbClientCommand):
121+
COMMAND = 'transact'
122+
123+
59124
def ovn_name(id):
60125
# The name of the OVN entry will be neutron-<UUID>
61126
# This is due to the fact that the OVN application checks if the name
@@ -697,3 +762,56 @@ def wrapper(*args, **kwargs):
697762
reraise=True)(func)(*args, **kwargs)
698763
return wrapper
699764
return inner
765+
766+
767+
def create_neutron_pg_drop():
768+
"""Create neutron_pg_drop Port Group.
769+
770+
It uses ovsdb-client to send to server transact command using ovsdb
771+
protocol that checks if the neutron_pg_drop row exists. If it exists
772+
it times out immediatelly. If it doesn't exist then it creates the
773+
Port_Group and default ACLs to drop all ingress and egress traffic.
774+
"""
775+
command = [
776+
"OVN_Northbound", {
777+
"op": "wait",
778+
"timeout": 0,
779+
"table": "Port_Group",
780+
"where": [
781+
["name", "==", constants.OVN_DROP_PORT_GROUP_NAME]
782+
],
783+
"until": "==",
784+
"rows": []
785+
}, {
786+
"op": "insert",
787+
"table": "ACL",
788+
"row": {
789+
"action": "drop",
790+
"direction": "to-lport",
791+
"match": "outport == @neutron_pg_drop && ip",
792+
"priority": 1001
793+
},
794+
"uuid-name": "droptoport"
795+
}, {
796+
"op": "insert",
797+
"table": "ACL",
798+
"row": {
799+
"action": "drop",
800+
"direction": "from-lport",
801+
"match": "inport == @neutron_pg_drop && ip",
802+
"priority": 1001
803+
},
804+
"uuid-name": "dropfromport"
805+
}, {
806+
"op": "insert",
807+
"table": "Port_Group",
808+
"row": {
809+
"name": constants.OVN_DROP_PORT_GROUP_NAME,
810+
"acls": ["set", [
811+
["named-uuid", "droptoport"],
812+
["named-uuid", "dropfromport"]
813+
]]
814+
}
815+
}]
816+
817+
OvsdbClientTransactCommand.run(command)

neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py

Lines changed: 11 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
from neutron_lib.plugins.ml2 import api
3737
from neutron_lib.utils import helpers
3838
from oslo_concurrency import lockutils
39-
from oslo_concurrency import processutils
4039
from oslo_config import cfg
4140
from oslo_db import exception as os_db_exc
4241
from oslo_log import log
@@ -59,7 +58,6 @@
5958
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import maintenance
6059
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_client
6160
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovn_db_sync
62-
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import ovsdb_monitor
6361
from neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb import worker
6462
from neutron import service
6563
from neutron.services.logapi.drivers.ovn import driver as log_driver
@@ -279,47 +277,7 @@ def pre_fork_initialize(self, resource, event, trigger, payload=None):
279277
"""Pre-initialize the ML2/OVN driver."""
280278
atexit.register(self._clean_hash_ring)
281279
signal.signal(signal.SIGTERM, self._clean_hash_ring)
282-
self._create_neutron_pg_drop()
283-
284-
def _create_neutron_pg_drop(self):
285-
"""Create neutron_pg_drop Port Group.
286-
287-
The method creates a short living connection to the Northbound
288-
database. Because of multiple controllers can attempt to create the
289-
Port Group at the same time the transaction can fail and raise
290-
RuntimeError. In such case, we make sure the Port Group was created,
291-
otherwise the error is something else and it's raised to the caller.
292-
"""
293-
idl = ovsdb_monitor.OvnInitPGNbIdl.from_server(
294-
ovn_conf.get_ovn_nb_connection(), self.nb_schema_helper, self)
295-
# Only one server should try to create the port group
296-
idl.set_lock('pg_drop_creation')
297-
with ovsdb_monitor.short_living_ovsdb_api(
298-
impl_idl_ovn.OvsdbNbOvnIdl, idl) as pre_ovn_nb_api:
299-
try:
300-
create_default_drop_port_group(pre_ovn_nb_api)
301-
except KeyError:
302-
# Due to a bug in python-ovs, we can send transactions before
303-
# the initial OVSDB is populated in memory. This can break
304-
# the AddCommand post_commit method which tries to return a
305-
# row looked up by the newly commited row's uuid. Since we
306-
# don't care about the return value from the PgAddCommand, we
307-
# can just catch the KeyError and continue. This can be
308-
# removed when the python-ovs bug is resolved.
309-
pass
310-
except RuntimeError as re:
311-
# If we don't get the lock, and the port group didn't exist
312-
# when we tried to create it, it might still have been
313-
# created by another server and we just haven't gotten the
314-
# update yet.
315-
LOG.info("Waiting for Port Group %(pg)s to be created",
316-
{'pg': ovn_const.OVN_DROP_PORT_GROUP_NAME})
317-
if not idl.neutron_pg_drop_event.wait():
318-
LOG.error("Port Group %(pg)s was not created in time",
319-
{'pg': ovn_const.OVN_DROP_PORT_GROUP_NAME})
320-
raise re
321-
LOG.info("Porg Group %(pg)s was created by another server",
322-
{'pg': ovn_const.OVN_DROP_PORT_GROUP_NAME})
280+
ovn_utils.create_neutron_pg_drop()
323281

324282
@staticmethod
325283
def should_post_fork_initialize(worker_class):
@@ -1191,19 +1149,17 @@ def set_port_status_down(self, port_id):
11911149

11921150
def delete_mac_binding_entries(self, external_ip):
11931151
"""Delete all MAC_Binding entries associated to this IP address"""
1194-
cmd = ['ovsdb-client', 'transact', ovn_conf.get_ovn_sb_connection(),
1195-
'--timeout', str(ovn_conf.get_ovn_ovsdb_timeout())]
1196-
1197-
if ovn_conf.get_ovn_sb_private_key():
1198-
cmd += ['-p', ovn_conf.get_ovn_sb_private_key(), '-c',
1199-
ovn_conf.get_ovn_sb_certificate(), '-C',
1200-
ovn_conf.get_ovn_sb_ca_cert()]
1201-
1202-
cmd += ['["OVN_Southbound", {"op": "delete", "table": "MAC_Binding", '
1203-
'"where": [["ip", "==", "%s"]]}]' % external_ip]
1152+
cmd = [
1153+
"OVN_Southbound", {
1154+
"op": "delete",
1155+
"table": "MAC_Binding",
1156+
"where": [
1157+
["ip", "==", external_ip]
1158+
]
1159+
}
1160+
]
12041161

1205-
return processutils.execute(*cmd,
1206-
log_errors=processutils.LOG_FINAL_ERROR)
1162+
return ovn_utils.OvsdbClientTransactCommand.run(cmd)
12071163

12081164
def update_segment_host_mapping(self, host, phy_nets):
12091165
"""Update SegmentHostMapping in DB"""
@@ -1375,28 +1331,6 @@ def delete_agent(self, context, id, _driver=None):
13751331
if_exists=True).execute(check_error=True)
13761332

13771333

1378-
def create_default_drop_port_group(nb_idl):
1379-
pg_name = ovn_const.OVN_DROP_PORT_GROUP_NAME
1380-
if nb_idl.get_port_group(pg_name):
1381-
LOG.debug("Port Group %s already exists", pg_name)
1382-
return
1383-
with nb_idl.transaction(check_error=True) as txn:
1384-
# If drop Port Group doesn't exist yet, create it.
1385-
txn.add(nb_idl.pg_add(pg_name, acls=[], may_exist=True))
1386-
# Add ACLs to this Port Group so that all traffic is dropped.
1387-
acls = ovn_acl.add_acls_for_drop_port_group(pg_name)
1388-
for acl in acls:
1389-
txn.add(nb_idl.pg_acl_add(may_exist=True, **acl))
1390-
1391-
ports_with_pg = set()
1392-
for pg in nb_idl.get_sg_port_groups().values():
1393-
ports_with_pg.update(pg['ports'])
1394-
1395-
if ports_with_pg:
1396-
# Add the ports to the default Port Group
1397-
txn.add(nb_idl.pg_add_ports(pg_name, list(ports_with_pg)))
1398-
1399-
14001334
def get_availability_zones(cls, context, _driver, filters=None, fields=None,
14011335
sorts=None, limit=None, marker=None,
14021336
page_reverse=False):

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
from neutron_lib.plugins import utils as p_utils
3434
from neutron_lib.utils import helpers
3535
from neutron_lib.utils import net as n_net
36-
from oslo_concurrency import processutils
3736
from oslo_config import cfg
3837
from oslo_log import log
3938
from oslo_utils import excutils
@@ -1676,16 +1675,16 @@ def delete_mac_binding_entries_by_mac(self, mac):
16761675
is refer to patch:
16771676
https://review.opendev.org/c/openstack/neutron/+/812805
16781677
"""
1679-
cmd = ['ovsdb-client', 'transact', ovn_conf.get_ovn_sb_connection(),
1680-
'--timeout', str(ovn_conf.get_ovn_ovsdb_timeout())]
1681-
if ovn_conf.get_ovn_sb_private_key():
1682-
cmd += ['-p', ovn_conf.get_ovn_sb_private_key(), '-c',
1683-
ovn_conf.get_ovn_sb_certificate(), '-C',
1684-
ovn_conf.get_ovn_sb_ca_cert()]
1685-
cmd += ['["OVN_Southbound", {"op": "delete", "table": "MAC_Binding", '
1686-
'"where": [["mac", "==", "%s"]]}]' % mac]
1687-
return processutils.execute(*cmd,
1688-
log_errors=processutils.LOG_FINAL_ERROR)
1678+
cmd = [
1679+
"OVN_Southbound", {
1680+
"op": "delete",
1681+
"table": "MAC_Binding",
1682+
"where": [
1683+
["mac", "==", mac]
1684+
]
1685+
}
1686+
]
1687+
return utils.OvsdbClientTransactCommand.run(cmd)
16891688

16901689
def _delete_lrouter_port(self, context, port_id, router_id=None, txn=None):
16911690
"""Delete a logical router port."""

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
# under the License.
1414

1515
import abc
16-
import contextlib
1716
import datetime
1817

1918
from neutron_lib import context as neutron_context
@@ -581,17 +580,6 @@ def run(self, event, row, old):
581580
self.driver.delete_mac_binding_entries(row.external_ip)
582581

583582

584-
class NeutronPgDropPortGroupCreated(row_event.WaitEvent):
585-
"""WaitEvent for neutron_pg_drop Create event."""
586-
def __init__(self, timeout=None):
587-
table = 'Port_Group'
588-
events = (self.ROW_CREATE,)
589-
conditions = (('name', '=', ovn_const.OVN_DROP_PORT_GROUP_NAME),)
590-
super(NeutronPgDropPortGroupCreated, self).__init__(
591-
events, table, conditions, timeout=timeout)
592-
self.event_name = 'PortGroupCreated'
593-
594-
595583
class OvnDbNotifyHandler(row_event.RowEventHandler):
596584
def __init__(self, driver):
597585
self.driver = driver
@@ -837,56 +825,6 @@ def post_connect(self):
837825
PortBindingChassisUpdateEvent(self.driver)])
838826

839827

840-
class OvnInitPGNbIdl(OvnIdl):
841-
"""Very limited OVN NB IDL.
842-
843-
This IDL is intended to be used only in initialization phase with short
844-
living DB connections.
845-
"""
846-
847-
tables = ['Port_Group', 'Logical_Switch_Port', 'ACL']
848-
849-
def __init__(self, driver, remote, schema):
850-
super(OvnInitPGNbIdl, self).__init__(driver, remote, schema)
851-
self.set_table_condition(
852-
'Port_Group', [['name', '==', ovn_const.OVN_DROP_PORT_GROUP_NAME]])
853-
self.neutron_pg_drop_event = NeutronPgDropPortGroupCreated(
854-
timeout=ovn_conf.get_ovn_ovsdb_timeout())
855-
self.notify_handler.watch_event(self.neutron_pg_drop_event)
856-
857-
def notify(self, event, row, updates=None):
858-
# Go ahead and process events even if the lock is contended so we can
859-
# know that some other server has created the drop group
860-
self.notify_handler.notify(event, row, updates)
861-
862-
@classmethod
863-
def from_server(cls, connection_string, helper, driver, pg_only=False):
864-
if pg_only:
865-
helper.register_table('Port_Group')
866-
else:
867-
for table in cls.tables:
868-
helper.register_table(table)
869-
870-
return cls(driver, connection_string, helper)
871-
872-
873-
@contextlib.contextmanager
874-
def short_living_ovsdb_api(api_class, idl):
875-
"""Context manager for short living connections to the database.
876-
877-
:param api_class: Class implementing the database calls
878-
(e.g. from the impl_idl module)
879-
:param idl: An instance of IDL class (e.g. instance of OvnNbIdl)
880-
"""
881-
conn = connection.Connection(
882-
idl, timeout=ovn_conf.get_ovn_ovsdb_timeout())
883-
api = api_class(conn)
884-
try:
885-
yield api
886-
finally:
887-
api.ovsdb_connection.stop()
888-
889-
890828
def _check_and_set_ssl_files(schema_name):
891829
if schema_name == 'OVN_Southbound':
892830
priv_key_file = ovn_conf.get_ovn_sb_private_key()

0 commit comments

Comments
 (0)