#9047 - Documentation and some more test fixes

This commit is contained in:
Daniel Sheppard 2023-03-22 16:00:21 -05:00
parent 1a6bd726eb
commit 51fe68c2ee
14 changed files with 80 additions and 39 deletions

View File

@ -30,6 +30,7 @@ The Django [content types](https://docs.djangoproject.com/en/stable/ref/contrib/
* [circuits.Circuit](../models/circuits/circuit.md)
* [circuits.Provider](../models/circuits/provider.md)
* [circuits.ProviderAccount](../models/circuits/provideracount.md)
* [circuits.ProviderNetwork](../models/circuits/providernetwork.md)
* [dcim.Cable](../models/dcim/cable.md)
* [dcim.Device](../models/dcim/device.md)

View File

@ -29,7 +29,7 @@ A SearchIndex subclass defines both its model and a list of two-tuples specifyin
| 60 | Unique serialized attribute (per related object) | Device.serial |
| 100 | Primary human identifier | Device.name, Circuit.cid, Cable.label |
| 110 | Slug | Site.slug |
| 200 | Secondary identifier | Provider.account, DeviceType.part_number |
| 200 | Secondary identifier | ProviderAccount.account, DeviceType.part_number |
| 300 | Highly unique descriptive attribute | CircuitTermination.xconnect_id, IPAddress.dns_name |
| 500 | Description | Site.description |
| 1000 | Custom field default | - |

View File

@ -5,13 +5,15 @@ NetBox is ideal for managing your network's transit and peering providers and ci
```mermaid
flowchart TD
ASN --> Provider
Provider --> ProviderNetwork & Circuit
Provider --> ProviderAccount --> Circuit
Provider --> ProviderNetwork
CircuitType --> Circuit
click ASN "../../models/circuits/asn/"
click Circuit "../../models/circuits/circuit/"
click CircuitType "../../models/circuits/circuittype/"
click Provider "../../models/circuits/provider/"
click ProviderAccount "../../models/circuits/provideraccount/"
click ProviderNetwork "../../models/circuits/providernetwork/"
```
@ -25,7 +27,7 @@ Sometimes you'll need to model provider networks into which you don't have full
A circuit is a physical connection between two points, which is installed and maintained by an external provider. For example, an Internet connection delivered as a fiber optic cable would be modeled as a circuit in NetBox.
Each circuit is associated with a provider and assigned a circuit ID, which must be unique to that provider. A circuit is also assigned a user-defined type, operational status, and various other operating characteristics.
Each circuit is associated with a provider account and assigned a circuit ID, which must be unique to that provider. A circuit is also assigned a user-defined type, operational status, and various other operating characteristics.
Each circuit may have up to two terminations (A and Z) defined. Each termination can be associated with a particular site or provider network. In the case of the former, a cable can be connected between the circuit termination and a device component to map its physical connectivity.

View File

@ -31,6 +31,7 @@ The following models support the assignment of contacts:
* circuits.Circuit
* circuits.Provider
* circuits.ProviderAccount
* dcim.Device
* dcim.Location
* dcim.Manufacturer

View File

@ -56,7 +56,7 @@ Below is the (rough) recommended order in which NetBox objects should be created
4. Manufacturers, device types, and module types
5. Platforms and device roles
6. Devices and modules
7. Providers and provider networks
7. Providers, provider accounts and provider networks
8. Circuit types and circuits
9. Wireless LAN groups and wireless LANs
10. Route targets and VRFs

View File

@ -4,9 +4,9 @@ A circuit represents a physical point-to-point data connection, typically used t
## Fields
### Provider
### Provider Account
The [provider](./provider.md) to which this circuit belongs.
The [provider account](./provideraccount.md) to which this circuit belongs.
### Circuit ID

View File

@ -23,10 +23,6 @@ The AS number assigned to this provider.
The [AS numbers](../ipam/asn.md) assigned to this provider (optional).
### Account Number
The administrative account identifier tied to this provider for your organization.
### Portal URL
The URL for the provider's customer service portal.

View File

@ -0,0 +1,17 @@
# Provider Accounts
This model can be used to represent individual accounts associated with a provider.
## Fields
### Provider
The [provider](./provider.md) the account belongs to.
### Name
A human-friendly name, unique to the provider.
### Account Number
The administrative account identifier tied to this provider for your organization.

View File

@ -30,9 +30,9 @@ class CircuitType(OrganizationalModel):
class Circuit(PrimaryModel):
"""
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
in Kbps.
A communications circuit connects two points. Each Circuit belongs to a Provider Account; ProviderAccounts may have
multiple circuits. Each circuit is also assigned a CircuitType and a Site. Circuit port speed and commit rate are
measured in Kbps.
"""
cid = models.CharField(
max_length=100,

View File

@ -14,8 +14,8 @@ __all__ = (
class Provider(PrimaryModel):
"""
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.
This is usually a telecommunications company or similar organization. This model stores information pertinent to
the user's relationship with the Provider.
"""
name = models.CharField(
max_length=100,
@ -50,7 +50,7 @@ class Provider(PrimaryModel):
class ProviderAccount(PrimaryModel):
"""
This represents a provider account
This is a discrete account within a provider. Each Circuit belongs to a Provider Account.
"""
account = models.CharField(
max_length=30,

View File

@ -36,6 +36,15 @@ class ProviderTestCase(TestCase, ChangeLoggedFilterSetTests):
providers[1].asns.set([asns[1]])
providers[2].asns.set([asns[2]])
provider_accounts = (
ProviderAccount(name='Account A', account='AAAA', provider=providers[0]),
ProviderAccount(name='Account B', account='BBBB', provider=providers[1]),
ProviderAccount(name='Account C', account='CCCC', provider=providers[2]),
ProviderAccount(name='Account D', account='DDDD', provider=providers[3]),
ProviderAccount(name='Account E', account='EEEE', provider=providers[4]),
)
ProviderAccount.objects.bulk_create(provider_accounts)
regions = (
Region(name='Test Region 1', slug='test-region-1'),
Region(name='Test Region 2', slug='test-region-2'),
@ -64,8 +73,8 @@ class ProviderTestCase(TestCase, ChangeLoggedFilterSetTests):
CircuitType.objects.bulk_create(circuit_types)
circuits = (
Circuit(provider=providers[0], type=circuit_types[0], cid='Test Circuit 1'),
Circuit(provider=providers[1], type=circuit_types[1], cid='Test Circuit 1'),
Circuit(provider_account=provider_accounts[0], type=circuit_types[0], cid='Test Circuit 1'),
Circuit(provider_account=provider_accounts[1], type=circuit_types[1], cid='Test Circuit 1'),
)
Circuit.objects.bulk_create(circuits)
@ -249,8 +258,8 @@ class CircuitTestCase(TestCase, ChangeLoggedFilterSetTests):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
def test_provider_account(self):
provider = ProviderAccount.objects.first()
params = {'provider_id': [provider.pk]}
provider_account = ProviderAccount.objects.first()
params = {'provider_account_id': [provider_account.pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
def test_provider_network(self):

View File

@ -30,8 +30,9 @@ class CablePathTestCase(TestCase):
cls.powerpanel = PowerPanel.objects.create(site=cls.site, name='Power Panel')
provider = Provider.objects.create(name='Provider', slug='provider')
provider_account = ProviderAccount.objects.create(name='Account', account='AAAA1111', provider=provider)
circuit_type = CircuitType.objects.create(name='Circuit Type', slug='circuit-type')
cls.circuit = Circuit.objects.create(provider=provider, type=circuit_type, cid='Circuit 1')
cls.circuit = Circuit.objects.create(provider_account=provider_account, type=circuit_type, cid='Circuit 1')
def assertPathExists(self, nodes, **kwargs):
"""
@ -1308,7 +1309,7 @@ class CablePathTestCase(TestCase):
[IF1] --C1-- [CT1] [CT2] --> [PN1]
"""
interface1 = Interface.objects.create(device=self.device, name='Interface 1')
providernetwork = ProviderNetwork.objects.create(name='Provider Network 1', provider=self.circuit.provider)
providernetwork = ProviderNetwork.objects.create(name='Provider Network 1', provider=self.circuit.provider_account.provider)
circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A')
circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit, provider_network=providernetwork, term_side='Z')
@ -1436,7 +1437,7 @@ class CablePathTestCase(TestCase):
"""
interface1 = Interface.objects.create(device=self.device, name='Interface 1')
interface2 = Interface.objects.create(device=self.device, name='Interface 2')
circuit2 = Circuit.objects.create(provider=self.circuit.provider, type=self.circuit.type, cid='Circuit 2')
circuit2 = Circuit.objects.create(provider_account=self.circuit.provider_account, type=self.circuit.type, cid='Circuit 2')
circuittermination1 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='A')
circuittermination2 = CircuitTermination.objects.create(circuit=self.circuit, site=self.site, term_side='Z')
circuittermination3 = CircuitTermination.objects.create(circuit=circuit2, site=self.site, term_side='A')

View File

@ -504,10 +504,11 @@ class CableTestCase(TestCase):
device=patch_pannel, name='FP4', type='8p8c', rear_port=rear_port4, rear_port_position=1
)
provider = Provider.objects.create(name='Provider 1', slug='provider-1')
provider_account = ProviderAccount.objects.create(name='Provider Account 1', account='A1', provider=provider)
provider_network = ProviderNetwork.objects.create(name='Provider Network 1', provider=provider)
circuittype = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1')
circuit1 = Circuit.objects.create(provider=provider, type=circuittype, cid='1')
circuit2 = Circuit.objects.create(provider=provider, type=circuittype, cid='2')
circuit1 = Circuit.objects.create(provider_account=provider_account, type=circuittype, cid='1')
circuit2 = Circuit.objects.create(provider_account=provider_account, type=circuittype, cid='2')
circuittermination1 = CircuitTermination.objects.create(circuit=circuit1, site=site, term_side='A')
circuittermination2 = CircuitTermination.objects.create(circuit=circuit1, site=site, term_side='Z')
circuittermination3 = CircuitTermination.objects.create(circuit=circuit2, provider_network=provider_network, term_side='A')

View File

@ -1,6 +1,6 @@
from django.test import TransactionTestCase
from circuits.models import Provider, Circuit, CircuitType
from circuits.models import Provider, Circuit, CircuitType, ProviderAccount
from extras.choices import ChangeActionChoices
from extras.models import Branch, StagedChange, Tag
from ipam.models import ASN, RIR
@ -28,18 +28,25 @@ class StagingTestCase(TransactionTestCase):
)
Provider.objects.bulk_create(providers)
provider_accounts = (
ProviderAccount(name='Account A', provider=providers[0], account='AAAA'),
ProviderAccount(name='Account B', provider=providers[1], account='BBBB'),
ProviderAccount(name='Account C', provider=providers[2], account='CCCC'),
)
ProviderAccount.objects.bulk_create(provider_accounts)
circuit_type = CircuitType.objects.create(name='Circuit Type 1', slug='circuit-type-1')
Circuit.objects.bulk_create((
Circuit(provider=providers[0], cid='Circuit A1', type=circuit_type),
Circuit(provider=providers[0], cid='Circuit A2', type=circuit_type),
Circuit(provider=providers[0], cid='Circuit A3', type=circuit_type),
Circuit(provider=providers[1], cid='Circuit B1', type=circuit_type),
Circuit(provider=providers[1], cid='Circuit B2', type=circuit_type),
Circuit(provider=providers[1], cid='Circuit B3', type=circuit_type),
Circuit(provider=providers[2], cid='Circuit C1', type=circuit_type),
Circuit(provider=providers[2], cid='Circuit C2', type=circuit_type),
Circuit(provider=providers[2], cid='Circuit C3', type=circuit_type),
Circuit(provider_account=provider_accounts[0], cid='Circuit A1', type=circuit_type),
Circuit(provider_account=provider_accounts[0], cid='Circuit A2', type=circuit_type),
Circuit(provider_account=provider_accounts[0], cid='Circuit A3', type=circuit_type),
Circuit(provider_account=provider_accounts[1], cid='Circuit B1', type=circuit_type),
Circuit(provider_account=provider_accounts[1], cid='Circuit B2', type=circuit_type),
Circuit(provider_account=provider_accounts[1], cid='Circuit B3', type=circuit_type),
Circuit(provider_account=provider_accounts[2], cid='Circuit C1', type=circuit_type),
Circuit(provider_account=provider_accounts[2], cid='Circuit C2', type=circuit_type),
Circuit(provider_account=provider_accounts[2], cid='Circuit C3', type=circuit_type),
))
def test_object_creation(self):
@ -50,7 +57,8 @@ class StagingTestCase(TransactionTestCase):
with checkout(branch):
provider = Provider.objects.create(name='Provider D', slug='provider-d')
provider.asns.set(asns)
circuit = Circuit.objects.create(provider=provider, cid='Circuit D1', type=CircuitType.objects.first())
provider_account = ProviderAccount.objects.create(name='Account D', provider=provider, account='DDDD')
circuit = Circuit.objects.create(provider_account=provider_account, cid='Circuit D1', type=CircuitType.objects.first())
circuit.tags.set(tags)
# Sanity-checking
@ -62,7 +70,7 @@ class StagingTestCase(TransactionTestCase):
# Verify that changes have been rolled back after exiting the context
self.assertEqual(Provider.objects.count(), 3)
self.assertEqual(Circuit.objects.count(), 9)
self.assertEqual(StagedChange.objects.count(), 5)
self.assertEqual(StagedChange.objects.count(), 6)
# Verify that changes are replayed upon entering the context
with checkout(branch):
@ -145,26 +153,31 @@ class StagingTestCase(TransactionTestCase):
with checkout(branch):
provider = Provider.objects.get(name='Provider A')
provider.circuits.all().delete()
Circuit.objects.filter(provider_account__provider=provider).delete()
provider.accounts.all().delete()
provider.delete()
# Sanity-checking
self.assertEqual(Provider.objects.count(), 2)
self.assertEqual(ProviderAccount.objects.count(), 2)
self.assertEqual(Circuit.objects.count(), 6)
# Verify that changes have been rolled back after exiting the context
self.assertEqual(Provider.objects.count(), 3)
self.assertEqual(ProviderAccount.objects.count(), 3)
self.assertEqual(Circuit.objects.count(), 9)
self.assertEqual(StagedChange.objects.count(), 4)
self.assertEqual(StagedChange.objects.count(), 5)
# Verify that changes are replayed upon entering the context
with checkout(branch):
self.assertEqual(Provider.objects.count(), 2)
self.assertEqual(ProviderAccount.objects.count(), 2)
self.assertEqual(Circuit.objects.count(), 6)
# Verify that changes are applied and deleted upon branch merge
branch.merge()
self.assertEqual(Provider.objects.count(), 2)
self.assertEqual(ProviderAccount.objects.count(), 2)
self.assertEqual(Circuit.objects.count(), 6)
self.assertEqual(StagedChange.objects.count(), 0)