From c5650bb2787c2fe1f55234904c6e0570f2eab17d Mon Sep 17 00:00:00 2001 From: jeremystretch Date: Wed, 26 Jan 2022 20:57:14 -0500 Subject: [PATCH] Rename PrimaryModel to NetBoxModel --- docs/development/adding-models.md | 2 +- docs/plugins/development/models.md | 7 ++++--- netbox/circuits/models/circuits.py | 4 ++-- netbox/circuits/models/providers.py | 6 +++--- netbox/dcim/models/cables.py | 4 ++-- netbox/dcim/models/device_components.py | 4 ++-- netbox/dcim/models/devices.py | 12 ++++++------ netbox/dcim/models/power.py | 6 +++--- netbox/dcim/models/racks.py | 6 +++--- netbox/dcim/models/sites.py | 4 ++-- netbox/ipam/models/fhrp.py | 4 ++-- netbox/ipam/models/ip.py | 12 ++++++------ netbox/ipam/models/services.py | 6 +++--- netbox/ipam/models/vlans.py | 4 ++-- netbox/ipam/models/vrfs.py | 6 +++--- netbox/netbox/models/__init__.py | 21 +++++++++++---------- netbox/tenancy/models/contacts.py | 4 ++-- netbox/tenancy/models/tenants.py | 4 ++-- netbox/virtualization/models.py | 8 ++++---- netbox/wireless/models.py | 6 +++--- 20 files changed, 66 insertions(+), 64 deletions(-) diff --git a/docs/development/adding-models.md b/docs/development/adding-models.md index d55afb2f2..f4d171f48 100644 --- a/docs/development/adding-models.md +++ b/docs/development/adding-models.md @@ -2,7 +2,7 @@ ## 1. Define the model class -Models within each app are stored in either `models.py` or within a submodule under the `models/` directory. When creating a model, be sure to subclass the [appropriate base model](models.md) from `netbox.models`. This will typically be PrimaryModel or OrganizationalModel. Remember to add the model class to the `__all__` listing for the module. +Models within each app are stored in either `models.py` or within a submodule under the `models/` directory. When creating a model, be sure to subclass the [appropriate base model](models.md) from `netbox.models`. This will typically be NetBoxModel or OrganizationalModel. Remember to add the model class to the `__all__` listing for the module. Each model should define, at a minimum: diff --git a/docs/plugins/development/models.md b/docs/plugins/development/models.md index b8b1e3122..225ac3d92 100644 --- a/docs/plugins/development/models.md +++ b/docs/plugins/development/models.md @@ -65,9 +65,10 @@ Simply subclass BaseModel when defining a model in your plugin: ```python # models.py -from netbox.models import BaseModel +from django.db import models +from netbox.models import NetBoxModel -class MyModel(BaseModel): +class MyModel(NetBoxModel): foo = models.CharField() ... ``` @@ -78,7 +79,7 @@ If you prefer instead to enable only a subset of these features for a plugin mod ```python # models.py -from django.db.models import models +from django.db import models from netbox.models.features import ExportTemplatesMixin, TagsMixin class MyModel(ExportTemplatesMixin, TagsMixin, models.Model): diff --git a/netbox/circuits/models/circuits.py b/netbox/circuits/models/circuits.py index faea380f0..0f3de91ed 100644 --- a/netbox/circuits/models/circuits.py +++ b/netbox/circuits/models/circuits.py @@ -5,7 +5,7 @@ from django.urls import reverse from circuits.choices import * from dcim.models import LinkTermination -from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel +from netbox.models import ChangeLoggedModel, OrganizationalModel, NetBoxModel from netbox.models.features import WebhooksMixin __all__ = ( @@ -43,7 +43,7 @@ class CircuitType(OrganizationalModel): return reverse('circuits:circuittype', args=[self.pk]) -class Circuit(PrimaryModel): +class Circuit(NetBoxModel): """ A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple circuits. Each circuit is also assigned a CircuitType and a Site. Circuit port speed and commit rate are measured diff --git a/netbox/circuits/models/providers.py b/netbox/circuits/models/providers.py index 8fd52c587..9cf4bf5c1 100644 --- a/netbox/circuits/models/providers.py +++ b/netbox/circuits/models/providers.py @@ -3,7 +3,7 @@ from django.db import models from django.urls import reverse from dcim.fields import ASNField -from netbox.models import PrimaryModel +from netbox.models import NetBoxModel __all__ = ( 'ProviderNetwork', @@ -11,7 +11,7 @@ __all__ = ( ) -class Provider(PrimaryModel): +class Provider(NetBoxModel): """ Each Circuit belongs to a Provider. This is usually a telecommunications company or similar organization. This model stores information pertinent to the user's relationship with the Provider. @@ -70,7 +70,7 @@ class Provider(PrimaryModel): return reverse('circuits:provider', args=[self.pk]) -class ProviderNetwork(PrimaryModel): +class ProviderNetwork(NetBoxModel): """ This represents a provider network which exists outside of NetBox, the details of which are unknown or unimportant to the user. diff --git a/netbox/dcim/models/cables.py b/netbox/dcim/models/cables.py index f1d4d7043..0d46d3c8f 100644 --- a/netbox/dcim/models/cables.py +++ b/netbox/dcim/models/cables.py @@ -11,7 +11,7 @@ from dcim.choices import * from dcim.constants import * from dcim.fields import PathField from dcim.utils import decompile_path_node, object_to_path_node, path_node_to_object -from netbox.models import PrimaryModel +from netbox.models import NetBoxModel from utilities.fields import ColorField from utilities.utils import to_meters from .devices import Device @@ -28,7 +28,7 @@ __all__ = ( # Cables # -class Cable(PrimaryModel): +class Cable(NetBoxModel): """ A physical connection between two endpoints. """ diff --git a/netbox/dcim/models/device_components.py b/netbox/dcim/models/device_components.py index de22708ea..a6887a768 100644 --- a/netbox/dcim/models/device_components.py +++ b/netbox/dcim/models/device_components.py @@ -11,7 +11,7 @@ from dcim.choices import * from dcim.constants import * from dcim.fields import MACAddressField, WWNField from dcim.svg import CableTraceSVG -from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, NetBoxModel from utilities.choices import ColorChoices from utilities.fields import ColorField, NaturalOrderingField from utilities.mptt import TreeManager @@ -39,7 +39,7 @@ __all__ = ( ) -class ComponentModel(PrimaryModel): +class ComponentModel(NetBoxModel): """ An abstract model inherited by any model which has a parent Device. """ diff --git a/netbox/dcim/models/devices.py b/netbox/dcim/models/devices.py index f94c9757d..37c900286 100644 --- a/netbox/dcim/models/devices.py +++ b/netbox/dcim/models/devices.py @@ -14,7 +14,7 @@ from dcim.constants import * from extras.models import ConfigContextModel from extras.querysets import ConfigContextModelQuerySet from netbox.config import ConfigItem -from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, NetBoxModel from utilities.choices import ColorChoices from utilities.fields import ColorField, NaturalOrderingField from .device_components import * @@ -68,7 +68,7 @@ class Manufacturer(OrganizationalModel): return reverse('dcim:manufacturer', args=[self.pk]) -class DeviceType(PrimaryModel): +class DeviceType(NetBoxModel): """ A DeviceType represents a particular make (Manufacturer) and model of device. It specifies rack height and depth, as well as high-level functional role(s). @@ -350,7 +350,7 @@ class DeviceType(PrimaryModel): return self.subdevice_role == SubdeviceRoleChoices.ROLE_CHILD -class ModuleType(PrimaryModel): +class ModuleType(NetBoxModel): """ A ModuleType represents a hardware element that can be installed within a device and which houses additional components; for example, a line card within a chassis-based switch such as the Cisco Catalyst 6500. Like a @@ -569,7 +569,7 @@ class Platform(OrganizationalModel): return reverse('dcim:platform', args=[self.pk]) -class Device(PrimaryModel, ConfigContextModel): +class Device(NetBoxModel, ConfigContextModel): """ A Device represents a piece of physical hardware mounted within a Rack. Each Device is assigned a DeviceType, DeviceRole, and (optionally) a Platform. Device names are not required, however if one is set it must be unique. @@ -1005,7 +1005,7 @@ class Device(PrimaryModel, ConfigContextModel): return DeviceStatusChoices.colors.get(self.status, 'secondary') -class Module(PrimaryModel, ConfigContextModel): +class Module(NetBoxModel, ConfigContextModel): """ A Module represents a field-installable component within a Device which may itself hold multiple device components (for example, a line card within a chassis switch). Modules are instantiated from ModuleTypes. @@ -1087,7 +1087,7 @@ class Module(PrimaryModel, ConfigContextModel): # Virtual chassis # -class VirtualChassis(PrimaryModel): +class VirtualChassis(NetBoxModel): """ A collection of Devices which operate with a shared control plane (e.g. a switch stack). """ diff --git a/netbox/dcim/models/power.py b/netbox/dcim/models/power.py index fe7f69df9..bbbdda83c 100644 --- a/netbox/dcim/models/power.py +++ b/netbox/dcim/models/power.py @@ -6,7 +6,7 @@ from django.urls import reverse from dcim.choices import * from dcim.constants import * -from netbox.models import PrimaryModel +from netbox.models import NetBoxModel from utilities.validators import ExclusionValidator from .device_components import LinkTermination, PathEndpoint @@ -20,7 +20,7 @@ __all__ = ( # Power # -class PowerPanel(PrimaryModel): +class PowerPanel(NetBoxModel): """ A distribution point for electrical power; e.g. a data center RPP. """ @@ -66,7 +66,7 @@ class PowerPanel(PrimaryModel): ) -class PowerFeed(PrimaryModel, PathEndpoint, LinkTermination): +class PowerFeed(NetBoxModel, PathEndpoint, LinkTermination): """ An electrical circuit delivered from a PowerPanel. """ diff --git a/netbox/dcim/models/racks.py b/netbox/dcim/models/racks.py index 1ebbbcba4..0fe84aa0c 100644 --- a/netbox/dcim/models/racks.py +++ b/netbox/dcim/models/racks.py @@ -14,7 +14,7 @@ from dcim.choices import * from dcim.constants import * from dcim.svg import RackElevationSVG from netbox.config import get_config -from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, NetBoxModel from utilities.choices import ColorChoices from utilities.fields import ColorField, NaturalOrderingField from utilities.utils import array_to_string @@ -63,7 +63,7 @@ class RackRole(OrganizationalModel): return reverse('dcim:rackrole', args=[self.pk]) -class Rack(PrimaryModel): +class Rack(NetBoxModel): """ Devices are housed within Racks. Each rack has a defined height measured in rack units, and a front and rear face. Each Rack is assigned to a Site and (optionally) a Location. @@ -435,7 +435,7 @@ class Rack(PrimaryModel): return int(allocated_draw_total / available_power_total * 100) -class RackReservation(PrimaryModel): +class RackReservation(NetBoxModel): """ One or more reserved units within a Rack. """ diff --git a/netbox/dcim/models/sites.py b/netbox/dcim/models/sites.py index 3756933ac..625422d6b 100644 --- a/netbox/dcim/models/sites.py +++ b/netbox/dcim/models/sites.py @@ -7,7 +7,7 @@ from timezone_field import TimeZoneField from dcim.choices import * from dcim.constants import * -from netbox.models import NestedGroupModel, PrimaryModel +from netbox.models import NestedGroupModel, NetBoxModel from utilities.fields import NaturalOrderingField __all__ = ( @@ -194,7 +194,7 @@ class SiteGroup(NestedGroupModel): # Sites # -class Site(PrimaryModel): +class Site(NetBoxModel): """ A Site represents a geographic location within a network; typically a building or campus. The optional facility field can be used to include an external designation, such as a data center name (e.g. Equinix SV6). diff --git a/netbox/ipam/models/fhrp.py b/netbox/ipam/models/fhrp.py index f0e3c2a23..2a8d1bdcd 100644 --- a/netbox/ipam/models/fhrp.py +++ b/netbox/ipam/models/fhrp.py @@ -4,7 +4,7 @@ from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from django.urls import reverse -from netbox.models import ChangeLoggedModel, PrimaryModel +from netbox.models import ChangeLoggedModel, NetBoxModel from netbox.models.features import WebhooksMixin from ipam.choices import * from ipam.constants import * @@ -15,7 +15,7 @@ __all__ = ( ) -class FHRPGroup(PrimaryModel): +class FHRPGroup(NetBoxModel): """ A grouping of next hope resolution protocol (FHRP) peers. (For instance, VRRP or HSRP.) """ diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index b13899f7c..1354c6e64 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -9,7 +9,7 @@ from django.utils.functional import cached_property from dcim.fields import ASNField from dcim.models import Device -from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, NetBoxModel from ipam.choices import * from ipam.constants import * from ipam.fields import IPNetworkField, IPAddressField @@ -88,7 +88,7 @@ class RIR(OrganizationalModel): return reverse('ipam:rir', args=[self.pk]) -class ASN(PrimaryModel): +class ASN(NetBoxModel): """ An autonomous system (AS) number is typically used to represent an independent routing domain. A site can have one or more ASNs assigned to it. @@ -147,7 +147,7 @@ class ASN(PrimaryModel): return self.asn -class Aggregate(GetAvailablePrefixesMixin, PrimaryModel): +class Aggregate(GetAvailablePrefixesMixin, NetBoxModel): """ An aggregate exists at the root level of the IP address space hierarchy in NetBox. Aggregates are used to organize the hierarchy and track the overall utilization of available address space. Each Aggregate is assigned to a RIR. @@ -280,7 +280,7 @@ class Role(OrganizationalModel): return reverse('ipam:role', args=[self.pk]) -class Prefix(GetAvailablePrefixesMixin, PrimaryModel): +class Prefix(GetAvailablePrefixesMixin, NetBoxModel): """ A Prefix represents an IPv4 or IPv6 network, including mask length. Prefixes can optionally be assigned to Sites and VRFs. A Prefix must be assigned a status and may optionally be assigned a used-define Role. A Prefix can also be @@ -557,7 +557,7 @@ class Prefix(GetAvailablePrefixesMixin, PrimaryModel): return min(utilization, 100) -class IPRange(PrimaryModel): +class IPRange(NetBoxModel): """ A range of IP addresses, defined by start and end addresses. """ @@ -752,7 +752,7 @@ class IPRange(PrimaryModel): return int(float(child_count) / self.size * 100) -class IPAddress(PrimaryModel): +class IPAddress(NetBoxModel): """ An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is configured in the real world. (Typically, only loopback interfaces are configured with /32 or /128 masks.) Like diff --git a/netbox/ipam/models/services.py b/netbox/ipam/models/services.py index bd8030a0a..70ad38197 100644 --- a/netbox/ipam/models/services.py +++ b/netbox/ipam/models/services.py @@ -6,7 +6,7 @@ from django.urls import reverse from ipam.choices import * from ipam.constants import * -from netbox.models import PrimaryModel +from netbox.models import NetBoxModel from utilities.utils import array_to_string @@ -46,7 +46,7 @@ class ServiceBase(models.Model): return array_to_string(self.ports) -class ServiceTemplate(ServiceBase, PrimaryModel): +class ServiceTemplate(ServiceBase, NetBoxModel): """ A template for a Service to be applied to a device or virtual machine. """ @@ -62,7 +62,7 @@ class ServiceTemplate(ServiceBase, PrimaryModel): return reverse('ipam:servicetemplate', args=[self.pk]) -class Service(ServiceBase, PrimaryModel): +class Service(ServiceBase, NetBoxModel): """ A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device or VirtualMachine. A Service may optionally be tied to one or more specific IPAddresses belonging to its parent. diff --git a/netbox/ipam/models/vlans.py b/netbox/ipam/models/vlans.py index f73571ea9..7cd03ed55 100644 --- a/netbox/ipam/models/vlans.py +++ b/netbox/ipam/models/vlans.py @@ -9,7 +9,7 @@ from dcim.models import Interface from ipam.choices import * from ipam.constants import * from ipam.querysets import VLANQuerySet -from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, NetBoxModel from virtualization.models import VMInterface @@ -116,7 +116,7 @@ class VLANGroup(OrganizationalModel): return None -class VLAN(PrimaryModel): +class VLAN(NetBoxModel): """ A VLAN is a distinct layer two forwarding domain identified by a 12-bit integer (1-4094). Each VLAN must be assigned to a Site, however VLAN IDs need not be unique within a Site. A VLAN may optionally be assigned to a VLANGroup, diff --git a/netbox/ipam/models/vrfs.py b/netbox/ipam/models/vrfs.py index f1b2d682f..fc34b5488 100644 --- a/netbox/ipam/models/vrfs.py +++ b/netbox/ipam/models/vrfs.py @@ -2,7 +2,7 @@ from django.db import models from django.urls import reverse from ipam.constants import * -from netbox.models import PrimaryModel +from netbox.models import NetBoxModel __all__ = ( @@ -11,7 +11,7 @@ __all__ = ( ) -class VRF(PrimaryModel): +class VRF(NetBoxModel): """ A virtual routing and forwarding (VRF) table represents a discrete layer three forwarding domain (e.g. a routing table). Prefixes and IPAddresses can optionally be assigned to VRFs. (Prefixes and IPAddresses not assigned to a VRF @@ -73,7 +73,7 @@ class VRF(PrimaryModel): return reverse('ipam:vrf', args=[self.pk]) -class RouteTarget(PrimaryModel): +class RouteTarget(NetBoxModel): """ A BGP extended community used to control the redistribution of routes among VRFs, as defined in RFC 4364. """ diff --git a/netbox/netbox/models/__init__.py b/netbox/netbox/models/__init__.py index bf120c8ac..b3bfe06c0 100644 --- a/netbox/netbox/models/__init__.py +++ b/netbox/netbox/models/__init__.py @@ -10,15 +10,11 @@ __all__ = ( 'ChangeLoggedModel', 'NestedGroupModel', 'OrganizationalModel', - 'PrimaryModel', + 'NetBoxModel', ) -# -# Base model classes -# - -class BaseModel( +class NetBoxFeatureSet( ChangeLoggingMixin, CustomFieldsMixin, CustomLinksMixin, @@ -32,9 +28,14 @@ class BaseModel( abstract = True +# +# Base model classes +# + class ChangeLoggedModel(ChangeLoggingMixin, CustomValidationMixin, models.Model): """ - Base model for all objects which support change logging. + Base model for ancillary models; provides limited functionality for models which don't + support NetBox's full feature set. """ objects = RestrictedQuerySet.as_manager() @@ -42,7 +43,7 @@ class ChangeLoggedModel(ChangeLoggingMixin, CustomValidationMixin, models.Model) abstract = True -class PrimaryModel(BaseModel, models.Model): +class NetBoxModel(NetBoxFeatureSet, models.Model): """ Primary models represent real objects within the infrastructure being modeled. """ @@ -52,7 +53,7 @@ class PrimaryModel(BaseModel, models.Model): abstract = True -class NestedGroupModel(BaseModel, MPTTModel): +class NestedGroupModel(NetBoxFeatureSet, MPTTModel): """ Base model for objects which are used to form a hierarchy (regions, locations, etc.). These models nest recursively using MPTT. Within each parent, each child instance must have a unique name. @@ -94,7 +95,7 @@ class NestedGroupModel(BaseModel, MPTTModel): }) -class OrganizationalModel(BaseModel, models.Model): +class OrganizationalModel(NetBoxFeatureSet, models.Model): """ Organizational models are those which are used solely to categorize and qualify other objects, and do not convey any real information about the infrastructure being modeled (for example, functional device roles). Organizational diff --git a/netbox/tenancy/models/contacts.py b/netbox/tenancy/models/contacts.py index cacd682cb..81dd99773 100644 --- a/netbox/tenancy/models/contacts.py +++ b/netbox/tenancy/models/contacts.py @@ -4,7 +4,7 @@ from django.db import models from django.urls import reverse from mptt.models import TreeForeignKey -from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, PrimaryModel +from netbox.models import ChangeLoggedModel, NestedGroupModel, OrganizationalModel, NetBoxModel from netbox.models.features import WebhooksMixin from tenancy.choices import * @@ -76,7 +76,7 @@ class ContactRole(OrganizationalModel): return reverse('tenancy:contactrole', args=[self.pk]) -class Contact(PrimaryModel): +class Contact(NetBoxModel): """ Contact information for a particular object(s) in NetBox. """ diff --git a/netbox/tenancy/models/tenants.py b/netbox/tenancy/models/tenants.py index 9952a700d..88d8d52f1 100644 --- a/netbox/tenancy/models/tenants.py +++ b/netbox/tenancy/models/tenants.py @@ -3,7 +3,7 @@ from django.db import models from django.urls import reverse from mptt.models import TreeForeignKey -from netbox.models import NestedGroupModel, PrimaryModel +from netbox.models import NestedGroupModel, NetBoxModel __all__ = ( 'Tenant', @@ -43,7 +43,7 @@ class TenantGroup(NestedGroupModel): return reverse('tenancy:tenantgroup', args=[self.pk]) -class Tenant(PrimaryModel): +class Tenant(NetBoxModel): """ A Tenant represents an organization served by the NetBox owner. This is typically a customer or an internal department. diff --git a/netbox/virtualization/models.py b/netbox/virtualization/models.py index 790cdcdbf..dda1d0bee 100644 --- a/netbox/virtualization/models.py +++ b/netbox/virtualization/models.py @@ -8,7 +8,7 @@ from dcim.models import BaseInterface, Device from extras.models import ConfigContextModel from extras.querysets import ConfigContextModelQuerySet from netbox.config import get_config -from netbox.models import OrganizationalModel, PrimaryModel +from netbox.models import OrganizationalModel, NetBoxModel from utilities.fields import NaturalOrderingField from utilities.ordering import naturalize_interface from utilities.query_functions import CollateAsChar @@ -100,7 +100,7 @@ class ClusterGroup(OrganizationalModel): # Clusters # -class Cluster(PrimaryModel): +class Cluster(NetBoxModel): """ A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices. """ @@ -183,7 +183,7 @@ class Cluster(PrimaryModel): # Virtual machines # -class VirtualMachine(PrimaryModel, ConfigContextModel): +class VirtualMachine(NetBoxModel, ConfigContextModel): """ A virtual machine which runs inside a Cluster. """ @@ -345,7 +345,7 @@ class VirtualMachine(PrimaryModel, ConfigContextModel): # Interfaces # -class VMInterface(PrimaryModel, BaseInterface): +class VMInterface(NetBoxModel, BaseInterface): virtual_machine = models.ForeignKey( to='virtualization.VirtualMachine', on_delete=models.CASCADE, diff --git a/netbox/wireless/models.py b/netbox/wireless/models.py index 621024d79..fc80e91df 100644 --- a/netbox/wireless/models.py +++ b/netbox/wireless/models.py @@ -5,7 +5,7 @@ from mptt.models import MPTTModel, TreeForeignKey from dcim.choices import LinkStatusChoices from dcim.constants import WIRELESS_IFACE_TYPES -from netbox.models import NestedGroupModel, PrimaryModel +from netbox.models import NestedGroupModel, NetBoxModel from .choices import * from .constants import * @@ -79,7 +79,7 @@ class WirelessLANGroup(NestedGroupModel): return reverse('wireless:wirelesslangroup', args=[self.pk]) -class WirelessLAN(WirelessAuthenticationBase, PrimaryModel): +class WirelessLAN(WirelessAuthenticationBase, NetBoxModel): """ A wireless network formed among an arbitrary number of access point and clients. """ @@ -117,7 +117,7 @@ class WirelessLAN(WirelessAuthenticationBase, PrimaryModel): return reverse('wireless:wirelesslan', args=[self.pk]) -class WirelessLink(WirelessAuthenticationBase, PrimaryModel): +class WirelessLink(WirelessAuthenticationBase, NetBoxModel): """ A point-to-point connection between two wireless Interfaces. """