| Total Complexity | 64 |
| Total Lines | 439 |
| Duplicated Lines | 2.51 % |
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like exabgp.reactor.api.response.json 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 | #!/usr/bin/env python |
||
| 2 | # encoding: utf-8 |
||
| 3 | """ |
||
| 4 | Response/json.py |
||
| 5 | |||
| 6 | Created by Thomas Mangin on 2012-12-30. |
||
| 7 | Copyright (c) 2009-2017 Exa Networks. All rights reserved. |
||
| 8 | License: 3-clause BSD. (See the COPYRIGHT file) |
||
| 9 | """ |
||
| 10 | |||
| 11 | import os |
||
| 12 | import socket |
||
| 13 | import sys |
||
| 14 | import time |
||
| 15 | import signal |
||
| 16 | |||
| 17 | from exabgp.vendoring import six |
||
| 18 | |||
| 19 | from exabgp.util import hexstring |
||
| 20 | |||
| 21 | from exabgp.bgp.message import Message |
||
| 22 | from exabgp.bgp.message import IN |
||
| 23 | |||
| 24 | from exabgp.configuration.environment import environment |
||
| 25 | from exabgp.bgp.message.open.capability.refresh import REFRESH |
||
| 26 | |||
| 27 | |||
| 28 | if sys.version_info > (3,): |
||
| 29 | long = int |
||
| 30 | |||
| 31 | SIGNAL_NAME = dict( |
||
| 32 | (k, v) for v, k in reversed(sorted(signal.__dict__.items())) if v.startswith('SIG') and not v.startswith('SIG_') |
||
| 33 | ) |
||
| 34 | |||
| 35 | |||
| 36 | def nop(_): |
||
| 37 | return _ |
||
| 38 | |||
| 39 | |||
| 40 | class JSON(object): |
||
| 41 | _count = {} |
||
| 42 | |||
| 43 | def __init__(self, version): |
||
| 44 | self.version = version |
||
| 45 | self.time = nop |
||
| 46 | self.compact = environment.settings().api.compact |
||
| 47 | |||
| 48 | # def _reset (self, neighbor): |
||
| 49 | # self._count[neighbor.uid] = 0 |
||
| 50 | # return 0 |
||
| 51 | |||
| 52 | def _counter(self, neighbor): |
||
| 53 | increased = self._count.get(neighbor.uid, 0) + 1 |
||
| 54 | self._count[neighbor.uid] = increased |
||
| 55 | return increased |
||
| 56 | |||
| 57 | def _string(self, object): |
||
| 58 | if issubclass(object.__class__, bool): |
||
| 59 | return 'true' if object else 'false' |
||
| 60 | if issubclass(object.__class__, long): |
||
|
|
|||
| 61 | return '%s' % object |
||
| 62 | if issubclass(object.__class__, int): |
||
| 63 | return '%s' % object |
||
| 64 | string = '%s' % object |
||
| 65 | if '{' in string: |
||
| 66 | return string |
||
| 67 | if '[' in string: |
||
| 68 | return string |
||
| 69 | return '"%s"' % object |
||
| 70 | |||
| 71 | def _header(self, content, header, body, neighbor, message_type=None): |
||
| 72 | peer = '"host" : "%s", ' % socket.gethostname() |
||
| 73 | pid = '"pid" : %s, ' % os.getpid() |
||
| 74 | ppid = '"ppid" : %s, ' % os.getppid() |
||
| 75 | counter = '"counter": %s, ' % self._counter(neighbor) if neighbor is not None else '' |
||
| 76 | header = '"header": "%s", ' % hexstring(header) if header else '' |
||
| 77 | body = '"body": "%s", ' % hexstring(body) if body else '' |
||
| 78 | mtype = '"type": "%s", ' % message_type if message_type else 'default' |
||
| 79 | |||
| 80 | return ( |
||
| 81 | '{ ' |
||
| 82 | '"exabgp": "%s", ' |
||
| 83 | '"time": %s, ' |
||
| 84 | '%s%s%s%s%s%s%s%s ' |
||
| 85 | '}' % (self.version, self.time(time.time()), peer, pid, ppid, counter, mtype, header, body, content) |
||
| 86 | ) |
||
| 87 | |||
| 88 | __neighbor = '''\ |
||
| 89 | "neighbor": { |
||
| 90 | "address": { "local": "%s", "peer": "%s" }, |
||
| 91 | "asn": { "local": %s, "peer": %s } |
||
| 92 | %s%s%s%s |
||
| 93 | }'''.replace( |
||
| 94 | '\t', '' |
||
| 95 | ).replace( |
||
| 96 | '\n', ' ' |
||
| 97 | ) |
||
| 98 | |||
| 99 | def _neighbor(self, neighbor, direction, content): |
||
| 100 | return self.__neighbor % ( |
||
| 101 | neighbor.local_address, |
||
| 102 | neighbor.peer_address, |
||
| 103 | neighbor.local_as, |
||
| 104 | neighbor.peer_as, |
||
| 105 | ', ' if direction else '', |
||
| 106 | '"direction": "%s"' % direction if direction else '', |
||
| 107 | ', ' if content else ' ', |
||
| 108 | content, |
||
| 109 | ) |
||
| 110 | |||
| 111 | def _kv(self, extra): |
||
| 112 | return ", ".join('"%s": %s' % (k, self._string(v)) for (k, v) in six.iteritems(extra)) |
||
| 113 | |||
| 114 | def _json_kv(self, extra): |
||
| 115 | return ", ".join('"%s": %s' % (k, v.json()) for (k, v) in six.iteritems(extra)) |
||
| 116 | |||
| 117 | def _json_list(self, extra): |
||
| 118 | return ", ".join('%s' % (v.json()) for v in six.iteritems(extra)) |
||
| 119 | |||
| 120 | def _minimalkv(self, extra): |
||
| 121 | return ", ".join('"%s": %s' % (k, self._string(v)) for (k, v) in six.iteritems(extra) if v) |
||
| 122 | |||
| 123 | def up(self, neighbor): |
||
| 124 | return self._header( |
||
| 125 | self._neighbor(neighbor, None, self._kv({'state': 'up',})), '', '', neighbor, message_type='state' |
||
| 126 | ) |
||
| 127 | |||
| 128 | def connected(self, neighbor): |
||
| 129 | return self._header( |
||
| 130 | self._neighbor(neighbor, None, self._kv({'state': 'connected',})), '', '', neighbor, message_type='state' |
||
| 131 | ) |
||
| 132 | |||
| 133 | def down(self, neighbor, reason=''): |
||
| 134 | def escape_quote(reason): |
||
| 135 | # the {} and [] change is an horrible hack until we generate python objects |
||
| 136 | # as otherwise we interpret the string as a list or dict |
||
| 137 | return reason.replace('[', '(').replace(']', ')').replace('{', '(').replace('}', ')').replace('"', '\\"') |
||
| 138 | |||
| 139 | return self._header( |
||
| 140 | self._neighbor(neighbor, None, self._kv({'state': 'down', 'reason': escape_quote(reason),})), |
||
| 141 | '', |
||
| 142 | '', |
||
| 143 | neighbor, |
||
| 144 | message_type='state', |
||
| 145 | ) |
||
| 146 | |||
| 147 | def shutdown(self): |
||
| 148 | return self._header(self._kv({'notification': 'shutdown',}), '', '', None, message_type='notification') |
||
| 149 | |||
| 150 | def _negotiated(self, negotiated): |
||
| 151 | return { |
||
| 152 | 'negotiated': '{ %s } ' |
||
| 153 | % self._kv( |
||
| 154 | { |
||
| 155 | 'message_size': negotiated.msg_size, |
||
| 156 | 'hold_time': negotiated.holdtime, |
||
| 157 | 'asn4': negotiated.asn4, |
||
| 158 | 'multisession': negotiated.multisession, |
||
| 159 | 'operational': negotiated.operational, |
||
| 160 | 'refresh': REFRESH.json(negotiated.refresh), |
||
| 161 | 'families': '[ %s ]' % ' ,'.join(['"%s %s"' % family for family in negotiated.families]), |
||
| 162 | 'nexthop': '[ %s ]' % ' ,'.join(['"%s %s %s"' % family for family in negotiated.nexthop]), |
||
| 163 | 'add_path': '{ "send": %s, "receive": %s }' |
||
| 164 | % ( |
||
| 165 | '[ %s ]' |
||
| 166 | % ', '.join([family for family in negotiated.families if negotiated.addpath.send(*family)]), |
||
| 167 | '[ %s ]' |
||
| 168 | % ', '.join( |
||
| 169 | [ |
||
| 170 | '"%s %s"' % family |
||
| 171 | for family in negotiated.families |
||
| 172 | if negotiated.addpath.receive(*family) |
||
| 173 | ] |
||
| 174 | ), |
||
| 175 | ), |
||
| 176 | } |
||
| 177 | ) |
||
| 178 | } |
||
| 179 | |||
| 180 | def negotiated(self, neighbor, negotiated): |
||
| 181 | return self._header( |
||
| 182 | self._neighbor(neighbor, None, self._kv(self._negotiated(negotiated))), |
||
| 183 | '', |
||
| 184 | '', |
||
| 185 | neighbor, |
||
| 186 | message_type='negotiated', |
||
| 187 | ) |
||
| 188 | |||
| 189 | def fsm(self, neighbor, fsm): |
||
| 190 | return self._header( |
||
| 191 | self._neighbor(neighbor, None, self._kv({'state': fsm.name()})), '', '', neighbor, message_type='fsm' |
||
| 192 | ) |
||
| 193 | |||
| 194 | def signal(self, neighbor, signal): |
||
| 195 | return self._header( |
||
| 196 | self._neighbor( |
||
| 197 | neighbor, None, self._kv({'code': '%d' % signal, 'name': SIGNAL_NAME.get(signal, 'UNKNOWN'),}) |
||
| 198 | ), |
||
| 199 | '', |
||
| 200 | '', |
||
| 201 | neighbor, |
||
| 202 | message_type='signal', |
||
| 203 | ) |
||
| 204 | |||
| 205 | def notification(self, neighbor, direction, message, negotiated, header, body): |
||
| 206 | return self._header( |
||
| 207 | self._neighbor( |
||
| 208 | neighbor, |
||
| 209 | direction, |
||
| 210 | self._kv( |
||
| 211 | { |
||
| 212 | 'notification': '{ %s } ' |
||
| 213 | % self._kv({'code': message.code, 'subcode': message.subcode, 'data': hexstring(message.data),}) |
||
| 214 | } |
||
| 215 | ), |
||
| 216 | ), |
||
| 217 | header, |
||
| 218 | body, |
||
| 219 | neighbor, |
||
| 220 | message_type='notification', |
||
| 221 | ) |
||
| 222 | |||
| 223 | def packets(self, neighbor, direction, category, negotiated, header, body): |
||
| 224 | message = { |
||
| 225 | 'message': '{ %s } ' |
||
| 226 | % self._kv({'category': category, 'header': hexstring(header), 'body': hexstring(body),}) |
||
| 227 | } |
||
| 228 | if negotiated: |
||
| 229 | message.update(self._negotiated(negotiated)) |
||
| 230 | return self._header( |
||
| 231 | self._neighbor(neighbor, direction, self._kv(message)), |
||
| 232 | '', |
||
| 233 | '', |
||
| 234 | neighbor, |
||
| 235 | message_type=Message.string(category), |
||
| 236 | ) |
||
| 237 | |||
| 238 | def keepalive(self, neighbor, direction, negotiated, header, body): |
||
| 239 | return self._header(self._neighbor(neighbor, direction, ''), header, body, neighbor, message_type='keepalive') |
||
| 240 | |||
| 241 | def open(self, neighbor, direction, message, negotiated, header, body): |
||
| 242 | return self._header( |
||
| 243 | self._neighbor( |
||
| 244 | neighbor, |
||
| 245 | direction, |
||
| 246 | self._kv( |
||
| 247 | { |
||
| 248 | 'open': '{ %s }' |
||
| 249 | % self._kv( |
||
| 250 | { |
||
| 251 | 'version': message.version, |
||
| 252 | 'asn': message.asn, |
||
| 253 | 'hold_time': message.hold_time, |
||
| 254 | 'router_id': message.router_id, |
||
| 255 | 'capabilities': '{ %s }' % self._json_kv(message.capabilities), |
||
| 256 | } |
||
| 257 | ) |
||
| 258 | } |
||
| 259 | ), |
||
| 260 | ), |
||
| 261 | header, |
||
| 262 | body, |
||
| 263 | neighbor, |
||
| 264 | message_type='open', |
||
| 265 | ) |
||
| 266 | |||
| 267 | def _update(self, update): |
||
| 268 | plus = {} |
||
| 269 | minus = {} |
||
| 270 | |||
| 271 | # all the next-hops should be the same but let's not assume it |
||
| 272 | |||
| 273 | for nlri in update.nlris: |
||
| 274 | nexthop = str(nlri.nexthop) if nlri.nexthop else 'null' |
||
| 275 | if nlri.action == IN.ANNOUNCED: # pylint: disable=E1101 |
||
| 276 | plus.setdefault(nlri.family(), {}).setdefault(nexthop, []).append(nlri) |
||
| 277 | if nlri.action == IN.WITHDRAWN: # pylint: disable=E1101 |
||
| 278 | minus.setdefault(nlri.family(), []).append(nlri) |
||
| 279 | |||
| 280 | add = [] |
||
| 281 | for family in plus: |
||
| 282 | s = '"%s %s": { ' % family |
||
| 283 | m = '' |
||
| 284 | for nexthop in plus[family]: |
||
| 285 | nlris = plus[family][nexthop] |
||
| 286 | m += '"%s": [ ' % nexthop |
||
| 287 | m += ', '.join('%s' % nlri.json(compact=self.compact) for nlri in nlris) |
||
| 288 | m += ' ], ' |
||
| 289 | s += m[:-2] |
||
| 290 | s += ' }' |
||
| 291 | add.append(s) |
||
| 292 | |||
| 293 | remove = [] |
||
| 294 | for family in minus: |
||
| 295 | nlris = minus[family] |
||
| 296 | s = '"%s %s": [ ' % family |
||
| 297 | s += ', '.join('%s' % nlri.json(compact=self.compact) for nlri in nlris) |
||
| 298 | s += ' ]' |
||
| 299 | remove.append(s) |
||
| 300 | |||
| 301 | nlri = '' |
||
| 302 | if not add and not remove: |
||
| 303 | if update.nlris: # an EOR |
||
| 304 | return {'message': '{ %s }' % update.nlris[0].json()} |
||
| 305 | if add: |
||
| 306 | nlri += '"announce": { %s }' % ', '.join(add) |
||
| 307 | if add and remove: |
||
| 308 | nlri += ', ' |
||
| 309 | if remove: |
||
| 310 | nlri += '"withdraw": { %s }' % ', '.join(remove) |
||
| 311 | |||
| 312 | attributes = '' if not update.attributes else '"attribute": { %s }' % update.attributes.json() |
||
| 313 | if not attributes or not nlri: |
||
| 314 | update = '"update": { %s%s }' % (attributes, nlri) |
||
| 315 | else: |
||
| 316 | update = '"update": { %s, %s }' % (attributes, nlri) |
||
| 317 | |||
| 318 | return {'message': '{ %s }' % update} |
||
| 319 | |||
| 320 | def update(self, neighbor, direction, update, negotiated, header, body): |
||
| 321 | message = self._update(update) |
||
| 322 | if negotiated: |
||
| 323 | message.update(self._negotiated(negotiated)) |
||
| 324 | return self._header( |
||
| 325 | self._neighbor(neighbor, direction, self._kv(message)), header, body, neighbor, message_type='update' |
||
| 326 | ) |
||
| 327 | |||
| 328 | def refresh(self, neighbor, direction, refresh, negotiated, header, body): |
||
| 329 | return self._header( |
||
| 330 | self._neighbor( |
||
| 331 | neighbor, |
||
| 332 | direction, |
||
| 333 | self._kv( |
||
| 334 | { |
||
| 335 | 'route-refresh': '{ %s }' |
||
| 336 | % self._kv( |
||
| 337 | { |
||
| 338 | 'afi': '"%s"' % refresh.afi, |
||
| 339 | 'safi': '"%s"' % refresh.safi, |
||
| 340 | 'subtype': '"%s"' % refresh.reserved, |
||
| 341 | } |
||
| 342 | ) |
||
| 343 | } |
||
| 344 | ), |
||
| 345 | ), |
||
| 346 | header, |
||
| 347 | body, |
||
| 348 | neighbor, |
||
| 349 | message_type='refresh', |
||
| 350 | ) |
||
| 351 | |||
| 352 | def _operational_query(self, neighbor, direction, operational, header, body): |
||
| 353 | return self._header( |
||
| 354 | self._neighbor( |
||
| 355 | neighbor, |
||
| 356 | direction, |
||
| 357 | self._kv( |
||
| 358 | { |
||
| 359 | 'operational': '{ %s }' |
||
| 360 | % self._kv( |
||
| 361 | { |
||
| 362 | 'name': '"%s"' % operational.name, |
||
| 363 | 'afi': '"%s"' % operational.afi, |
||
| 364 | 'safi': '"%s"' % operational.safi, |
||
| 365 | } |
||
| 366 | ) |
||
| 367 | } |
||
| 368 | ), |
||
| 369 | ), |
||
| 370 | header, |
||
| 371 | body, |
||
| 372 | neighbor, |
||
| 373 | message_type='operational', |
||
| 374 | ) |
||
| 375 | |||
| 376 | def _operational_advisory(self, neighbor, direction, operational, header, body): |
||
| 377 | return self._header( |
||
| 378 | self._neighbor( |
||
| 379 | neighbor, |
||
| 380 | direction, |
||
| 381 | self._kv( |
||
| 382 | { |
||
| 383 | 'operational': '{ %s }' |
||
| 384 | % self._kv( |
||
| 385 | { |
||
| 386 | 'name': '"%s"' % operational.name, |
||
| 387 | 'afi': '"%s"' % operational.afi, |
||
| 388 | 'safi': '"%s"' % operational.safi, |
||
| 389 | 'advisory': '"%s"' % operational.data, |
||
| 390 | } |
||
| 391 | ) |
||
| 392 | } |
||
| 393 | ), |
||
| 394 | ), |
||
| 395 | header, |
||
| 396 | body, |
||
| 397 | neighbor, |
||
| 398 | message_type='operational', |
||
| 399 | ) |
||
| 400 | |||
| 401 | def _operational_counter(self, neighbor, direction, operational, header, body): |
||
| 402 | return self._header( |
||
| 403 | self._neighbor( |
||
| 404 | neighbor, |
||
| 405 | direction, |
||
| 406 | self._kv( |
||
| 407 | { |
||
| 408 | 'operational': '{ %s }' |
||
| 409 | % self._kv( |
||
| 410 | { |
||
| 411 | 'name': '"%s"' % operational.name, |
||
| 412 | 'afi': '"%s"' % operational.afi, |
||
| 413 | 'safi': '"%s"' % operational.safi, |
||
| 414 | 'router-id': operational.routerid, |
||
| 415 | 'sequence': operational.sequence, |
||
| 416 | 'counter': operational.counter, |
||
| 417 | } |
||
| 418 | ) |
||
| 419 | } |
||
| 420 | ), |
||
| 421 | ), |
||
| 422 | header, |
||
| 423 | body, |
||
| 424 | neighbor, |
||
| 425 | message_type='operational', |
||
| 426 | ) |
||
| 427 | |||
| 428 | View Code Duplication | def operational(self, neighbor, direction, what, operational, negotiated, header, body): |
|
| 429 | if what == 'advisory': |
||
| 430 | return self._operational_advisory(neighbor, direction, operational, header, body) |
||
| 431 | elif what == 'query': |
||
| 432 | return self._operational_query(neighbor, direction, operational, header, body) |
||
| 433 | elif what == 'counter': |
||
| 434 | return self._operational_counter(neighbor, direction, operational, header, body) |
||
| 435 | # elif what == 'interface': |
||
| 436 | # return self._operational_interface(peer,operational) |
||
| 437 | else: |
||
| 438 | raise RuntimeError('the code is broken, we are trying to print a unknown type of operational message') |
||
| 439 |