|
| 1 | +<!-- |
| 2 | +SPDX-FileCopyrightText: 2025 diffo_example contributors <https://github.com/diffo-dev/diffo_example/graphs.contributors> |
| 3 | +
|
| 4 | +SPDX-License-Identifier: MIT |
| 5 | +--> |
| 6 | + |
| 7 | +# Diffo Example - NBN Domain |
| 8 | + |
| 9 | +```elixir |
| 10 | +Mix.install( |
| 11 | + [ |
| 12 | + {:diffo_example, "~> 0.2.0"} |
| 13 | + ], |
| 14 | + consolidate_protocols: false |
| 15 | +) |
| 16 | +``` |
| 17 | + |
| 18 | +## Overview |
| 19 | + |
| 20 | +[Diffo](https://github.com/diffo-dev/diffo) is a Telecommunications Management Forum (TMF) Service and Resource Manager, built for autonomous networks. It is implemented using the [Ash Framework](https://www.ash-hq.org) and stores data in Neo4j via [AshNeo4j](https://github.com/diffo-dev/ash_neo4j). |
| 21 | + |
| 22 | +If you are new to Diffo, start with the [Diffo livebook](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fdiffo-dev%2Fdiffo%2Fblob%2Fdev%2Fdiffo.livemd) which introduces the core Provider concepts — Specification, Instance, Feature, Characteristic, Party, Place, and Relationship. |
| 23 | + |
| 24 | +Diffo includes a Provider Instance extension that lets you declare specialised TMF Services and Resources using a Spark DSL with very little Elixir code. The [Provider Instance Extension livebook](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fdiffo-dev%2Fdiffo%2Fblob%2Fdev%2Fdocumentation%2Fhow_to%2Fuse_diffo_provider_instance_extension.livemd) covers this in detail. |
| 25 | + |
| 26 | +The NBN domain in this example was built entirely with that DSL — a declarative model of a realistic NBN Ethernet access hierarchy with minimal custom Elixir, derived from a short domain description. It demonstrates how much can be expressed through the Provider extension alone. |
| 27 | + |
| 28 | +The NBN domain models a fictional NBN Ethernet access circuit and its constituent resources: |
| 29 | + |
| 30 | +* **NbnEthernet** — the parent circuit resource (identified by a PRI) |
| 31 | +* **UNI** — User Network Interface at the customer premises |
| 32 | +* **AVC** — Access Virtual Circuit (dedicated, carries traffic between UNI and CVC) |
| 33 | +* **NTD** — Network Termination Device (installed at customer premises, assigns ports to UNI) |
| 34 | +* **CVC** — Connectivity Virtual Circuit (aggregates AVCs, terminates at NNI Group) |
| 35 | +* **NNI Group** — group of NNIs at the point of interconnect |
| 36 | +* **NNI** — Network-to-Network Interface |
| 37 | + |
| 38 | +## Installing Neo4j and Configuring Bolty |
| 39 | + |
| 40 | +Update the configuration below to match your Neo4j installation and evaluate. |
| 41 | + |
| 42 | +```elixir |
| 43 | +config = [ |
| 44 | + uri: "bolt://localhost:7687", |
| 45 | + auth: [username: "neo4j", password: "password"], |
| 46 | + user_agent: "diffoExampleLivebook/1", |
| 47 | + pool_size: 15, |
| 48 | + max_overflow: 3, |
| 49 | + prefix: :default, |
| 50 | + name: Bolt, |
| 51 | + log: false, |
| 52 | + log_hex: false |
| 53 | +] |
| 54 | +``` |
| 55 | + |
| 56 | +```elixir |
| 57 | +AshNeo4j.BoltyHelper.start(config) |
| 58 | +``` |
| 59 | + |
| 60 | +```elixir |
| 61 | +AshNeo4j.BoltyHelper.is_connected() |
| 62 | +``` |
| 63 | + |
| 64 | +It is helpful to have a Neo4j browser open locally, typically at http://localhost:7474/browser/ |
| 65 | + |
| 66 | +**OPTIONAL** Clear the database before starting: |
| 67 | + |
| 68 | +```elixir |
| 69 | +AshNeo4j.Neo4jHelper.delete_all() |
| 70 | +``` |
| 71 | + |
| 72 | +## Setup Aliases |
| 73 | + |
| 74 | +```elixir |
| 75 | +require Ash.Query |
| 76 | +alias DiffoExample.Nbn |
| 77 | +alias DiffoExample.Nbn.NbnEthernet |
| 78 | +alias DiffoExample.Nbn.Uni |
| 79 | +alias DiffoExample.Nbn.Avc |
| 80 | +alias DiffoExample.Nbn.Ntd |
| 81 | +alias DiffoExample.Nbn.Cvc |
| 82 | +alias DiffoExample.Nbn.NniGroup |
| 83 | +alias DiffoExample.Nbn.Nni |
| 84 | +alias DiffoExample.Nbn.Technology |
| 85 | +alias DiffoExample.Nbn.Speeds |
| 86 | +import Jason, only: [encode: 2] |
| 87 | +``` |
| 88 | + |
| 89 | +## About NBN |
| 90 | + |
| 91 | +NBN (National Broadband Network) is Australia's wholesale fixed-line access network, operated by NBN Co. It provides standardised access products to Retail Service Providers (RSPs), who in turn deliver internet and other services to end customers. |
| 92 | + |
| 93 | +An RSP typically combines: |
| 94 | + |
| 95 | +* An **NBN Ethernet** access circuit (UNI + AVC) at the customer premises — the access and aggregation layer modelled in this domain |
| 96 | +* A **home gateway** device installed at the UNI, which provides the customer's LAN, Wi-Fi, and sometimes voice |
| 97 | +* Transport, aggregation, and edge infrastructure connecting the NNI to the RSP's network and on to the internet |
| 98 | + |
| 99 | +NBN Co connects the customer premises to the RSP's network via a Point of Interconnect (POI). The NNI sits at the POI, grouped into NNI Groups. AVCs carrying customer traffic are aggregated onto a CVC, which terminates at the NNI Group. The RSP purchases CVC capacity to carry the aggregate traffic of its customers at that POI. |
| 100 | + |
| 101 | +NBN is delivered over several access technologies — FTTP, FTTN, FTTB, FTTC, HFC, Fixed Wireless, and Satellite — which determine which bandwidth profiles and speeds are available to a given premises. |
| 102 | + |
| 103 | +## Technology and Speeds |
| 104 | + |
| 105 | +The NBN domain defines Technology as an Ash Enum covering all NBN access types: |
| 106 | + |
| 107 | +```elixir |
| 108 | +Technology.values() |
| 109 | +``` |
| 110 | + |
| 111 | +Speeds are derived from a bandwidth_profile and technology combination. For example: |
| 112 | + |
| 113 | +```elixir |
| 114 | +Speeds.speeds(:home_fast, :FTTP) |
| 115 | +``` |
| 116 | + |
| 117 | +```elixir |
| 118 | +Speeds.speeds(:home_hyperfast, :HFC) |
| 119 | +``` |
| 120 | + |
| 121 | +```elixir |
| 122 | +Speeds.speeds(:wireless_superfast, :FixedWireless) |
| 123 | +``` |
| 124 | + |
| 125 | +```elixir |
| 126 | +# returns :error for invalid combinations |
| 127 | +Speeds.speeds(:home_fast, :FixedWireless) |
| 128 | +``` |
| 129 | + |
| 130 | +## Building the Network Hinterland |
| 131 | + |
| 132 | +Before we can provision an NBN Ethernet access we need the shared network resources: NNI, NNI Group, and CVC. |
| 133 | + |
| 134 | +Build an NNI — the physical interconnect between the RSP and NBN Co: |
| 135 | + |
| 136 | +```elixir |
| 137 | +nni = Nbn.build_nni!(%{}) |
| 138 | +nni |> Jason.encode!(pretty: true) |> IO.puts |
| 139 | +``` |
| 140 | + |
| 141 | +Build an NNI Group — a logical grouping of NNIs at a point of interconnect: |
| 142 | + |
| 143 | +```elixir |
| 144 | +nni_group = Nbn.build_nni_group!(%{}) |
| 145 | +nni_group |> Jason.encode!(pretty: true) |> IO.puts |
| 146 | +``` |
| 147 | + |
| 148 | +Define the NNI Group with an SVLAN assignment and relate the NNI: |
| 149 | + |
| 150 | +```elixir |
| 151 | +nni_group = Nbn.define_nni_group!(%{ |
| 152 | + characteristic_value_updates: [nni_group: [svlan: 100]] |
| 153 | +}) |
| 154 | +nni_group = Nbn.relate_nni_group!(nni_group, %{ |
| 155 | + relationships: [%{alias: :nni, target_id: nni.id, type: :isAssigned}] |
| 156 | +}) |
| 157 | +nni_group |> Jason.encode!(pretty: true) |> IO.puts |
| 158 | +``` |
| 159 | + |
| 160 | +Build a CVC — the aggregation virtual circuit that terminates at the NNI Group: |
| 161 | + |
| 162 | +```elixir |
| 163 | +cvc = Nbn.build_cvc!(%{}) |
| 164 | +cvc = Nbn.relate_cvc!(cvc, %{ |
| 165 | + relationships: [%{alias: :nni_group, target_id: nni_group.id, type: :isAssigned}] |
| 166 | +}) |
| 167 | +cvc |> Jason.encode!(pretty: true) |> IO.puts |
| 168 | +``` |
| 169 | + |
| 170 | +## Provisioning an NBN Ethernet Access |
| 171 | + |
| 172 | +With the hinterland in place we can provision a customer-facing NBN Ethernet access. |
| 173 | + |
| 174 | +Build an NTD — the device installed at the customer premises: |
| 175 | + |
| 176 | +```elixir |
| 177 | +ntd = Nbn.build_ntd!(%{}) |
| 178 | +ntd = Nbn.define_ntd!(ntd, %{ |
| 179 | + characteristic_value_updates: [ntd: [technology: :FTTP, ports: [1, 2, 3, 4]]] |
| 180 | +}) |
| 181 | +ntd |> Jason.encode!(pretty: true) |> IO.puts |
| 182 | +``` |
| 183 | + |
| 184 | +Build a UNI — the interface at the customer premises — and assign a port from the NTD: |
| 185 | + |
| 186 | +```elixir |
| 187 | +uni = Nbn.build_uni!(%{}) |
| 188 | +alias Diffo.Provider.Assignment |
| 189 | +ntd = Nbn.assign_port!(ntd, %{ |
| 190 | + assignment: %Assignment{assignee_id: uni.id, value: 1} |
| 191 | +}) |
| 192 | +ntd |> Jason.encode!(pretty: true) |> IO.puts |
| 193 | +``` |
| 194 | + |
| 195 | +Relate the UNI back to the NTD so it can mine technology and port from it: |
| 196 | + |
| 197 | +```elixir |
| 198 | +uni = Nbn.relate_uni!(uni, %{ |
| 199 | + relationships: [%{alias: :ntd, target_id: ntd.id, type: :isAssigned}] |
| 200 | +}) |
| 201 | +uni = Nbn.mine_uni!(uni, %{}) |
| 202 | +uni |> Jason.encode!(pretty: true) |> IO.puts |
| 203 | +``` |
| 204 | + |
| 205 | +Build an AVC and assign it a CVLAN from the CVC: |
| 206 | + |
| 207 | +```elixir |
| 208 | +avc = Nbn.build_avc!(%{}) |
| 209 | +avc = Nbn.define_avc!(avc, %{ |
| 210 | + characteristic_value_updates: [avc: [bandwidth_profile: :home_ultrafast]] |
| 211 | +}) |
| 212 | +cvc = Nbn.assign_cvlan!(cvc, %{ |
| 213 | + assignment: %Assignment{assignee_id: avc.id, value: 200} |
| 214 | +}) |
| 215 | +avc = Nbn.mine_avc!(avc, %{}) |
| 216 | +avc |> Jason.encode!(pretty: true) |> IO.puts |
| 217 | +``` |
| 218 | + |
| 219 | +Now build the top-level NBN Ethernet access and relate it to both the UNI and AVC: |
| 220 | + |
| 221 | +```elixir |
| 222 | +pri = Nbn.build_nbn_ethernet!(%{}) |
| 223 | +pri = Nbn.relate_nbn_ethernet!(pri, %{ |
| 224 | + relationships: [ |
| 225 | + %{alias: :uni, target_id: uni.id, type: :isAssigned}, |
| 226 | + %{alias: :avc, target_id: avc.id, type: :isAssigned} |
| 227 | + ] |
| 228 | +}) |
| 229 | +pri = Nbn.mine_nbn_ethernet!(pri, %{}) |
| 230 | +pri |> Jason.encode!(pretty: true) |> IO.puts |
| 231 | +``` |
| 232 | + |
| 233 | +The `mine` action on NbnEthernet extracts technology from the UNI and bandwidth_profile from the AVC and derives the speeds automatically. |
| 234 | + |
| 235 | +## Exploring the Graph |
| 236 | + |
| 237 | +You can query all nodes and relationships in Neo4j browser with: |
| 238 | + |
| 239 | +```cypher |
| 240 | +MATCH (n1)-[r]->(n2) RETURN r, n1, n2 LIMIT 50 |
| 241 | +``` |
| 242 | + |
| 243 | +Or from Elixir: |
| 244 | + |
| 245 | +```elixir |
| 246 | +AshNeo4j.Cypher.run("MATCH (n1)-[r]->(n2) RETURN r, n1, n2 LIMIT 50") |
| 247 | +``` |
| 248 | + |
| 249 | +## What Next? |
| 250 | + |
| 251 | +You've provisioned a complete NBN Ethernet access — NTD, UNI, AVC, CVC, NNI Group, and NNI — and seen how the `mine` actions propagate technology, speeds, CVLAN and port assignments up the resource hierarchy automatically. |
| 252 | + |
| 253 | +The Access domain in `diffo_example` shows a similar pattern for DSL access services. Explore `lib/access/` for copper-network equivalents (Cable, Card, Path, Shelf). |
| 254 | + |
| 255 | +If you find Diffo useful please visit and star on [GitHub](https://github.com/diffo-dev/diffo/). |
0 commit comments