|
27 | 27 | from neutron_lib import exceptions as n_exc
|
28 | 28 | from neutron_lib.plugins import directory
|
29 | 29 | from neutron_lib.utils import net as n_utils
|
| 30 | +from oslo_concurrency import processutils |
30 | 31 | from oslo_config import cfg
|
31 | 32 | from oslo_log import log
|
32 | 33 | from oslo_serialization import jsonutils
|
33 | 34 | from oslo_utils import netutils
|
34 | 35 | from oslo_utils import strutils
|
35 | 36 | from ovsdbapp import constants as ovsdbapp_const
|
| 37 | +import tenacity |
36 | 38 |
|
37 | 39 | from neutron._i18n import _
|
38 | 40 | from neutron.common.ovn import constants
|
|
55 | 57 | 'PortExtraDHCPValidation', ['failed', 'invalid_ipv4', 'invalid_ipv6'])
|
56 | 58 |
|
57 | 59 |
|
| 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 | + |
58 | 124 | def ovn_name(id):
|
59 | 125 | # The name of the OVN entry will be neutron-<UUID>
|
60 | 126 | # This is due to the fact that the OVN application checks if the name
|
@@ -693,3 +759,67 @@ def is_port_external(port):
|
693 | 759 |
|
694 | 760 | return (vnic_type in constants.EXTERNAL_PORT_TYPES and
|
695 | 761 | constants.PORT_CAP_SWITCHDEV not in capabilities)
|
| 762 | + |
| 763 | + |
| 764 | +def retry(max_=None): |
| 765 | + def inner(func): |
| 766 | + def wrapper(*args, **kwargs): |
| 767 | + local_max = max_ or ovn_conf.get_ovn_ovsdb_retry_max_interval() |
| 768 | + return tenacity.retry( |
| 769 | + wait=tenacity.wait_exponential(max=local_max), |
| 770 | + reraise=True)(func)(*args, **kwargs) |
| 771 | + return wrapper |
| 772 | + return inner |
| 773 | + |
| 774 | + |
| 775 | +def create_neutron_pg_drop(): |
| 776 | + """Create neutron_pg_drop Port Group. |
| 777 | +
|
| 778 | + It uses ovsdb-client to send to server transact command using ovsdb |
| 779 | + protocol that checks if the neutron_pg_drop row exists. If it exists |
| 780 | + it times out immediatelly. If it doesn't exist then it creates the |
| 781 | + Port_Group and default ACLs to drop all ingress and egress traffic. |
| 782 | + """ |
| 783 | + command = [ |
| 784 | + "OVN_Northbound", { |
| 785 | + "op": "wait", |
| 786 | + "timeout": 0, |
| 787 | + "table": "Port_Group", |
| 788 | + "where": [ |
| 789 | + ["name", "==", constants.OVN_DROP_PORT_GROUP_NAME] |
| 790 | + ], |
| 791 | + "until": "==", |
| 792 | + "rows": [] |
| 793 | + }, { |
| 794 | + "op": "insert", |
| 795 | + "table": "ACL", |
| 796 | + "row": { |
| 797 | + "action": "drop", |
| 798 | + "direction": "to-lport", |
| 799 | + "match": "outport == @neutron_pg_drop && ip", |
| 800 | + "priority": 1001 |
| 801 | + }, |
| 802 | + "uuid-name": "droptoport" |
| 803 | + }, { |
| 804 | + "op": "insert", |
| 805 | + "table": "ACL", |
| 806 | + "row": { |
| 807 | + "action": "drop", |
| 808 | + "direction": "from-lport", |
| 809 | + "match": "inport == @neutron_pg_drop && ip", |
| 810 | + "priority": 1001 |
| 811 | + }, |
| 812 | + "uuid-name": "dropfromport" |
| 813 | + }, { |
| 814 | + "op": "insert", |
| 815 | + "table": "Port_Group", |
| 816 | + "row": { |
| 817 | + "name": constants.OVN_DROP_PORT_GROUP_NAME, |
| 818 | + "acls": ["set", [ |
| 819 | + ["named-uuid", "droptoport"], |
| 820 | + ["named-uuid", "dropfromport"] |
| 821 | + ]] |
| 822 | + } |
| 823 | + }] |
| 824 | + |
| 825 | + OvsdbClientTransactCommand.run(command) |
0 commit comments