os-vif 3.1.0__py3-none-any.whl → 3.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of os-vif might be problematic. Click here for more details.

@@ -12,6 +12,7 @@
12
12
 
13
13
  import os
14
14
  import re
15
+ import time
15
16
 
16
17
  from oslo_concurrency import processutils
17
18
  from oslo_utils import excutils
@@ -39,6 +40,12 @@ class ShellIpCommands(object):
39
40
  'peer', 'name', peer)
40
41
  elif 'dummy' == dev_type:
41
42
  _execute_command('ip', 'link', 'add', device, 'type', dev_type)
43
+ # ensure that the device exists to prevent racing
44
+ # with other ip commands
45
+ for _ in range(10):
46
+ if self.exist_device(device):
47
+ return
48
+ time.sleep(0.1)
42
49
 
43
50
  def del_device(self, device):
44
51
  if self.exist_device(device):
@@ -17,5 +17,5 @@ os_vif_pctxt = priv_context.PrivContext(
17
17
  'os_vif',
18
18
  cfg_section='os_vif_privileged',
19
19
  pypath=__name__ + '.os_vif_pctxt',
20
- capabilities=[c.CAP_NET_ADMIN],
20
+ capabilities=[c.CAP_NET_ADMIN, c.CAP_DAC_OVERRIDE],
21
21
  )
@@ -1,6 +1,7 @@
1
1
  Adrian Chiris <adrianc@mellanox.com>
2
2
  Alin Balutoiu <abalutoiu@cloudbasesolutions.com>
3
3
  Andreas Jaeger <aj@suse.com>
4
+ Balazs Gibizer <gibi@redhat.com>
4
5
  Brian Haley <brian.haley@hpe.com>
5
6
  Cao Xuan Hoang <hoangcx@vn.fujitsu.com>
6
7
  Carlos Goncalves <cgoncalves@redhat.com>
@@ -78,4 +79,5 @@ qingszhao <zhao.daqing@99cloud.net>
78
79
  shaleijie <sha.leijie@99cloud.net>
79
80
  sunjia <sunjia@inspur.com>
80
81
  vagrant <vagrant@devstack>
82
+ wangjiaqi07 <wangjiaqi07@inspur.com>
81
83
  wu.shiming <wushiming@yovole.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: os-vif
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: A library for plugging and unplugging virtual interfaces in OpenStack.
5
5
  Home-page: https://docs.openstack.org/os-vif/latest/
6
6
  Author: OpenStack
@@ -26,11 +26,11 @@ os_vif/objects/vif.py,sha256=VpVgaXez_SRZvoY0BcoSWQY_nF02tP09o8ZjWKldiLI,21668
26
26
  os_vif/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  os_vif/tests/functional/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  os_vif/tests/functional/base.py,sha256=m2uqvH9BkBcCYU7PtlnBYUmwmQLUiuwVS99scJjYQ04,5005
29
- os_vif/tests/functional/privsep.py,sha256=6iPmUMxtJTPFmg8h7Yr6ux2x4aoaDR3wDs6WDDEx0P8,824
29
+ os_vif/tests/functional/privsep.py,sha256=c7dqBiO4d-hxgCq-DgbP2A7HB6fdhZK4X0ADYUgwlvI,844
30
30
  os_vif/tests/functional/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
31
  os_vif/tests/functional/internal/command/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
32
  os_vif/tests/functional/internal/command/ip/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- os_vif/tests/functional/internal/command/ip/test_impl_pyroute2.py,sha256=8hOQjz12P92ko9EjX0tQ_1a8zU-Bmqt449WRUB5bl74,9758
33
+ os_vif/tests/functional/internal/command/ip/test_impl_pyroute2.py,sha256=J1NsdZAK_OY6dvKlb6HstJAhDvKUOEMzox8sAZGe-mo,9982
34
34
  os_vif/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  os_vif/tests/unit/base.py,sha256=sEjrOSy3yW16WwOorv164ZuUyvKh2TY0gMoVf6lg3kA,795
36
36
  os_vif/tests/unit/test_base.py,sha256=iqgweFRWprY98mkGL3HIbV9D8DuX_tUPH8uRiDflwDc,3137
@@ -65,29 +65,29 @@ vif_plug_ovs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
65
  vif_plug_ovs/constants.py,sha256=P1ngaAA2MioPrSr58JtiDA8rxkl3thttgOvabEyZGIM,944
66
66
  vif_plug_ovs/exception.py,sha256=ZiECZVvjCFTVXhuii3uhJn4lZvozKRxziedZB3R9JNo,1328
67
67
  vif_plug_ovs/linux_net.py,sha256=nr2dNOECVZtiHSIQHFb5iRVyh7cfLPiIm4VEg3frgDE,14289
68
- vif_plug_ovs/ovs.py,sha256=hXPSCj3w12Wi6YTftlurNrL65Qwd5kOC7Y60SxnNCcc,21217
69
- vif_plug_ovs/privsep.py,sha256=Ll3iRpxZKOKgg8wnLPBgNmADB1ooHzAW_8KiYdSKJjw,866
68
+ vif_plug_ovs/ovs.py,sha256=gUpOqhGqmWRfcDWFfctGwP3nJqNKmQqij7HRGNBSdaY,23740
69
+ vif_plug_ovs/privsep.py,sha256=nPQUkYgjbSjaHMuu40fZArRnf8RBiR73l4YpDF1K1yQ,1100
70
70
  vif_plug_ovs/ovsdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
71
  vif_plug_ovs/ovsdb/api.py,sha256=_jHR8xokDAPI8sw3IEWuT3hObJuskqVzeVMDaWRbIq0,1293
72
- vif_plug_ovs/ovsdb/impl_idl.py,sha256=pqFnNsMlJxcdpV--_utI9zkcTqlTR__P0KItrPjtl9Y,3020
73
- vif_plug_ovs/ovsdb/impl_vsctl.py,sha256=gDXDsmcEDWsKjrjGlBx-FlLbAYMqjUa_wBFr5CxN1H0,14266
74
- vif_plug_ovs/ovsdb/ovsdb_lib.py,sha256=hryLB7pu-4QSGv9ZmLyF9RyI156n4Bow9XJZRan_nOg,8565
72
+ vif_plug_ovs/ovsdb/impl_idl.py,sha256=DR1aHN8riBpZyNePwpvT9PRYbf9vLk4lRJ9VHRVq_to,3028
73
+ vif_plug_ovs/ovsdb/impl_vsctl.py,sha256=MSf8JMrMqrwt5vH8KTsThjdnsttJEYJikIcJ12CTz8c,14437
74
+ vif_plug_ovs/ovsdb/ovsdb_lib.py,sha256=UfVJG8PtIp2ps51JXp3El9rUgtfR12Mp5dna6_i-20M,10599
75
75
  vif_plug_ovs/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
76
  vif_plug_ovs/tests/functional/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- vif_plug_ovs/tests/functional/base.py,sha256=xDihDAHbhfMvTxR4yclbJ9M7eOodebkxDLFME4IsM3Y,1303
78
- vif_plug_ovs/tests/functional/test_plugin.py,sha256=gYj4uKYkNDdgU8wAZVtksD4MrxXDMX_RqiwgH3DtpHE,3550
77
+ vif_plug_ovs/tests/functional/base.py,sha256=9EDYKUqsoqVgrYmljb6fZvGmIruL2zNJdIHlEZ9kzu8,1872
78
+ vif_plug_ovs/tests/functional/test_plugin.py,sha256=40gbf5bvxgauHf3U3oT9xeIOcZ3r8-fFRyHOrhWbZVo,6648
79
79
  vif_plug_ovs/tests/functional/ovsdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
- vif_plug_ovs/tests/functional/ovsdb/test_ovsdb_lib.py,sha256=dEJmD8Rx2BkF3shNip6ygmf6pt6gtB4dolLjkS10v2k,7702
80
+ vif_plug_ovs/tests/functional/ovsdb/test_ovsdb_lib.py,sha256=oB06rO9KgezJ5fe4kxsKANkC6b7sqKkzSyO2_M-Q8gw,13513
81
81
  vif_plug_ovs/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
82
  vif_plug_ovs/tests/unit/test_linux_net.py,sha256=B3buNpn9EKZQPio5rNrIma_E5MUL9vBspdg5EqZ3RHk,17532
83
- vif_plug_ovs/tests/unit/test_plugin.py,sha256=QVNqO8g6VtHC4kPwkgn-WBUZr1bxvcdzCxtPx-bGudw,31000
83
+ vif_plug_ovs/tests/unit/test_plugin.py,sha256=6Nk8Dsv-DlmhXofSnb_f8Qs6SCDGzMRu4SEUShxwWaw,31199
84
84
  vif_plug_ovs/tests/unit/ovsdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
85
  vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py,sha256=rNPmkH_xkGCrRDeUV_xQEAJ6A7sOH-sEGXvPYju1zwk,10705
86
- os_vif-3.1.0.dist-info/AUTHORS,sha256=kHQIUdcfkbLV3s4oSLUX7MGAHq6RabUFDw9wm-nj57g,3062
87
- os_vif-3.1.0.dist-info/LICENSE,sha256=XfKg2H1sVi8OoRxoisUlMqoo10TKvHmU_wU39ks7MyA,10143
88
- os_vif-3.1.0.dist-info/METADATA,sha256=VeyAirLvZmTTMrRpSqJwrf6x4ZhSScdux_Vs-ydJ89A,2266
89
- os_vif-3.1.0.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
90
- os_vif-3.1.0.dist-info/entry_points.txt,sha256=UhIaj_WPv49yG316SjjDuMlSqGvym-_TVYEn5JKlEaQ,207
91
- os_vif-3.1.0.dist-info/pbr.json,sha256=CczCPlPJfe78gmtN2p8FcHimKyZBJwU8eXWw9CJQKT0,46
92
- os_vif-3.1.0.dist-info/top_level.txt,sha256=ULBxtkTk3bkfzCSYJjifWehfjJdMODVzC6SX5l_CNKo,56
93
- os_vif-3.1.0.dist-info/RECORD,,
86
+ os_vif-3.2.0.dist-info/AUTHORS,sha256=pqvtQTCtkonuLkD0R1k5wJdehEO19mDpXtHaSQh-Axs,3132
87
+ os_vif-3.2.0.dist-info/LICENSE,sha256=XfKg2H1sVi8OoRxoisUlMqoo10TKvHmU_wU39ks7MyA,10143
88
+ os_vif-3.2.0.dist-info/METADATA,sha256=7a_Hm63QMuaPZXD1GdhMafB_7tfNxVpINHuPfIj7YTQ,2266
89
+ os_vif-3.2.0.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
90
+ os_vif-3.2.0.dist-info/entry_points.txt,sha256=UhIaj_WPv49yG316SjjDuMlSqGvym-_TVYEn5JKlEaQ,207
91
+ os_vif-3.2.0.dist-info/pbr.json,sha256=yPKC04UCFJTK52u4xAajBv7s59GkiBMmvEbGu6qAjW0,46
92
+ os_vif-3.2.0.dist-info/top_level.txt,sha256=ULBxtkTk3bkfzCSYJjifWehfjJdMODVzC6SX5l_CNKo,56
93
+ os_vif-3.2.0.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ {"git_version": "da742a8", "is_release": true}
vif_plug_ovs/ovs.py CHANGED
@@ -102,7 +102,26 @@ class OvsPlugin(plugin.PluginBase):
102
102
  'bridge. This is experimental and controls the plugging '
103
103
  'behavior when not using hybrid-plug.'
104
104
  'This is only used on linux and should be set to false '
105
- 'in all other cases such as ironic smartnic ports.')
105
+ 'in all other cases such as ironic smartnic ports.'),
106
+ cfg.StrOpt('default_qos_type',
107
+ choices=[
108
+ 'linux-htb', 'linux-hfsc', 'linux-sfq', 'linux-codel',
109
+ 'linux-fq_codel', 'linux-noop'
110
+ ],
111
+ default='linux-noop',
112
+ help="""
113
+ The default qos type to apply to ovs ports.
114
+ linux-noop is the default. ovs will not modify
115
+ the qdisc on the port if linux-noop is specified.
116
+ This allows operators to manage QOS out of band
117
+ of OVS. For more information see the ovs man pages
118
+ https://manpages.debian.org/testing/openvswitch-common/ovs-vswitchd.conf.db.5.en.html#type~4
119
+
120
+ Note: This will only be set when a port is first created
121
+ on the ovs bridge to ensure that the qos type can be
122
+ managed via neutron if required for bandwidth limiting
123
+ and other use-cases.
124
+ """),
106
125
  )
107
126
 
108
127
  def __init__(self, config):
@@ -159,6 +178,14 @@ class OvsPlugin(plugin.PluginBase):
159
178
  return vif.network.mtu
160
179
  return self.config.network_device_mtu
161
180
 
181
+ def supports_tc_qdisc(self, vif) -> bool:
182
+ if self._get_vif_datapath_type(vif) != constants.OVS_DATAPATH_SYSTEM:
183
+ return False
184
+ if sys.platform == constants.PLATFORM_WIN32:
185
+ return False
186
+
187
+ return True
188
+
162
189
  def _create_vif_port(self, vif, vif_name, instance_info, **kwargs):
163
190
  mtu = self._get_mtu(vif)
164
191
  # NOTE(sean-k-mooney): As part of a partial fix to bug #1734320
@@ -175,6 +202,19 @@ class OvsPlugin(plugin.PluginBase):
175
202
  # can be enabled automatically in the future.
176
203
  if self.config.isolate_vif:
177
204
  kwargs['tag'] = constants.DEAD_VLAN
205
+ qos_type = self._get_qos_type(vif)
206
+ if qos_type is not None:
207
+ # NOTE(sean-k-mooney): If the port is not already created
208
+ # on the bridge we need to set the default qos type to
209
+ # ensure that the port is created with the correct qos
210
+ # type. This is only needed for the linux kernel datapath
211
+ # as the qos type is not managed by neutron for the other
212
+ # datapaths.
213
+ # This is a mitigation for the performance regression
214
+ # introduced by the fix for bug #1734320. See bug #2017868
215
+ # for more details.
216
+ if not self.ovsdb.port_exists(vif_name, vif.network.bridge):
217
+ kwargs['qos_type'] = qos_type
178
218
  bridge = kwargs.pop('bridge', vif.network.bridge)
179
219
  self.ovsdb.create_ovs_vif_port(
180
220
  bridge,
@@ -382,9 +422,18 @@ class OvsPlugin(plugin.PluginBase):
382
422
 
383
423
  linux_net.delete_bridge(linux_bridge_name, v1_name)
384
424
 
385
- self.ovsdb.delete_ovs_vif_port(vif.network.bridge, v2_name)
425
+ qos_type = self._get_qos_type(vif)
426
+ self.ovsdb.delete_ovs_vif_port(
427
+ vif.network.bridge, v2_name, qos_type=qos_type
428
+ )
386
429
  self._delete_bridge_if_trunk(vif)
387
430
 
431
+ def _get_qos_type(self, vif):
432
+ qos_type = None
433
+ if self.supports_tc_qdisc(vif):
434
+ qos_type = self.config.default_qos_type
435
+ return qos_type
436
+
388
437
  def _unplug_vif_windows(self, vif, instance_info):
389
438
  """Remove port from OVS."""
390
439
  self.ovsdb.delete_ovs_vif_port(vif.network.bridge, vif.id,
@@ -400,7 +449,10 @@ class OvsPlugin(plugin.PluginBase):
400
449
  int_bridge_patch = self.gen_port_name('ibp', vif.id, max_length=64)
401
450
  self.ovsdb.delete_ovs_vif_port(vif.network.bridge, int_bridge_patch)
402
451
  self.ovsdb.delete_ovs_vif_port(port_bridge_name, port_bridge_patch)
403
- self.ovsdb.delete_ovs_vif_port(port_bridge_name, vif.vif_name)
452
+ qos_type = self._get_qos_type(vif)
453
+ self.ovsdb.delete_ovs_vif_port(
454
+ port_bridge_name, vif.vif_name, qos_type=qos_type
455
+ )
404
456
  self.ovsdb.delete_ovs_bridge(port_bridge_name)
405
457
  self._delete_bridge_if_trunk(vif)
406
458
 
@@ -409,7 +461,10 @@ class OvsPlugin(plugin.PluginBase):
409
461
  # NOTE(sean-k-mooney): even with the partial revert of change
410
462
  # Iaf15fa7a678ec2624f7c12f634269c465fbad930 this should be correct
411
463
  # so this is not removed.
412
- self.ovsdb.delete_ovs_vif_port(vif.network.bridge, vif.vif_name)
464
+ qos_type = self._get_qos_type(vif)
465
+ self.ovsdb.delete_ovs_vif_port(
466
+ vif.network.bridge, vif.vif_name, qos_type=qos_type
467
+ )
413
468
  self._delete_bridge_if_trunk(vif)
414
469
 
415
470
  def _unplug_vf(self, vif):
@@ -428,8 +483,11 @@ class OvsPlugin(plugin.PluginBase):
428
483
  # The representor interface can't be deleted because it bind the
429
484
  # SR-IOV VF, therefore we just need to remove it from the ovs bridge
430
485
  # and set the status to down
486
+ qos_type = self._get_qos_type(vif)
431
487
  self.ovsdb.delete_ovs_vif_port(
432
- vif.network.bridge, representor, delete_netdev=False)
488
+ vif.network.bridge, representor, delete_netdev=False,
489
+ qos_type=qos_type
490
+ )
433
491
  if datapath == constants.OVS_DATAPATH_SYSTEM:
434
492
  linux_net.set_interface_state(representor, 'down')
435
493
  self._delete_bridge_if_trunk(vif)
@@ -23,7 +23,7 @@ from ovsdbapp.schema.open_vswitch import impl_idl
23
23
 
24
24
  from vif_plug_ovs.ovsdb import api
25
25
 
26
- REQUIRED_TABLES = ('Interface', 'Port', 'Bridge', 'Open_vSwitch')
26
+ REQUIRED_TABLES = ('Interface', 'Port', 'Bridge', 'Open_vSwitch', 'QoS')
27
27
 
28
28
 
29
29
  def idl_factory(config):
@@ -48,6 +48,7 @@ class NeutronOvsdbIdl(impl_idl.OvsdbIdl, api.ImplAPI):
48
48
  This class provides an OVSDB IDL (Open vSwitch Database Interface
49
49
  Definition Language) interface to the OVS back-end.
50
50
  """
51
+
51
52
  def __init__(self, conn):
52
53
  vlog.use_python_logger()
53
54
  super(NeutronOvsdbIdl, self).__init__(conn)
@@ -116,6 +116,15 @@ class BaseCommand(ovsdb_api.Command):
116
116
  self.cmd = cmd
117
117
  self.opts = [] if opts is None else opts
118
118
  self.args = [] if args is None else args
119
+ self._result = None
120
+
121
+ @property
122
+ def result(self):
123
+ return self._result
124
+
125
+ @result.setter
126
+ def result(self, value):
127
+ self._result = value
119
128
 
120
129
  def execute(self, check_error=False, log_errors=True):
121
130
  with Transaction(self.context, check_error=check_error,
@@ -11,6 +11,7 @@
11
11
  # under the License.
12
12
 
13
13
  import sys
14
+ import uuid
14
15
 
15
16
  from oslo_log import log as logging
16
17
 
@@ -20,6 +21,7 @@ from vif_plug_ovs.ovsdb import api as ovsdb_api
20
21
 
21
22
 
22
23
  LOG = logging.getLogger(__name__)
24
+ QOS_UUID_NAMESPACE = uuid.UUID("68da264a-847f-42a8-8ab0-5e774aee3d95")
23
25
 
24
26
 
25
27
  class BaseOVS(object):
@@ -142,7 +144,8 @@ class BaseOVS(object):
142
144
  def create_ovs_vif_port(
143
145
  self, bridge, dev, iface_id, mac, instance_id,
144
146
  mtu=None, interface_type=None, vhost_server_path=None,
145
- tag=None, pf_pci=None, vf_num=None, set_ids=True, datapath_type=None
147
+ tag=None, pf_pci=None, vf_num=None, set_ids=True, datapath_type=None,
148
+ qos_type=None
146
149
  ):
147
150
  """Create OVS port
148
151
 
@@ -159,6 +162,7 @@ class BaseOVS(object):
159
162
  :param vf_num: VF number of PF for dpdk representor port.
160
163
  :param set_ids: set external ids on port (bool).
161
164
  :param datapath_type: datapath type for port's bridge
165
+ :param qos_type: qos type for a port
162
166
 
163
167
  .. note:: create DPDK representor port by setting all three values:
164
168
  `interface_type`, `pf_pci` and `vf_num`. if interface type is
@@ -181,6 +185,24 @@ class BaseOVS(object):
181
185
  PF_PCI=pf_pci, VF_NUM=vf_num)
182
186
  col_values.append(('options',
183
187
  {'dpdk-devargs': devargs_string}))
188
+ # create qos record if qos type is specified
189
+ # and get the qos id. This is done outside of the transaction
190
+ # because we need the qos id to set the qos on the port.
191
+ # The qos uuid cannot be set when creating the record so we
192
+ # have to look it up after the record is created. this means
193
+ # we need to create the qos record outside of the transaction
194
+ # that creates the port.
195
+ qid = None
196
+ if qos_type:
197
+ self.delete_qos_if_exists(dev, qos_type)
198
+ qos_id = uuid.uuid5(QOS_UUID_NAMESPACE, dev)
199
+ qos_external_ids = {'id': str(qos_id), '_type': qos_type}
200
+ self.ovsdb.db_create(
201
+ 'QoS', type=qos_type, external_ids=qos_external_ids
202
+ ).execute(check_error=True)
203
+ record = self.get_qos(dev, qos_type)
204
+ qid = record[0]['_uuid']
205
+
184
206
  with self.ovsdb.transaction() as txn:
185
207
  if datapath_type:
186
208
  txn.add(self.ovsdb.add_br(bridge, may_exist=True,
@@ -188,19 +210,46 @@ class BaseOVS(object):
188
210
  txn.add(self.ovsdb.add_port(bridge, dev))
189
211
  if tag:
190
212
  txn.add(self.ovsdb.db_set('Port', dev, ('tag', tag)))
213
+ if qid:
214
+ txn.add(self.ovsdb.db_set('Port', dev, ('qos', qid)))
191
215
  if col_values:
192
216
  txn.add(self.ovsdb.db_set('Interface', dev, *col_values))
193
217
  self.update_device_mtu(
194
218
  txn, dev, mtu, interface_type=interface_type
195
219
  )
196
220
 
221
+ def port_exists(self, port_name, bridge):
222
+ ports = self.ovsdb.list_ports(bridge).execute()
223
+ return ports is not None and port_name in ports
224
+
225
+ def get_qos(self, dev, qos_type):
226
+ qos_id = uuid.uuid5(QOS_UUID_NAMESPACE, dev)
227
+ external_ids = {'id': str(qos_id), '_type': qos_type}
228
+ return self.ovsdb.db_find(
229
+ 'QoS', ('external_ids', '=', external_ids),
230
+ colmuns=['_uuid']
231
+ ).execute()
232
+
233
+ def delete_qos_if_exists(self, dev, qos_type):
234
+ qos_ids = self.get_qos(dev, qos_type)
235
+ if qos_ids is not None and len(qos_ids) > 0:
236
+ for qos_id in qos_ids:
237
+ if '_uuid' in qos_id:
238
+ self.ovsdb.db_destroy(
239
+ 'QoS', str(qos_id['_uuid'])
240
+ ).execute()
241
+
197
242
  def update_ovs_vif_port(self, dev, mtu=None, interface_type=None):
198
243
  with self.ovsdb.transaction() as txn:
199
244
  self.update_device_mtu(
200
245
  txn, dev, mtu, interface_type=interface_type
201
246
  )
202
247
 
203
- def delete_ovs_vif_port(self, bridge, dev, delete_netdev=True):
248
+ def delete_ovs_vif_port(
249
+ self, bridge, dev, delete_netdev=True, qos_type=None
250
+ ):
204
251
  self.ovsdb.del_port(dev, bridge=bridge, if_exists=True).execute()
252
+ if qos_type:
253
+ self.delete_qos_if_exists(dev, qos_type)
205
254
  if delete_netdev:
206
255
  linux_net.delete_net_dev(dev)
vif_plug_ovs/privsep.py CHANGED
@@ -20,5 +20,16 @@ vif_plug = priv_context.PrivContext(
20
20
  "vif_plug_ovs",
21
21
  cfg_section="vif_plug_ovs_privileged",
22
22
  pypath=__name__ + ".vif_plug",
23
- capabilities=[c.CAP_NET_ADMIN],
23
+ capabilities=[
24
+ c.CAP_NET_ADMIN,
25
+ ],
26
+ )
27
+
28
+ vif_plug_test = priv_context.PrivContext(
29
+ "vif_plug_ovs",
30
+ cfg_section="vif_plug_ovs_privileged",
31
+ pypath=__name__ + ".vif_plug_test",
32
+ capabilities=[
33
+ c.CAP_NET_ADMIN, c.CAP_DAC_OVERRIDE,
34
+ ],
24
35
  )
@@ -25,6 +25,22 @@ class VifPlugOvsBaseFunctionalTestCase(os_vif_base.BaseFunctionalTestCase):
25
25
  def _check_bridge(self, name):
26
26
  return self._ovsdb.br_exists(name).execute()
27
27
 
28
+ def _check_port(self, name, bridge):
29
+ return self.ovs.port_exists(name, bridge)
30
+
31
+ def _check_parameter(self, table, port, parameter, expected_value):
32
+ def get_value():
33
+ return self._ovsdb.db_get(table, port, parameter).execute()
34
+
35
+ def check_value():
36
+ val = get_value()
37
+ return val == expected_value
38
+ self.assertTrue(
39
+ wait_until_true(check_value, timeout=2, sleep=0.5),
40
+ f"Parameter {parameter} of {table} {port} is {get_value()} "
41
+ f"not {expected_value}"
42
+ )
43
+
28
44
  def _add_bridge(self, name, may_exist=True, datapath_type=None):
29
45
  self._ovsdb.add_br(name, may_exist=may_exist,
30
46
  datapath_type=datapath_type).execute()
@@ -10,8 +10,10 @@
10
10
  # License for the specific language governing permissions and limitations
11
11
  # under the License.
12
12
 
13
+ import fixtures
13
14
  import random
14
15
 
16
+
15
17
  from unittest import mock
16
18
 
17
19
  import testscenarios
@@ -19,7 +21,6 @@ import testscenarios
19
21
  from oslo_concurrency import processutils
20
22
  from oslo_config import cfg
21
23
  from oslo_utils import uuidutils
22
- from ovsdbapp.schema.open_vswitch import impl_idl
23
24
 
24
25
  from vif_plug_ovs import constants
25
26
  from vif_plug_ovs import linux_net
@@ -32,7 +33,7 @@ from vif_plug_ovs.tests.functional import base
32
33
  CONF = cfg.CONF
33
34
 
34
35
 
35
- @privsep.vif_plug.entrypoint
36
+ @privsep.vif_plug_test.entrypoint
36
37
  def run_privileged(*full_args):
37
38
  return processutils.execute(*full_args)[0].rstrip()
38
39
 
@@ -59,17 +60,17 @@ class TestOVSDBLib(testscenarios.WithScenarios,
59
60
  self.interface)
60
61
 
61
62
  # Make sure exceptions pass through by calling do_post_commit directly
62
- mock.patch.object(
63
- impl_idl.OvsVsctlTransaction, 'post_commit',
64
- side_effect=impl_idl.OvsVsctlTransaction.do_post_commit).start()
63
+ post_commit = (
64
+ 'ovsdbapp.schema.open_vswitch.impl_idl.'
65
+ 'OvsVsctlTransaction.post_commit'
66
+ )
67
+ # "this" is the self parmater which is a reference to the
68
+ # OvsVsctlTransaction instance on which do_post_commit is defiend.
65
69
 
66
- def _check_parameter(self, table, port, parameter, expected_value):
67
- def check_value():
68
- return (self._ovsdb.db_get(
69
- table, port, parameter).execute() == expected_value)
70
+ def direct_post_commit(this, transaction):
71
+ this.do_post_commit(transaction)
70
72
 
71
- self.assertTrue(base.wait_until_true(check_value, timeout=2,
72
- sleep=0.5))
73
+ self.useFixture(fixtures.MonkeyPatch(post_commit, direct_post_commit))
73
74
 
74
75
  def _add_port(self, bridge, port, may_exist=True):
75
76
  with self._ovsdb.transaction() as txn:
@@ -122,11 +123,11 @@ class TestOVSDBLib(testscenarios.WithScenarios,
122
123
  expected_external_ids)
123
124
  self._check_parameter('Interface', port_name, 'type', interface_type)
124
125
  expected_vhost_server_path = {'vhost-server-path': vhost_server_path}
125
- self._check_parameter('Interface', port_name, 'options',
126
- expected_vhost_server_path)
127
- self._check_parameter('Interface', port_name, 'options',
128
- expected_vhost_server_path)
126
+ self._check_parameter(
127
+ 'Interface', port_name, 'options', expected_vhost_server_path
128
+ )
129
129
  self._check_parameter('Port', port_name, 'tag', 2000)
130
+ self._check_parameter('Port', port_name, 'qos', [])
130
131
 
131
132
  @mock.patch.object(linux_net, 'delete_net_dev')
132
133
  def test_delete_ovs_vif_port(self, *mock):
@@ -180,3 +181,156 @@ class TestOVSDBLib(testscenarios.WithScenarios,
180
181
  port_opts = {'peer': int_bridge_port}
181
182
  self._check_parameter(
182
183
  'Interface', port_bridge_port, 'options', port_opts)
184
+
185
+ def test_create_ovs_vif_port_with_default_qos(self):
186
+ port_name = 'qos-port-' + self.interface
187
+ iface_id = 'iface_id'
188
+ mac = 'ca:fe:ca:fe:ca:fe'
189
+ instance_id = uuidutils.generate_uuid()
190
+ mtu = 1500
191
+ interface_type = 'internal'
192
+ qos_type = CONF.os_vif_ovs.default_qos_type
193
+
194
+ self.addCleanup(self._del_bridge, self.brname)
195
+ self._add_bridge(self.brname)
196
+
197
+ self.addCleanup(
198
+ self.ovs.delete_ovs_vif_port, self.brname, port_name,
199
+ delete_netdev=False, qos_type=qos_type
200
+ )
201
+ self.ovs.create_ovs_vif_port(
202
+ self.brname, port_name, iface_id, mac,
203
+ instance_id, mtu=mtu, interface_type=interface_type,
204
+ tag=2000, qos_type=qos_type
205
+ )
206
+
207
+ # first we assert that the standard parameters are set correctly
208
+ expected_external_ids = {'iface-status': 'active',
209
+ 'iface-id': iface_id,
210
+ 'attached-mac': mac,
211
+ 'vm-uuid': instance_id}
212
+ self._check_parameter('Interface', port_name, 'external_ids',
213
+ expected_external_ids)
214
+ self._check_parameter('Interface', port_name, 'type', interface_type)
215
+ self._check_parameter('Port', port_name, 'tag', 2000)
216
+
217
+ # now we check that the port has a qos policy attached
218
+ qos_uuid = self.ovs.get_qos(
219
+ port_name, qos_type
220
+ )[0]['_uuid']
221
+ self._check_parameter('Port', port_name, 'qos', qos_uuid)
222
+
223
+ # finally we check that the qos policy has the correct parameters
224
+ self._check_parameter(
225
+ 'QoS', str(qos_uuid), 'type', qos_type
226
+ )
227
+
228
+ def test_delete_qos_if_exists(self):
229
+ port_name = 'del-qos-port-' + self.interface
230
+ iface_id = 'iface_id'
231
+ mac = 'ca:fe:ca:fe:ca:fe'
232
+ instance_id = uuidutils.generate_uuid()
233
+ interface_type = 'internal'
234
+ qos_type = CONF.os_vif_ovs.default_qos_type
235
+
236
+ # setup test by creating a bridge and port, and register
237
+ # cleanup funcitons to avoid leaking them.
238
+ self.addCleanup(self._del_bridge, self.brname)
239
+ self._add_bridge(self.brname)
240
+ self.addCleanup(
241
+ self.ovs.delete_ovs_vif_port, self.brname, port_name,
242
+ delete_netdev=False, qos_type=qos_type
243
+ )
244
+ self.ovs.create_ovs_vif_port(
245
+ self.brname, port_name, iface_id, mac,
246
+ instance_id, interface_type=interface_type,
247
+ qos_type=qos_type
248
+ )
249
+
250
+ # now we check that the port has a qos policy attached
251
+ qos_uuid = self.ovs.get_qos(
252
+ port_name, CONF.os_vif_ovs.default_qos_type
253
+ )[0]['_uuid']
254
+ self._check_parameter('Port', port_name, 'qos', qos_uuid)
255
+
256
+ # finally we check that the qos policy has the correct parameters
257
+ self._check_parameter(
258
+ 'QoS', str(qos_uuid), 'type', qos_type
259
+ )
260
+
261
+ # we need to delete the port directly in the db to remove
262
+ # any references to the qos policy
263
+ self.ovs.ovsdb.del_port(
264
+ port_name, bridge=self.brname, if_exists=True).execute()
265
+ # then we can delete the qos policy
266
+ self.ovs.delete_qos_if_exists(port_name, qos_type)
267
+ self._check_parameter(
268
+ 'QoS', str(qos_uuid), 'type', None
269
+ )
270
+ # invoking the delete when the policy does not exist
271
+ # should not result in an error
272
+ self.ovs.delete_qos_if_exists(port_name, qos_type)
273
+ self._check_parameter(
274
+ 'QoS', str(qos_uuid), 'type', None
275
+ )
276
+
277
+ def test_get_qos(self):
278
+ port_name = 'get-qos-' + self.interface
279
+ iface_id = 'iface_id'
280
+ mac = 'ca:fe:ca:fe:ca:fe'
281
+ instance_id = uuidutils.generate_uuid()
282
+ interface_type = 'internal'
283
+ qos_type = CONF.os_vif_ovs.default_qos_type
284
+ # initally no qos policy should exist
285
+ self.assertEqual(0, len(self.ovs.get_qos(port_name, qos_type)))
286
+
287
+ # if we create a port with a qos policy get_qos should
288
+ # return the policy
289
+ self.addCleanup(self._del_bridge, self.brname)
290
+ self._add_bridge(self.brname)
291
+ self.addCleanup(
292
+ self.ovs.delete_ovs_vif_port, self.brname, port_name,
293
+ delete_netdev=False, qos_type=qos_type
294
+ )
295
+ self.ovs.create_ovs_vif_port(
296
+ self.brname, port_name, iface_id, mac,
297
+ instance_id, interface_type=interface_type,
298
+ qos_type=qos_type
299
+ )
300
+ # result should be a list of lenght 1 containing the
301
+ # qos policy created for the port we defied.
302
+ result = self.ovs.get_qos(port_name, qos_type)
303
+ self.assertEqual(1, len(result))
304
+ self.assertIn('_uuid', result[0])
305
+ self._check_parameter(
306
+ 'Port', port_name, 'qos', result[0]['_uuid']
307
+ )
308
+ # if we delete the port and its qos policy get_qos should
309
+ # not return it.
310
+ self.ovs.delete_ovs_vif_port(
311
+ self.brname, port_name,
312
+ delete_netdev=False, qos_type=qos_type
313
+ )
314
+ self.assertEqual(0, len(self.ovs.get_qos(port_name, qos_type)))
315
+
316
+ def test_port_exists(self):
317
+ port_name = 'port-exists-' + self.interface
318
+ iface_id = 'iface_id'
319
+ mac = 'ca:fe:ca:fe:ca:fe'
320
+ instance_id = uuidutils.generate_uuid()
321
+ interface_type = 'internal'
322
+
323
+ self.assertFalse(self.ovs.port_exists(port_name, self.brname))
324
+
325
+ self.addCleanup(self._del_bridge, self.brname)
326
+ self._add_bridge(self.brname)
327
+ self.addCleanup(
328
+ self.ovs.delete_ovs_vif_port, self.brname, port_name,
329
+ delete_netdev=False,
330
+ )
331
+ self.ovs.create_ovs_vif_port(
332
+ self.brname, port_name, iface_id, mac,
333
+ instance_id, interface_type=interface_type,
334
+ )
335
+
336
+ self.assertTrue(self.ovs.port_exists(port_name, self.brname))
@@ -11,6 +11,7 @@
11
11
  # under the License.
12
12
 
13
13
  import testscenarios
14
+ import time
14
15
 
15
16
  from oslo_concurrency import processutils
16
17
  from oslo_config import cfg
@@ -33,6 +34,41 @@ def run_privileged(*full_args):
33
34
  return processutils.execute(*full_args)[0].rstrip()
34
35
 
35
36
 
37
+ # derived from test_impl_pyroute2
38
+
39
+ def exist_device(device):
40
+ try:
41
+ run_privileged('ip', 'link', 'show', device)
42
+ return True
43
+ except processutils.ProcessExecutionError as e:
44
+ if e.exit_code == 1:
45
+ return False
46
+ raise
47
+
48
+
49
+ def add_device(device, dev_type, peer=None, link=None,
50
+ vlan_id=None):
51
+ if 'vlan' == dev_type:
52
+ run_privileged('ip', 'link', 'add', 'link', link,
53
+ 'name', device, 'type', dev_type, 'vlan', 'id',
54
+ vlan_id)
55
+ elif 'veth' == dev_type:
56
+ run_privileged('ip', 'link', 'add', device, 'type', dev_type,
57
+ 'peer', 'name', peer)
58
+ elif 'dummy' == dev_type:
59
+ run_privileged('ip', 'link', 'add', device, 'type', dev_type)
60
+ # ensure that the device exists to prevent racing with other ip commands
61
+ for _ in range(10):
62
+ if exist_device(device):
63
+ return
64
+ time.sleep(0.1)
65
+
66
+
67
+ def del_device(device):
68
+ if exist_device(device):
69
+ run_privileged('ip', 'link', 'del', device)
70
+
71
+
36
72
  class TestOVSPlugin(testscenarios.WithScenarios,
37
73
  base.VifPlugOvsBaseFunctionalTestCase):
38
74
 
@@ -82,6 +118,24 @@ class TestOVSPlugin(testscenarios.WithScenarios,
82
118
  mode='client',
83
119
  port_profile=self.profile_ovs)
84
120
 
121
+ self.profile_ovs_system = objects.vif.VIFPortProfileOpenVSwitch(
122
+ interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa',
123
+ datapath_type='system',
124
+ create_port=True)
125
+
126
+ self.network_ovs = objects.network.Network(
127
+ id='437c6db5-4e6f-4b43-b64b-ed6a11ee5ba7',
128
+ bridge='br-qos-' + self.interface,
129
+ subnets=self.subnets,
130
+ vlan=99)
131
+
132
+ self.vif_ovs_port = objects.vif.VIFOpenVSwitch(
133
+ id='b679325f-ca89-4ee0-a8be-6db1409b69ea',
134
+ address='ca:fe:de:ad:be:ef',
135
+ network=self.network_ovs,
136
+ port_profile=self.profile_ovs_system,
137
+ vif_name="qos-port-" + self.interface)
138
+
85
139
  self.instance = objects.instance_info.InstanceInfo(
86
140
  name='demo',
87
141
  uuid='f0000000-0000-0000-0000-000000000001')
@@ -98,3 +152,34 @@ class TestOVSPlugin(testscenarios.WithScenarios,
98
152
  self.plugin.unplug(self.vif_vhostuser_trunk, self.instance)
99
153
  self.assertTrue(self._check_bridge(other_bridge))
100
154
  self.assertFalse(self._check_bridge(trunk_bridge))
155
+
156
+ def test_plug_unplug_ovs_port_with_qos(self):
157
+ bridge = 'br-qos-' + self.interface
158
+ vif_name = "qos-port-" + self.interface
159
+ qos_type = CONF.os_vif_ovs.default_qos_type
160
+ self.addCleanup(self._del_bridge, bridge)
161
+ self.addCleanup(
162
+ self.ovs.delete_ovs_vif_port, bridge, vif_name,
163
+ delete_netdev=False, qos_type=qos_type
164
+ )
165
+ self.addCleanup(del_device, vif_name)
166
+ add_device(vif_name, 'dummy')
167
+ # pluging a vif will create the port and bridge
168
+ # if either does not exist
169
+ self.plugin.plug(self.vif_ovs_port, self.instance)
170
+ self.assertTrue(self._check_bridge(bridge))
171
+ self.assertTrue(self._check_port(vif_name, bridge))
172
+ qos_uuid = self.ovs.get_qos(
173
+ vif_name, qos_type
174
+ )[0]['_uuid']
175
+ self._check_parameter('Port', vif_name, 'qos', qos_uuid)
176
+ self._check_parameter(
177
+ 'QoS', str(qos_uuid), 'type', qos_type
178
+ )
179
+ # unpluging a port will not delete the bridge.
180
+ self.plugin.unplug(self.vif_ovs_port, self.instance)
181
+ self.assertTrue(self._check_bridge(bridge))
182
+ self.assertFalse(self._check_port(vif_name, bridge))
183
+ self._check_parameter(
184
+ 'QoS', str(qos_uuid), 'type', None
185
+ )
@@ -362,7 +362,9 @@ class PluginTest(testtools.TestCase):
362
362
  delete_ovs_vif_port, delete_ovs_bridge):
363
363
  calls = {
364
364
  'delete_bridge': [mock.call('qbrvif-xxx-yyy', 'qvbb679325f-ca')],
365
- 'delete_ovs_vif_port': [mock.call('br0', 'qvob679325f-ca')]
365
+ 'delete_ovs_vif_port': [mock.call(
366
+ 'br0', 'qvob679325f-ca', qos_type='linux-noop'
367
+ )]
366
368
  }
367
369
  mock_sys.platform = 'linux'
368
370
  plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
@@ -510,8 +512,12 @@ class PluginTest(testtools.TestCase):
510
512
  'get_vf_num_by_pci_address': [mock.call('0002:24:12.3')],
511
513
  'get_representor_port': [mock.call('eth0', '2')],
512
514
  'set_interface_state': [mock.call('eth0_2', 'down')],
513
- 'delete_ovs_vif_port': [mock.call('br0', 'eth0_2',
514
- delete_netdev=False)]
515
+ 'delete_ovs_vif_port': [
516
+ mock.call(
517
+ 'br0', 'eth0_2', delete_netdev=False,
518
+ qos_type='linux-noop'
519
+ )
520
+ ]
515
521
  }
516
522
 
517
523
  get_ifname_by_pci_address.return_value = 'eth0'
@@ -602,8 +608,13 @@ class PluginTest(testtools.TestCase):
602
608
  calls = {
603
609
  'get_dpdk_representor_port_name': [mock.call(
604
610
  self.vif_ovs_vf_dpdk.id)],
605
- 'delete_ovs_vif_port': [mock.call('br0', devname,
606
- delete_netdev=False)]}
611
+ 'delete_ovs_vif_port': [
612
+ mock.call(
613
+ 'br0', devname, delete_netdev=False,
614
+ qos_type=None
615
+ )
616
+ ]
617
+ }
607
618
  plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
608
619
  plugin.unplug(self.vif_ovs_vf_dpdk, self.instance)
609
620
  get_dpdk_representor_port_name.assert_has_calls(
@@ -636,7 +647,7 @@ class PluginTest(testtools.TestCase):
636
647
  mock.call('br0', 'ibpb679325f-ca89-4ee0-a8be-6db1409b69ea'),
637
648
  mock.call(
638
649
  'pbb679325f-ca8', 'pbpb679325f-ca89-4ee0-a8be-6db1409b69ea'),
639
- mock.call('pbb679325f-ca8', 'tap-xxx-yyy-zzz')
650
+ mock.call('pbb679325f-ca8', 'tap-xxx-yyy-zzz', qos_type=None)
640
651
  ]
641
652
  delete_ovs_vif_port.assert_has_calls(calls)
642
653
  delete_ovs_bridge.assert_called_once_with('pbb679325f-ca8')
@@ -1 +0,0 @@
1
- {"git_version": "05c8a68", "is_release": true}
File without changes