@@ -300,10 +300,83 @@ def _populate(self, json):
300300
301301@dataclass
302302class ConfigInterfaceIPv4 (JSONObject ):
303+ """
304+ ConfigInterfaceIPv4 represents the IPv4 configuration of a VPC interface.
305+ """
306+
303307 vpc : str = ""
304308 nat_1_1 : str = ""
305309
306310
311+ @dataclass
312+ class ConfigInterfaceIPv6SLAACOptions (JSONObject ):
313+ """
314+ ConfigInterfaceIPv6SLAACOptions is used to set a single IPv6 SLAAC configuration of a VPC interface.
315+ """
316+
317+ range : str = ""
318+
319+
320+ @dataclass
321+ class ConfigInterfaceIPv6RangeOptions (JSONObject ):
322+ """
323+ ConfigInterfaceIPv6RangeOptions is used to set a single IPv6 range configuration of a VPC interface.
324+ """
325+
326+ range : str = ""
327+
328+
329+ @dataclass
330+ class ConfigInterfaceIPv6Options (JSONObject ):
331+ """
332+ ConfigInterfaceIPv6Options is used to set the IPv6 configuration of a VPC interface.
333+ """
334+
335+ slaac : List [ConfigInterfaceIPv6SLAACOptions ] = field (
336+ default_factory = lambda : []
337+ )
338+ ranges : List [ConfigInterfaceIPv6RangeOptions ] = field (
339+ default_factory = lambda : []
340+ )
341+ is_public : bool = False
342+
343+
344+ @dataclass
345+ class ConfigInterfaceIPv6SLAAC (JSONObject ):
346+ """
347+ ConfigInterfaceIPv6SLAAC represents a single SLAAC address under a VPC interface's IPv6 configuration.
348+ """
349+
350+ put_class = ConfigInterfaceIPv6SLAACOptions
351+
352+ range : str = ""
353+ address : str = ""
354+
355+
356+ @dataclass
357+ class ConfigInterfaceIPv6Range (JSONObject ):
358+ """
359+ ConfigInterfaceIPv6Range represents a single IPv6 address under a VPC interface's IPv6 configuration.
360+ """
361+
362+ put_class = ConfigInterfaceIPv6RangeOptions
363+
364+ range : str = ""
365+
366+
367+ @dataclass
368+ class ConfigInterfaceIPv6 (JSONObject ):
369+ """
370+ ConfigInterfaceIPv6 represents the IPv6 configuration of a VPC interface.
371+ """
372+
373+ put_class = ConfigInterfaceIPv6Options
374+
375+ slaac : List [ConfigInterfaceIPv6SLAAC ] = field (default_factory = lambda : [])
376+ ranges : List [ConfigInterfaceIPv6Range ] = field (default_factory = lambda : [])
377+ is_public : bool = False
378+
379+
307380class NetworkInterface (DerivedBase ):
308381 """
309382 This class represents a Configuration Profile's network interface object.
@@ -329,6 +402,7 @@ class NetworkInterface(DerivedBase):
329402 "vpc_id" : Property (id_relationship = VPC ),
330403 "subnet_id" : Property (),
331404 "ipv4" : Property (mutable = True , json_object = ConfigInterfaceIPv4 ),
405+ "ipv6" : Property (mutable = True , json_object = ConfigInterfaceIPv6 ),
332406 "ip_ranges" : Property (mutable = True ),
333407 }
334408
@@ -400,7 +474,10 @@ class ConfigInterface(JSONObject):
400474 # VPC-specific
401475 vpc_id : Optional [int ] = None
402476 subnet_id : Optional [int ] = None
477+
403478 ipv4 : Optional [Union [ConfigInterfaceIPv4 , Dict [str , Any ]]] = None
479+ ipv6 : Optional [Union [ConfigInterfaceIPv6 , Dict [str , Any ]]] = None
480+
404481 ip_ranges : Optional [List [str ]] = None
405482
406483 # Computed
@@ -409,7 +486,7 @@ class ConfigInterface(JSONObject):
409486 def __repr__ (self ):
410487 return f"Interface: { self .purpose } "
411488
412- def _serialize (self , * args , ** kwargs ):
489+ def _serialize (self , is_put : bool = False ):
413490 purpose_formats = {
414491 "public" : {"purpose" : "public" , "primary" : self .primary },
415492 "vlan" : {
@@ -421,11 +498,8 @@ def _serialize(self, *args, **kwargs):
421498 "purpose" : "vpc" ,
422499 "primary" : self .primary ,
423500 "subnet_id" : self .subnet_id ,
424- "ipv4" : (
425- self .ipv4 .dict
426- if isinstance (self .ipv4 , ConfigInterfaceIPv4 )
427- else self .ipv4
428- ),
501+ "ipv4" : self .ipv4 ,
502+ "ipv6" : self .ipv6 ,
429503 "ip_ranges" : self .ip_ranges ,
430504 },
431505 }
@@ -435,11 +509,14 @@ def _serialize(self, *args, **kwargs):
435509 f"Unknown interface purpose: { self .purpose } " ,
436510 )
437511
438- return {
439- k : v
440- for k , v in purpose_formats [self .purpose ].items ()
441- if v is not None
442- }
512+ return _flatten_request_body_recursive (
513+ {
514+ k : v
515+ for k , v in purpose_formats [self .purpose ].items ()
516+ if v is not None
517+ },
518+ is_put = is_put ,
519+ )
443520
444521
445522class Config (DerivedBase ):
@@ -580,6 +657,7 @@ def interface_create_vpc(
580657 subnet : Union [int , VPCSubnet ],
581658 primary = False ,
582659 ipv4 : Union [Dict [str , Any ], ConfigInterfaceIPv4 ] = None ,
660+ ipv6 : Union [Dict [str , Any ], ConfigInterfaceIPv6Options ] = None ,
583661 ip_ranges : Optional [List [str ]] = None ,
584662 ) -> NetworkInterface :
585663 """
@@ -593,6 +671,8 @@ def interface_create_vpc(
593671 :type primary: bool
594672 :param ipv4: The IPv4 configuration of the interface for the associated subnet.
595673 :type ipv4: Dict or ConfigInterfaceIPv4
674+ :param ipv6: The IPv6 configuration of the interface for the associated subnet.
675+ :type ipv6: Dict or ConfigInterfaceIPv6Options
596676 :param ip_ranges: A list of IPs or IP ranges in the VPC subnet.
597677 Packets to these CIDRs are routed through the
598678 VPC network interface.
@@ -603,19 +683,16 @@ def interface_create_vpc(
603683 """
604684 params = {
605685 "purpose" : "vpc" ,
606- "subnet_id" : subnet . id if isinstance ( subnet , VPCSubnet ) else subnet ,
686+ "subnet_id" : subnet ,
607687 "primary" : primary ,
688+ "ipv4" : ipv4 ,
689+ "ipv6" : ipv6 ,
690+ "ip_ranges" : ip_ranges ,
608691 }
609692
610- if ipv4 is not None :
611- params ["ipv4" ] = (
612- ipv4 .dict if isinstance (ipv4 , ConfigInterfaceIPv4 ) else ipv4
613- )
614-
615- if ip_ranges is not None :
616- params ["ip_ranges" ] = ip_ranges
617-
618- return self ._interface_create (params )
693+ return self ._interface_create (
694+ drop_null_keys (_flatten_request_body_recursive (params ))
695+ )
619696
620697 def interface_reorder (self , interfaces : List [Union [int , NetworkInterface ]]):
621698 """
@@ -2018,6 +2095,7 @@ def linode_interfaces(self) -> Optional[list[LinodeInterface]]:
20182095
20192096 if self .interface_generation != InterfaceGeneration .LINODE :
20202097 return None
2098+
20212099 if not hasattr (self , "_interfaces" ):
20222100 result = self ._client .get (
20232101 "{}/interfaces" .format (Instance .api_endpoint ),
0 commit comments