Negotiated._negotiate()   F
last analyzed

Complexity

Conditions 24

Size

Total Lines 74
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 24
eloc 50
nop 1
dl 0
loc 74
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like exabgp.bgp.message.open.capability.negotiated.Negotiated._negotiate() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# encoding: utf-8
2
"""
3
negotiated.py
4
5
Created by Thomas Mangin on 2012-07-19.
6
Copyright (c) 2009-2017 Exa Networks. All rights reserved.
7
License: 3-clause BSD. (See the COPYRIGHT file)
8
"""
9
10
from exabgp.protocol.family import AFI
11
from exabgp.bgp.message.open.asn import ASN
12
from exabgp.bgp.message.open.asn import AS_TRANS
13
from exabgp.bgp.message.open.holdtime import HoldTime
14
from exabgp.bgp.message.open.capability.capability import Capability
15
from exabgp.bgp.message.open.capability.refresh import REFRESH
16
from exabgp.bgp.message.open.routerid import RouterID
17
from exabgp.bgp.message.open.capability.extended import ExtendedMessage
18
19
20
class Negotiated(object):
21
    FREE_SIZE = ExtendedMessage.INITIAL_SIZE - 19 - 2 - 2
22
23
    def __init__(self, neighbor):
24
        self.neighbor = neighbor
25
26
        self.sent_open = None
27
        self.received_open = None
28
29
        self.holdtime = HoldTime(0)
30
        self.local_as = ASN(0)
31
        self.peer_as = ASN(0)
32
        self.families = []
33
        self.nexthop = []
34
        self.asn4 = False
35
        self.addpath = RequirePath()
36
        self.multisession = False
37
        self.msg_size = ExtendedMessage.INITIAL_SIZE
38
        self.operational = False
39
        self.refresh = REFRESH.ABSENT  # pylint: disable=E1101
40
        self.aigp = None
41
        self.mismatch = []
42
43
    def sent(self, sent_open):
44
        self.sent_open = sent_open
45
        if self.received_open:
46
            self._negotiate()
47
48
    def received(self, received_open):
49
        self.received_open = received_open
50
        if self.sent_open:
51
            self._negotiate()
52
53
    def _negotiate(self):
54
        sent_capa = self.sent_open.capabilities
55
        recv_capa = self.received_open.capabilities
56
57
        self.holdtime = HoldTime(min(self.sent_open.hold_time, self.received_open.hold_time))
58
59
        self.addpath.setup(self.received_open, self.sent_open)
60
        self.asn4 = sent_capa.announced(Capability.CODE.FOUR_BYTES_ASN) and recv_capa.announced(
61
            Capability.CODE.FOUR_BYTES_ASN
62
        )
63
        self.operational = sent_capa.announced(Capability.CODE.OPERATIONAL) and recv_capa.announced(
64
            Capability.CODE.OPERATIONAL
65
        )
66
67
        self.local_as = self.sent_open.asn
68
        self.peer_as = self.received_open.asn
69
        if self.received_open.asn == AS_TRANS and self.asn4:
70
            self.peer_as = recv_capa.get(Capability.CODE.FOUR_BYTES_ASN, self.peer_as)
71
72
        self.families = []
73
        if recv_capa.announced(Capability.CODE.MULTIPROTOCOL) and sent_capa.announced(Capability.CODE.MULTIPROTOCOL):
74
            for family in recv_capa[Capability.CODE.MULTIPROTOCOL]:
75
                if family in sent_capa[Capability.CODE.MULTIPROTOCOL]:
76
                    self.families.append(family)
77
78
        self.nexthop = []
79
        if recv_capa.announced(Capability.CODE.NEXTHOP) and sent_capa.announced(Capability.CODE.NEXTHOP):
80
            for family in recv_capa[Capability.CODE.NEXTHOP]:
81
                if family in sent_capa[Capability.CODE.NEXTHOP]:
82
                    self.nexthop.append(family)
83
84
        if recv_capa.announced(Capability.CODE.ENHANCED_ROUTE_REFRESH) and sent_capa.announced(
85
            Capability.CODE.ENHANCED_ROUTE_REFRESH
86
        ):
87
            self.refresh = REFRESH.ENHANCED  # pylint: disable=E1101
88
        elif recv_capa.announced(Capability.CODE.ROUTE_REFRESH) and sent_capa.announced(Capability.CODE.ROUTE_REFRESH):
89
            self.refresh = REFRESH.NORMAL  # pylint: disable=E1101
90
91
        if recv_capa.announced(Capability.CODE.EXTENDED_MESSAGE) and sent_capa.announced(
92
            Capability.CODE.EXTENDED_MESSAGE
93
        ):
94
            self.msg_size = ExtendedMessage.EXTENDED_SIZE
95
96
        self.multisession = sent_capa.announced(Capability.CODE.MULTISESSION) and recv_capa.announced(
97
            Capability.CODE.MULTISESSION
98
        )
99
        self.multisession |= sent_capa.announced(Capability.CODE.MULTISESSION_CISCO) and recv_capa.announced(
100
            Capability.CODE.MULTISESSION_CISCO
101
        )
102
103
        if self.multisession:
104
            sent_ms_capa = set(sent_capa[Capability.CODE.MULTISESSION])
105
            recv_ms_capa = set(recv_capa[Capability.CODE.MULTISESSION])
106
107
            if sent_ms_capa == set([]):
108
                sent_ms_capa = set([Capability.CODE.MULTIPROTOCOL])
109
            if recv_ms_capa == set([]):
110
                recv_ms_capa = set([Capability.CODE.MULTIPROTOCOL])
111
112
            if sent_ms_capa != recv_ms_capa:
113
                self.multisession = (2, 8, 'multisession, our peer did not reply with the same sessionid')
114
115
            # The way we implement MS-BGP, we only send one MP per session
116
            # therefore we can not collide due to the way we generate the configuration
117
118
            for capa in sent_ms_capa:
119
                # no need to check that the capability exists, we generated it
120
                # checked it is what we sent and only send MULTIPROTOCOL
121
                if sent_capa[capa] != recv_capa[capa]:
122
                    self.multisession = (2, 8, 'when checking session id, capability %s did not match' % str(capa))
123
                    break
124
125
        elif sent_capa.announced(Capability.CODE.MULTISESSION):
126
            self.multisession = (2, 9, 'multisession is mandatory with this peer')
127
128
        # XXX: Does not work as the capa is not yet defined
129
        # if received_open.capabilities.announced(Capability.CODE.EXTENDED_MESSAGE) \
130
        # and sent_open.capabilities.announced(Capability.CODE.EXTENDED_MESSAGE):
131
        # 	if self.peer.bgp.received_open_size:
132
        # 		self.received_open_size = self.peer.bgp.received_open_size - 19
133
134
    def validate(self, neighbor):
135
        if neighbor.peer_as is not None and self.peer_as != neighbor.peer_as:
136
            return (
137
                2,
138
                2,
139
                'ASN in OPEN (%d) did not match ASN expected (%d)' % (self.received_open.asn, neighbor.peer_as),
140
            )
141
142
        # RFC 6286 : https://tools.ietf.org/html/rfc6286
143
        # XXX: FIXME: check that router id is not self
144
        if self.received_open.router_id == RouterID('0.0.0.0'):
145
            return (2, 3, '0.0.0.0 is an invalid router_id')
146
147
        if self.received_open.asn == neighbor.local_as:
148
            # router-id must be unique within an ASN
149
            if self.received_open.router_id == neighbor.router_id:
150
                return (
151
                    2,
152
                    3,
153
                    'BGP Identifier collision, same router-id (%s) on both sides of this IBGP session'
154
                    % self.received_open.router_id,
155
                )
156
157
        if self.received_open.hold_time and self.received_open.hold_time < 3:
158
            return (2, 6, 'Hold Time is invalid (%d)' % self.received_open.hold_time)
159
160
        if self.multisession not in (True, False):
161
            # XXX: FIXME: should we not use a string and perform a split like we do elswhere ?
162
            # XXX: FIXME: or should we use this trick in the other case ?
163
            return self.multisession
164
165
        s = set(self.sent_open.capabilities.get(Capability.CODE.MULTIPROTOCOL, []))
166
        r = set(self.received_open.capabilities.get(Capability.CODE.MULTIPROTOCOL, []))
167
        mismatch = s ^ r
168
169
        for family in mismatch:
170
            self.mismatch.append(('exabgp' if family in r else 'peer', family))
171
172
        return None
173
174
    def nexthopself(self, afi):
175
        if afi == self.neighbor.local_address.afi:
176
            return self.neighbor.local_address
177
178
        # attempting to not barf for next-hop self when the peer is IPv6
179
        if afi == AFI.ipv4:
180
            return self.neighbor.router_id
181
182
        raise TypeError(
183
            'use of "next-hop self": the route (%s) does not have the same family as the BGP tcp session (%s)'
184
            % (afi, self.neighbor.local_address.afi)
185
        )
186
187
188
# =================================================================== RequirePath
189
190
191
class RequirePath(object):
192
    CANT = 0b00
193
    RECEIVE = 0b01
194
    SEND = 0b10
195
    BOTH = SEND | RECEIVE
196
197
    def __init__(self):
198
        self._send = {}
199
        self._receive = {}
200
201
    def setup(self, received_open, sent_open):
202
        # A Dict always returning False
203
        class FalseDict(dict):
204
            def __getitem__(self, key):
205
                return False
206
207
        receive = received_open.capabilities.get(Capability.CODE.ADD_PATH, FalseDict())
208
        send = sent_open.capabilities.get(Capability.CODE.ADD_PATH, FalseDict())
209
210
        # python 2.4 compatibility mean no simple union but using sets.Set
211
        union = []
212
        union.extend(send.keys())
213
        union.extend([k for k in receive.keys() if k not in send.keys()])
214
215
        for k in union:
216
            self._send[k] = bool(send.get(k, self.CANT) & self.SEND and receive.get(k, self.CANT) & self.RECEIVE)
217
            self._receive[k] = bool(send.get(k, self.CANT) & self.RECEIVE and receive.get(k, self.CANT) & self.SEND)
218
219
    def send(self, afi, safi):
220
        return self._send.get((afi, safi), False)
221
222
    def receive(self, afi, safi):
223
        return self._receive.get((afi, safi), False)
224