Skip to content

Commit c495ccc

Browse files
thomas-manginclaude
andcommitted
Refactor: BGP-LS node attributes to packed-bytes-first pattern
Convert remaining BGP-LS node attribute classes to properly use BaseLS.__init__(packed: bytes) and @Property content accessors: - IsisArea: content property unpacks int from hex - LocalTeRid: content property unpacks list[str], custom merge - NodeOpaque: content property returns raw bytes - SrAlgorithm: content property unpacks list[int] Progress: ~90% complete (105/118 convertible classes) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 519fe15 commit c495ccc

File tree

5 files changed

+47
-35
lines changed

5 files changed

+47
-35
lines changed

plan/packed-bytes/progress.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,16 @@ cached instances for identical parameter combinations.
198198
| `attribute/bgpls/link/temetric.py` | TeMetric || `make_temetric(int)` |
199199
| `attribute/bgpls/link/unrsvpbw.py` | UnRsvpBw || `make_unrsvpbw(list[float])` |
200200

201-
### BGP-LS Node Attributes 🔄 PARTIAL
201+
### BGP-LS Node Attributes ✅ COMPLETE
202202

203203
| File | Class | Status | Notes |
204204
|------|-------|--------|-------|
205-
| `attribute/bgpls/node/isisarea.py` | IsisArea | | |
206-
| `attribute/bgpls/node/lterid.py` | LocalTeRid | | |
205+
| `attribute/bgpls/node/isisarea.py` | IsisArea | | `content` property unpacks int |
206+
| `attribute/bgpls/node/lterid.py` | LocalTeRid | | `content` property unpacks list[str] |
207207
| `attribute/bgpls/node/nodeflags.py` | NodeFlags || (FlagLS, use `unpack_bgpls`) |
208208
| `attribute/bgpls/node/nodename.py` | NodeName || `make_nodename(str)` |
209-
| `attribute/bgpls/node/opaque.py` | NodeOpaque | | |
210-
| `attribute/bgpls/node/sralgo.py` | SrAlgorithm | | |
209+
| `attribute/bgpls/node/opaque.py` | NodeOpaque | | `content` property returns bytes |
210+
| `attribute/bgpls/node/sralgo.py` | SrAlgorithm | | `content` property unpacks list[int] |
211211
| `attribute/bgpls/node/srcap.py` | SrCapabilities || Properties unpack from `_packed` |
212212

213213
### BGP-LS Prefix Attributes 🔄 PARTIAL
@@ -321,14 +321,14 @@ cached instances for identical parameter combinations.
321321
| Wave 1 | 4 | 0 | 0 | 0 | 4 |
322322
| Wave 2 | 10 | 0 | 0 | 1 | 11 |
323323
| Wave 3 | ~20 | 0 | 0 | 0 | ~20 |
324-
| Wave 4 | ~31 | 0 | ~16 | 3 | ~50 |
324+
| Wave 4 | ~35 | 0 | ~12 | 3 | ~50 |
325325
| Wave 5 | 5 | 0 | 0 | 0 | 5 |
326326
| Wave 6 | 5 | 1 | 0 | 3 | 9 |
327327
| Wave 7 | ~20 | 0 | 0 | 0 | ~20 |
328328
| Wave 8 | 6 | 0 | 0 | 0 | 6 |
329-
| **TOTAL** | **~101** | **1** | **~16** | **7** | **~125** |
329+
| **TOTAL** | **~105** | **1** | **~12** | **7** | **~125** |
330330

331-
**Completion: ~86%** (101 done + 1 partial out of ~118 convertible classes)
331+
**Completion: ~90%** (105 done + 1 partial out of ~118 convertible classes)
332332

333333
---
334334

@@ -341,7 +341,8 @@ cached instances for identical parameter combinations.
341341
- Updated GenericSrv6* classes: reordered params to `__init__(packed, code)`
342342
- Marked Srv6L2Service/L3Service/SidInformation as N/A (container pattern like PrefixSid)
343343
- Updated call sites in `configuration/static/mpls.py`
344-
- All 9 test suites pass (89 SR-specific tests)
344+
- Converted BGP-LS Node attributes (IsisArea, LocalTeRid, NodeOpaque, SrAlgorithm) to use `content` property
345+
- All 9 test suites pass
345346

346347
### 2025-12-04 (Session 2)
347348
- Converted MPRNLRI/MPURNLRI to hybrid packed-bytes pattern
@@ -361,7 +362,6 @@ Converted BGP-LS base classes and key subclasses to packed-bytes-first pattern:
361362

362363
## Next Priority
363364

364-
**Remaining Wave 4 classes** (~16 pending):
365-
1. BGP-LS node attributes (IsisArea, LocalTeRid, NodeOpaque, SrAlgorithm)
366-
2. BGP-LS prefix attributes (IgpExTags, IgpFlags, IgpTags, PrefixOpaque, OspfForwardingAddress, SrIgpPrefixAttr, SrSourceRouterID)
367-
3. BGP-LS link SRv6 attributes (Srv6Capabilities, Srv6EndpointBehavior, Srv6Locator, Srv6SidStructure)
365+
**Remaining Wave 4 classes** (~12 pending):
366+
1. BGP-LS prefix attributes (IgpExTags, IgpFlags, IgpTags, PrefixOpaque, OspfForwardingAddress, SrIgpPrefixAttr, SrSourceRouterID)
367+
2. BGP-LS link SRv6 attributes (Srv6Capabilities, Srv6EndpointBehavior, Srv6Locator, Srv6SidStructure)

src/exabgp/bgp/message/update/attribute/bgpls/node/isisarea.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ class IsisArea(BaseLS):
2626
REPR = 'ISIS area id'
2727
JSON = 'area-id'
2828

29-
def __init__(self, areaid: int) -> None:
30-
BaseLS.__init__(self, areaid)
31-
3229
@classmethod
3330
def unpack_bgpls(cls, data: bytes) -> IsisArea:
3431
if not data:
3532
raise Notify(3, 5, 'ISIS Area: empty data')
36-
return cls(int(data.hex(), 16))
33+
return cls(data)
34+
35+
@property
36+
def content(self) -> int:
37+
"""ISIS area ID as integer."""
38+
return int(self._packed.hex(), 16)
3739

3840
def json(self, compact: bool = False) -> str:
3941
return f'"{self.JSON}": "{self.content}"'

src/exabgp/bgp/message/update/attribute/bgpls/node/lterid.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ class LocalTeRid(BaseLS):
2828
REPR = 'Local TE Router IDs'
2929
JSON = 'local-te-router-ids'
3030

31-
def __init__(self, terids: list[str]) -> None:
32-
BaseLS.__init__(self, terids)
31+
def __init__(self, packed: bytes) -> None:
32+
self._packed = packed
33+
# For merge support, content is a list of packed bytes
34+
self._content_list: list[bytes] = [packed]
3335

3436
@classmethod
3537
def unpack_bgpls(cls, data: bytes) -> LocalTeRid:
@@ -38,7 +40,16 @@ def unpack_bgpls(cls, data: bytes) -> LocalTeRid:
3840
if length not in (4, 16):
3941
raise Notify(3, 5, 'Invalid remote-te size')
4042

41-
return cls([str(IP.unpack_ip(data))])
43+
return cls(data)
44+
45+
@property
46+
def content(self) -> list[str]:
47+
"""List of TE Router IDs as strings."""
48+
return [str(IP.unpack_ip(data)) for data in self._content_list]
49+
50+
def merge(self, other: 'LocalTeRid') -> None:
51+
"""Merge another LocalTeRid's packed bytes into this one."""
52+
self._content_list.extend(other._content_list)
4253

4354
def json(self, compact: bool = False) -> str:
4455
joined = '", "'.join(self.content)

src/exabgp/bgp/message/update/attribute/bgpls/node/opaque.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from __future__ import annotations
88

99
import json
10-
from struct import unpack
1110

1211
from exabgp.bgp.message.update.attribute.bgpls.linkstate import BaseLS
1312
from exabgp.bgp.message.update.attribute.bgpls.linkstate import LinkState
@@ -32,12 +31,14 @@ class NodeOpaque(BaseLS):
3231
REPR = 'Node Opaque attribute'
3332
JSON = 'opaque'
3433

35-
def __init__(self, opaque: bytes) -> None:
36-
BaseLS.__init__(self, opaque)
37-
3834
@classmethod
3935
def unpack_bgpls(cls, data: bytes) -> NodeOpaque:
40-
return cls(unpack('!%ds' % len(data), data)[0])
36+
return cls(data)
37+
38+
@property
39+
def content(self) -> bytes:
40+
"""Opaque data as bytes."""
41+
return self._packed
4142

4243
def json(self, compact: bool = False) -> str:
43-
return f'"{self.JSON}": {json.dumps(self.content)}'
44+
return f'"{self.JSON}": {json.dumps(self._packed.hex())}'

src/exabgp/bgp/message/update/attribute/bgpls/node/sralgo.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,17 @@ class SrAlgorithm(BaseLS):
3030
REPR = 'SrAlgorithms'
3131
JSON = 'sr-algorithms'
3232

33-
def __init__(self, sr_algos: list[int]) -> None:
34-
BaseLS.__init__(self, sr_algos)
35-
3633
@classmethod
3734
def unpack_bgpls(cls, data: bytes) -> SrAlgorithm:
3835
# Looks like IOS XR advertises len 0 on this sub TLV
3936
# when using default SPF.
40-
return cls(
41-
[_ for _ in data]
42-
or [
43-
0,
44-
],
45-
)
37+
return cls(data)
38+
39+
@property
40+
def content(self) -> list[int]:
41+
"""List of SR algorithm values."""
42+
# Empty data means default SPF algorithm (0)
43+
return list(self._packed) or [0]
4644

4745
def json(self, compact: bool = False) -> str:
4846
return f'"{self.JSON}": {json.dumps(self.content)}'

0 commit comments

Comments
 (0)