1#    Licensed under the Apache License, Version 2.0 (the "License"); you may
2#    not use this file except in compliance with the License. You may obtain
3#    a copy of the License at
4#
5#         http://www.apache.org/licenses/LICENSE-2.0
6#
7#    Unless required by applicable law or agreed to in writing, software
8#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10#    License for the specific language governing permissions and limitations
11#    under the License.
12
13from debtcollector import removals
14
15from oslo_utils import versionutils
16from oslo_versionedobjects import base
17from oslo_versionedobjects import fields
18
19from os_vif.objects import base as osv_base
20from os_vif.objects import fields as osv_fields
21
22
23@base.VersionedObjectRegistry.register
24class VIFBase(osv_base.VersionedObject, base.ComparableVersionedObject):
25    """Represents a virtual network interface.
26
27    The base VIF defines fields that are common to all types of VIF and
28    provides an association to the network the VIF is plugged into. It should
29    not be instantiated itself - use a subclass instead.
30    """
31
32    # Version 1.0: Initial release
33    VERSION = '1.0'
34
35    fields = {
36        #: Unique identifier of the VIF port.
37        'id': fields.UUIDField(),
38
39        #: The guest MAC address.
40        'address': fields.MACAddressField(nullable=True),
41
42        #: The network to which the VIF is connected.
43        'network': fields.ObjectField('Network', nullable=True),
44
45        #: Name of the registered os_vif plugin.
46        'plugin': fields.StringField(),
47
48        #: Whether the VIF is initially online.
49        'active': fields.BooleanField(default=True),
50
51        #: Whether the host VIF should be preserved on unplug.
52        'preserve_on_delete': fields.BooleanField(default=False),
53
54        #: Whether the network service has provided traffic filtering.
55        'has_traffic_filtering': fields.BooleanField(default=False),
56
57        #: The virtual port profile metadata.
58        'port_profile': fields.ObjectField('VIFPortProfileBase',
59                                           subclasses=True)
60    }
61
62
63@base.VersionedObjectRegistry.register
64class VIFGeneric(VIFBase):
65    """A generic-style VIF.
66
67    Generic-style VIFs are unbound, floating TUN/TAP devices that should be
68    setup by the plugin, not the hypervisor. The way the TAP device is
69    connected to the host network stack is explicitly left undefined.
70
71    For libvirt drivers, this maps to type="ethernet" which just implies a bare
72    TAP device with all setup delegated to the plugin.
73    """
74
75    # Version 1.0: Initial release
76    VERSION = '1.0'
77
78    fields = {
79        #: Name of the device to create.
80        'vif_name': fields.StringField()
81    }
82
83
84@base.VersionedObjectRegistry.register
85class VIFBridge(VIFBase):
86    """A bridge-style VIF.
87
88    Bridge-style VIFs are bound to a Linux host bridge by the hypervisor. This
89    provides Ethernet layer bridging, typically to the LAN. Other devices may
90    be bound to the same L2 virtual bridge.
91
92    For libvirt drivers, this maps to type='bridge'.
93    """
94
95    # Version 1.0: Initial release
96    VERSION = '1.0'
97
98    fields = {
99        #: Name of the virtual device to create.
100        'vif_name': fields.StringField(),
101
102        #: Name of the physical device to connect to (e.g. ``br0``).
103        'bridge_name': fields.StringField(),
104    }
105
106
107@base.VersionedObjectRegistry.register
108class VIFOpenVSwitch(VIFBase):
109    """A bridge-style VIF specifically for use with OVS.
110
111    Open vSwitch VIFs are bound directly (or indirectly) to an Open vSwitch
112    bridge by the hypervisor. Other devices may be bound to the same virtual
113    bridge.
114
115    For libvirt drivers, this also maps to type='bridge'.
116    """
117
118    # Version 1.0: Initial release
119    VERSION = '1.0'
120
121    fields = {
122        #: Name of the virtual device to create.
123        'vif_name': fields.StringField(),
124
125        #: Name of the physical device to connect to (e.g. ``br0``).
126        'bridge_name': fields.StringField(),
127    }
128
129
130@base.VersionedObjectRegistry.register
131class VIFDirect(VIFBase):
132    """A direct-style VIF.
133
134    Despite the confusing name, direct-style VIFs utilize macvtap which is a
135    device driver that inserts a software layer between a guest and an SR-IOV
136    Virtual Function (VF). Contrast this with
137    :class:`~os_vif.objects.vif.VIFHostDevice`, which allows the guest to
138    directly connect to the VF.
139
140    The connection to the device may operate in one of a number of different
141    modes, :term:`VEPA` (either :term:`802.1Qbg` or :term:`802.1Qbh`),
142    passthrough (exclusive assignment of the host NIC) or bridge (ethernet
143    layer bridging of traffic). The passthrough mode would be used when there
144    is a network device which needs to have a MAC address or VLAN
145    configuration. For passthrough of network devices without MAC/VLAN
146    configuration, :class:`~os_vif.objects.vif.VIFHostDevice` should be used
147    instead.
148
149    For libvirt drivers, this maps to type='direct'
150    """
151
152    # Version 1.0: Initial release
153    VERSION = '1.0'
154
155    fields = {
156        #: Name of the device to create.
157        'vif_name': fields.StringField(),
158
159        #: The PCI address of the host device.
160        'dev_address': fields.PCIAddressField(),
161
162        #: Port connection mode.
163        'mode': osv_fields.VIFDirectModeField(),
164
165        #: The VLAN device name to use.
166        'vlan_name': fields.StringField(),
167    }
168
169
170@base.VersionedObjectRegistry.register
171class VIFVHostUser(VIFBase):
172    """A vhostuser-style VIF.
173
174    vhostuser-style VIFs utilize a :term:`userspace vhost <vhost-user>`
175    backend, which allows traffic to traverse between the guest and a host
176    userspace application (commonly a virtual switch), bypassing the kernel
177    network stack. Contrast this with :class:`~os_vif.objects.vif.VIFBridge`,
178    where all packets must be handled by the hypervisor.
179
180    For libvirt drivers, this maps to type='vhostuser'
181    """
182
183    # Version 1.0: Initial release
184    # Version 1.1: Added 'vif_name'
185    VERSION = '1.1'
186
187    fields = {
188        #: Name of the vhostuser port to create.
189        'vif_name': fields.StringField(),
190
191        #: UNIX socket path.
192        'path': fields.StringField(),
193
194        #: UNIX socket access permissions.
195        'mode': osv_fields.VIFVHostUserModeField(),
196    }
197
198    def obj_make_compatible(self, primitive, target_version):
199        target_version = versionutils.convert_version_to_tuple(target_version)
200        if target_version < (1, 1) and 'vif_name' in primitive:
201            del primitive['vif_name']
202        super(VIFVHostUser, self).obj_make_compatible(primitive, '1.0')
203
204
205@base.VersionedObjectRegistry.register
206class VIFHostDevice(VIFBase):
207    """A hostdev-style VIF.
208
209    Hostdev-style VIFs provide a guest with direct access to an :term:`SR-IOV`
210    :term:`Virtual Function` (VF) or an entire :term:`Physical Function` (PF).
211    Contrast this with :class:`~ovs_vif.objects.vif.VIFDirect`, which includes
212    a software layer between the interface and the guest.
213
214    For libvirt drivers, this maps to type='hostdev'
215    """
216
217    # Version 1.0: Initial release
218    VERSION = '1.0'
219
220    fields = {
221        #: The type of the host device.
222        #:
223        #: Valid values are ``ethernet`` and ``generic``.
224        #:
225        #: - ``ethernet`` is ``<interface type='hostdev'>``
226        #: - ``generic`` is ``<hostdev mode='subsystem' type='pci'>``
227        'dev_type': osv_fields.VIFHostDeviceDevTypeField(),
228
229        #: The PCI address of the host device.
230        'dev_address': fields.PCIAddressField(),
231    }
232
233
234@base.VersionedObjectRegistry.register
235class VIFNestedDPDK(VIFBase):
236    """A nested DPDK-style VIF.
237
238    Nested DPDK-style VIFs are used by Kuryr-Kubernetes to provide accelerated
239    DPDK datapath for nested Kubernetes pods running inside the VM. The port
240    is first attached to the virtual machine, bound to the userspace driver
241    (e.g. ``uio_pci_generic``, ``igb_uio`` or ``vfio-pci``) and then consumed
242    by Kubernetes pod via the kuryr-kubernetes CNI plugin.
243
244    This does not apply to libvirt drivers.
245    """
246
247    # Version 1.0: Initial release
248    VERSION = '1.0'
249
250    fields = {
251        #: PCI address of the device.
252        'pci_address': fields.StringField(),
253
254        #: Name of the driver the device was previously bound to; it makes
255        #: the controller driver agnostic (virtio, SR-IOV, etc.).
256        'dev_driver': fields.StringField(),
257    }
258
259
260@base.VersionedObjectRegistry.register
261class DatapathOffloadBase(osv_base.VersionedObject,
262                          base.ComparableVersionedObject):
263    """Base class for all types of datapath offload."""
264
265    # Version 1.0: Initial release
266    VERSION = '1.0'
267
268
269@base.VersionedObjectRegistry.register
270class DatapathOffloadRepresentor(DatapathOffloadBase):
271    """Offload type for VF Representors conforming to the switchdev model.
272
273    This datapath offloads provides the metadata required to associate a VIF
274    with a :term:`VF` representor conforming to the `switchdev`_ kernel model.
275    If ``representor_name`` is specified, it indicates a desire to rename the
276    representor to the given name on plugging.
277
278    .. _switchdev: https://netdevconf.org/1.2/session.html?or-gerlitz
279    """
280
281    # Version 1.0: Initial release
282    VERSION = '1.0'
283
284    fields = {
285        #: Name to set on the representor (if set).
286        'representor_name': fields.StringField(nullable=True),
287
288        #: The PCI address of the Virtual Function.
289        'representor_address': fields.StringField(nullable=True),
290    }
291
292
293@base.VersionedObjectRegistry.register
294class VIFPortProfileBase(osv_base.VersionedObject,
295                         base.ComparableVersionedObject):
296    """Base class for all types of port profile.
297
298    The base profile defines fields that are common to all types of profile. It
299    should not be instantiated itself - use a subclass instead.
300    """
301
302    # Version 1.0: Initial release
303    # Version 1.1: Added 'datapath_offload'
304    VERSION = '1.1'
305
306    fields = {
307        #: Datapath offload type of the port.
308        'datapath_offload': fields.ObjectField('DatapathOffloadBase',
309                                               nullable=True,
310                                               subclasses=True),
311    }
312
313    obj_relationships = {
314        'datapath_offload': (('1.1', '1.0'),),
315    }
316
317
318@base.VersionedObjectRegistry.register
319class VIFPortProfileOpenVSwitch(VIFPortProfileBase):
320    """Port profile info for Open vSwitch networks.
321
322    This profile provides the metadata required to associate a VIF with an Open
323    vSwitch interface.
324    """
325
326    # Version 1.0: Initial release
327    # Version 1.1: Added 'datapath_type'
328    # Version 1.2: VIFPortProfileBase updated to 1.1 from 1.0
329    # Version 1.3: Added 'create_port'
330    VERSION = '1.3'
331
332    fields = {
333        #: A UUID to uniquely identify the interface. If omitted one will be
334        #: generated automatically.
335        'interface_id': fields.UUIDField(),
336
337        #: The OpenVSwitch port profile for the interface.
338        'profile_id': fields.StringField(),
339
340        #: Datapath type of the bridge.
341        'datapath_type': fields.StringField(nullable=True),
342
343        #: Whether the os-vif plugin should add the port to the bridge.
344        'create_port': fields.BooleanField(default=False),
345    }
346
347    def obj_make_compatible(self, primitive, target_version):
348        target_version = versionutils.convert_version_to_tuple(target_version)
349        if target_version < (1, 3) and 'create_port' in primitive:
350            del primitive['create_port']
351        if target_version < (1, 1) and 'datapath_type' in primitive:
352            del primitive['datapath_type']
353        if target_version < (1, 2):
354            super(VIFPortProfileOpenVSwitch, self).obj_make_compatible(
355                primitive, '1.0')
356        else:
357            super(VIFPortProfileOpenVSwitch, self).obj_make_compatible(
358                primitive, '1.1')
359
360
361@base.VersionedObjectRegistry.register
362class VIFPortProfileFPOpenVSwitch(VIFPortProfileOpenVSwitch):
363    """Port profile info for Open vSwitch networks using fast path.
364
365    This profile provides the metadata required to associate a :term:`fast
366    path <Fast Path>` VIF with an :term:`Open vSwitch` port.
367    """
368
369    # Version 1.0: Initial release
370    # Version 1.1: VIFPortProfileOpenVSwitch updated to 1.1 from 1.0
371    # Version 1.2: VIFPortProfileOpenVSwitch updated to 1.2 from 1.1
372    # Version 1.3: VIFPortProfileOpenVSwitch updated to 1.3 from 1.2
373    VERSION = '1.3'
374
375    fields = {
376        #: Name of the bridge (managed by fast path) to connect to.
377        'bridge_name': fields.StringField(),
378
379        #: Whether the OpenVSwitch network is using hybrid plug.
380        'hybrid_plug': fields.BooleanField(default=False),
381    }
382
383    def obj_make_compatible(self, primitive, target_version):
384        target_version = versionutils.convert_version_to_tuple(target_version)
385        if target_version < (1, 1):
386            super(VIFPortProfileFPOpenVSwitch, self).obj_make_compatible(
387                primitive, '1.0')
388        elif target_version < (1, 2):
389            super(VIFPortProfileFPOpenVSwitch, self).obj_make_compatible(
390                primitive, '1.1')
391        elif target_version < (1, 3):
392            super(VIFPortProfileFPOpenVSwitch, self).obj_make_compatible(
393                primitive, '1.2')
394        else:
395            super(VIFPortProfileFPOpenVSwitch, self).obj_make_compatible(
396                primitive, '1.3')
397
398
399@removals.removed_class("VIFPortProfileOVSRepresentor",
400                        category=PendingDeprecationWarning)
401@base.VersionedObjectRegistry.register
402class VIFPortProfileOVSRepresentor(VIFPortProfileOpenVSwitch):
403    """Port profile info for OpenVSwitch networks using a representor.
404
405    This profile provides the metadata required to associate a VIF with a
406    :term:`VF` representor and :term:`Open vSwitch` port. If `representor_name`
407    is specified, it indicates a desire to rename the representor to the given
408    name on plugging.
409
410    .. note::
411
412        This port profile is provided for backwards compatibility only.
413
414        This interface has been superceded by the one provided by the
415        :class:`DatapathOffloadRepresentor` class, which is now a field element
416        of the :class:`VIFPortProfileBase` class. The ``datapath_offload``
417        field in port profiles should be used instead.
418    """
419
420    # Version 1.0: Initial release
421    # Version 1.1: VIFPortProfileOpenVSwitch updated to 1.1 from 1.0
422    # Version 1.2: VIFPortProfileOpenVSwitch updated to 1.2 from 1.1
423    # Version 1.3: VIFPortProfileOpenVSwitch updated to 1.3 from 1.2
424    VERSION = '1.3'
425
426    fields = {
427        #: Name to set on the representor (if set).
428        'representor_name': fields.StringField(nullable=True),
429
430        #: The PCI address of the Virtual Function.
431        'representor_address': fields.PCIAddressField(nullable=True),
432    }
433
434    def obj_make_compatible(self, primitive, target_version):
435        target_version = versionutils.convert_version_to_tuple(target_version)
436        if target_version < (1, 1):
437            super(VIFPortProfileOVSRepresentor, self).obj_make_compatible(
438                primitive, '1.0')
439        elif target_version < (1, 2):
440            super(VIFPortProfileOVSRepresentor, self).obj_make_compatible(
441                primitive, '1.1')
442        elif target_version < (1, 3):
443            super(VIFPortProfileOVSRepresentor, self).obj_make_compatible(
444                primitive, '1.2')
445        else:
446            super(VIFPortProfileOVSRepresentor, self).obj_make_compatible(
447                primitive, '1.3')
448
449
450@base.VersionedObjectRegistry.register
451class VIFPortProfileFPBridge(VIFPortProfileBase):
452    """Port profile info for Linux Bridge networks using fast path.
453
454    This profile provides the metadata required to associate a :term:`fast
455    path <Fast Path>` VIF with a :term:`Linux Bridge` port.
456    """
457
458    # Version 1.0: Initial release
459    # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0
460    VERSION = '1.1'
461
462    fields = {
463        #: Name of the bridge (managed by fast path) to connect to.
464        'bridge_name': fields.StringField(),
465    }
466
467    def obj_make_compatible(self, primitive, target_version):
468        target_version = versionutils.convert_version_to_tuple(target_version)
469        if target_version < (1, 1):
470            super(VIFPortProfileFPBridge, self).obj_make_compatible(
471                primitive, '1.0')
472        else:
473            super(VIFPortProfileFPBridge, self).obj_make_compatible(
474                primitive, '1.1')
475
476
477@base.VersionedObjectRegistry.register
478class VIFPortProfileFPTap(VIFPortProfileBase):
479    """Port profile info for Calico networks using fast path.
480
481    This profile provides the metadata required to associate a :term:`fast
482    path <Fast Path>` VIF with a :term:`Calico` port.
483    """
484
485    # Version 1.0: Initial release
486    # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0
487    VERSION = '1.1'
488
489    fields = {
490        #: The MAC address of the host vhostuser port.
491        'mac_address': fields.MACAddressField(nullable=True),
492    }
493
494    def obj_make_compatible(self, primitive, target_version):
495        target_version = versionutils.convert_version_to_tuple(target_version)
496        if target_version < (1, 1):
497            super(VIFPortProfileFPTap, self).obj_make_compatible(
498                primitive, '1.0')
499        else:
500            super(VIFPortProfileFPTap, self).obj_make_compatible(
501                primitive, '1.1')
502
503
504@base.VersionedObjectRegistry.register
505class VIFPortProfile8021Qbg(VIFPortProfileBase):
506    """Port profile info for VEPA 802.1qbg networks.
507
508    This profile provides the metadata required to associate a VIF with a VEPA
509    host device supporting the :term:`802.1Qbg` spec.
510    """
511
512    # Version 1.0: Initial release
513    # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0
514    VERSION = '1.1'
515
516    fields = {
517        # TODO(stephenfin): Apparently the value 0 is reserved for manager_id,
518        # so should we set 'minimum=1'?
519        # https://libvirt.org/formatdomain.html#elementsNICS
520
521        #: The VSI Manager ID identifies the database containing the VSI type
522        #: and instance definitions.
523        'manager_id': fields.IntegerField(),
524
525        #: The VSI Type ID identifies a VSI type characterizing the network
526        #: access. VSI types are typically managed by network administrator.
527        'type_id': fields.IntegerField(),
528
529        #: The VSI Type Version allows multiple versions of a VSI Type.
530        'type_id_version': fields.IntegerField(),
531
532        #: The VSI Instance ID Identifier is generated when a VSI instance
533        #: (i.e. a virtual interface of a virtual machine) is created. This is
534        #: a globally unique identifier.
535        'instance_id': fields.UUIDField(),
536    }
537
538    def obj_make_compatible(self, primitive, target_version):
539        target_version = versionutils.convert_version_to_tuple(target_version)
540        if target_version < (1, 1):
541            super(VIFPortProfile8021Qbg, self).obj_make_compatible(
542                primitive, '1.0')
543        else:
544            super(VIFPortProfile8021Qbg, self).obj_make_compatible(
545                primitive, '1.1')
546
547
548@base.VersionedObjectRegistry.register
549class VIFPortProfile8021Qbh(VIFPortProfileBase):
550    """Port profile info for VEPA 802.1qbh networks.
551
552    This profile provides the metadata required to associate a VIF with a VEPA
553    host device supporting the :term:`802.1Qbh` spec.
554    """
555
556    # Version 1.0: Initial release
557    # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0
558    VERSION = '1.1'
559
560    fields = {
561        #: The name of the port profile that is to be applied to this
562        #: interface. This name is resolved by the port profile database into
563        #: the network parameters from the port profile, and those network
564        #: parameters will be applied to this interface.
565        'profile_id': fields.StringField()
566    }
567
568    def obj_make_compatible(self, primitive, target_version):
569        target_version = versionutils.convert_version_to_tuple(target_version)
570        if target_version < (1, 1):
571            super(VIFPortProfile8021Qbh, self).obj_make_compatible(
572                primitive, '1.0')
573        else:
574            super(VIFPortProfile8021Qbh, self).obj_make_compatible(
575                primitive, '1.1')
576
577
578@base.VersionedObjectRegistry.register
579class VIFPortProfileK8sDPDK(VIFPortProfileBase):
580    """Port profile info for Kuryr-Kubernetes DPDK ports.
581
582    This profile provides the metadata required to associate nested DPDK VIF
583    with a Kubernetes pod.
584    """
585
586    # Version 1.0: Initial release
587    # Version 1.1: VIFPortProfileBase updated to 1.1 from 1.0
588    VERSION = '1.1'
589
590    fields = {
591        #: Specify whether this vif requires L3 setup.
592        'l3_setup': fields.BooleanField(),
593
594        #: String containing URL representing object in Kubernetes v1 API.
595        'selflink': fields.StringField(),
596
597        #: String used in Kubernetes v1 API to identify the server's internal
598        #: version of this object.
599        'resourceversion': fields.StringField()
600    }
601
602    def obj_make_compatible(self, primitive, target_version):
603        target_version = versionutils.convert_version_to_tuple(target_version)
604        if target_version < (1, 1):
605            super(VIFPortProfileK8sDPDK, self).obj_make_compatible(
606                primitive, '1.0')
607        else:
608            super(VIFPortProfileK8sDPDK, self).obj_make_compatible(
609                primitive, '1.1')
610