1010from core .models import ObjectType
1111from dcim .choices import *
1212from dcim .constants import *
13+ from dcim .exceptions import UnsupportedCablePath
1314from dcim .fields import PathField
1415from dcim .utils import decompile_path_node , object_to_path_node
1516from netbox .choices import ColorChoices
2829 'CableTermination' ,
2930)
3031
31- from ..exceptions import UnsupportedCablePath
32-
3332trace_paths = Signal ()
3433
3534
@@ -615,7 +614,7 @@ def from_origin(cls, terminations):
615614 Cable or WirelessLink connects (interfaces, console ports, circuit termination, etc.). All terminations must be
616615 of the same type and must belong to the same parent object.
617616 """
618- from circuits .models import CircuitTermination
617+ from circuits .models import CircuitTermination , Circuit
619618
620619 if not terminations :
621620 return None
@@ -637,8 +636,11 @@ def from_origin(cls, terminations):
637636 raise UnsupportedCablePath (_ ("All mid-span terminations must have the same termination type" ))
638637
639638 # All mid-span terminations must all be attached to the same device
640- if (not isinstance (terminations [0 ], PathEndpoint ) and not
641- all (t .parent_object == terminations [0 ].parent_object for t in terminations [1 :])):
639+ if (
640+ not isinstance (terminations [0 ], PathEndpoint ) and
641+ not isinstance (terminations [0 ].parent_object , Circuit ) and
642+ not all (t .parent_object == terminations [0 ].parent_object for t in terminations [1 :])
643+ ):
642644 raise UnsupportedCablePath (_ ("All mid-span terminations must have the same parent object" ))
643645
644646 # Check for a split path (e.g. rear port fanning out to multiple front ports with
@@ -782,32 +784,39 @@ def from_origin(cls, terminations):
782784
783785 elif isinstance (remote_terminations [0 ], CircuitTermination ):
784786 # Follow a CircuitTermination to its corresponding CircuitTermination (A to Z or vice versa)
785- if len (remote_terminations ) > 1 :
786- is_split = True
787- break
788- circuit_termination = CircuitTermination .objects .filter (
789- circuit = remote_terminations [0 ].circuit ,
790- term_side = 'Z' if remote_terminations [0 ].term_side == 'A' else 'A'
791- ).first ()
792- if circuit_termination is None :
787+ qs = Q ()
788+ for remote_termination in remote_terminations :
789+ qs |= Q (
790+ circuit = remote_termination .circuit ,
791+ term_side = 'Z' if remote_termination .term_side == 'A' else 'A'
792+ )
793+
794+ # Get all circuit terminations
795+ circuit_terminations = CircuitTermination .objects .filter (qs )
796+
797+ if not circuit_terminations .exists ():
793798 break
794- elif circuit_termination ._provider_network :
799+ elif all ([ ct ._provider_network for ct in circuit_terminations ]) :
795800 # Circuit terminates to a ProviderNetwork
796801 path .extend ([
797- [object_to_path_node (circuit_termination ) ],
798- [object_to_path_node (circuit_termination ._provider_network )],
802+ [object_to_path_node (ct ) for ct in circuit_terminations ],
803+ [object_to_path_node (ct ._provider_network ) for ct in circuit_terminations ],
799804 ])
800805 is_complete = True
801806 break
802- elif circuit_termination .termination and not circuit_termination .cable :
807+ elif all ([ ct .termination and not ct .cable for ct in circuit_terminations ]) :
803808 # Circuit terminates to a Region/Site/etc.
804809 path .extend ([
805- [object_to_path_node (circuit_termination ) ],
806- [object_to_path_node (circuit_termination .termination )],
810+ [object_to_path_node (ct ) for ct in circuit_terminations ],
811+ [object_to_path_node (ct .termination ) for ct in circuit_terminations ],
807812 ])
808813 break
814+ elif any ([ct .cable in links for ct in circuit_terminations ]):
815+ # No valid path
816+ is_split = True
817+ break
809818
810- terminations = [ circuit_termination ]
819+ terminations = circuit_terminations
811820
812821 else :
813822 # Check for non-symmetric path
0 commit comments