diff -Nru python-os-vif-1.15.1/AUTHORS python-os-vif-1.17.0/AUTHORS --- python-os-vif-1.15.1/AUTHORS 2019-02-28 14:31:03.000000000 +0000 +++ python-os-vif-1.17.0/AUTHORS 2019-09-06 17:04:15.000000000 +0000 @@ -6,6 +6,7 @@ ChangBo Guo(gcb) Charles Short Claudiu Belu +Corey Bryant Daniel P. Berrange Davanum Srinivas Doug Hellmann @@ -14,6 +15,7 @@ Francesco Santoro Hamdy Khader Hangdong Zhang +Ian Wienand Ihar Hrachyshka Jan Gutter Janonymous @@ -41,6 +43,7 @@ Stephen Finucane Swapnil Kulkarni (coolsvap) Takashi NATSUME +Thomas Bechtold Tony Breeds Tony Xu Vieri <15050873171@163.com> @@ -48,13 +51,17 @@ YAMAMOTO Takashi ZhijunWei blue55 +caoyuan gecong1973 +jacky06 kavithahr lingyongxu loooosy melanie witt melissaml +pengyuesheng pranabjb qingszhao +shaleijie sunjia vagrant diff -Nru python-os-vif-1.15.1/ChangeLog python-os-vif-1.17.0/ChangeLog --- python-os-vif-1.15.1/ChangeLog 2019-02-28 14:31:03.000000000 +0000 +++ python-os-vif-1.17.0/ChangeLog 2019-09-06 17:04:15.000000000 +0000 @@ -1,6 +1,33 @@ CHANGES ======= +1.17.0 +------ + +* Fix code bug in document +* only disable mac ageing for ovs hybrid plug +* Bump the openstackdocstheme extension to 1.20 +* Blacklist sphinx 2.1.0 (autodoc bug) +* Sync Sphinx requirement +* Add Python 3 Train unit tests +* set ignore\_basepython\_conflict = True in tox.ini +* OVS DPDK port representors support +* Fix mock of built in "open" function in unit tests + +1.16.0 +------ + +* Remove unused vif\_plug\_ovs.i18n module +* Fix Kuryr-Kubernetes job name +* Replace git.openstack.org URLs with opendev.org URLs +* Prevent "qbr" Linux Bridge from replying to ARP messages +* Remove IP proxy methods +* OpenDev Migration Patch +* Refactor functional base test classes +* Drop testtools from test-requirements.txt +* Replace openstack.org git:// URLs with https:// +* Update master for stable/stein + 1.15.1 ------ @@ -11,6 +38,8 @@ ------ * Add native implementation OVSDB API +* docs: Use sphinx.ext.autodoc for profile, datapath offload types +* docs: Use sphinx.ext.autodoc for VIF types * make functional tests run on python 3 * Fix nits in brctl removal (vif\_plug\_linux\_bridge) * docs: Add API docs for profile, datapath offload types diff -Nru python-os-vif-1.15.1/CONTRIBUTING.rst python-os-vif-1.17.0/CONTRIBUTING.rst --- python-os-vif-1.15.1/CONTRIBUTING.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/CONTRIBUTING.rst 2019-09-06 17:03:26.000000000 +0000 @@ -1,13 +1,13 @@ If you would like to contribute to the development of OpenStack, you must follow the steps in this page: - http://docs.openstack.org/infra/manual/developers.html + https://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: - http://docs.openstack.org/infra/manual/developers.html#development-workflow + https://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. diff -Nru python-os-vif-1.15.1/debian/changelog python-os-vif-1.17.0/debian/changelog --- python-os-vif-1.15.1/debian/changelog 2019-07-11 10:02:46.000000000 +0000 +++ python-os-vif-1.17.0/debian/changelog 2019-09-25 18:18:48.000000000 +0000 @@ -1,3 +1,10 @@ +python-os-vif (1.17.0-0ubuntu1) eoan; urgency=medium + + * New upstream release for OpenStack Train. + * d/control: Align (Build-)Depends with upstream. + + -- Corey Bryant Wed, 25 Sep 2019 14:18:48 -0400 + python-os-vif (1.15.1-0ubuntu2) eoan; urgency=medium * d/control: Drop BDI's on python-* packages. diff -Nru python-os-vif-1.15.1/debian/control python-os-vif-1.17.0/debian/control --- python-os-vif-1.15.1/debian/control 2019-07-11 10:02:46.000000000 +0000 +++ python-os-vif-1.17.0/debian/control 2019-09-25 18:18:48.000000000 +0000 @@ -22,7 +22,7 @@ python3-debtcollector (>= 1.19.0), python3-hacking (>= 1.1.0), python3-netaddr (>= 0.7.18), - python3-openstackdocstheme (>= 1.18.1), + python3-openstackdocstheme (>= 1.20.0), python3-openvswitch (>= 2.9.2), python3-oslo.concurrency (>= 3.20.0), python3-oslo.config (>= 1:5.1.0), diff -Nru python-os-vif-1.15.1/doc/requirements.txt python-os-vif-1.17.0/doc/requirements.txt --- python-os-vif-1.15.1/doc/requirements.txt 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/doc/requirements.txt 2019-09-06 17:03:26.000000000 +0000 @@ -1,3 +1,4 @@ -sphinx>=1.6.5,!=1.6.6,!=1.6.7 #BSD -openstackdocstheme>=1.18.1 # Apache-2.0 +sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD +sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2;python_version>='3.4' # BSD +openstackdocstheme>=1.20.0 # Apache-2.0 reno>=2.5.0 # Apache-2.0 diff -Nru python-os-vif-1.15.1/doc/source/conf.py python-os-vif-1.17.0/doc/source/conf.py --- python-os-vif-1.15.1/doc/source/conf.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/doc/source/conf.py 2019-09-06 17:03:26.000000000 +0000 @@ -16,6 +16,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ + 'sphinx.ext.todo', 'sphinx.ext.autodoc', 'openstackdocstheme', 'reno.sphinxext', @@ -25,7 +26,6 @@ repository_name = 'openstack/os-vif' bug_project = 'os-vif' bug_tag = '' -html_last_updated_fmt = '%Y-%m-%d %H:%M' # The suffix of source filenames. source_suffix = '.rst' @@ -34,7 +34,6 @@ master_doc = 'index' # General information about the project. -project = u'os-vif' copyright = u'2016, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. diff -Nru python-os-vif-1.15.1/doc/source/reference/glossary.rst python-os-vif-1.17.0/doc/source/reference/glossary.rst --- python-os-vif-1.15.1/doc/source/reference/glossary.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/doc/source/reference/glossary.rst 2019-09-06 17:03:26.000000000 +0000 @@ -14,6 +14,13 @@ __ http://docs.projectcalico.org + Fast Path + + When used, 6Wind's proprietary fast path technology behaves as a + transparent acceleration layer for traditional switches (:term:`Open + vSwitch`, :term:`Linux Bridge`) and for alternative networking mechanisms + (:term:`Calico`, Midonet). + Linux Bridge The native networking "backend" found in Linux. @@ -85,15 +92,21 @@ vhost - An alternative to :term:`virtio` that allows a userspace process to share - *virtqueues* directly with the kernel, preventing the QEMU process from - becoming a bottleneck. + An alternative to :term:`virtio` that allows userspace guest processes to + share *virtqueues* directly with the kernel (or, more specifically, a + kernel module) preventing the QEMU process from becoming a bottleneck. vhost-user A variation of :term:`vhost` that operates entirely in userspace. This - allows processes operating in userspace, such as virtual switches, to - avoid the kernel entirely and maximize performance. + allows userspace guest processes to share *virtqueues* with other processes + operating in userspace, such as virtual switches, avoiding the kernel + entirely and maximize performance. + + When used, a guest exposes a UNIX socket for its control plane, allowing + the external userspace service to provide the backend data plane via a + mapped memory region. This process must implement the corresponding virtio + vhost protocol, such as :term:`virtio-net` for networking, on this socket. Refer to the `QEMU documentation`__ for more information. diff -Nru python-os-vif-1.15.1/doc/source/user/plugins/linux-bridge.rst python-os-vif-1.17.0/doc/source/user/plugins/linux-bridge.rst --- python-os-vif-1.15.1/doc/source/user/plugins/linux-bridge.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/doc/source/user/plugins/linux-bridge.rst 2019-09-06 17:03:26.000000000 +0000 @@ -11,11 +11,9 @@ The Linux Bridge plugin provides support for the following VIF types: -``VIFBridge`` +:mod:`~os_vif.objects.VIFBridge` Configuration where a guest is connected to a Linux bridge via a TAP device. This is the only supported configuration for this plugin. - Refer to :ref:`vif-bridge` for more information. - For information on the VIF type objects, refer to :doc:`/user/vif-types`. Note that only the above VIF types are supported by this plugin. diff -Nru python-os-vif-1.15.1/doc/source/user/plugins/noop.rst python-os-vif-1.17.0/doc/source/user/plugins/noop.rst --- python-os-vif-1.15.1/doc/source/user/plugins/noop.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/doc/source/user/plugins/noop.rst 2019-09-06 17:03:26.000000000 +0000 @@ -12,11 +12,9 @@ The no-op plugin provides support for the following VIF types: -``VIFVHostUser`` +:mod:`~os_vif.objects.VIFVHostUser` Configuration where a guest exposes a UNIX socket for its control plane. This configuration is used with a userspace dataplane such as VPP or Snabb switch. - Refer to :ref:`vif-vhostuser` for more information. - For information on the VIF type objects, refer to :doc:`/user/vif-types`. Note that only the above VIF types are supported by this plugin. diff -Nru python-os-vif-1.15.1/doc/source/user/plugins/ovs.rst python-os-vif-1.17.0/doc/source/user/plugins/ovs.rst --- python-os-vif-1.15.1/doc/source/user/plugins/ovs.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/doc/source/user/plugins/ovs.rst 2019-09-06 17:03:26.000000000 +0000 @@ -11,28 +11,24 @@ The Open vSwitch plugin provides support for the following VIF types: -``VIFOpenVSwitch`` +:mod:`~os_vif.objects.VIFOpenVSwitch` Configuration where a guest is directly connected an Open vSwitch bridge. - Refer to :ref:`vif-openvswitch` for more information. - -``VIFBridge`` +:mod:`~os_vif.objects.VIFBridge` Configuration where a guest is connected to a Linux bridge via a TAP device, and that bridge is connected to the Open vSwitch bridge. This allows for the use of ``iptables`` rules for filtering traffic. - Refer to :ref:`vif-bridge` for more information. - -``VIFVHostUser`` +:mod:`~os_vif.objects.VIFVHostUser` Configuration where a guest exposes a UNIX socket for its control plane. This configuration is used with the `DPDK datapath of Open vSwitch`__. - Refer to :ref:`vif-vhostuser` for more information. + __ http://docs.openvswitch.org/en/latest/howto/dpdk/ -``VIFHostDevice`` +:mod:`~os_vif.objects.VIFHostDevice` Configuration where an :term:`SR-IOV` PCI device :term:`VF` is passed through to a guest. The ``hw-tc-offload`` feature should be enabled on the SR-IOV - :term:`PF` using ``ethtool``: + :term:`PF` using :command:`ethtool`: .. code-block:: shell @@ -48,11 +44,7 @@ 2.8. These add support for :term:`tc`-based hardware offloads for SR-IOV VFs and offloading of OVS datapath rules using tc, respectively. - Refer to :ref:`vif-hostdevice` for more information. - .. versionadded:: 1.5.0 For information on the VIF type objects, refer to :doc:`/user/vif-types`. Note that only the above VIF types are supported by this plugin. - -__ http://docs.openvswitch.org/en/latest/howto/dpdk/ diff -Nru python-os-vif-1.15.1/doc/source/user/usage.rst python-os-vif-1.17.0/doc/source/user/usage.rst --- python-os-vif-1.15.1/doc/source/user/usage.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/doc/source/user/usage.rst 2019-09-06 17:03:26.000000000 +0000 @@ -26,7 +26,7 @@ from os_vif.objects import fields from os_vif.objects import instance_info from os_vif.objects import network - from os_vif.objects import subnet + from os_vif.objects import subnet as os_subnet from os_vif.objects import vif as vif_obj instance_uuid = 'd7a730ca-3c28-49c3-8f26-4662b909fe8a' @@ -36,8 +36,8 @@ name=instance.name, project_id=instance.project_id) - subnet = subnet.Subnet(cidr='192.168.1.0/24') - subnets = subnet.SubnetList([subnet]) + subnet = os_subnet.Subnet(cidr='192.168.1.0/24') + subnets = os_subnet.SubnetList([subnet]) network = network.Network(label='tenantnet', subnets=subnets, multi_host=False, diff -Nru python-os-vif-1.15.1/doc/source/user/vif-types.rst python-os-vif-1.17.0/doc/source/user/vif-types.rst --- python-os-vif-1.15.1/doc/source/user/vif-types.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/doc/source/user/vif-types.rst 2019-09-06 17:03:26.000000000 +0000 @@ -2,90 +2,36 @@ VIF Types ========= -In os-vif, a VIF type refers to a particular approach for configuring the +In *os-vif*, a VIF type refers to a particular approach for configuring the backend of a guest virtual network interface. There is a small, finite set of ways that a VIF backend can be configured for any given hypervisor and a limited amount of metadata is associated with each approach. -VIF objects -=========== - -Each distinct type of VIF configuration is represented by a versioned object in -os-vif, subclassed from `os_vif.objects.VIFBase`. The `VIFBase` class defines -some fields that are common to all types of VIF, and provides an association to -a versioned object describing the network the VIF is plugged into. - -.. _vif-generic: - -VIFGeneric ----------- - -This class provides a totally generic type of configuration, where the guest is -simply associated with an arbitrary TAP device (or equivalent). The way the -TAP device is connected to the host network stack is explicitly left undefined -and entirely up to the plugin to decide. - -.. _vif-bridge: - -VIFBridge ---------- - -This class provides a configuration where the guest is connected directly to an -explicit host bridge device. This provides ethernet layer bridging, typically -to the LAN. - -.. _vif-openvswitch: - -VIFOpenVSwitch --------------- - -This class provides a configuration where the guest is connected to an Open -vSwitch port. -.. _vif-direct: +.. py:module:: os_vif.objects.vif -VIFDirect ---------- +VIF objects +=========== -This class provides a configuration where the guest is connected to a physical -network device. The connection to the device may operate in one of a number of -different modes, :term:`VEPA` (either :term:`802.1Qbg` or :term:`802.1Qbh`), -passthrough (exclusive assignment of the host NIC) or bridge (ethernet layer -bridging of traffic). The passthrough mode would be used when there is a -network device which needs to have a MAC address or VLAN configuration. For -passthrough of network devices without MAC/VLAN configuration, the -`VIFHostDevice` should be used instead. +Each distinct type of VIF configuration is represented by a versioned object, +subclassing :class:`VIFBase`. -.. _vif-vhostuser: +.. autoclass:: VIFBase -VIFVHostUser ------------- +.. autoclass:: VIFGeneric -This class provides another totally generic type of configuration, where the -guest is exposing a UNIX socket for its control plane, allowing an external -userspace service to provide the backend data plane via a mapped memory region. -The process must implement the :term:`virtio-net` vhost protocol on this socket -in whatever means is most suitable. +.. autoclass:: VIFBridge -.. _vif-hostdevice: +.. autoclass:: VIFOpenVSwitch -VIFHostDevice -------------- +.. autoclass:: VIFDirect -This class provides a way to pass a physical device to the guest. Either an -entire physical device, or an SR-IOV PCI device virtual function, are -permitted. +.. autoclass:: VIFVHostUser -.. _vif-nesteddpdk: +.. autoclass:: VIFDirect -VIFNestedDPDK -------------- +.. autoclass:: VIFNestedDPDK -This class provides a configuration, where kuryr-kuberentes is used to provide -accelerated DPDK datapath for nested Kubernetes pods running inside the VM. -Port is first attached to the virtual machine, bound to the userspace driver -(e.g. uio_pci_generic, igb_uio or vfio-pci) and then consumed by Kubernetes -pod via kuryr-kubernetes CNI plugin. VIF port profile objects ======================== @@ -94,80 +40,39 @@ provides a set of metadata attributes that serve to identify the guest virtual interface to the host. Different types of host connectivity will require different port profile object metadata. Each port profile type is associated -with a versioned object, subclassing `VIFPortProfileBase`. - -VIFPortProfileOpenVSwitch -------------------------- - -This profile provides the metadata required to associate a VIF with an Open -vSwitch host port. - -VIFPortProfile8021Qbg ---------------------- - -This profile provides the metadata required to associate a VIF with a VEPA host -device supporting the :term:`802.1Qbg` spec. +with a versioned object, subclassing :class:`VIFPortProfileBase`. -VIFPortProfile8021Qbh ---------------------- +.. autoclass:: VIFPortProfileBase -This profile provides the metadata required to associate a VIF with a VEPA host -device supporting the :term:`802.1Qbh` spec. +.. autoclass:: VIFPortProfileOpenVSwitch -VIFPortProfileFPOpenVSwitch ---------------------------- +.. autoclass:: VIFPortProfileFPOpenVSwitch -This profile provides the metadata required to associate a fast path -:term:`vhost-user` VIF with an :term:`Open vSwitch` port. +.. autoclass:: VIFPortProfileOVSRepresentor -VIFPortProfileOVSRepresentor ----------------------------- +.. autoclass:: VIFPortProfileFPBridge -This profile provides the metadata required to associate a VIF with a -:term:`VF` representor and :term:`Open vSwitch` port. If `representor_name` is -specified, it indicates a desire to rename the representor to the given name -on plugging. +.. autoclass:: VIFPortProfileFPTap -.. note:: This port profile is provided for backwards compatibility only. +.. autoclass:: VIFPortProfile8021Qbg -This interface has been superceded by the one provided by the -`DatapathOffloadRepresentor` class, which is now a field element of the -`VIFPortProfileBase` class. +.. autoclass:: VIFPortProfile8021Qbh -VIFPortProfileFPBridge ----------------------- +.. autoclass:: VIFPortProfileK8sDPDK -This profile provides the metadata required to associate a fast path vhost-user -VIF with a :term:`Linux bridge` port. - -VIFPortProfileFPTap -------------------- - -This profile provides the metadata required to associate a fast path vhost-user -VIF with a Calico port. - -VIFPortProfileK8sDPDK ---------------------- - -This profile provides the metadata required to associate nested DPDK VIF with -a Kubernetes pod. Datapath Offload type object ============================ -Port profiles can be associated with a `datapath_offload` object. This +Port profiles can be associated with a ``datapath_offload`` object. This provides a set of metadata attributes that serve to identify the datapath offload parameters of a VIF. Each different type of datapath offload is -associated with a versioned object, subclassing `DatapathOffloadBase`. +associated with a versioned object, subclassing :class:`DatapathOffloadBase`. + +.. autoclass:: DatapathOffloadBase -DatapathOffloadRepresentor --------------------------- +.. autoclass:: DatapathOffloadRepresentor -This object provides the metadata required to associate a VIF with a -:term:`VF` representor conforming to the -`switchdev `_ kernel model. -If `representor_name` is specified, it indicates a desire to rename the -representor to the given name on plugging. VIF network objects =================== @@ -176,4 +81,4 @@ logical network that the guest will be plugged into. This information is again represented by a set of versioned objects -TODO :-( +.. todo:: Populate this! diff -Nru python-os-vif-1.15.1/lower-constraints.txt python-os-vif-1.17.0/lower-constraints.txt --- python-os-vif-1.15.1/lower-constraints.txt 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/lower-constraints.txt 2019-09-06 17:03:26.000000000 +0000 @@ -34,7 +34,7 @@ msgpack==0.5.6 netaddr==0.7.18 netifaces==0.10.6 -openstackdocstheme==1.17.0 +openstackdocstheme==1.20.0 os-client-config==1.28.0 oslo.concurrency==3.20.0 oslo.config==5.1.0 diff -Nru python-os-vif-1.15.1/os_vif/internal/command/ip/api.py python-os-vif-1.17.0/os_vif/internal/command/ip/api.py --- python-os-vif-1.15.1/os_vif/internal/command/ip/api.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/command/ip/api.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os - -from oslo_log import log as logging - -if os.name == 'nt': - from os_vif.internal.command.ip.windows.impl_netifaces import \ - Netifaces as ip_lib_class -else: - from os_vif.internal.command.ip.linux.impl_pyroute2 import \ - PyRoute2 as ip_lib_class - - -LOG = logging.getLogger(__name__) - -ip = ip_lib_class() diff -Nru python-os-vif-1.15.1/os_vif/internal/command/ip/__init__.py python-os-vif-1.17.0/os_vif/internal/command/ip/__init__.py --- python-os-vif-1.15.1/os_vif/internal/command/ip/__init__.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/command/ip/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from os_vif.internal.command.ip import api - - -def set(device, check_exit_code=None, state=None, mtu=None, address=None, - promisc=None, master=None): - """Method to set a parameter in an interface.""" - return api.ip.set(device, check_exit_code=check_exit_code, state=state, - mtu=mtu, address=address, promisc=promisc, master=master) - - -def add(device, dev_type, check_exit_code=None, peer=None, link=None, - vlan_id=None): - """Method to add an interface.""" - return api.ip.add(device, dev_type, check_exit_code=check_exit_code, - peer=peer, link=link, vlan_id=vlan_id) - - -def delete(device, check_exit_code=None): - """Method to delete an interface.""" - return api.ip.delete(device, check_exit_code=check_exit_code) - - -def exists(device): - """Method to check if an interface exists.""" - return api.ip.exists(device) diff -Nru python-os-vif-1.15.1/os_vif/internal/command/ip/ip_command.py python-os-vif-1.17.0/os_vif/internal/command/ip/ip_command.py --- python-os-vif-1.15.1/os_vif/internal/command/ip/ip_command.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/command/ip/ip_command.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import abc -import six - - -@six.add_metaclass(abc.ABCMeta) -class IpCommand(object): - - TYPE_VETH = 'veth' - TYPE_VLAN = 'vlan' - TYPE_BRIDGE = 'bridge' - - @abc.abstractmethod - def set(self, device, check_exit_code=None, state=None, mtu=None, - address=None, promisc=None, master=None): - """Method to set a parameter in an interface. - - :param device: A network device (string) - :param check_exit_code: List of integers of allowed execution exit - codes - :param state: String network device state - :param mtu: Integer MTU value - :param address: String MAC address - :param promisc: Boolean promiscuous mode - :param master: String the master device that this device belongs to - :return: status of the command execution - """ - - @abc.abstractmethod - def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, - vlan_id=None): - """Method to add an interface. - - :param device: A network device (string) - :param dev_type: String network device type (TYPE_VETH, TYPE_VLAN) - :param check_exit_code: List of integers of allowed execution exit - codes - :param peer: String peer name, for veth interfaces - :param link: String root network interface name, 'device' will be a - VLAN tagged virtual interface - :param vlan_id: Integer VLAN ID for VLAN devices - :return: status of the command execution - """ - - @abc.abstractmethod - def delete(self, device, check_exit_code=None): - """Method to delete an interface. - - :param device: A network device (string) - :param dev_type: String network device type (TYPE_VETH, TYPE_VLAN) - :return: status of the command execution - """ - - @abc.abstractmethod - def exists(self, device): - """Method to dectect if a device exists. - - :param device: A network device (string) - :return: True if device exists else False - """ diff -Nru python-os-vif-1.15.1/os_vif/internal/command/ip/linux/impl_pyroute2.py python-os-vif-1.17.0/os_vif/internal/command/ip/linux/impl_pyroute2.py --- python-os-vif-1.15.1/os_vif/internal/command/ip/linux/impl_pyroute2.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/command/ip/linux/impl_pyroute2.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_log import log as logging -from oslo_utils import excutils -from pyroute2 import iproute -from pyroute2.netlink import exceptions as ipexc -from pyroute2.netlink.rtnl import ifinfmsg - -from os_vif import exception -from os_vif.internal.command.ip import ip_command -from os_vif import utils - -LOG = logging.getLogger(__name__) - - -class PyRoute2(ip_command.IpCommand): - - def _ip_link(self, ip, command, check_exit_code, **kwargs): - try: - LOG.debug('pyroute2 command %(command)s, arguments %(args)s' % - {'command': command, 'args': kwargs}) - return ip.link(command, **kwargs) - except ipexc.NetlinkError as e: - with excutils.save_and_reraise_exception() as ctx: - if e.code in check_exit_code: - LOG.error('NetlinkError was raised, code %s, message: %s' % - (e.code, str(e))) - ctx.reraise = False - - def set(self, device, check_exit_code=None, state=None, mtu=None, - address=None, promisc=None, master=None): - check_exit_code = check_exit_code or [] - with iproute.IPRoute() as ip: - idx = ip.link_lookup(ifname=device) - if not idx: - raise exception.NetworkInterfaceNotFound(interface=device) - idx = idx[0] - - args = {'index': idx} - if state: - args['state'] = state - if mtu: - args['mtu'] = mtu - if address: - args['address'] = address - if promisc is not None: - flags = ip.link('get', index=idx)[0]['flags'] - args['flags'] = (utils.set_mask(flags, ifinfmsg.IFF_PROMISC) - if promisc is True else - utils.unset_mask(flags, ifinfmsg.IFF_PROMISC)) - if master: - args['master'] = ip.link_lookup(ifname=master) - - if isinstance(check_exit_code, int): - check_exit_code = [check_exit_code] - - return self._ip_link(ip, 'set', check_exit_code, **args) - - def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, - vlan_id=None): - check_exit_code = check_exit_code or [] - with iproute.IPRoute() as ip: - args = {'ifname': device, - 'kind': dev_type} - if self.TYPE_VLAN == dev_type: - args['vlan_id'] = vlan_id - idx = ip.link_lookup(ifname=link) - if 0 == len(idx): - raise exception.NetworkInterfaceNotFound(interface=link) - args['link'] = idx[0] - elif self.TYPE_VETH == dev_type: - args['peer'] = peer - elif self.TYPE_BRIDGE == dev_type: - # NOTE(sean-k-mooney): the keys are defined in the pyroute2 - # codebase but are not documented. see the nla_map field - # in the bridge_data class located in the - # pyroute2.netlink.rtnl.ifinfmsg module for mode details - # https://github.com/svinota/pyroute2/blob/3ba9cdde34b2346ef8c2f8ba17cef5dbeb4c6d52/pyroute2/netlink/rtnl/ifinfmsg/__init__.py#L776-L820 - args['IFLA_BR_AGEING_TIME'] = 0 # disable mac learning ageing - args['IFLA_BR_FORWARD_DELAY'] = 0 # set no delay - args['IFLA_BR_STP_STATE'] = 0 # disable spanning tree - args['IFLA_BR_MCAST_SNOOPING'] = 0 # disable snooping - else: - raise exception.NetworkInterfaceTypeNotDefined(type=dev_type) - - return self._ip_link(ip, 'add', check_exit_code, **args) - - def delete(self, device, check_exit_code=None): - check_exit_code = check_exit_code or [] - with iproute.IPRoute() as ip: - idx = ip.link_lookup(ifname=device) - if len(idx) == 0: - raise exception.NetworkInterfaceNotFound(interface=device) - idx = idx[0] - - return self._ip_link(ip, 'del', check_exit_code, **{'index': idx}) - - def exists(self, device): - """Return True if the device exists.""" - with iproute.IPRoute() as ip: - idx = ip.link_lookup(ifname=device) - return True if idx else False diff -Nru python-os-vif-1.15.1/os_vif/internal/command/ip/windows/impl_netifaces.py python-os-vif-1.17.0/os_vif/internal/command/ip/windows/impl_netifaces.py --- python-os-vif-1.15.1/os_vif/internal/command/ip/windows/impl_netifaces.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/command/ip/windows/impl_netifaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -# Derived from: neutron/agent/windows/ip_lib.py -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import netifaces - -from oslo_log import log as logging - -from os_vif import exception -from os_vif.internal.command.ip import ip_command - -LOG = logging.getLogger(__name__) - - -class Netifaces(ip_command.IpCommand): - - def exists(self, device): - """Return True if the device exists in the namespace.""" - try: - return bool(netifaces.ifaddresses(device)) - except ValueError: - LOG.warning("The device does not exist on the system: %s", device) - except OSError: - LOG.error("Failed to get interface addresses: %s", device) - return False - - def set(self, device, check_exit_code=None, state=None, mtu=None, - address=None, promisc=None, master=None): - exception.NotImplementedForOS(function='ip.set', os='Windows') - - def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, - vlan_id=None): - exception.NotImplementedForOS(function='ip.add', os='Windows') - - def delete(self, device, check_exit_code=None): - exception.NotImplementedForOS(function='ip.delete', os='Windows') diff -Nru python-os-vif-1.15.1/os_vif/internal/ip/api.py python-os-vif-1.17.0/os_vif/internal/ip/api.py --- python-os-vif-1.15.1/os_vif/internal/ip/api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/ip/api.py 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,27 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os + +from oslo_log import log as logging + +if os.name == 'nt': + from os_vif.internal.ip.windows.impl_netifaces import \ + Netifaces as ip_lib_class +else: + from os_vif.internal.ip.linux.impl_pyroute2 import \ + PyRoute2 as ip_lib_class + + +LOG = logging.getLogger(__name__) + +ip = ip_lib_class() diff -Nru python-os-vif-1.15.1/os_vif/internal/ip/ip_command.py python-os-vif-1.17.0/os_vif/internal/ip/ip_command.py --- python-os-vif-1.15.1/os_vif/internal/ip/ip_command.py 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/ip/ip_command.py 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,73 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import abc +import six + + +@six.add_metaclass(abc.ABCMeta) +class IpCommand(object): + + TYPE_VETH = 'veth' + TYPE_VLAN = 'vlan' + TYPE_BRIDGE = 'bridge' + + @abc.abstractmethod + def set(self, device, check_exit_code=None, state=None, mtu=None, + address=None, promisc=None, master=None): + """Method to set a parameter in an interface. + + :param device: A network device (string) + :param check_exit_code: List of integers of allowed execution exit + codes + :param state: String network device state + :param mtu: Integer MTU value + :param address: String MAC address + :param promisc: Boolean promiscuous mode + :param master: String the master device that this device belongs to + :return: status of the command execution + """ + + @abc.abstractmethod + def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, + vlan_id=None, ageing=None): + """Method to add an interface. + + :param device: A network device (string) + :param dev_type: String network device type (TYPE_VETH, TYPE_VLAN) + :param check_exit_code: List of integers of allowed execution exit + codes + :param peer: String peer name, for veth interfaces + :param link: String root network interface name, 'device' will be a + VLAN tagged virtual interface + :param vlan_id: Integer VLAN ID for VLAN devices + :param ageing: integer value in seconds before learned + mac addresses are forgotten. + :return: status of the command execution + """ + + @abc.abstractmethod + def delete(self, device, check_exit_code=None): + """Method to delete an interface. + + :param device: A network device (string) + :param dev_type: String network device type (TYPE_VETH, TYPE_VLAN) + :return: status of the command execution + """ + + @abc.abstractmethod + def exists(self, device): + """Method to dectect if a device exists. + + :param device: A network device (string) + :return: True if device exists else False + """ diff -Nru python-os-vif-1.15.1/os_vif/internal/ip/linux/impl_pyroute2.py python-os-vif-1.17.0/os_vif/internal/ip/linux/impl_pyroute2.py --- python-os-vif-1.15.1/os_vif/internal/ip/linux/impl_pyroute2.py 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/ip/linux/impl_pyroute2.py 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,120 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_log import log as logging +from oslo_utils import excutils +from pyroute2 import iproute +from pyroute2.netlink import exceptions as ipexc +from pyroute2.netlink.rtnl import ifinfmsg + +from os_vif import exception +from os_vif.internal.ip import ip_command +from os_vif import utils + +LOG = logging.getLogger(__name__) + + +class PyRoute2(ip_command.IpCommand): + + def _ip_link(self, ip, command, check_exit_code, **kwargs): + try: + LOG.debug('pyroute2 command %(command)s, arguments %(args)s' % + {'command': command, 'args': kwargs}) + return ip.link(command, **kwargs) + except ipexc.NetlinkError as e: + with excutils.save_and_reraise_exception() as ctx: + if e.code in check_exit_code: + LOG.error('NetlinkError was raised, code %s, message: %s' % + (e.code, str(e))) + ctx.reraise = False + + def set(self, device, check_exit_code=None, state=None, mtu=None, + address=None, promisc=None, master=None): + check_exit_code = check_exit_code or [] + with iproute.IPRoute() as ip: + idx = ip.link_lookup(ifname=device) + if not idx: + raise exception.NetworkInterfaceNotFound(interface=device) + idx = idx[0] + + args = {'index': idx} + if state: + args['state'] = state + if mtu: + args['mtu'] = mtu + if address: + args['address'] = address + if promisc is not None: + flags = ip.link('get', index=idx)[0]['flags'] + args['flags'] = (utils.set_mask(flags, ifinfmsg.IFF_PROMISC) + if promisc is True else + utils.unset_mask(flags, ifinfmsg.IFF_PROMISC)) + if master: + args['master'] = ip.link_lookup(ifname=master) + + if isinstance(check_exit_code, int): + check_exit_code = [check_exit_code] + + return self._ip_link(ip, 'set', check_exit_code, **args) + + def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, + vlan_id=None, ageing=None): + check_exit_code = check_exit_code or [] + with iproute.IPRoute() as ip: + args = {'ifname': device, + 'kind': dev_type} + if self.TYPE_VLAN == dev_type: + args['vlan_id'] = vlan_id + idx = ip.link_lookup(ifname=link) + if 0 == len(idx): + raise exception.NetworkInterfaceNotFound(interface=link) + args['link'] = idx[0] + elif self.TYPE_VETH == dev_type: + args['peer'] = peer + elif self.TYPE_BRIDGE == dev_type: + # NOTE(sean-k-mooney): the keys are defined in the pyroute2 + # codebase but are not documented. see the nla_map field + # in the bridge_data class located in the + # pyroute2.netlink.rtnl.ifinfmsg module for mode details + # https://github.com/svinota/pyroute2/blob/3ba9cdde34b2346ef8c2f8ba17cef5dbeb4c6d52/pyroute2/netlink/rtnl/ifinfmsg/__init__.py#L776-L820 + args['IFLA_BR_FORWARD_DELAY'] = 0 # set no delay + args['IFLA_BR_STP_STATE'] = 0 # disable spanning tree + args['IFLA_BR_MCAST_SNOOPING'] = 0 # disable snooping + # NOTE(sean-k-mooney): we conditionally enable mac ageing as + # this code is shared between the ovs and linux bridge + # plugins. For linux bridge we want to allow the default + # ageing of 300 seconds, whereas for ovs with the ip-tables + # firewall we want to disable ageing. None was chosen as + # the default value of ageing to allow the caller to determine + # what policy to use and keep this code generic. + if ageing is not None: + args['IFLA_BR_AGEING_TIME'] = ageing + else: + raise exception.NetworkInterfaceTypeNotDefined(type=dev_type) + + return self._ip_link(ip, 'add', check_exit_code, **args) + + def delete(self, device, check_exit_code=None): + check_exit_code = check_exit_code or [] + with iproute.IPRoute() as ip: + idx = ip.link_lookup(ifname=device) + if len(idx) == 0: + raise exception.NetworkInterfaceNotFound(interface=device) + idx = idx[0] + + return self._ip_link(ip, 'del', check_exit_code, **{'index': idx}) + + def exists(self, device): + """Return True if the device exists.""" + with iproute.IPRoute() as ip: + idx = ip.link_lookup(ifname=device) + return True if idx else False diff -Nru python-os-vif-1.15.1/os_vif/internal/ip/windows/impl_netifaces.py python-os-vif-1.17.0/os_vif/internal/ip/windows/impl_netifaces.py --- python-os-vif-1.15.1/os_vif/internal/ip/windows/impl_netifaces.py 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/internal/ip/windows/impl_netifaces.py 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,46 @@ +# Derived from: neutron/agent/windows/ip_lib.py +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import netifaces + +from oslo_log import log as logging + +from os_vif import exception +from os_vif.internal.ip import ip_command + +LOG = logging.getLogger(__name__) + + +class Netifaces(ip_command.IpCommand): + + def exists(self, device): + """Return True if the device exists in the namespace.""" + try: + return bool(netifaces.ifaddresses(device)) + except ValueError: + LOG.warning("The device does not exist on the system: %s", device) + except OSError: + LOG.error("Failed to get interface addresses: %s", device) + return False + + def set(self, device, check_exit_code=None, state=None, mtu=None, + address=None, promisc=None, master=None): + exception.NotImplementedForOS(function='ip.set', os='Windows') + + def add(self, device, dev_type, check_exit_code=None, peer=None, link=None, + vlan_id=None): + exception.NotImplementedForOS(function='ip.add', os='Windows') + + def delete(self, device, check_exit_code=None): + exception.NotImplementedForOS(function='ip.delete', os='Windows') diff -Nru python-os-vif-1.15.1/os_vif/objects/vif.py python-os-vif-1.17.0/os_vif/objects/vif.py --- python-os-vif-1.15.1/os_vif/objects/vif.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/objects/vif.py 2019-09-06 17:03:26.000000000 +0000 @@ -22,7 +22,12 @@ @base.VersionedObjectRegistry.register class VIFBase(osv_base.VersionedObject, base.ComparableVersionedObject): - """Represents a virtual network interface.""" + """Represents a virtual network interface. + + The base VIF defines fields that are common to all types of VIF and + provides an association to the network the VIF is plugged into. It should + not be instantiated itself - use a subclass instead. + """ # Version 1.0: Initial release VERSION = '1.0' @@ -60,7 +65,8 @@ """A generic-style VIF. Generic-style VIFs are unbound, floating TUN/TAP devices that should be - setup by the plugin, not the hypervisor. + setup by the plugin, not the hypervisor. The way the TAP device is + connected to the host network stack is explicitly left undefined. For libvirt drivers, this maps to type="ethernet" which just implies a bare TAP device with all setup delegated to the plugin. @@ -79,8 +85,9 @@ class VIFBridge(VIFBase): """A bridge-style VIF. - Bridge-style VIFs are bound to a linux bridge by the hypervisor. Other - devices may be bound to the same L2 virtual bridge. + Bridge-style VIFs are bound to a Linux host bridge by the hypervisor. This + provides Ethernet layer bridging, typically to the LAN. Other devices may + be bound to the same L2 virtual bridge. For libvirt drivers, this maps to type='bridge'. """ @@ -130,6 +137,15 @@ :class:`~os_vif.objects.vif.VIFHostDevice`, which allows the guest to directly connect to the VF. + The connection to the device may operate in one of a number of different + modes, :term:`VEPA` (either :term:`802.1Qbg` or :term:`802.1Qbh`), + passthrough (exclusive assignment of the host NIC) or bridge (ethernet + layer bridging of traffic). The passthrough mode would be used when there + is a network device which needs to have a MAC address or VLAN + configuration. For passthrough of network devices without MAC/VLAN + configuration, :class:`~os_vif.objects.vif.VIFHostDevice` should be used + instead. + For libvirt drivers, this maps to type='direct' """ @@ -155,11 +171,11 @@ class VIFVHostUser(VIFBase): """A vhostuser-style VIF. - vhostuser-style VIFs utilize a userspace vhost backend, which allows - traffic to traverse between the guest and a host userspace application - (commonly a virtual switch), bypassing the kernel network stack. Contrast - this with :class:`~os_vif.objects.vif.VIFBridge`, where all packets must be - handled by the hypervisor. + vhostuser-style VIFs utilize a :term:`userspace vhost ` + backend, which allows traffic to traverse between the guest and a host + userspace application (commonly a virtual switch), bypassing the kernel + network stack. Contrast this with :class:`~os_vif.objects.vif.VIFBridge`, + where all packets must be handled by the hypervisor. For libvirt drivers, this maps to type='vhostuser' """ @@ -190,9 +206,10 @@ class VIFHostDevice(VIFBase): """A hostdev-style VIF. - Hostdev-style VIFs provide a guest with direct access to an SR-IOV Virtual - Function (VF). Contrast this with :class:`~ovs_vif.objects.vif.VIFDirect`, - which includes a software layer between the VF and the guest. + Hostdev-style VIFs provide a guest with direct access to an :term:`SR-IOV` + :term:`Virtual Function` (VF) or an entire :term:`Physical Function` (PF). + Contrast this with :class:`~ovs_vif.objects.vif.VIFDirect`, which includes + a software layer between the interface and the guest. For libvirt drivers, this maps to type='hostdev' """ @@ -216,9 +233,15 @@ @base.VersionedObjectRegistry.register class VIFNestedDPDK(VIFBase): - """TODO. + """A nested DPDK-style VIF. - For kuryr-kubernetes nested DPDK interfaces. + Nested DPDK-style VIFs are used by Kuryr-Kubernetes to provide accelerated + DPDK datapath for nested Kubernetes pods running inside the VM. The port + is first attached to the virtual machine, bound to the userspace driver + (e.g. ``uio_pci_generic``, ``igb_uio`` or ``vfio-pci``) and then consumed + by Kubernetes pod via the kuryr-kubernetes CNI plugin. + + This does not apply to libvirt drivers. """ # Version 1.0: Initial release @@ -245,7 +268,15 @@ @base.VersionedObjectRegistry.register class DatapathOffloadRepresentor(DatapathOffloadBase): - """Offload type for VF Representors conforming to the switchdev model.""" + """Offload type for VF Representors conforming to the switchdev model. + + This datapath offloads provides the metadata required to associate a VIF + with a :term:`VF` representor conforming to the `switchdev`_ kernel model. + If ``representor_name`` is specified, it indicates a desire to rename the + representor to the given name on plugging. + + .. _switchdev: https://netdevconf.org/1.2/session.html?or-gerlitz + """ # Version 1.0: Initial release VERSION = '1.0' @@ -262,7 +293,11 @@ @base.VersionedObjectRegistry.register class VIFPortProfileBase(osv_base.VersionedObject, base.ComparableVersionedObject): - """Base class for all types of port profile.""" + """Base class for all types of port profile. + + The base profile defines fields that are common to all types of profile. It + should not be instantiated itself - use a subclass instead. + """ # Version 1.0: Initial release # Version 1.1: Added 'datapath_offload' @@ -282,7 +317,11 @@ @base.VersionedObjectRegistry.register class VIFPortProfileOpenVSwitch(VIFPortProfileBase): - """Port profile info for Open vSwitch networks.""" + """Port profile info for Open vSwitch networks. + + This profile provides the metadata required to associate a VIF with an Open + vSwitch interface. + """ # Version 1.0: Initial release # Version 1.1: Added 'datapath_type' @@ -321,7 +360,11 @@ @base.VersionedObjectRegistry.register class VIFPortProfileFPOpenVSwitch(VIFPortProfileOpenVSwitch): - """Port profile info for Open vSwitch networks using fastpath.""" + """Port profile info for Open vSwitch networks using fast path. + + This profile provides the metadata required to associate a :term:`fast + path ` VIF with an :term:`Open vSwitch` port. + """ # Version 1.0: Initial release # Version 1.1: VIFPortProfileOpenVSwitch updated to 1.1 from 1.0 @@ -359,8 +402,19 @@ class VIFPortProfileOVSRepresentor(VIFPortProfileOpenVSwitch): """Port profile info for OpenVSwitch networks using a representor. - This class is now frozen and retained for backwards compatibility. The - ``datapath_offload`` field in port profiles should be used instead. + This profile provides the metadata required to associate a VIF with a + :term:`VF` representor and :term:`Open vSwitch` port. If `representor_name` + is specified, it indicates a desire to rename the representor to the given + name on plugging. + + .. note:: + + This port profile is provided for backwards compatibility only. + + This interface has been superceded by the one provided by the + :class:`DatapathOffloadRepresentor` class, which is now a field element + of the :class:`VIFPortProfileBase` class. The ``datapath_offload`` + field in port profiles should be used instead. """ # Version 1.0: Initial release @@ -395,7 +449,11 @@ @base.VersionedObjectRegistry.register class VIFPortProfileFPBridge(VIFPortProfileBase): - """Port profile info for LinuxBridge networks using fastpath.""" + """Port profile info for Linux Bridge networks using fast path. + + This profile provides the metadata required to associate a :term:`fast + path ` VIF with a :term:`Linux bridge` port. + """ # Version 1.0: Initial release # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0 @@ -418,7 +476,11 @@ @base.VersionedObjectRegistry.register class VIFPortProfileFPTap(VIFPortProfileBase): - """Port profile info for Calico networks using fastpath.""" + """Port profile info for Calico networks using fast path. + + This profile provides the metadata required to associate a :term:`fast + path ` VIF with a :term:`Calico` port. + """ # Version 1.0: Initial release # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0 @@ -441,7 +503,11 @@ @base.VersionedObjectRegistry.register class VIFPortProfile8021Qbg(VIFPortProfileBase): - """Port profile info for VEPA 802.1qbg networks.""" + """Port profile info for VEPA 802.1qbg networks. + + This profile provides the metadata required to associate a VIF with a VEPA + host device supporting the :term:`802.1Qbg` spec. + """ # Version 1.0: Initial release # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0 @@ -481,7 +547,11 @@ @base.VersionedObjectRegistry.register class VIFPortProfile8021Qbh(VIFPortProfileBase): - """Port profile info for VEPA 802.1qbh networks.""" + """Port profile info for VEPA 802.1qbh networks. + + This profile provides the metadata required to associate a VIF with a VEPA + host device supporting the :term:`802.1Qbh` spec. + """ # Version 1.0: Initial release # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0 @@ -507,7 +577,11 @@ @base.VersionedObjectRegistry.register class VIFPortProfileK8sDPDK(VIFPortProfileBase): - """Port profile info for Kuryr-Kubernetes DPDK ports.""" + """Port profile info for Kuryr-Kubernetes DPDK ports. + + This profile provides the metadata required to associate nested DPDK VIF + with a Kubernetes pod. + """ # Version 1.0: Initial release # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0 diff -Nru python-os-vif-1.15.1/os_vif/tests/functional/base.py python-os-vif-1.17.0/os_vif/tests/functional/base.py --- python-os-vif-1.15.1/os_vif/tests/functional/base.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/tests/functional/base.py 2019-09-06 17:03:26.000000000 +0000 @@ -40,14 +40,24 @@ DEFAULT_LOG_DIR = os.path.join(_get_test_log_path(), 'osvif-functional-logs') -def _catch_timeout(f): - @functools.wraps(f) - def func(self, *args, **kwargs): - try: - return f(self, *args, **kwargs) - except eventlet.Timeout as e: - self.fail('Execution of this test timed out: %s' % e) - return func +def wait_until_true(predicate, timeout=15, sleep=1): + """Wait until callable predicate is evaluated as True + + :param predicate: Callable deciding whether waiting should continue. + Best practice is to instantiate predicate with + ``functools.partial()``. + :param timeout: Timeout in seconds how long should function wait. + :param sleep: Polling interval for results in seconds. + :return: True if the predicate is evaluated as True within the timeout, + False in case of timeout evaluating the predicate. + """ + try: + with eventlet.Timeout(timeout): + while not predicate(): + eventlet.sleep(sleep) + except eventlet.Timeout: + return False + return True class _CatchTimeoutMetaclass(abc.ABCMeta): @@ -58,13 +68,22 @@ # both unbound methods (python2) and functions (python3) cls, predicate=inspect.isroutine): if name.startswith('test_'): - setattr(cls, name, _catch_timeout(method)) + setattr(cls, name, cls._catch_timeout(method)) + + @staticmethod + def _catch_timeout(f): + @functools.wraps(f) + def func(self, *args, **kwargs): + try: + return f(self, *args, **kwargs) + except eventlet.Timeout as e: + self.fail('Execution of this test timed out: %s' % e) + return func -def setup_logging(): +def setup_logging(component_name): """Sets up the logging options for a log with supplied name.""" - product_name = "os_vif" - logging.setup(cfg.CONF, product_name) + logging.setup(cfg.CONF, component_name) LOG.info("Logging enabled!") LOG.info("%(prog)s version %(version)s", {'prog': sys.argv[0], 'version': osvif_version.__version__}) @@ -84,21 +103,25 @@ class BaseFunctionalTestCase(base.BaseTestCase): """Base class for functional tests.""" + COMPONENT_NAME = 'os_vif' + PRIVILEGED_GROUP = 'os_vif_privileged' + def setUp(self): super(BaseFunctionalTestCase, self).setUp() logging.register_options(CONF) - setup_logging() + setup_logging(self.COMPONENT_NAME) fileutils.ensure_tree(DEFAULT_LOG_DIR, mode=0o755) log_file = sanitize_log_path( os.path.join(DEFAULT_LOG_DIR, "%s.txt" % self.id())) - self.config(log_file=log_file) + self.flags(log_file=log_file) privsep_helper = os.path.join( - os.getenv('VIRTUAL_ENV'), 'bin', 'privsep-helper') - self.config( + os.getenv('VIRTUAL_ENV', os.path.dirname(sys.executable)[:-4]), + 'bin', 'privsep-helper') + self.flags( helper_command=' '.join(['sudo', '-E', privsep_helper]), - group='os_vif_privileged') + group=self.PRIVILEGED_GROUP) - def config(self, **kw): + def flags(self, **kw): """Override some configuration values. The keyword arguments are the names of configuration options to diff -Nru python-os-vif-1.15.1/os_vif/tests/functional/internal/command/ip/test_impl_pyroute2.py python-os-vif-1.17.0/os_vif/tests/functional/internal/command/ip/test_impl_pyroute2.py --- python-os-vif-1.15.1/os_vif/tests/functional/internal/command/ip/test_impl_pyroute2.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/tests/functional/internal/command/ip/test_impl_pyroute2.py 2019-09-06 17:03:26.000000000 +0000 @@ -16,7 +16,7 @@ from oslo_concurrency import processutils from oslo_utils import excutils -from os_vif.internal.command import ip as ip_lib +from os_vif.internal.ip.api import ip as ip_lib from os_vif.tests.functional import base from os_vif.tests.functional import privsep @@ -217,14 +217,36 @@ self.assertEqual("0", f.readline().rstrip('\n')) with open(base_path % "stp_state", "r") as f: self.assertEqual("0", f.readline().rstrip('\n')) + with open(base_path % "multicast_snooping", "r") as f: + self.assertEqual("0", f.readline().rstrip('\n')) + with open(base_path % "ageing_time", "r") as f: + value = int(f.readline().rstrip('\n')) + # NOTE(sean-k-mooney): IEEE 8021-Q recommends that the default + # ageing should be 300 and the linux kernel defaults to 300 + # via an unconditional define. As such we expect this to be + # 300 however since services like network-manager could change + # the default on bridge creation we check that if it is not 300 + # then the value should not be 0. + self.assertTrue(300 == value or value != 0) + + def test_add_bridge_with_mac_ageing_0(self): + device = "test_dev_12" + self.addCleanup(self.del_device, device) + _ip_cmd_add(device, 'bridge', ageing=0) + self.assertTrue(self.exist_device(device)) + base_path = "/sys/class/net/test_dev_12/bridge/%s" + with open(base_path % "forward_delay", "r") as f: + self.assertEqual("0", f.readline().rstrip('\n')) + with open(base_path % "stp_state", "r") as f: + self.assertEqual("0", f.readline().rstrip('\n')) with open(base_path % "ageing_time", "r") as f: self.assertEqual("0", f.readline().rstrip('\n')) with open(base_path % "multicast_snooping", "r") as f: self.assertEqual("0", f.readline().rstrip('\n')) def test_add_port_to_bridge(self): - device = "test_dev_12" - bridge = "test_dev_13" + device = "test_dev_13" + bridge = "test_dev_14" self.addCleanup(self.del_device, device) self.addCleanup(self.del_device, bridge) self.add_device(device, 'dummy') diff -Nru python-os-vif-1.15.1/os_vif/tests/unit/internal/command/ip/linux/test_impl_pyroute2.py python-os-vif-1.17.0/os_vif/tests/unit/internal/command/ip/linux/test_impl_pyroute2.py --- python-os-vif-1.15.1/os_vif/tests/unit/internal/command/ip/linux/test_impl_pyroute2.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/tests/unit/internal/command/ip/linux/test_impl_pyroute2.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,156 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -from pyroute2 import iproute -from pyroute2.netlink import exceptions as ipexc -from pyroute2.netlink.rtnl import ifinfmsg - -from os_vif import exception -from os_vif.internal.command.ip.linux import impl_pyroute2 -from os_vif.tests.unit import base - - -class TestIpCommand(base.TestCase): - - ERROR_CODE = 40 - OTHER_ERROR_CODE = 50 - DEVICE = 'device' - MTU = 1500 - MAC = 'ca:fe:ca:fe:ca:fe' - UP = 'up' - TYPE_VETH = 'veth' - TYPE_VLAN = 'vlan' - TYPE_BRIDGE = 'bridge' - LINK = 'device2' - VLAN_ID = 14 - - def setUp(self): - super(TestIpCommand, self).setUp() - self.ip = impl_pyroute2.PyRoute2() - self.ip_link_p = mock.patch.object(iproute.IPRoute, 'link') - self.ip_link = self.ip_link_p.start() - - def test_set(self): - with mock.patch.object(iproute.IPRoute, 'link_lookup', - return_value=[1]) as mock_link_lookup: - self.ip_link.return_value = [{'flags': 0x4000}] - self.ip.set(self.DEVICE, state=self.UP, mtu=self.MTU, - address=self.MAC, promisc=True) - mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) - args = {'state': self.UP, - 'mtu': self.MTU, - 'address': self.MAC, - 'flags': 0x4000 | ifinfmsg.IFF_PROMISC} - calls = [mock.call('get', index=1), - mock.call('set', index=1, **args)] - self.ip_link.assert_has_calls(calls) - - def test_set_exit_code(self): - with mock.patch.object(iproute.IPRoute, 'link_lookup', - return_value=[1]) as mock_link_lookup: - self.ip_link.side_effect = ipexc.NetlinkError(self.ERROR_CODE, - msg="Error message") - - self.ip.set(self.DEVICE, check_exit_code=[self.ERROR_CODE]) - mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) - self.ip_link.assert_called_once_with('set', index=1) - - self.assertRaises(ipexc.NetlinkError, self.ip.set, self.DEVICE, - check_exit_code=[self.OTHER_ERROR_CODE]) - - def test_set_no_interface_found(self): - with mock.patch.object(iproute.IPRoute, 'link_lookup', - return_value=[]) as mock_link_lookup: - self.assertRaises(exception.NetworkInterfaceNotFound, self.ip.set, - self.DEVICE) - mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) - self.ip_link.assert_not_called() - - def test_add_veth(self): - self.ip.add(self.DEVICE, self.TYPE_VETH, peer='peer') - self.ip_link.assert_called_once_with( - 'add', ifname=self.DEVICE, kind=self.TYPE_VETH, peer='peer') - - def test_add_vlan(self): - with mock.patch.object(iproute.IPRoute, 'link_lookup', - return_value=[1]) as mock_link_lookup: - self.ip.add(self.DEVICE, self.TYPE_VLAN, link=self.LINK, - vlan_id=self.VLAN_ID) - mock_link_lookup.assert_called_once_with(ifname=self.LINK) - args = {'ifname': self.DEVICE, - 'kind': self.TYPE_VLAN, - 'vlan_id': self.VLAN_ID, - 'link': 1} - self.ip_link.assert_called_once_with('add', **args) - - def test_add_bridge(self): - self.ip.add(self.DEVICE, self.TYPE_BRIDGE) - args = {'ifname': self.DEVICE, - 'kind': self.TYPE_BRIDGE, - 'IFLA_BR_AGEING_TIME': 0, - 'IFLA_BR_FORWARD_DELAY': 0, - 'IFLA_BR_STP_STATE': 0, - 'IFLA_BR_MCAST_SNOOPING': 0} - self.ip_link.assert_called_once_with('add', **args) - - def test_add_vlan_no_interface_found(self): - with mock.patch.object(iproute.IPRoute, 'link_lookup', - return_value=[]) as mock_link_lookup: - self.assertRaises(exception.NetworkInterfaceNotFound, self.ip.add, - self.DEVICE, self.TYPE_VLAN, link=self.LINK) - mock_link_lookup.assert_called_once_with(ifname=self.LINK) - self.ip_link.assert_not_called() - - def test_add_other_type(self): - self.assertRaises(exception.NetworkInterfaceTypeNotDefined, - self.ip.add, self.DEVICE, 'type_not_defined') - - def test_add_exit_code(self): - self.ip_link.side_effect = ipexc.NetlinkError(self.ERROR_CODE, - msg="Error message") - - self.ip.add(self.DEVICE, self.TYPE_VETH, peer='peer', - check_exit_code=[self.ERROR_CODE]) - self.ip_link.assert_called_once_with( - 'add', ifname=self.DEVICE, kind=self.TYPE_VETH, peer='peer') - - self.assertRaises(ipexc.NetlinkError, self.ip.add, self.DEVICE, - self.TYPE_VLAN, peer='peer', - check_exit_code=[self.OTHER_ERROR_CODE]) - - def test_delete(self): - with mock.patch.object(iproute.IPRoute, 'link_lookup', - return_value=[1]) as mock_link_lookup: - self.ip.delete(self.DEVICE) - mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) - self.ip_link.assert_called_once_with('del', index=1) - - def test_delete_no_interface_found(self): - with mock.patch.object(iproute.IPRoute, 'link_lookup', - return_value=[]) as mock_link_lookup: - self.assertRaises(exception.NetworkInterfaceNotFound, - self.ip.delete, self.DEVICE) - mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) - - def test_delete_exit_code(self): - with mock.patch.object(iproute.IPRoute, 'link_lookup', - return_value=[1]) as mock_link_lookup: - self.ip_link.side_effect = ipexc.NetlinkError(self.ERROR_CODE, - msg="Error message") - - self.ip.delete(self.DEVICE, check_exit_code=[self.ERROR_CODE]) - mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) - self.ip_link.assert_called_once_with('del', index=1) - - self.assertRaises(ipexc.NetlinkError, self.ip.delete, self.DEVICE, - check_exit_code=[self.OTHER_ERROR_CODE]) diff -Nru python-os-vif-1.15.1/os_vif/tests/unit/internal/command/ip/test_api.py python-os-vif-1.17.0/os_vif/tests/unit/internal/command/ip/test_api.py --- python-os-vif-1.15.1/os_vif/tests/unit/internal/command/ip/test_api.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/tests/unit/internal/command/ip/test_api.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -from six import moves - -from os_vif.internal.command.ip import api -from os_vif.tests.unit import base - - -class TestIpApi(base.TestCase): - - @staticmethod - def _reload_original_os_module(): - moves.reload_module(api) - - def test_get_impl_windows(self): - self.addCleanup(self._reload_original_os_module) - with mock.patch('os.name', 'nt'): - moves.reload_module(api) - from os_vif.internal.command.ip.windows import impl_netifaces - self.assertIsInstance(api.ip, impl_netifaces.Netifaces) - - def test_get_impl_linux(self): - self.addCleanup(self._reload_original_os_module) - with mock.patch('os.name', 'posix'): - moves.reload_module(api) - from os_vif.internal.command.ip.linux import impl_pyroute2 - self.assertIsInstance(api.ip, impl_pyroute2.PyRoute2) diff -Nru python-os-vif-1.15.1/os_vif/tests/unit/internal/command/ip/windows/test_impl_netifaces.py python-os-vif-1.17.0/os_vif/tests/unit/internal/command/ip/windows/test_impl_netifaces.py --- python-os-vif-1.15.1/os_vif/tests/unit/internal/command/ip/windows/test_impl_netifaces.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/tests/unit/internal/command/ip/windows/test_impl_netifaces.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -import netifaces - -from os_vif.internal.command.ip.windows import impl_netifaces as ip_lib -from os_vif.tests.unit import base - - -class TestIPDevice(base.TestCase): - - def setUp(self): - super(TestIPDevice, self).setUp() - self.device_name = 'test_device' - self.mock_log = mock.patch.object(ip_lib, "LOG").start() - self.ip_lib = ip_lib.Netifaces() - - @mock.patch.object(netifaces, 'ifaddresses', return_value=True) - def test_exists(self, mock_ifaddresses): - self.assertTrue(self.ip_lib.exists(self.device_name)) - mock_ifaddresses.assert_called_once_with(self.device_name) - - @mock.patch.object(netifaces, 'ifaddresses', side_effect=ValueError()) - def test_exists_not_found(self, mock_ifaddresses): - self.assertFalse(self.ip_lib.exists(self.device_name)) - mock_ifaddresses.assert_called_once_with(self.device_name) - self.mock_log.warning.assert_called_once_with( - "The device does not exist on the system: %s", self.device_name) - - @mock.patch.object(netifaces, 'ifaddresses', side_effect=OSError()) - def test_exists_os_error_exception(self, mock_ifaddresses): - self.assertFalse(self.ip_lib.exists(self.device_name)) - mock_ifaddresses.assert_called_once_with(self.device_name) - self.mock_log.error.assert_called_once_with( - "Failed to get interface addresses: %s", self.device_name) diff -Nru python-os-vif-1.15.1/os_vif/tests/unit/internal/ip/linux/test_impl_pyroute2.py python-os-vif-1.17.0/os_vif/tests/unit/internal/ip/linux/test_impl_pyroute2.py --- python-os-vif-1.15.1/os_vif/tests/unit/internal/ip/linux/test_impl_pyroute2.py 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/tests/unit/internal/ip/linux/test_impl_pyroute2.py 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,165 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from pyroute2 import iproute +from pyroute2.netlink import exceptions as ipexc +from pyroute2.netlink.rtnl import ifinfmsg + +from os_vif import exception +from os_vif.internal.ip.linux import impl_pyroute2 +from os_vif.tests.unit import base + + +class TestIpCommand(base.TestCase): + + ERROR_CODE = 40 + OTHER_ERROR_CODE = 50 + DEVICE = 'device' + MTU = 1500 + MAC = 'ca:fe:ca:fe:ca:fe' + UP = 'up' + TYPE_VETH = 'veth' + TYPE_VLAN = 'vlan' + TYPE_BRIDGE = 'bridge' + LINK = 'device2' + VLAN_ID = 14 + + def setUp(self): + super(TestIpCommand, self).setUp() + self.ip = impl_pyroute2.PyRoute2() + self.ip_link_p = mock.patch.object(iproute.IPRoute, 'link') + self.ip_link = self.ip_link_p.start() + + def test_set(self): + with mock.patch.object(iproute.IPRoute, 'link_lookup', + return_value=[1]) as mock_link_lookup: + self.ip_link.return_value = [{'flags': 0x4000}] + self.ip.set(self.DEVICE, state=self.UP, mtu=self.MTU, + address=self.MAC, promisc=True) + mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) + args = {'state': self.UP, + 'mtu': self.MTU, + 'address': self.MAC, + 'flags': 0x4000 | ifinfmsg.IFF_PROMISC} + calls = [mock.call('get', index=1), + mock.call('set', index=1, **args)] + self.ip_link.assert_has_calls(calls) + + def test_set_exit_code(self): + with mock.patch.object(iproute.IPRoute, 'link_lookup', + return_value=[1]) as mock_link_lookup: + self.ip_link.side_effect = ipexc.NetlinkError(self.ERROR_CODE, + msg="Error message") + + self.ip.set(self.DEVICE, check_exit_code=[self.ERROR_CODE]) + mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) + self.ip_link.assert_called_once_with('set', index=1) + + self.assertRaises(ipexc.NetlinkError, self.ip.set, self.DEVICE, + check_exit_code=[self.OTHER_ERROR_CODE]) + + def test_set_no_interface_found(self): + with mock.patch.object(iproute.IPRoute, 'link_lookup', + return_value=[]) as mock_link_lookup: + self.assertRaises(exception.NetworkInterfaceNotFound, self.ip.set, + self.DEVICE) + mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) + self.ip_link.assert_not_called() + + def test_add_veth(self): + self.ip.add(self.DEVICE, self.TYPE_VETH, peer='peer') + self.ip_link.assert_called_once_with( + 'add', ifname=self.DEVICE, kind=self.TYPE_VETH, peer='peer') + + def test_add_vlan(self): + with mock.patch.object(iproute.IPRoute, 'link_lookup', + return_value=[1]) as mock_link_lookup: + self.ip.add(self.DEVICE, self.TYPE_VLAN, link=self.LINK, + vlan_id=self.VLAN_ID) + mock_link_lookup.assert_called_once_with(ifname=self.LINK) + args = {'ifname': self.DEVICE, + 'kind': self.TYPE_VLAN, + 'vlan_id': self.VLAN_ID, + 'link': 1} + self.ip_link.assert_called_once_with('add', **args) + + def test_add_bridge(self): + self.ip.add(self.DEVICE, self.TYPE_BRIDGE) + args = {'ifname': self.DEVICE, + 'kind': self.TYPE_BRIDGE, + 'IFLA_BR_FORWARD_DELAY': 0, + 'IFLA_BR_STP_STATE': 0, + 'IFLA_BR_MCAST_SNOOPING': 0} + self.ip_link.assert_called_once_with('add', **args) + + def test_add_bridge_with_ageing(self): + self.ip.add(self.DEVICE, self.TYPE_BRIDGE, ageing=0) + args = {'ifname': self.DEVICE, + 'kind': self.TYPE_BRIDGE, + 'IFLA_BR_AGEING_TIME': 0, + 'IFLA_BR_FORWARD_DELAY': 0, + 'IFLA_BR_STP_STATE': 0, + 'IFLA_BR_MCAST_SNOOPING': 0} + self.ip_link.assert_called_once_with('add', **args) + + def test_add_vlan_no_interface_found(self): + with mock.patch.object(iproute.IPRoute, 'link_lookup', + return_value=[]) as mock_link_lookup: + self.assertRaises(exception.NetworkInterfaceNotFound, self.ip.add, + self.DEVICE, self.TYPE_VLAN, link=self.LINK) + mock_link_lookup.assert_called_once_with(ifname=self.LINK) + self.ip_link.assert_not_called() + + def test_add_other_type(self): + self.assertRaises(exception.NetworkInterfaceTypeNotDefined, + self.ip.add, self.DEVICE, 'type_not_defined') + + def test_add_exit_code(self): + self.ip_link.side_effect = ipexc.NetlinkError(self.ERROR_CODE, + msg="Error message") + + self.ip.add(self.DEVICE, self.TYPE_VETH, peer='peer', + check_exit_code=[self.ERROR_CODE]) + self.ip_link.assert_called_once_with( + 'add', ifname=self.DEVICE, kind=self.TYPE_VETH, peer='peer') + + self.assertRaises(ipexc.NetlinkError, self.ip.add, self.DEVICE, + self.TYPE_VLAN, peer='peer', + check_exit_code=[self.OTHER_ERROR_CODE]) + + def test_delete(self): + with mock.patch.object(iproute.IPRoute, 'link_lookup', + return_value=[1]) as mock_link_lookup: + self.ip.delete(self.DEVICE) + mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) + self.ip_link.assert_called_once_with('del', index=1) + + def test_delete_no_interface_found(self): + with mock.patch.object(iproute.IPRoute, 'link_lookup', + return_value=[]) as mock_link_lookup: + self.assertRaises(exception.NetworkInterfaceNotFound, + self.ip.delete, self.DEVICE) + mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) + + def test_delete_exit_code(self): + with mock.patch.object(iproute.IPRoute, 'link_lookup', + return_value=[1]) as mock_link_lookup: + self.ip_link.side_effect = ipexc.NetlinkError(self.ERROR_CODE, + msg="Error message") + + self.ip.delete(self.DEVICE, check_exit_code=[self.ERROR_CODE]) + mock_link_lookup.assert_called_once_with(ifname=self.DEVICE) + self.ip_link.assert_called_once_with('del', index=1) + + self.assertRaises(ipexc.NetlinkError, self.ip.delete, self.DEVICE, + check_exit_code=[self.OTHER_ERROR_CODE]) diff -Nru python-os-vif-1.15.1/os_vif/tests/unit/internal/ip/test_api.py python-os-vif-1.17.0/os_vif/tests/unit/internal/ip/test_api.py --- python-os-vif-1.15.1/os_vif/tests/unit/internal/ip/test_api.py 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/tests/unit/internal/ip/test_api.py 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,38 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +from six import moves + +from os_vif.internal.ip import api +from os_vif.tests.unit import base + + +class TestIpApi(base.TestCase): + + @staticmethod + def _reload_original_os_module(): + moves.reload_module(api) + + def test_get_impl_windows(self): + self.addCleanup(self._reload_original_os_module) + with mock.patch('os.name', 'nt'): + moves.reload_module(api) + from os_vif.internal.ip.windows import impl_netifaces + self.assertIsInstance(api.ip, impl_netifaces.Netifaces) + + def test_get_impl_linux(self): + self.addCleanup(self._reload_original_os_module) + with mock.patch('os.name', 'posix'): + moves.reload_module(api) + from os_vif.internal.ip.linux import impl_pyroute2 + self.assertIsInstance(api.ip, impl_pyroute2.PyRoute2) diff -Nru python-os-vif-1.15.1/os_vif/tests/unit/internal/ip/windows/test_impl_netifaces.py python-os-vif-1.17.0/os_vif/tests/unit/internal/ip/windows/test_impl_netifaces.py --- python-os-vif-1.15.1/os_vif/tests/unit/internal/ip/windows/test_impl_netifaces.py 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/os_vif/tests/unit/internal/ip/windows/test_impl_netifaces.py 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,45 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +import netifaces + +from os_vif.internal.ip.windows import impl_netifaces as ip_lib +from os_vif.tests.unit import base + + +class TestIPDevice(base.TestCase): + + def setUp(self): + super(TestIPDevice, self).setUp() + self.device_name = 'test_device' + self.mock_log = mock.patch.object(ip_lib, "LOG").start() + self.ip_lib = ip_lib.Netifaces() + + @mock.patch.object(netifaces, 'ifaddresses', return_value=True) + def test_exists(self, mock_ifaddresses): + self.assertTrue(self.ip_lib.exists(self.device_name)) + mock_ifaddresses.assert_called_once_with(self.device_name) + + @mock.patch.object(netifaces, 'ifaddresses', side_effect=ValueError()) + def test_exists_not_found(self, mock_ifaddresses): + self.assertFalse(self.ip_lib.exists(self.device_name)) + mock_ifaddresses.assert_called_once_with(self.device_name) + self.mock_log.warning.assert_called_once_with( + "The device does not exist on the system: %s", self.device_name) + + @mock.patch.object(netifaces, 'ifaddresses', side_effect=OSError()) + def test_exists_os_error_exception(self, mock_ifaddresses): + self.assertFalse(self.ip_lib.exists(self.device_name)) + mock_ifaddresses.assert_called_once_with(self.device_name) + self.mock_log.error.assert_called_once_with( + "Failed to get interface addresses: %s", self.device_name) diff -Nru python-os-vif-1.15.1/os_vif.egg-info/pbr.json python-os-vif-1.17.0/os_vif.egg-info/pbr.json --- python-os-vif-1.15.1/os_vif.egg-info/pbr.json 2019-02-28 14:31:03.000000000 +0000 +++ python-os-vif-1.17.0/os_vif.egg-info/pbr.json 2019-09-06 17:04:15.000000000 +0000 @@ -1 +1 @@ -{"git_version": "4d95199", "is_release": true} \ No newline at end of file +{"git_version": "3a08cc4", "is_release": true} \ No newline at end of file diff -Nru python-os-vif-1.15.1/os_vif.egg-info/PKG-INFO python-os-vif-1.17.0/os_vif.egg-info/PKG-INFO --- python-os-vif-1.15.1/os_vif.egg-info/PKG-INFO 2019-02-28 14:31:03.000000000 +0000 +++ python-os-vif-1.17.0/os_vif.egg-info/PKG-INFO 2019-09-06 17:04:15.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: os-vif -Version: 1.15.1 +Version: 1.17.0 Summary: A library for plugging and unplugging virtual interfaces in OpenStack. Home-page: https://docs.openstack.org/os-vif/latest/ Author: OpenStack @@ -31,7 +31,7 @@ * License: Apache License, Version 2.0 * Documentation: https://docs.openstack.org/os-vif/latest/ - * Source: https://git.openstack.org/cgit/openstack/os-vif + * Source: https://opendev.org/openstack/os-vif * Bugs: https://bugs.launchpad.net/os-vif * Release Notes: https://docs.openstack.org/releasenotes/os-vif @@ -46,4 +46,5 @@ Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 diff -Nru python-os-vif-1.15.1/os_vif.egg-info/SOURCES.txt python-os-vif-1.17.0/os_vif.egg-info/SOURCES.txt --- python-os-vif-1.15.1/os_vif.egg-info/SOURCES.txt 2019-02-28 14:31:03.000000000 +0000 +++ python-os-vif-1.17.0/os_vif.egg-info/SOURCES.txt 2019-09-06 17:04:15.000000000 +0000 @@ -41,14 +41,13 @@ os_vif.egg-info/requires.txt os_vif.egg-info/top_level.txt os_vif/internal/__init__.py -os_vif/internal/command/__init__.py -os_vif/internal/command/ip/__init__.py -os_vif/internal/command/ip/api.py -os_vif/internal/command/ip/ip_command.py -os_vif/internal/command/ip/linux/__init__.py -os_vif/internal/command/ip/linux/impl_pyroute2.py -os_vif/internal/command/ip/windows/__init__.py -os_vif/internal/command/ip/windows/impl_netifaces.py +os_vif/internal/ip/__init__.py +os_vif/internal/ip/api.py +os_vif/internal/ip/ip_command.py +os_vif/internal/ip/linux/__init__.py +os_vif/internal/ip/linux/impl_pyroute2.py +os_vif/internal/ip/windows/__init__.py +os_vif/internal/ip/windows/impl_netifaces.py os_vif/objects/__init__.py os_vif/objects/base.py os_vif/objects/fields.py @@ -76,13 +75,12 @@ os_vif/tests/unit/test_os_vif.py os_vif/tests/unit/test_vif.py os_vif/tests/unit/internal/__init__.py -os_vif/tests/unit/internal/command/__init__.py -os_vif/tests/unit/internal/command/ip/__init__.py -os_vif/tests/unit/internal/command/ip/test_api.py -os_vif/tests/unit/internal/command/ip/linux/__init__.py -os_vif/tests/unit/internal/command/ip/linux/test_impl_pyroute2.py -os_vif/tests/unit/internal/command/ip/windows/__init__.py -os_vif/tests/unit/internal/command/ip/windows/test_impl_netifaces.py +os_vif/tests/unit/internal/ip/__init__.py +os_vif/tests/unit/internal/ip/test_api.py +os_vif/tests/unit/internal/ip/linux/__init__.py +os_vif/tests/unit/internal/ip/linux/test_impl_pyroute2.py +os_vif/tests/unit/internal/ip/windows/__init__.py +os_vif/tests/unit/internal/ip/windows/test_impl_netifaces.py playbooks/openstack-tox-functional-ovs-with-sudo/Debian.yaml playbooks/openstack-tox-functional-ovs-with-sudo/Gentoo.yaml playbooks/openstack-tox-functional-ovs-with-sudo/RedHat.yaml @@ -99,6 +97,7 @@ releasenotes/notes/always-plug-vifs-for-ovs-1d033fc49a9c6c4e.yaml releasenotes/notes/brctl-removal-a5b0e69b865afa57.yaml releasenotes/notes/contextlib-and-nested-with-statements-2747a9ebb9a5bfd7.yaml +releasenotes/notes/do-not-force-mac-ageing-c6e8d750130c5740.yaml releasenotes/notes/ensure-ovs-bridge-a0c1b51f469c92d0.yaml releasenotes/notes/extend-vhostuser-object-fada14a1457d4e56.yaml releasenotes/notes/fix-ovs-plugin-describe-049750609559f1ba.yaml @@ -108,6 +107,7 @@ releasenotes/notes/initial-release-2c71d6bbf55f763b.yaml releasenotes/notes/port-profile-info-linux-bridge-4800f5a0b7328615.yaml releasenotes/notes/port-profile-info-ovs-63b46a3eafc11de2.yaml +releasenotes/notes/prevent-lb-reply-arp-6459133bfb056069.yaml releasenotes/notes/remove_iptools_implementation-2eb866573a680e61.yaml releasenotes/notes/revert-always-plug-port-for-ovs-hybrid-plug-false-dc015985cbc5443b.yaml releasenotes/notes/vhost-user-mtu-support-cbc7d02a6665fab1.yaml @@ -119,6 +119,7 @@ releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst +releasenotes/source/stein.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder @@ -140,7 +141,6 @@ vif_plug_ovs/__init__.py vif_plug_ovs/constants.py vif_plug_ovs/exception.py -vif_plug_ovs/i18n.py vif_plug_ovs/linux_net.py vif_plug_ovs/ovs.py vif_plug_ovs/privsep.py diff -Nru python-os-vif-1.15.1/PKG-INFO python-os-vif-1.17.0/PKG-INFO --- python-os-vif-1.15.1/PKG-INFO 2019-02-28 14:31:03.000000000 +0000 +++ python-os-vif-1.17.0/PKG-INFO 2019-09-06 17:04:15.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: os_vif -Version: 1.15.1 +Version: 1.17.0 Summary: A library for plugging and unplugging virtual interfaces in OpenStack. Home-page: https://docs.openstack.org/os-vif/latest/ Author: OpenStack @@ -31,7 +31,7 @@ * License: Apache License, Version 2.0 * Documentation: https://docs.openstack.org/os-vif/latest/ - * Source: https://git.openstack.org/cgit/openstack/os-vif + * Source: https://opendev.org/openstack/os-vif * Bugs: https://bugs.launchpad.net/os-vif * Release Notes: https://docs.openstack.org/releasenotes/os-vif @@ -46,4 +46,5 @@ Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 diff -Nru python-os-vif-1.15.1/playbooks/os-vif-ovs/run.yaml python-os-vif-1.17.0/playbooks/os-vif-ovs/run.yaml --- python-os-vif-1.15.1/playbooks/os-vif-ovs/run.yaml 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/playbooks/os-vif-ovs/run.yaml 2019-09-06 17:03:26.000000000 +0000 @@ -13,12 +13,12 @@ set -x cat > clonemap.yaml << EOF clonemap: - - name: openstack-infra/devstack-gate + - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ - git://git.openstack.org \ - openstack-infra/devstack-gate + https://opendev.org \ + openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' diff -Nru python-os-vif-1.15.1/README.rst python-os-vif-1.17.0/README.rst --- python-os-vif-1.15.1/README.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/README.rst 2019-09-06 17:03:26.000000000 +0000 @@ -23,6 +23,6 @@ * License: Apache License, Version 2.0 * Documentation: https://docs.openstack.org/os-vif/latest/ -* Source: https://git.openstack.org/cgit/openstack/os-vif +* Source: https://opendev.org/openstack/os-vif * Bugs: https://bugs.launchpad.net/os-vif * Release Notes: https://docs.openstack.org/releasenotes/os-vif diff -Nru python-os-vif-1.15.1/releasenotes/notes/do-not-force-mac-ageing-c6e8d750130c5740.yaml python-os-vif-1.17.0/releasenotes/notes/do-not-force-mac-ageing-c6e8d750130c5740.yaml --- python-os-vif-1.15.1/releasenotes/notes/do-not-force-mac-ageing-c6e8d750130c5740.yaml 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/releasenotes/notes/do-not-force-mac-ageing-c6e8d750130c5740.yaml 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,13 @@ +--- +fixes: + - | + As part of a `bug #1715317`_, MAC ageing was disabled for the intermediate + bridge created as part of the hybrid plug mechanism. During the removal + of ``brctl``, this behavior was inadvertently applied to all linux bridges + created by os-vif including those used in the linuxbridge driver. + As a result this can lead to packet flooding (see bug #1837252) when + instances are migrated. This behavior has been reverted so that the + default mac ageing is determined by the kernel and is not set when using + the os-vif linux bridge plugin. + + .. _bug #1715317: https://bugs.launchpad.net/os-vif/+bug/1837252 \ No newline at end of file diff -Nru python-os-vif-1.15.1/releasenotes/notes/prevent-lb-reply-arp-6459133bfb056069.yaml python-os-vif-1.17.0/releasenotes/notes/prevent-lb-reply-arp-6459133bfb056069.yaml --- python-os-vif-1.15.1/releasenotes/notes/prevent-lb-reply-arp-6459133bfb056069.yaml 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/releasenotes/notes/prevent-lb-reply-arp-6459133bfb056069.yaml 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,8 @@ +--- +security: + - | + Prevent Linux Bridge from replying to ARP messages. It should reply only if + the target IP address is a local address configured on the incoming + interface and it should always use the best local address. See `The ARP + flux problem `_ for + more information. diff -Nru python-os-vif-1.15.1/releasenotes/source/conf.py python-os-vif-1.17.0/releasenotes/source/conf.py --- python-os-vif-1.15.1/releasenotes/source/conf.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/releasenotes/source/conf.py 2019-09-06 17:03:26.000000000 +0000 @@ -30,7 +30,6 @@ master_doc = 'index' # General information about the project. -project = u'os-vif release Notes' copyright = u'2017, OpenStack Foundation' # Release notes do not need a version in the title, they span diff -Nru python-os-vif-1.15.1/releasenotes/source/index.rst python-os-vif-1.17.0/releasenotes/source/index.rst --- python-os-vif-1.15.1/releasenotes/source/index.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/releasenotes/source/index.rst 2019-09-06 17:03:26.000000000 +0000 @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + stein rocky queens pike diff -Nru python-os-vif-1.15.1/releasenotes/source/pike.rst python-os-vif-1.17.0/releasenotes/source/pike.rst --- python-os-vif-1.15.1/releasenotes/source/pike.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/releasenotes/source/pike.rst 2019-09-06 17:03:26.000000000 +0000 @@ -1,6 +1,6 @@ -=================================== - Pike Series Release Notes -=================================== +========================= +Pike Series Release Notes +========================= .. release-notes:: :branch: stable/pike diff -Nru python-os-vif-1.15.1/releasenotes/source/queens.rst python-os-vif-1.17.0/releasenotes/source/queens.rst --- python-os-vif-1.15.1/releasenotes/source/queens.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/releasenotes/source/queens.rst 2019-09-06 17:03:26.000000000 +0000 @@ -1,6 +1,6 @@ -=================================== - Queens Series Release Notes -=================================== +=========================== +Queens Series Release Notes +=========================== .. release-notes:: :branch: stable/queens diff -Nru python-os-vif-1.15.1/releasenotes/source/stein.rst python-os-vif-1.17.0/releasenotes/source/stein.rst --- python-os-vif-1.15.1/releasenotes/source/stein.rst 1970-01-01 00:00:00.000000000 +0000 +++ python-os-vif-1.17.0/releasenotes/source/stein.rst 2019-09-06 17:03:26.000000000 +0000 @@ -0,0 +1,6 @@ +========================== +Stein Series Release Notes +========================== + +.. release-notes:: + :branch: stable/stein diff -Nru python-os-vif-1.15.1/releasenotes/source/unreleased.rst python-os-vif-1.17.0/releasenotes/source/unreleased.rst --- python-os-vif-1.15.1/releasenotes/source/unreleased.rst 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/releasenotes/source/unreleased.rst 2019-09-06 17:03:26.000000000 +0000 @@ -1,5 +1,5 @@ -============================== - Current Series Release Notes -============================== +============================ +Current Series Release Notes +============================ .. release-notes:: diff -Nru python-os-vif-1.15.1/setup.cfg python-os-vif-1.17.0/setup.cfg --- python-os-vif-1.15.1/setup.cfg 2019-02-28 14:31:03.000000000 +0000 +++ python-os-vif-1.17.0/setup.cfg 2019-09-06 17:04:15.000000000 +0000 @@ -15,7 +15,8 @@ Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 - Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 [files] packages = diff -Nru python-os-vif-1.15.1/test-requirements.txt python-os-vif-1.17.0/test-requirements.txt --- python-os-vif-1.15.1/test-requirements.txt 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/test-requirements.txt 2019-09-06 17:03:26.000000000 +0000 @@ -10,4 +10,3 @@ stestr>=1.0.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD -testtools>=2.2.0 # MIT diff -Nru python-os-vif-1.15.1/tox.ini python-os-vif-1.17.0/tox.ini --- python-os-vif-1.15.1/tox.ini 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/tox.ini 2019-09-06 17:03:26.000000000 +0000 @@ -2,6 +2,7 @@ minversion = 3.1.1 envlist = py37,py27,pep8 skipsdist = True +ignore_basepython_conflict = True [testenv] basepython = python3 @@ -9,7 +10,7 @@ install_command = pip install -U {opts} {packages} setenv = VIRTUAL_ENV={envdir} - CONSTRAINTS_OPT=-c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} + CONSTRAINTS_OPT=-c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} deps = {env:CONSTRAINTS_OPT} -r{toxinidir}/requirements.txt @@ -74,7 +75,7 @@ # reason: no improvement in readability # # H904 wrap long lines in parentheses instead of a backslash -# reason: removed in hacking (https://review.openstack.org/#/c/101701/) +# reason: removed in hacking (https://review.opendev.org/#/c/101701/) # # H404 skipped on purpose per jay pipes discussion. # diff -Nru python-os-vif-1.15.1/vif_plug_linux_bridge/linux_net.py python-os-vif-1.17.0/vif_plug_linux_bridge/linux_net.py --- python-os-vif-1.15.1/vif_plug_linux_bridge/linux_net.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_linux_bridge/linux_net.py 2019-09-06 17:03:26.000000000 +0000 @@ -21,7 +21,7 @@ import os -from os_vif.internal.command import ip as ip_lib +from os_vif.internal.ip.api import ip as ip_lib from oslo_concurrency import lockutils from oslo_concurrency import processutils from oslo_log import log as logging @@ -112,6 +112,22 @@ f.write('1') +# TODO(ralonsoh): extract into common module +def _arp_filtering(bridge): + """Prevent the bridge from replying to ARP messages with machine local IPs + + 1. Reply only if the target IP address is local address configured on the + incoming interface. + 2. Always use the best local address. + """ + arp_params = [('/proc/sys/net/ipv4/conf/%s/arp_ignore' % bridge, '1'), + ('/proc/sys/net/ipv4/conf/%s/arp_announce' % bridge, '2')] + for parameter, value in arp_params: + if os.path.exists(parameter): + with open(parameter, 'w') as f: + f.write(value) + + def _update_bridge_routes(interface, bridge): """Updates routing table for a given bridge and interface. :param interface: string interface name @@ -176,6 +192,7 @@ LOG.debug('Starting Bridge %s', bridge) ip_lib.add(bridge, 'bridge') _disable_ipv6(bridge) + _arp_filtering(bridge) ip_lib.set(bridge, state='up') if interface and ip_lib.exists(interface): diff -Nru python-os-vif-1.15.1/vif_plug_linux_bridge/tests/unit/test_linux_net.py python-os-vif-1.17.0/vif_plug_linux_bridge/tests/unit/test_linux_net.py --- python-os-vif-1.15.1/vif_plug_linux_bridge/tests/unit/test_linux_net.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_linux_bridge/tests/unit/test_linux_net.py 2019-09-06 17:03:26.000000000 +0000 @@ -14,7 +14,7 @@ import testtools import fixtures -from os_vif.internal.command import ip as ip_lib +from os_vif.internal.ip.api import ip as ip_lib from oslo_concurrency import lockutils from oslo_concurrency import processutils from oslo_config import cfg @@ -92,12 +92,14 @@ @mock.patch.object(ip_lib, "add") @mock.patch.object(ip_lib, "set") + @mock.patch.object(linux_net, "_arp_filtering") @mock.patch.object(linux_net, "_set_device_mtu") @mock.patch.object(linux_net, "_disable_ipv6") @mock.patch.object(linux_net, "_update_bridge_routes") @mock.patch.object(ip_lib, "exists") def test_ensure_bridge_priv_mtu_not_called(self, mock_dev_exists, - mock_routes, mock_disable_ipv6, mock_set_mtu, mock_ip_set, mock_add): + mock_routes, mock_disable_ipv6, mock_set_mtu, mock_arp_filtering, + mock_ip_set, mock_add): """This test validates that mtus are updated only if an interface is added to the bridge """ @@ -109,12 +111,14 @@ @mock.patch.object(ip_lib, "add") @mock.patch.object(ip_lib, "set") + @mock.patch.object(linux_net, "_arp_filtering") @mock.patch.object(linux_net, "_set_device_mtu") @mock.patch.object(linux_net, "_disable_ipv6") @mock.patch.object(linux_net, "_update_bridge_routes") @mock.patch.object(ip_lib, "exists") def test_ensure_bridge_priv_mtu_order(self, mock_dev_exists, mock_routes, - mock_disable_ipv6, mock_set_mtu, mock_ip_set, mock_add): + mock_disable_ipv6, mock_set_mtu, mock_arp_filtering, mock_ip_set, + mock_add): """This test validates that when adding an interface to a bridge, the interface mtu is updated first followed by the bridge. This is required to work around diff -Nru python-os-vif-1.15.1/vif_plug_ovs/constants.py python-os-vif-1.17.0/vif_plug_ovs/constants.py --- python-os-vif-1.15.1/vif_plug_ovs/constants.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/constants.py 2019-09-06 17:03:26.000000000 +0000 @@ -22,5 +22,7 @@ PLATFORM_LINUX = 'linux2' PLATFORM_WIN32 = 'win32' +OVS_DPDK_INTERFACE_TYPE = 'dpdk' + # Neutron dead VLAN. DEAD_VLAN = 4095 diff -Nru python-os-vif-1.15.1/vif_plug_ovs/i18n.py python-os-vif-1.17.0/vif_plug_ovs/i18n.py --- python-os-vif-1.15.1/vif_plug_ovs/i18n.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/i18n.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -# Copyright 2014 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""oslo.i18n integration module. - -See https://docs.openstack.org/oslo.i18n/latest/user/usage.html. - -""" - -import oslo_i18n - -# Normally this would be the plugin specific name -# eg 'vif_plug_ovs', but since the OVS plugin is -# in-tree, this is a special case -DOMAIN = 'os_vif' - -_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) - -# The primary translation function using the well-known name "_" -_ = _translators.primary - - -def translate(value, user_locale): - return oslo_i18n.translate(value, user_locale) - - -def get_available_languages(): - return oslo_i18n.get_available_languages(DOMAIN) diff -Nru python-os-vif-1.15.1/vif_plug_ovs/linux_net.py python-os-vif-1.17.0/vif_plug_ovs/linux_net.py --- python-os-vif-1.15.1/vif_plug_ovs/linux_net.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/linux_net.py 2019-09-06 17:03:26.000000000 +0000 @@ -24,7 +24,7 @@ import re import sys -from os_vif.internal.command import ip as ip_lib +from os_vif.internal.ip.api import ip as ip_lib from oslo_concurrency import processutils from oslo_log import log as logging from oslo_utils import excutils @@ -47,6 +47,7 @@ PF_FUNC_RE = re.compile(r"\.(\d+)", 0) _SRIOV_TOTALVFS = "sriov_totalvfs" +NIC_NAME_LEN = 14 def _update_device_mtu(dev, mtu): @@ -109,11 +110,31 @@ f.write('1') +# TODO(ralonsoh): extract into common module +def _arp_filtering(bridge): + """Prevent the bridge from replying to ARP messages with machine local IPs + + 1. Reply only if the target IP address is local address configured on the + incoming interface. + 2. Always use the best local address. + """ + arp_params = [('/proc/sys/net/ipv4/conf/%s/arp_ignore' % bridge, '1'), + ('/proc/sys/net/ipv4/conf/%s/arp_announce' % bridge, '2')] + for parameter, value in arp_params: + if os.path.exists(parameter): + with open(parameter, 'w') as f: + f.write(value) + + @privsep.vif_plug.entrypoint def ensure_bridge(bridge): if not ip_lib.exists(bridge): - ip_lib.add(bridge, 'bridge') + # NOTE(sean-k-mooney): we set mac ageing to 0 to disable mac ageing + # on the hybrid plug bridge to avoid packet loss during live + # migration. This avoids bug #1715317 and related bug #1414559 + ip_lib.add(bridge, 'bridge', ageing=0) _disable_ipv6(bridge) + _arp_filtering(bridge) # we bring up the bridge to allow it to switch packets set_interface_state(bridge, 'up') @@ -353,3 +374,18 @@ if vf_num is None: raise exception.PciDeviceNotFoundById(id=pci_addr) return vf_num + + +def get_dpdk_representor_port_name(port_id): + devname = "vfr" + port_id + return devname[:NIC_NAME_LEN] + + +def get_pf_pci_from_vf(vf_pci): + """Get physical function PCI address of a VF + + :param vf_pci: the PCI address of the VF + :return: the PCI address of the PF + """ + physfn_path = os.readlink("/sys/bus/pci/devices/%s/physfn" % vf_pci) + return os.path.basename(physfn_path) diff -Nru python-os-vif-1.15.1/vif_plug_ovs/ovsdb/ovsdb_lib.py python-os-vif-1.17.0/vif_plug_ovs/ovsdb/ovsdb_lib.py --- python-os-vif-1.15.1/vif_plug_ovs/ovsdb/ovsdb_lib.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/ovsdb/ovsdb_lib.py 2019-09-06 17:03:26.000000000 +0000 @@ -62,7 +62,27 @@ def create_ovs_vif_port(self, bridge, dev, iface_id, mac, instance_id, mtu=None, interface_type=None, - vhost_server_path=None, tag=None): + vhost_server_path=None, tag=None, + pf_pci=None, vf_num=None): + """Create OVS port + + :param bridge: bridge name to create the port on. + :param dev: port name. + :param iface_id: port ID. + :param mac: port MAC. + :param instance_id: VM ID on which the port is attached to. + :param mtu: port MTU. + :param interface_type: OVS interface type. + :param vhost_server_path: path to socket file of vhost server. + :param tag: OVS interface tag. + :param pf_pci: PCI address of PF for dpdk representor port. + :param vf_num: VF number of PF for dpdk representor port. + + .. note:: create DPDK representor port by setting all three values: + `interface_type`, `pf_pci` and `vf_num`. if interface type is + not `OVS_DPDK_INTERFACE_TYPE` then `pf_pci` and `vf_num` values + are ignored. + """ external_ids = {'iface-id': iface_id, 'iface-status': 'active', 'attached-mac': mac, @@ -75,7 +95,12 @@ {'vhost-server-path': vhost_server_path})) if tag: col_values.append(('tag', tag)) - + if (interface_type == constants.OVS_DPDK_INTERFACE_TYPE and + pf_pci and vf_num): + devargs_string = "{PF_PCI},representor=[{VF_NUM}]".format( + PF_PCI=pf_pci, VF_NUM=vf_num) + col_values.append(('options', + {'dpdk-devargs': devargs_string})) with self.ovsdb.transaction() as txn: txn.add(self.ovsdb.add_port(bridge, dev)) txn.add(self.ovsdb.db_set('Interface', dev, *col_values)) diff -Nru python-os-vif-1.15.1/vif_plug_ovs/ovs.py python-os-vif-1.17.0/vif_plug_ovs/ovs.py --- python-os-vif-1.15.1/vif_plug_ovs/ovs.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/ovs.py 2019-09-06 17:03:26.000000000 +0000 @@ -19,7 +19,7 @@ import sys -from os_vif.internal.command import ip as ip_lib +from os_vif.internal.ip.api import ip as ip_lib from os_vif import objects from os_vif import plugin from oslo_config import cfg @@ -252,16 +252,28 @@ vif.port_profile.create_port): self._create_vif_port(vif, vif.vif_name, instance_info) - def _plug_vf_passthrough(self, vif, instance_info): - self.ovsdb.ensure_ovs_bridge( - vif.network.bridge, constants.OVS_DATAPATH_SYSTEM) + def _plug_vf(self, vif, instance_info): + datapath = self._get_vif_datapath_type(vif) + self.ovsdb.ensure_ovs_bridge(vif.network.bridge, datapath) pci_slot = vif.dev_address - pf_ifname = linux_net.get_ifname_by_pci_address( - pci_slot, pf_interface=True, switchdev=True) vf_num = linux_net.get_vf_num_by_pci_address(pci_slot) - representor = linux_net.get_representor_port(pf_ifname, vf_num) - linux_net.set_interface_state(representor, 'up') - self._create_vif_port(vif, representor, instance_info) + args = [] + kwargs = {} + if datapath == constants.OVS_DATAPATH_SYSTEM: + pf_ifname = linux_net.get_ifname_by_pci_address( + pci_slot, pf_interface=True, switchdev=True) + representor = linux_net.get_representor_port(pf_ifname, vf_num) + linux_net.set_interface_state(representor, 'up') + args = [vif, representor, instance_info] + else: + representor = linux_net.get_dpdk_representor_port_name( + vif.id) + pf_pci = linux_net.get_pf_pci_from_vf(pci_slot) + args = [vif, representor, instance_info] + kwargs = {'interface_type': constants.OVS_DPDK_INTERFACE_TYPE, + 'pf_pci': pf_pci, + 'vf_num': vf_num} + self._create_vif_port(*args, **kwargs) def plug(self, vif, instance_info): if not hasattr(vif, "port_profile"): @@ -284,7 +296,7 @@ elif isinstance(vif, objects.vif.VIFVHostUser): self._plug_vhostuser(vif, instance_info) elif isinstance(vif, objects.vif.VIFHostDevice): - self._plug_vf_passthrough(vif, instance_info) + self._plug_vf(vif, instance_info) def _unplug_vhostuser(self, vif, instance_info): self.ovsdb.delete_ovs_vif_port(vif.network.bridge, @@ -317,19 +329,26 @@ # so this is not removed. self.ovsdb.delete_ovs_vif_port(vif.network.bridge, vif.vif_name) - def _unplug_vf_passthrough(self, vif, instance_info): + def _unplug_vf(self, vif): """Remove port from OVS.""" - pci_slot = vif.dev_address - pf_ifname = linux_net.get_ifname_by_pci_address(pci_slot, - pf_interface=True, switchdev=True) - vf_num = linux_net.get_vf_num_by_pci_address(pci_slot) - representor = linux_net.get_representor_port(pf_ifname, vf_num) + datapath = self._get_vif_datapath_type(vif) + if datapath == constants.OVS_DATAPATH_SYSTEM: + pci_slot = vif.dev_address + pf_ifname = linux_net.get_ifname_by_pci_address( + pci_slot, pf_interface=True, switchdev=True) + vf_num = linux_net.get_vf_num_by_pci_address(pci_slot) + representor = linux_net.get_representor_port(pf_ifname, vf_num) + else: + representor = linux_net.get_dpdk_representor_port_name( + vif.id) + # The representor interface can't be deleted because it bind the # SR-IOV VF, therefore we just need to remove it from the ovs bridge # and set the status to down self.ovsdb.delete_ovs_vif_port( vif.network.bridge, representor, delete_netdev=False) - linux_net.set_interface_state(representor, 'down') + if datapath == constants.OVS_DATAPATH_SYSTEM: + linux_net.set_interface_state(representor, 'down') def unplug(self, vif, instance_info): if not hasattr(vif, "port_profile"): @@ -352,4 +371,4 @@ elif isinstance(vif, objects.vif.VIFVHostUser): self._unplug_vhostuser(vif, instance_info) elif isinstance(vif, objects.vif.VIFHostDevice): - self._unplug_vf_passthrough(vif, instance_info) + self._unplug_vf(vif) diff -Nru python-os-vif-1.15.1/vif_plug_ovs/tests/functional/base.py python-os-vif-1.17.0/vif_plug_ovs/tests/functional/base.py --- python-os-vif-1.15.1/vif_plug_ovs/tests/functional/base.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/tests/functional/base.py 2019-09-06 17:03:26.000000000 +0000 @@ -1,6 +1,3 @@ -# Derived from: neutron/tests/functional/base.py -# neutron/tests/base.py -# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at @@ -13,128 +10,14 @@ # License for the specific language governing permissions and limitations # under the License. -import abc -import functools -import inspect -import os -import six -import sys - -import eventlet -from os_vif import version as osvif_version -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import fileutils -from oslotest import base - - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -def _get_test_log_path(): - return os.environ.get('OS_LOG_PATH', '/tmp') - - -# This is the directory from which infra fetches log files for functional tests -DEFAULT_LOG_DIR = os.path.join(_get_test_log_path(), 'osvif-functional-logs') - - -def wait_until_true(predicate, timeout=15, sleep=1): - """Wait until callable predicate is evaluated as True - - :param predicate: Callable deciding whether waiting should continue. - Best practice is to instantiate predicate with - ``functools.partial()``. - :param timeout: Timeout in seconds how long should function wait. - :param sleep: Polling interval for results in seconds. - :return: True if the predicate is evaluated as True within the timeout, - False in case of timeout evaluating the predicate. - """ - try: - with eventlet.Timeout(timeout): - while not predicate(): - eventlet.sleep(sleep) - except eventlet.Timeout: - return False - return True - - -class _CatchTimeoutMetaclass(abc.ABCMeta): - def __init__(cls, name, bases, dct): - super(_CatchTimeoutMetaclass, cls).__init__(name, bases, dct) - for name, method in inspect.getmembers( - # NOTE(ihrachys): we should use isroutine because it will catch - # both unbound methods (python2) and functions (python3) - cls, predicate=inspect.isroutine): - if name.startswith('test_'): - setattr(cls, name, cls._catch_timeout(method)) - - @staticmethod - def _catch_timeout(f): - @functools.wraps(f) - def func(self, *args, **kwargs): - try: - return f(self, *args, **kwargs) - except eventlet.Timeout as e: - self.fail('Execution of this test timed out: %s' % e) - return func - - -def setup_logging(): - """Sets up the logging options for a log with supplied name.""" - product_name = "vif_plug_ovs" - logging.setup(cfg.CONF, product_name) - LOG.info("Logging enabled!") - LOG.info("%(prog)s version %(version)s", - {'prog': sys.argv[0], 'version': osvif_version.__version__}) - LOG.debug("command line: %s", " ".join(sys.argv)) - - -def sanitize_log_path(path): - # Sanitize the string so that its log path is shell friendly - replace_map = {' ': '-', '(': '_', ')': '_'} - for s, r in replace_map.items(): - path = path.replace(s, r) - return path - - -@six.add_metaclass(_CatchTimeoutMetaclass) -class BaseFunctionalTestCase(base.BaseTestCase): - """Base class for functional tests. - - Test worker cannot survive eventlet's Timeout exception, which effectively - kills the whole worker, with all test cases scheduled to it. This metaclass - makes all test cases convert Timeout exceptions into unittest friendly - failure mode (self.fail). - """ - def setUp(self): - super(BaseFunctionalTestCase, self).setUp() - logging.register_options(CONF) - setup_logging() - fileutils.ensure_tree(DEFAULT_LOG_DIR, mode=0o755) - log_file = sanitize_log_path( - os.path.join(DEFAULT_LOG_DIR, "%s.txt" % self.id())) - self.flags(log_file=log_file) - privsep_helper = os.path.join( - os.getenv('VIRTUAL_ENV', os.path.dirname(sys.executable)[:-4]), - 'bin', 'privsep-helper') - self.flags( - helper_command=' '.join(['sudo', '-E', privsep_helper]), - group='vif_plug_ovs_privileged') - - def flags(self, **kw): - """Override some configuration values. - - The keyword arguments are the names of configuration options to - override and their values. - - If a group argument is supplied, the overrides are applied to - the specified configuration option group. - - All overrides are automatically cleared at the end of the current - test by the fixtures cleanup process. - """ - group = kw.pop('group', None) - for k, v in kw.items(): - CONF.set_override(k, v, group) +from os_vif.tests.functional import base as os_vif_base + + +wait_until_true = os_vif_base.wait_until_true + + +class VifPlugOvsBaseFunctionalTestCase(os_vif_base.BaseFunctionalTestCase): + """Base class for vif_plug_ovs functional tests.""" + + COMPONENT_NAME = 'vif_plug_ovs' + PRIVILEGED_GROUP = 'vif_plug_ovs_privileged' diff -Nru python-os-vif-1.15.1/vif_plug_ovs/tests/functional/ovsdb/test_ovsdb_lib.py python-os-vif-1.17.0/vif_plug_ovs/tests/functional/ovsdb/test_ovsdb_lib.py --- python-os-vif-1.15.1/vif_plug_ovs/tests/functional/ovsdb/test_ovsdb_lib.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/tests/functional/ovsdb/test_ovsdb_lib.py 2019-09-06 17:03:26.000000000 +0000 @@ -35,7 +35,8 @@ return processutils.execute(*full_args)[0].rstrip() -class TestOVSDBLib(testscenarios.WithScenarios, base.BaseFunctionalTestCase): +class TestOVSDBLib(testscenarios.WithScenarios, + base.VifPlugOvsBaseFunctionalTestCase): scenarios = [ ('native', {'interface': 'native'}), diff -Nru python-os-vif-1.15.1/vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py python-os-vif-1.17.0/vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py --- python-os-vif-1.15.1/vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py 2019-09-06 17:03:26.000000000 +0000 @@ -115,6 +115,38 @@ mock_update_device_mtu.assert_has_calls( [mock.call(device, mtu, interface_type=interface_type)]) + def test_create_ovs_vif_port_type_dpdk(self): + iface_id = 'iface_id' + mac = 'ca:fe:ca:fe:ca:fe' + instance_id = uuidutils.generate_uuid() + interface_type = constants.OVS_DPDK_INTERFACE_TYPE + device = 'device' + bridge = 'bridge' + mtu = 1500 + pf_pci = '0000:02:00.1' + vf_num = '0' + external_ids = {'iface-id': iface_id, + 'iface-status': 'active', + 'attached-mac': mac, + 'vm-uuid': instance_id} + values = [('external_ids', external_ids), + ('type', interface_type), + ('options', {'dpdk-devargs': + '0000:02:00.1,representor=[0]'})] + with mock.patch.object(self.br, 'update_device_mtu', + return_value=True) as mock_update_device_mtu, \ + mock.patch.object(self.br, '_ovs_supports_mtu_requests', + return_value=True): + self.br.create_ovs_vif_port(bridge, device, iface_id, mac, + instance_id, mtu=mtu, + interface_type=interface_type, + pf_pci=pf_pci, vf_num=vf_num) + self.mock_add_port.assert_has_calls([mock.call(bridge, device)]) + self.mock_db_set.assert_has_calls( + [mock.call('Interface', device, *values)]) + mock_update_device_mtu.assert_has_calls( + [mock.call(device, mtu, interface_type=interface_type)]) + def test_update_ovs_vif_port(self): with mock.patch.object(self.br, 'update_device_mtu') as \ mock_update_device_mtu: diff -Nru python-os-vif-1.15.1/vif_plug_ovs/tests/unit/test_linux_net.py python-os-vif-1.17.0/vif_plug_ovs/tests/unit/test_linux_net.py --- python-os-vif-1.15.1/vif_plug_ovs/tests/unit/test_linux_net.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/tests/unit/test_linux_net.py 2019-09-06 17:03:26.000000000 +0000 @@ -15,7 +15,8 @@ import os.path import testtools -from os_vif.internal.command import ip as ip_lib +from os_vif.internal.ip.api import ip as ip_lib +from six.moves import builtins from vif_plug_ovs import exception from vif_plug_ovs import linux_net @@ -29,31 +30,37 @@ privsep.vif_plug.set_client_mode(False) + @mock.patch.object(linux_net, "_arp_filtering") @mock.patch.object(linux_net, "set_interface_state") @mock.patch.object(linux_net, "_disable_ipv6") @mock.patch.object(ip_lib, "add") @mock.patch.object(ip_lib, "exists", return_value=False) def test_ensure_bridge(self, mock_dev_exists, mock_add, - mock_disable_ipv6, mock_set_state): + mock_disable_ipv6, mock_set_state, + mock_arp_filtering): linux_net.ensure_bridge("br0") mock_dev_exists.assert_called_once_with("br0") - mock_add.assert_called_once_with("br0", "bridge") + mock_add.assert_called_once_with("br0", "bridge", ageing=0) mock_disable_ipv6.assert_called_once_with("br0") mock_set_state.assert_called_once_with("br0", "up") + mock_arp_filtering.assert_called_once_with("br0") + @mock.patch.object(linux_net, "_arp_filtering") @mock.patch.object(linux_net, "set_interface_state") @mock.patch.object(linux_net, "_disable_ipv6") @mock.patch.object(ip_lib, "add") @mock.patch.object(ip_lib, "exists", return_value=True) def test_ensure_bridge_exists(self, mock_dev_exists, mock_add, - mock_disable_ipv6, mock_set_state): + mock_disable_ipv6, mock_set_state, + mock_arp_filtering): linux_net.ensure_bridge("br0") mock_dev_exists.assert_called_once_with("br0") mock_add.assert_not_called() mock_disable_ipv6.assert_called_once_with("br0") mock_set_state.assert_called_once_with("br0", "up") + mock_arp_filtering.assert_called_once_with("br0") @mock.patch('six.moves.builtins.open') @mock.patch("os.path.exists") @@ -72,6 +79,19 @@ mock_exists.assert_called_once_with(exists_path) mock_open.assert_called_once_with(exists_path, 'w') + @mock.patch.object(os.path, 'exists', return_value=True) + @mock.patch.object(builtins, 'open') + def test__arp_filtering(self, mock_open, *args): + mock_open.side_effect = mock.mock_open() + linux_net._arp_filtering("br0") + + mock_open.assert_has_calls([ + mock.call('/proc/sys/net/ipv4/conf/br0/arp_ignore', 'w'), + mock.call('/proc/sys/net/ipv4/conf/br0/arp_announce', 'w')]) + mock_open.side_effect.return_value.write.assert_has_calls([ + mock.call('1'), + mock.call('2')]) + @mock.patch.object(ip_lib, "delete") @mock.patch.object(ip_lib, "exists", return_value=False) def test_delete_bridge_none(self, mock_dev_exists, mock_delete): diff -Nru python-os-vif-1.15.1/vif_plug_ovs/tests/unit/test_plugin.py python-os-vif-1.17.0/vif_plug_ovs/tests/unit/test_plugin.py --- python-os-vif-1.15.1/vif_plug_ovs/tests/unit/test_plugin.py 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/vif_plug_ovs/tests/unit/test_plugin.py 2019-09-06 17:03:26.000000000 +0000 @@ -13,7 +13,7 @@ import mock import testtools -from os_vif.internal.command import ip as ip_lib +from os_vif.internal.ip.api import ip as ip_lib from os_vif import objects from os_vif.objects import fields @@ -61,6 +61,10 @@ interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa', datapath_type='netdev') + self.profile_ovs_system = objects.vif.VIFPortProfileOpenVSwitch( + interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa', + datapath_type='system') + self.profile_ovs_smart_nic = objects.vif.VIFPortProfileOpenVSwitch( interface_id='e65867e0-9340-4a7f-a256-09af6eb7a3aa', create_port=True) @@ -114,6 +118,14 @@ dev_type=fields.VIFHostDeviceDevType.ETHERNET, dev_address='0002:24:12.3', bridge_name='br-int', + port_profile=self.profile_ovs_system) + + self.vif_ovs_vf_dpdk = objects.vif.VIFHostDevice( + id='b679325f-ca89-4ee0-a8be-6db1409b69ea', + address='ca:fe:de:ad:be:ef', + network=self.network_ovs, + dev_type=fields.VIFHostDeviceDevType.ETHERNET, + dev_address='0002:24:12.3', port_profile=self.profile_ovs) self.instance = objects.instance_info.InstanceInfo( @@ -470,3 +482,64 @@ plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME) plugin.unplug(self.vif_ovs_smart_nic, self.instance) delete_port.assert_called_once() + + @mock.patch.object(linux_net, 'get_dpdk_representor_port_name') + @mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge') + @mock.patch.object(linux_net, 'get_vf_num_by_pci_address') + @mock.patch.object(linux_net, 'get_pf_pci_from_vf') + @mock.patch.object(ovs.OvsPlugin, '_create_vif_port') + def test_plug_ovs_vf_dpdk(self, _create_vif_port, + get_pf_pci_from_vf, + get_vf_num_by_pci_address, + ensure_ovs_bridge, + get_dpdk_representor_port_name): + + pf_pci = self.vif_ovs_vf_dpdk.dev_address + devname = 'vfrb679325f-ca' + get_vf_num_by_pci_address.return_value = '2' + get_pf_pci_from_vf.return_value = pf_pci + get_dpdk_representor_port_name.return_value = devname + calls = { + 'ensure_ovs_bridge': [mock.call('br0', + constants.OVS_DATAPATH_NETDEV)], + 'get_vf_num_by_pci_address': [mock.call('0002:24:12.3')], + 'get_pf_pci_from_vf': [mock.call(pf_pci)], + 'get_dpdk_representor_port_name': [mock.call( + self.vif_ovs_vf_dpdk.id)], + '_create_vif_port': [mock.call( + self.vif_ovs_vf_dpdk, + devname, + self.instance, + interface_type='dpdk', + pf_pci=pf_pci, + vf_num='2')]} + + plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME) + plugin.plug(self.vif_ovs_vf_dpdk, self.instance) + ensure_ovs_bridge.assert_has_calls( + calls['ensure_ovs_bridge']) + get_vf_num_by_pci_address.assert_has_calls( + calls['get_vf_num_by_pci_address']) + get_pf_pci_from_vf.assert_has_calls( + calls['get_pf_pci_from_vf']) + get_dpdk_representor_port_name.assert_has_calls( + calls['get_dpdk_representor_port_name']) + _create_vif_port.assert_has_calls( + calls['_create_vif_port']) + + @mock.patch.object(linux_net, 'get_dpdk_representor_port_name') + @mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_vif_port') + def test_unplug_ovs_vf_dpdk(self, delete_ovs_vif_port, + get_dpdk_representor_port_name): + devname = 'vfrb679325f-ca' + get_dpdk_representor_port_name.return_value = devname + calls = { + 'get_dpdk_representor_port_name': [mock.call( + self.vif_ovs_vf_dpdk.id)], + 'delete_ovs_vif_port': [mock.call('br0', devname, + delete_netdev=False)]} + plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME) + plugin.unplug(self.vif_ovs_vf_dpdk, self.instance) + get_dpdk_representor_port_name.assert_has_calls( + calls['get_dpdk_representor_port_name']) + delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port']) diff -Nru python-os-vif-1.15.1/.zuul.yaml python-os-vif-1.17.0/.zuul.yaml --- python-os-vif-1.15.1/.zuul.yaml 2019-02-28 14:27:57.000000000 +0000 +++ python-os-vif-1.17.0/.zuul.yaml 2019-09-06 17:03:26.000000000 +0000 @@ -7,7 +7,7 @@ post-run: playbooks/os-vif-ovs/post.yaml timeout: 7800 required-projects: - - openstack-infra/devstack-gate + - openstack/devstack-gate - openstack/nova - openstack/os-vif - openstack/neutron @@ -17,7 +17,7 @@ name: openstack-tox-functional-ovs-with-sudo parent: openstack-tox-functional-with-sudo required-projects: - - git.openstack.org/openstack-dev/devstack + - opendev.org/openstack/devstack pre-run: playbooks/openstack-tox-functional-ovs-with-sudo/pre.yaml timeout: 600 @@ -29,7 +29,7 @@ This is derived from tempest-full-py3 and adapted for use in os-vif required-projects: - - openstack-infra/devstack-gate + - openstack/devstack-gate - openstack/nova - openstack/os-vif - openstack/neutron @@ -37,7 +37,7 @@ vars: tempest_concurrency: 4 devstack_plugins: - neutron: git://git.openstack.org/openstack/neutron.git + neutron: https://opendev.org/openstack/neutron.git devstack_localrc: # TODO(sean-k-mooney) move all tempest jobs to py3 by default. # USE_PYTHON3: true @@ -81,8 +81,8 @@ ml2_type_vlan: network_vlan_ranges: foo:1:10 agent: - enable_distributed_routing: True - l2_population: True + enable_distributed_routing: true + l2_population: true tunnel_types: vxlan,gre securitygroup: firewall_driver: iptables_hybrid @@ -119,13 +119,12 @@ - check-requirements - openstack-lower-constraints-jobs - openstack-python-jobs - - openstack-python36-jobs - - openstack-python37-jobs + - openstack-python3-train-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 check: jobs: - - kuryr-kubernetes-tempest-daemon-octavia: + - kuryr-kubernetes-tempest: voting: false - openstack-tox-functional-ovs-with-sudo - os-vif-ovs