os-vif 3.7.0__py3-none-any.whl → 4.0.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.
- os_vif/internal/ip/api.py +2 -13
- os_vif/tests/unit/internal/ip/test_api.py +3 -21
- {os_vif-3.7.0.dist-info → os_vif-4.0.0.dist-info}/AUTHORS +1 -0
- {os_vif-3.7.0.dist-info → os_vif-4.0.0.dist-info}/METADATA +3 -3
- {os_vif-3.7.0.dist-info → os_vif-4.0.0.dist-info}/RECORD +18 -22
- os_vif-4.0.0.dist-info/pbr.json +1 -0
- vif_plug_ovs/constants.py +0 -3
- vif_plug_ovs/linux_net.py +1 -8
- vif_plug_ovs/ovs.py +2 -37
- vif_plug_ovs/ovsdb/ovsdb_lib.py +1 -7
- vif_plug_ovs/tests/functional/base.py +8 -1
- vif_plug_ovs/tests/functional/ovsdb/test_ovsdb_lib.py +5 -0
- vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py +1 -11
- vif_plug_ovs/tests/unit/test_plugin.py +6 -63
- os_vif/internal/ip/windows/__init__.py +0 -0
- os_vif/internal/ip/windows/impl_netifaces.py +0 -47
- os_vif/tests/unit/internal/ip/windows/__init__.py +0 -0
- os_vif/tests/unit/internal/ip/windows/test_impl_netifaces.py +0 -46
- os_vif-3.7.0.dist-info/pbr.json +0 -1
- {os_vif-3.7.0.dist-info → os_vif-4.0.0.dist-info}/LICENSE +0 -0
- {os_vif-3.7.0.dist-info → os_vif-4.0.0.dist-info}/WHEEL +0 -0
- {os_vif-3.7.0.dist-info → os_vif-4.0.0.dist-info}/entry_points.txt +0 -0
- {os_vif-3.7.0.dist-info → os_vif-4.0.0.dist-info}/top_level.txt +0 -0
os_vif/internal/ip/api.py
CHANGED
|
@@ -10,21 +10,10 @@
|
|
|
10
10
|
# License for the specific language governing permissions and limitations
|
|
11
11
|
# under the License.
|
|
12
12
|
|
|
13
|
-
import os
|
|
14
|
-
import warnings
|
|
15
|
-
|
|
16
13
|
from oslo_log import log as logging
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
warnings.warn('Support for Windows OS is deprecated.',
|
|
20
|
-
category=DeprecationWarning)
|
|
21
|
-
from os_vif.internal.ip.windows.impl_netifaces import \
|
|
22
|
-
Netifaces as ip_lib_class
|
|
23
|
-
else:
|
|
24
|
-
from os_vif.internal.ip.linux.impl_pyroute2 import \
|
|
25
|
-
PyRoute2 as ip_lib_class
|
|
26
|
-
|
|
15
|
+
from os_vif.internal.ip.linux.impl_pyroute2 import PyRoute2
|
|
27
16
|
|
|
28
17
|
LOG = logging.getLogger(__name__)
|
|
29
18
|
|
|
30
|
-
ip =
|
|
19
|
+
ip = PyRoute2()
|
|
@@ -10,30 +10,12 @@
|
|
|
10
10
|
# License for the specific language governing permissions and limitations
|
|
11
11
|
# under the License.
|
|
12
12
|
|
|
13
|
-
import importlib
|
|
14
|
-
|
|
15
|
-
from unittest import mock
|
|
16
|
-
|
|
17
13
|
from os_vif.internal.ip import api
|
|
14
|
+
from os_vif.internal.ip.linux import impl_pyroute2
|
|
18
15
|
from os_vif.tests.unit import base
|
|
19
16
|
|
|
20
17
|
|
|
21
18
|
class TestIpApi(base.TestCase):
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
importlib.reload(api)
|
|
26
|
-
|
|
27
|
-
def test_get_impl_windows(self):
|
|
28
|
-
self.addCleanup(self._reload_original_os_module)
|
|
29
|
-
with mock.patch('os.name', 'nt'):
|
|
30
|
-
importlib.reload(api)
|
|
31
|
-
from os_vif.internal.ip.windows import impl_netifaces
|
|
32
|
-
self.assertIsInstance(api.ip, impl_netifaces.Netifaces)
|
|
33
|
-
|
|
34
|
-
def test_get_impl_linux(self):
|
|
35
|
-
self.addCleanup(self._reload_original_os_module)
|
|
36
|
-
with mock.patch('os.name', 'posix'):
|
|
37
|
-
importlib.reload(api)
|
|
38
|
-
from os_vif.internal.ip.linux import impl_pyroute2
|
|
39
|
-
self.assertIsInstance(api.ip, impl_pyroute2.PyRoute2)
|
|
20
|
+
def test_get_impl(self):
|
|
21
|
+
self.assertIsInstance(api.ip, impl_pyroute2.PyRoute2)
|
|
@@ -47,6 +47,7 @@ Sahid Orentino Ferdjaoui <sahid.ferdjaoui@redhat.com>
|
|
|
47
47
|
Sean Dague <sean@dague.net>
|
|
48
48
|
Sean M. Collins <sean@coreitpro.com>
|
|
49
49
|
Sean Mooney <sean.k.mooney@intel.com>
|
|
50
|
+
Sean Mooney <sean@seanmooney.info>
|
|
50
51
|
Sean Mooney <work@seanmooney.info>
|
|
51
52
|
Sergey Belous <sbelous@mirantis.com>
|
|
52
53
|
Spencer Yu <yushb@gohighsec.com>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: os-vif
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.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
|
|
@@ -15,12 +15,12 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.9
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
22
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
24
|
Requires-Dist: debtcollector (>=1.19.0)
|
|
25
25
|
Requires-Dist: oslo.concurrency (>=3.20.0)
|
|
26
26
|
Requires-Dist: oslo.config (>=5.1.0)
|
|
@@ -7,12 +7,10 @@ os_vif/utils.py,sha256=bmJRam0laHAVLIHpQufzLASu_VTEqsGTuJYmoYfqOco,713
|
|
|
7
7
|
os_vif/version.py,sha256=ilQe2xTxPDxlzJYWbq8KdHeashflTfC1OZewgFPKPRI,713
|
|
8
8
|
os_vif/internal/__init__.py,sha256=VGE1e9o-7RkuTzNhZl4Wivz1tojriLYypVBObhSZfSk,967
|
|
9
9
|
os_vif/internal/ip/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
os_vif/internal/ip/api.py,sha256=
|
|
10
|
+
os_vif/internal/ip/api.py,sha256=BlGJdRe_Yn7P23Dejo7gLK-M6UasY8sKrMCz0FPvx2s,722
|
|
11
11
|
os_vif/internal/ip/ip_command.py,sha256=agySNBD4GHp87uo6HbtIg4JwtOMhfuRZN5X7ytPir5k,2787
|
|
12
12
|
os_vif/internal/ip/linux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
os_vif/internal/ip/linux/impl_pyroute2.py,sha256=ddQ9d7fzBJEYmE1wJhMfpmHftTBFdJ2C94dCNGRsB0A,5511
|
|
14
|
-
os_vif/internal/ip/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
os_vif/internal/ip/windows/impl_netifaces.py,sha256=hPgC7KaymxdD4XrYw5G5IkXiKjth-tc9Gc1_pzcsyuo,1745
|
|
16
14
|
os_vif/objects/__init__.py,sha256=ePMVrOlb8ti6YmpdZ15pOXI1XLeFjHvhBvw35abZRZE,883
|
|
17
15
|
os_vif/objects/base.py,sha256=kI0c1gPDN37RhGuOYg18AA7l98uUfpqs14AS9GfE4oE,1139
|
|
18
16
|
os_vif/objects/fields.py,sha256=i_kVqeCOBfcNKuZiB3gBat-8qJWhLtwEQxsl-k5plgw,1761
|
|
@@ -41,11 +39,9 @@ os_vif/tests/unit/test_os_vif.py,sha256=ixMy3_RolD3K4ebWynRt7JLI63m_fb3XIO7qrXGC
|
|
|
41
39
|
os_vif/tests/unit/test_vif.py,sha256=mhg2PZ3iVEPzffO5I0VT1wIUP3c-h7MAqrZgCuZdjxE,18000
|
|
42
40
|
os_vif/tests/unit/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
41
|
os_vif/tests/unit/internal/ip/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
|
-
os_vif/tests/unit/internal/ip/test_api.py,sha256=
|
|
42
|
+
os_vif/tests/unit/internal/ip/test_api.py,sha256=LnkAM0M8giNjAw_Q4-oRic3WIW3OWq-SCGlde9SCenY,820
|
|
45
43
|
os_vif/tests/unit/internal/ip/linux/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
44
|
os_vif/tests/unit/internal/ip/linux/test_impl_pyroute2.py,sha256=MbsDkFkVSmx3mqteP7GW3ruy6ehvBCiQorkdWJjE5Qk,7364
|
|
47
|
-
os_vif/tests/unit/internal/ip/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
-
os_vif/tests/unit/internal/ip/windows/test_impl_netifaces.py,sha256=fs1DI706OXTWGr4C8_UtbBEvxwImv3klrxmuktUYol8,1992
|
|
49
45
|
vif_plug_linux_bridge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
46
|
vif_plug_linux_bridge/constants.py,sha256=NC8n4uOMFCYrhq0sM1ZeGBKo-Gs4OMR60cYP47wMRCk,602
|
|
51
47
|
vif_plug_linux_bridge/iptables.py,sha256=q4LmAMMzg-r9IgDNTu3jCLjNDM8b0JbGo-qmiA2B-JE,20641
|
|
@@ -62,32 +58,32 @@ vif_plug_noop/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
62
58
|
vif_plug_noop/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
63
59
|
vif_plug_noop/tests/unit/test_plugin.py,sha256=x8m9zuGtHsPoZRZP6Q6BREAFx0wiEagFtTFlB_i_Bxc,1236
|
|
64
60
|
vif_plug_ovs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
|
-
vif_plug_ovs/constants.py,sha256=
|
|
61
|
+
vif_plug_ovs/constants.py,sha256=vQKnYSeBMjV2RGQPGxk96D8FtgpvJIZEX_yZzUlsGg8,892
|
|
66
62
|
vif_plug_ovs/exception.py,sha256=ZiECZVvjCFTVXhuii3uhJn4lZvozKRxziedZB3R9JNo,1328
|
|
67
|
-
vif_plug_ovs/linux_net.py,sha256=
|
|
68
|
-
vif_plug_ovs/ovs.py,sha256=
|
|
63
|
+
vif_plug_ovs/linux_net.py,sha256=HvhfSXjeAlU2l9iZNiN8CFr-GL9psS8Hk8_KAaktH6I,13922
|
|
64
|
+
vif_plug_ovs/ovs.py,sha256=VfZtPMnBXIBZ7dDPfS5d9RGZFb7mkwPPptjFX_iO2YM,22767
|
|
69
65
|
vif_plug_ovs/privsep.py,sha256=nPQUkYgjbSjaHMuu40fZArRnf8RBiR73l4YpDF1K1yQ,1100
|
|
70
66
|
vif_plug_ovs/ovsdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
67
|
vif_plug_ovs/ovsdb/api.py,sha256=_jHR8xokDAPI8sw3IEWuT3hObJuskqVzeVMDaWRbIq0,1293
|
|
72
68
|
vif_plug_ovs/ovsdb/impl_idl.py,sha256=DR1aHN8riBpZyNePwpvT9PRYbf9vLk4lRJ9VHRVq_to,3028
|
|
73
69
|
vif_plug_ovs/ovsdb/impl_vsctl.py,sha256=MSf8JMrMqrwt5vH8KTsThjdnsttJEYJikIcJ12CTz8c,14437
|
|
74
|
-
vif_plug_ovs/ovsdb/ovsdb_lib.py,sha256=
|
|
70
|
+
vif_plug_ovs/ovsdb/ovsdb_lib.py,sha256=ePYap6ZzxgJQRNmViFBKXy8WH0qWGCbd8FTaSSB_BYA,10227
|
|
75
71
|
vif_plug_ovs/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
76
72
|
vif_plug_ovs/tests/functional/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
|
-
vif_plug_ovs/tests/functional/base.py,sha256=
|
|
73
|
+
vif_plug_ovs/tests/functional/base.py,sha256=GZKyA9UR9BaEgZ0tv6xyMTQnz9FpKedV2do1ePMB-rw,2059
|
|
78
74
|
vif_plug_ovs/tests/functional/test_plugin.py,sha256=40gbf5bvxgauHf3U3oT9xeIOcZ3r8-fFRyHOrhWbZVo,6648
|
|
79
75
|
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=
|
|
76
|
+
vif_plug_ovs/tests/functional/ovsdb/test_ovsdb_lib.py,sha256=tXSTmjYWOmqLXYQ42_3Sv8XNNktlDxPDalJRr8KRwCU,13775
|
|
81
77
|
vif_plug_ovs/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
82
78
|
vif_plug_ovs/tests/unit/test_linux_net.py,sha256=B3buNpn9EKZQPio5rNrIma_E5MUL9vBspdg5EqZ3RHk,17532
|
|
83
|
-
vif_plug_ovs/tests/unit/test_plugin.py,sha256
|
|
79
|
+
vif_plug_ovs/tests/unit/test_plugin.py,sha256=93IhauJ-LscWMOJ0yCHBXAM47zVi-0oOOrMaln_dflU,30731
|
|
84
80
|
vif_plug_ovs/tests/unit/ovsdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
|
-
vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py,sha256
|
|
86
|
-
os_vif-
|
|
87
|
-
os_vif-
|
|
88
|
-
os_vif-
|
|
89
|
-
os_vif-
|
|
90
|
-
os_vif-
|
|
91
|
-
os_vif-
|
|
92
|
-
os_vif-
|
|
93
|
-
os_vif-
|
|
81
|
+
vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py,sha256=-Z50UgEgg2awEgPb_r_iqQBsyYTKlSadi3S9laGFfjg,10270
|
|
82
|
+
os_vif-4.0.0.dist-info/AUTHORS,sha256=60BktTkASWCf8_6tpYaV0vj_2qxp9yPMKsPhZv0PM6c,3250
|
|
83
|
+
os_vif-4.0.0.dist-info/LICENSE,sha256=XfKg2H1sVi8OoRxoisUlMqoo10TKvHmU_wU39ks7MyA,10143
|
|
84
|
+
os_vif-4.0.0.dist-info/METADATA,sha256=qKa6NbHg0SplQTLycmCwnOeQjrtoP43XHnxx7EjaIiI,2357
|
|
85
|
+
os_vif-4.0.0.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
|
|
86
|
+
os_vif-4.0.0.dist-info/entry_points.txt,sha256=UhIaj_WPv49yG316SjjDuMlSqGvym-_TVYEn5JKlEaQ,207
|
|
87
|
+
os_vif-4.0.0.dist-info/pbr.json,sha256=5Hmya6COSIJXwp-Fb4_ZtNvHUG8ylqw2qkLwyvfPuWU,46
|
|
88
|
+
os_vif-4.0.0.dist-info/top_level.txt,sha256=ULBxtkTk3bkfzCSYJjifWehfjJdMODVzC6SX5l_CNKo,56
|
|
89
|
+
os_vif-4.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"git_version": "7152159", "is_release": true}
|
vif_plug_ovs/constants.py
CHANGED
vif_plug_ovs/linux_net.py
CHANGED
|
@@ -22,14 +22,12 @@
|
|
|
22
22
|
import glob
|
|
23
23
|
import os
|
|
24
24
|
import re
|
|
25
|
-
import sys
|
|
26
25
|
|
|
27
26
|
from os_vif.internal.ip.api import ip as ip_lib
|
|
28
27
|
from oslo_concurrency import processutils
|
|
29
28
|
from oslo_log import log as logging
|
|
30
29
|
from oslo_utils import excutils
|
|
31
30
|
|
|
32
|
-
from vif_plug_ovs import constants
|
|
33
31
|
from vif_plug_ovs import exception
|
|
34
32
|
from vif_plug_ovs import privsep
|
|
35
33
|
|
|
@@ -55,12 +53,7 @@ NIC_NAME_LEN = 14
|
|
|
55
53
|
def _update_device_mtu(dev, mtu):
|
|
56
54
|
if not mtu:
|
|
57
55
|
return
|
|
58
|
-
|
|
59
|
-
# Hyper-V with OVS does not support external programming of
|
|
60
|
-
# virtual interface MTUs via netsh or other Windows tools.
|
|
61
|
-
# When plugging an interface on Windows, we therefore skip
|
|
62
|
-
# programming the MTU and fallback to DHCP advertisement.
|
|
63
|
-
set_device_mtu(dev, mtu)
|
|
56
|
+
set_device_mtu(dev, mtu)
|
|
64
57
|
|
|
65
58
|
|
|
66
59
|
@privsep.vif_plug.entrypoint
|
vif_plug_ovs/ovs.py
CHANGED
|
@@ -17,8 +17,6 @@
|
|
|
17
17
|
# License for the specific language governing permissions and limitations
|
|
18
18
|
# under the License.
|
|
19
19
|
|
|
20
|
-
import sys
|
|
21
|
-
|
|
22
20
|
from oslo_config import cfg
|
|
23
21
|
from oslo_log import log as logging
|
|
24
22
|
|
|
@@ -27,7 +25,6 @@ from os_vif.internal.ip.api import ip as ip_lib
|
|
|
27
25
|
from os_vif import objects
|
|
28
26
|
from os_vif import plugin
|
|
29
27
|
|
|
30
|
-
|
|
31
28
|
from vif_plug_ovs import constants
|
|
32
29
|
from vif_plug_ovs import exception
|
|
33
30
|
from vif_plug_ovs import linux_net
|
|
@@ -181,9 +178,6 @@ class OvsPlugin(plugin.PluginBase):
|
|
|
181
178
|
def supports_tc_qdisc(self, vif) -> bool:
|
|
182
179
|
if self._get_vif_datapath_type(vif) != constants.OVS_DATAPATH_SYSTEM:
|
|
183
180
|
return False
|
|
184
|
-
if sys.platform == constants.PLATFORM_WIN32:
|
|
185
|
-
return False
|
|
186
|
-
|
|
187
181
|
return True
|
|
188
182
|
|
|
189
183
|
def _isolate_vif(self, vif_name, bridge):
|
|
@@ -283,14 +277,6 @@ class OvsPlugin(plugin.PluginBase):
|
|
|
283
277
|
linux_net.update_veth_pair(v1_name, v2_name, mtu)
|
|
284
278
|
self._update_vif_port(vif, v2_name)
|
|
285
279
|
|
|
286
|
-
def _plug_vif_windows(self, vif, instance_info):
|
|
287
|
-
"""Create a per-VIF OVS port."""
|
|
288
|
-
|
|
289
|
-
if not ip_lib.exists(vif.id):
|
|
290
|
-
self.ovsdb.ensure_ovs_bridge(vif.network.bridge,
|
|
291
|
-
self._get_vif_datapath_type(vif))
|
|
292
|
-
self._create_vif_port(vif, vif.id, instance_info)
|
|
293
|
-
|
|
294
280
|
def _plug_port_bridge(self, vif, instance_info):
|
|
295
281
|
"""Create a per-VIF OVS bridge and patch pair."""
|
|
296
282
|
|
|
@@ -381,15 +367,7 @@ class OvsPlugin(plugin.PluginBase):
|
|
|
381
367
|
raise exception.WrongPortProfile(
|
|
382
368
|
profile=vif.port_profile.__class__.__name__)
|
|
383
369
|
|
|
384
|
-
if
|
|
385
|
-
if type(vif) not in (
|
|
386
|
-
objects.vif.VIFOpenVSwitch, objects.vif.VIFBridge
|
|
387
|
-
):
|
|
388
|
-
raise osv_exception.PlugException(
|
|
389
|
-
vif=vif, err="This vif type is not supported on Windows")
|
|
390
|
-
|
|
391
|
-
self._plug_vif_windows(vif, instance_info)
|
|
392
|
-
elif isinstance(vif, objects.vif.VIFOpenVSwitch):
|
|
370
|
+
if isinstance(vif, objects.vif.VIFOpenVSwitch):
|
|
393
371
|
if self.config.per_port_bridge:
|
|
394
372
|
self._plug_port_bridge(vif, instance_info)
|
|
395
373
|
else:
|
|
@@ -443,12 +421,6 @@ class OvsPlugin(plugin.PluginBase):
|
|
|
443
421
|
qos_type = self.config.default_qos_type
|
|
444
422
|
return qos_type
|
|
445
423
|
|
|
446
|
-
def _unplug_vif_windows(self, vif, instance_info):
|
|
447
|
-
"""Remove port from OVS."""
|
|
448
|
-
self.ovsdb.delete_ovs_vif_port(vif.network.bridge, vif.id,
|
|
449
|
-
delete_netdev=False)
|
|
450
|
-
self._delete_bridge_if_trunk(vif)
|
|
451
|
-
|
|
452
424
|
def _unplug_port_bridge(self, vif, instance_info):
|
|
453
425
|
"""Create a per-VIF OVS bridge and patch pair."""
|
|
454
426
|
# NOTE(sean-k-mooney): the port name prefix should not be
|
|
@@ -508,14 +480,7 @@ class OvsPlugin(plugin.PluginBase):
|
|
|
508
480
|
objects.vif.VIFPortProfileOpenVSwitch):
|
|
509
481
|
raise exception.WrongPortProfile(
|
|
510
482
|
profile=vif.port_profile.__class__.__name__)
|
|
511
|
-
if
|
|
512
|
-
if type(vif) not in (
|
|
513
|
-
objects.vif.VIFOpenVSwitch, objects.vif.VIFBridge
|
|
514
|
-
):
|
|
515
|
-
raise osv_exception.UnplugException(
|
|
516
|
-
vif=vif, err="This vif type is not supported on windows.")
|
|
517
|
-
self._unplug_vif_windows(vif, instance_info)
|
|
518
|
-
elif isinstance(vif, objects.vif.VIFOpenVSwitch):
|
|
483
|
+
if isinstance(vif, objects.vif.VIFOpenVSwitch):
|
|
519
484
|
if self.config.per_port_bridge:
|
|
520
485
|
self._unplug_port_bridge(vif, instance_info)
|
|
521
486
|
else:
|
vif_plug_ovs/ovsdb/ovsdb_lib.py
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
# License for the specific language governing permissions and limitations
|
|
11
11
|
# under the License.
|
|
12
12
|
|
|
13
|
-
import sys
|
|
14
13
|
import uuid
|
|
15
14
|
|
|
16
15
|
from oslo_log import log as logging
|
|
@@ -59,12 +58,7 @@ class BaseOVS(object):
|
|
|
59
58
|
if interface_type not in [
|
|
60
59
|
constants.OVS_VHOSTUSER_INTERFACE_TYPE,
|
|
61
60
|
constants.OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE]:
|
|
62
|
-
|
|
63
|
-
# Hyper-V with OVS does not support external programming of
|
|
64
|
-
# virtual interface MTUs via netsh or other Windows tools.
|
|
65
|
-
# When plugging an interface on Windows, we therefore skip
|
|
66
|
-
# programming the MTU and fallback to DHCP advertisement.
|
|
67
|
-
linux_net.set_device_mtu(dev, mtu)
|
|
61
|
+
linux_net.set_device_mtu(dev, mtu)
|
|
68
62
|
elif self._ovs_supports_mtu_requests():
|
|
69
63
|
self._set_mtu_request(txn, dev, mtu)
|
|
70
64
|
else:
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
10
10
|
# License for the specific language governing permissions and limitations
|
|
11
11
|
# under the License.
|
|
12
|
+
import functools
|
|
13
|
+
import os
|
|
12
14
|
|
|
13
15
|
from os_vif.tests.functional import base as os_vif_base
|
|
14
16
|
|
|
@@ -28,6 +30,10 @@ class VifPlugOvsBaseFunctionalTestCase(os_vif_base.BaseFunctionalTestCase):
|
|
|
28
30
|
def _check_port(self, name, bridge):
|
|
29
31
|
return self.ovs.port_exists(name, bridge)
|
|
30
32
|
|
|
33
|
+
@functools.cache
|
|
34
|
+
def _get_timeout(self):
|
|
35
|
+
return int(os.environ.get('OS_VIF_CHECK_PARAMETER_TIMEOUT', '10'))
|
|
36
|
+
|
|
31
37
|
def _check_parameter(self, table, port, parameter, expected_value):
|
|
32
38
|
def get_value():
|
|
33
39
|
return self._ovsdb.db_get(table, port, parameter).execute()
|
|
@@ -36,7 +42,8 @@ class VifPlugOvsBaseFunctionalTestCase(os_vif_base.BaseFunctionalTestCase):
|
|
|
36
42
|
val = get_value()
|
|
37
43
|
return val == expected_value
|
|
38
44
|
self.assertTrue(
|
|
39
|
-
wait_until_true(
|
|
45
|
+
wait_until_true(
|
|
46
|
+
check_value, timeout=self._get_timeout(), sleep=0.5),
|
|
40
47
|
f"Parameter {parameter} of {table} {port} is {get_value()} "
|
|
41
48
|
f"not {expected_value}"
|
|
42
49
|
)
|
|
@@ -183,6 +183,11 @@ class TestOVSDBLib(testscenarios.WithScenarios,
|
|
|
183
183
|
'Interface', port_bridge_port, 'options', port_opts)
|
|
184
184
|
|
|
185
185
|
def test_create_ovs_vif_port_with_default_qos(self):
|
|
186
|
+
if self.interface == 'native':
|
|
187
|
+
self.skipTest(
|
|
188
|
+
'test_create_ovs_vif_port_with_default_qos is unstable '
|
|
189
|
+
'when run with the native driver, see: '
|
|
190
|
+
'https://bugs.launchpad.net/os-vif/+bug/2087982')
|
|
186
191
|
port_name = 'qos-port-' + self.interface
|
|
187
192
|
iface_id = 'iface_id'
|
|
188
193
|
mac = 'ca:fe:ca:fe:ca:fe'
|
|
@@ -53,24 +53,14 @@ class BaseOVSTest(testtools.TestCase):
|
|
|
53
53
|
calls = [mock.call('Interface', 'device', ('mtu_request', 1500))]
|
|
54
54
|
self.mock_db_set.assert_has_calls(calls)
|
|
55
55
|
|
|
56
|
-
@mock.patch('sys.platform', constants.PLATFORM_LINUX)
|
|
57
56
|
@mock.patch.object(linux_net, 'set_device_mtu')
|
|
58
|
-
def
|
|
57
|
+
def test__update_device_mtu_interface_not_vhostuser(self,
|
|
59
58
|
mock_set_device_mtu):
|
|
60
59
|
self.br.update_device_mtu(
|
|
61
60
|
self.mock_transaction, 'device', 1500, 'not_vhost'
|
|
62
61
|
)
|
|
63
62
|
mock_set_device_mtu.assert_has_calls([mock.call('device', 1500)])
|
|
64
63
|
|
|
65
|
-
@mock.patch('sys.platform', constants.PLATFORM_WIN32)
|
|
66
|
-
@mock.patch.object(linux_net, 'set_device_mtu')
|
|
67
|
-
def test__update_device_mtu_interface_not_vhostuser_windows(self,
|
|
68
|
-
mock_set_device_mtu):
|
|
69
|
-
self.br.update_device_mtu(
|
|
70
|
-
self.mock_transaction, 'device', 1500, 'not_vhost'
|
|
71
|
-
)
|
|
72
|
-
mock_set_device_mtu.assert_not_called()
|
|
73
|
-
|
|
74
64
|
def test__update_device_mtu_interface_vhostuser_supports_mtu_req(self):
|
|
75
65
|
with mock.patch.object(self.br, '_ovs_supports_mtu_requests',
|
|
76
66
|
return_value=True), \
|
|
@@ -238,20 +238,16 @@ class PluginTest(testtools.TestCase):
|
|
|
238
238
|
mtu=plugin.config.network_device_mtu,
|
|
239
239
|
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
|
|
240
240
|
|
|
241
|
-
@mock.patch.object(ovs, 'sys')
|
|
242
241
|
@mock.patch.object(ovs.OvsPlugin, '_plug_vif_generic')
|
|
243
|
-
def test_plug_ovs_port_bridge_false(self, plug_vif_generic
|
|
244
|
-
mock_sys.platform = 'linux'
|
|
242
|
+
def test_plug_ovs_port_bridge_false(self, plug_vif_generic):
|
|
245
243
|
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
|
246
244
|
with mock.patch.object(plugin.config, 'per_port_bridge', False):
|
|
247
245
|
plugin.plug(self.vif_ovs, self.instance)
|
|
248
246
|
plug_vif_generic.assert_called_once_with(
|
|
249
247
|
self.vif_ovs, self.instance)
|
|
250
248
|
|
|
251
|
-
@mock.patch.object(ovs, 'sys')
|
|
252
249
|
@mock.patch.object(ovs.OvsPlugin, '_plug_port_bridge')
|
|
253
|
-
def test_plug_ovs_port_bridge_true(self, plug_vif
|
|
254
|
-
mock_sys.platform = 'linux'
|
|
250
|
+
def test_plug_ovs_port_bridge_true(self, plug_vif):
|
|
255
251
|
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
|
256
252
|
with mock.patch.object(plugin.config, 'per_port_bridge', True):
|
|
257
253
|
plugin.plug(self.vif_ovs, self.instance)
|
|
@@ -276,8 +272,7 @@ class PluginTest(testtools.TestCase):
|
|
|
276
272
|
@mock.patch.object(linux_net, 'create_veth_pair')
|
|
277
273
|
@mock.patch.object(ip_lib, 'exists')
|
|
278
274
|
@mock.patch.object(linux_net, 'ensure_bridge')
|
|
279
|
-
|
|
280
|
-
def test_plug_ovs_bridge(self, mock_sys, ensure_bridge, device_exists,
|
|
275
|
+
def test_plug_ovs_bridge(self, ensure_bridge, device_exists,
|
|
281
276
|
create_veth_pair, update_veth_pair,
|
|
282
277
|
add_bridge_port, _create_vif_port,
|
|
283
278
|
_update_vif_port, ensure_ovs_bridge,
|
|
@@ -307,7 +302,6 @@ class PluginTest(testtools.TestCase):
|
|
|
307
302
|
# plugging new devices should result in devices being created
|
|
308
303
|
|
|
309
304
|
device_exists.return_value = False
|
|
310
|
-
mock_sys.platform = 'linux'
|
|
311
305
|
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
|
312
306
|
plugin.plug(self.vif_ovs_hybrid, self.instance)
|
|
313
307
|
ensure_bridge.assert_has_calls(calls['ensure_bridge'])
|
|
@@ -333,38 +327,10 @@ class PluginTest(testtools.TestCase):
|
|
|
333
327
|
update_veth_pair.assert_has_calls(calls['update_veth_pair'])
|
|
334
328
|
_update_vif_port.assert_has_calls(calls['_update_vif_port'])
|
|
335
329
|
|
|
336
|
-
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
|
|
337
|
-
@mock.patch.object(ovs.OvsPlugin, '_create_vif_port')
|
|
338
|
-
@mock.patch.object(ip_lib, 'exists', return_value=False)
|
|
339
|
-
@mock.patch.object(ovs, 'sys')
|
|
340
|
-
def _check_plug_ovs_windows(self, vif, mock_sys, mock_exists,
|
|
341
|
-
_create_vif_port, ensure_ovs_bridge):
|
|
342
|
-
dp_type = ovs.OvsPlugin._get_vif_datapath_type(vif)
|
|
343
|
-
calls = {
|
|
344
|
-
'exists': [mock.call(vif.id)],
|
|
345
|
-
'_create_vif_port': [mock.call(vif, vif.id, self.instance)],
|
|
346
|
-
'ensure_ovs_bridge': [mock.call('br0', dp_type)]
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
mock_sys.platform = constants.PLATFORM_WIN32
|
|
350
|
-
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
|
351
|
-
plugin.plug(vif, self.instance)
|
|
352
|
-
mock_exists.assert_has_calls(calls['exists'])
|
|
353
|
-
_create_vif_port.assert_has_calls(calls['_create_vif_port'])
|
|
354
|
-
ensure_ovs_bridge.assert_has_calls(calls['ensure_ovs_bridge'])
|
|
355
|
-
|
|
356
|
-
def test_plug_ovs_windows(self):
|
|
357
|
-
self._check_plug_ovs_windows(self.vif_ovs)
|
|
358
|
-
|
|
359
|
-
def test_plug_ovs_bridge_windows(self):
|
|
360
|
-
self._check_plug_ovs_windows(self.vif_ovs_hybrid)
|
|
361
|
-
|
|
362
330
|
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_bridge')
|
|
363
|
-
@mock.patch.object(ovs, 'sys')
|
|
364
331
|
@mock.patch.object(ovs.OvsPlugin, '_unplug_vif_generic')
|
|
365
|
-
def test_unplug_ovs_port_bridge_false(self, unplug,
|
|
332
|
+
def test_unplug_ovs_port_bridge_false(self, unplug,
|
|
366
333
|
delete_ovs_bridge):
|
|
367
|
-
mock_sys.platform = 'linux'
|
|
368
334
|
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
|
369
335
|
with mock.patch.object(plugin.config, 'per_port_bridge', False):
|
|
370
336
|
plugin.unplug(self.vif_ovs, self.instance)
|
|
@@ -372,11 +338,9 @@ class PluginTest(testtools.TestCase):
|
|
|
372
338
|
delete_ovs_bridge.assert_not_called()
|
|
373
339
|
|
|
374
340
|
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_bridge')
|
|
375
|
-
@mock.patch.object(ovs, 'sys')
|
|
376
341
|
@mock.patch.object(ovs.OvsPlugin, '_unplug_port_bridge')
|
|
377
|
-
def test_unplug_ovs_port_bridge_true(self, unplug,
|
|
342
|
+
def test_unplug_ovs_port_bridge_true(self, unplug,
|
|
378
343
|
delete_ovs_bridge):
|
|
379
|
-
mock_sys.platform = 'linux'
|
|
380
344
|
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
|
381
345
|
with mock.patch.object(plugin.config, 'per_port_bridge', True):
|
|
382
346
|
plugin.unplug(self.vif_ovs, self.instance)
|
|
@@ -394,8 +358,7 @@ class PluginTest(testtools.TestCase):
|
|
|
394
358
|
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_bridge')
|
|
395
359
|
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_vif_port')
|
|
396
360
|
@mock.patch.object(linux_net, 'delete_bridge')
|
|
397
|
-
|
|
398
|
-
def test_unplug_ovs_bridge(self, mock_sys, delete_bridge,
|
|
361
|
+
def test_unplug_ovs_bridge(self, delete_bridge,
|
|
399
362
|
delete_ovs_vif_port, delete_ovs_bridge):
|
|
400
363
|
calls = {
|
|
401
364
|
'delete_bridge': [mock.call('qbrvif-xxx-yyy', 'qvbb679325f-ca')],
|
|
@@ -403,32 +366,12 @@ class PluginTest(testtools.TestCase):
|
|
|
403
366
|
'br0', 'qvob679325f-ca', qos_type='linux-noop'
|
|
404
367
|
)]
|
|
405
368
|
}
|
|
406
|
-
mock_sys.platform = 'linux'
|
|
407
369
|
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
|
408
370
|
plugin.unplug(self.vif_ovs_hybrid, self.instance)
|
|
409
371
|
delete_bridge.assert_has_calls(calls['delete_bridge'])
|
|
410
372
|
delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port'])
|
|
411
373
|
delete_ovs_bridge.assert_not_called()
|
|
412
374
|
|
|
413
|
-
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_vif_port')
|
|
414
|
-
@mock.patch.object(ovs, 'sys')
|
|
415
|
-
def _check_unplug_ovs_windows(self, vif, mock_sys, delete_ovs_vif_port):
|
|
416
|
-
mock_sys.platform = constants.PLATFORM_WIN32
|
|
417
|
-
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
|
|
418
|
-
plugin.unplug(vif, self.instance)
|
|
419
|
-
delete_ovs_vif_port.assert_called_once_with('br0', vif.id,
|
|
420
|
-
delete_netdev=False)
|
|
421
|
-
|
|
422
|
-
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_bridge')
|
|
423
|
-
def test_unplug_ovs_windows(self, delete_ovs_bridge):
|
|
424
|
-
self._check_unplug_ovs_windows(self.vif_ovs)
|
|
425
|
-
delete_ovs_bridge.assert_not_called()
|
|
426
|
-
|
|
427
|
-
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_bridge')
|
|
428
|
-
def test_unplug_ovs_bridge_windows(self, delete_ovs_bridge):
|
|
429
|
-
self._check_unplug_ovs_windows(self.vif_ovs_hybrid)
|
|
430
|
-
delete_ovs_bridge.assert_not_called()
|
|
431
|
-
|
|
432
375
|
@mock.patch.object(ovs.OvsPlugin, '_create_vif_port')
|
|
433
376
|
def test_plug_ovs_vhostuser(self, _create_vif_port):
|
|
434
377
|
dp_type = ovs.OvsPlugin._get_vif_datapath_type(self.vif_vhostuser)
|
|
File without changes
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# Derived from: neutron/agent/windows/ip_lib.py
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
4
|
-
# not use this file except in compliance with the License. You may obtain
|
|
5
|
-
# a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
11
|
-
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
12
|
-
# License for the specific language governing permissions and limitations
|
|
13
|
-
# under the License.
|
|
14
|
-
|
|
15
|
-
import netifaces
|
|
16
|
-
|
|
17
|
-
from oslo_log import log as logging
|
|
18
|
-
|
|
19
|
-
from os_vif import exception
|
|
20
|
-
from os_vif.internal.ip import ip_command
|
|
21
|
-
|
|
22
|
-
LOG = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class Netifaces(ip_command.IpCommand):
|
|
26
|
-
|
|
27
|
-
def exists(self, device):
|
|
28
|
-
"""Return True if the device exists in the namespace."""
|
|
29
|
-
try:
|
|
30
|
-
return bool(netifaces.ifaddresses(device))
|
|
31
|
-
except ValueError:
|
|
32
|
-
LOG.warning("The device does not exist on the system: %s", device)
|
|
33
|
-
return False
|
|
34
|
-
except OSError:
|
|
35
|
-
LOG.error("Failed to get interface addresses: %s", device)
|
|
36
|
-
return False
|
|
37
|
-
|
|
38
|
-
def set(self, device, check_exit_code=None, state=None, mtu=None,
|
|
39
|
-
address=None, promisc=None, master=None):
|
|
40
|
-
exception.NotImplementedForOS(function='ip.set', os='Windows')
|
|
41
|
-
|
|
42
|
-
def add(self, device, dev_type, check_exit_code=None, peer=None, link=None,
|
|
43
|
-
vlan_id=None):
|
|
44
|
-
exception.NotImplementedForOS(function='ip.add', os='Windows')
|
|
45
|
-
|
|
46
|
-
def delete(self, device, check_exit_code=None):
|
|
47
|
-
exception.NotImplementedForOS(function='ip.delete', os='Windows')
|
|
File without changes
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
2
|
-
# not use this file except in compliance with the License. You may obtain
|
|
3
|
-
# a copy of the License at
|
|
4
|
-
#
|
|
5
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
-
#
|
|
7
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
8
|
-
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
9
|
-
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
10
|
-
# License for the specific language governing permissions and limitations
|
|
11
|
-
# under the License.
|
|
12
|
-
|
|
13
|
-
from unittest import mock
|
|
14
|
-
|
|
15
|
-
import netifaces
|
|
16
|
-
|
|
17
|
-
from os_vif.internal.ip.windows import impl_netifaces as ip_lib
|
|
18
|
-
from os_vif.tests.unit import base
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class TestIPDevice(base.TestCase):
|
|
22
|
-
|
|
23
|
-
def setUp(self):
|
|
24
|
-
super(TestIPDevice, self).setUp()
|
|
25
|
-
self.device_name = 'test_device'
|
|
26
|
-
self.mock_log = mock.patch.object(ip_lib, "LOG").start()
|
|
27
|
-
self.ip_lib = ip_lib.Netifaces()
|
|
28
|
-
|
|
29
|
-
@mock.patch.object(netifaces, 'ifaddresses', return_value=True)
|
|
30
|
-
def test_exists(self, mock_ifaddresses):
|
|
31
|
-
self.assertTrue(self.ip_lib.exists(self.device_name))
|
|
32
|
-
mock_ifaddresses.assert_called_once_with(self.device_name)
|
|
33
|
-
|
|
34
|
-
@mock.patch.object(netifaces, 'ifaddresses', side_effect=ValueError())
|
|
35
|
-
def test_exists_not_found(self, mock_ifaddresses):
|
|
36
|
-
self.assertFalse(self.ip_lib.exists(self.device_name))
|
|
37
|
-
mock_ifaddresses.assert_called_once_with(self.device_name)
|
|
38
|
-
self.mock_log.warning.assert_called_once_with(
|
|
39
|
-
"The device does not exist on the system: %s", self.device_name)
|
|
40
|
-
|
|
41
|
-
@mock.patch.object(netifaces, 'ifaddresses', side_effect=OSError())
|
|
42
|
-
def test_exists_os_error_exception(self, mock_ifaddresses):
|
|
43
|
-
self.assertFalse(self.ip_lib.exists(self.device_name))
|
|
44
|
-
mock_ifaddresses.assert_called_once_with(self.device_name)
|
|
45
|
-
self.mock_log.error.assert_called_once_with(
|
|
46
|
-
"Failed to get interface addresses: %s", self.device_name)
|
os_vif-3.7.0.dist-info/pbr.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"git_version": "bb3e3da", "is_release": true}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|