PageRenderTime 25ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/exabgp/bgp/neighbor.py

https://gitlab.com/rendoaw/exabgp
Python | 274 lines | 225 code | 25 blank | 24 comment | 14 complexity | 37f5f97925d1a87e71acd64e4f8b5c01 MD5 | raw file
  1. # encoding: utf-8
  2. """
  3. neighbor.py
  4. Created by Thomas Mangin on 2009-11-05.
  5. Copyright (c) 2009-2015 Exa Networks. All rights reserved.
  6. """
  7. import os
  8. import uuid
  9. from collections import deque
  10. # collections.counter is python2.7 only ..
  11. from exabgp.dep.counter import Counter
  12. from exabgp.protocol.family import AFI
  13. from exabgp.bgp.message import Message
  14. from exabgp.bgp.message.open.capability import AddPath
  15. from exabgp.rib import RIB
  16. # The definition of a neighbor (from reading the configuration)
  17. class Neighbor (object):
  18. def __init__ (self):
  19. # self.logger should not be used here as long as we do use deepcopy as it contains a Lock
  20. self.description = None
  21. self.router_id = None
  22. self.host_name = None
  23. self.domain_name = None
  24. self.local_address = None
  25. self.peer_address = None
  26. self.peer_as = None
  27. self.local_as = None
  28. self.hold_time = None
  29. self.asn4 = None
  30. self.add_path = None
  31. self.md5_password = None
  32. self.md5_ip = None
  33. self.ttl_in = None
  34. self.ttl_out = None
  35. self.group_updates = None
  36. self.flush = None
  37. self.adjribout = None
  38. self.manual_eor = False
  39. self.api = None
  40. # passive indicate that we do not establish outgoing connections
  41. self.passive = False
  42. # the port to listen on ( zero mean that we do not listen )
  43. self.listen = 0
  44. # the port to connect to
  45. self.connect = 0
  46. # capability
  47. self.route_refresh = False
  48. self.graceful_restart = False
  49. self.multisession = None
  50. self.add_path = None
  51. self.aigp = None
  52. self._families = []
  53. self.rib = None
  54. # The routes we have parsed from the configuration
  55. self.changes = []
  56. # On signal update, the previous routes so we can compare what changed
  57. self.backup_changes = []
  58. self.operational = None
  59. self.eor = deque()
  60. self.asm = dict()
  61. self.messages = deque()
  62. self.refresh = deque()
  63. self.counter = Counter()
  64. # It is possible to :
  65. # - have multiple exabgp toward one peer on the same host ( use of pid )
  66. # - have more than once connection toward a peer
  67. # - each connection has it own neihgbor (hence why identificator is not in Protocol)
  68. self.uid = '%d-%s' % (os.getpid(),uuid.uuid1())
  69. def make_rib (self):
  70. self.rib = RIB(self.name(),self.adjribout,self._families)
  71. # will resend all the routes once we reconnect
  72. def reset_rib (self):
  73. self.rib.reset()
  74. self.messages = deque()
  75. self.refresh = deque()
  76. # back to square one, all the routes are removed
  77. def clear_rib (self):
  78. self.rib.clear()
  79. self.messages = deque()
  80. self.refresh = deque()
  81. def name (self):
  82. if self.multisession:
  83. session = '/'.join("%s-%s" % (afi.name(),safi.name()) for (afi,safi) in self.families())
  84. else:
  85. session = 'in-open'
  86. return "neighbor %s local-ip %s local-as %s peer-as %s router-id %s family-allowed %s" % (self.peer_address,self.local_address,self.local_as,self.peer_as,self.router_id,session)
  87. def families (self):
  88. # this list() is important .. as we use the function to modify self._families
  89. return list(self._families)
  90. def add_family (self, family):
  91. # the families MUST be sorted for neighbor indexing name to be predictable for API users
  92. if family not in self.families():
  93. afi,safi = family
  94. d = dict()
  95. d[afi] = [safi,]
  96. for afi,safi in self._families:
  97. d.setdefault(afi,[]).append(safi)
  98. self._families = [(afi,safi) for afi in sorted(d) for safi in sorted(d[afi])]
  99. def remove_family (self, family):
  100. if family in self.families():
  101. self._families.remove(family)
  102. def missing (self):
  103. if self.local_address is None:
  104. return 'local-address'
  105. if self.peer_address is None:
  106. return 'peer-address'
  107. if self.local_as is None:
  108. return 'local-as'
  109. if self.peer_as is None:
  110. return 'peer-as'
  111. if self.peer_address.afi == AFI.ipv6 and not self.router_id:
  112. return 'router-id'
  113. return ''
  114. # This function only compares the neighbor BUT NOT ITS ROUTES
  115. def __eq__ (self, other):
  116. return \
  117. self.router_id == other.router_id and \
  118. self.local_address == other.local_address and \
  119. self.local_as == other.local_as and \
  120. self.peer_address == other.peer_address and \
  121. self.peer_as == other.peer_as and \
  122. self.passive == other.passive and \
  123. self.listen == other.listen and \
  124. self.connect == other.connect and \
  125. self.hold_time == other.hold_time and \
  126. self.host_name == other.host_name and \
  127. self.domain_name == other.domain_name and \
  128. self.md5_password == other.md5_password and \
  129. self.md5_ip == other.md5_ip and \
  130. self.ttl_in == other.ttl_in and \
  131. self.ttl_out == other.ttl_out and \
  132. self.route_refresh == other.route_refresh and \
  133. self.graceful_restart == other.graceful_restart and \
  134. self.multisession == other.multisession and \
  135. self.add_path == other.add_path and \
  136. self.operational == other.operational and \
  137. self.group_updates == other.group_updates and \
  138. self.flush == other.flush and \
  139. self.adjribout == other.adjribout and \
  140. self.families() == other.families()
  141. def __ne__ (self, other):
  142. return not self.__eq__(other)
  143. def string (self, with_changes=True):
  144. changes = ''
  145. if with_changes:
  146. changes += '\nstatic { '
  147. for changes in self.rib.incoming.queued_changes():
  148. changes += '\n\t\t%s' % changes.extensive()
  149. changes += '\n}'
  150. families = ''
  151. for afi,safi in self.families():
  152. families += '\n\t\t%s %s;' % (afi.name(),safi.name())
  153. codes = Message.CODE
  154. _extension_receive = {
  155. 'receive-packets': 'packets',
  156. 'receive-parsed': 'parsed',
  157. 'receive-consolidate': 'consolidate',
  158. 'receive-%s' % codes.NOTIFICATION.SHORT: 'notification',
  159. 'receive-%s' % codes.OPEN.SHORT: 'open',
  160. 'receive-%s' % codes.KEEPALIVE.SHORT: 'keepalive',
  161. 'receive-%s' % codes.UPDATE.SHORT: 'update',
  162. 'receive-%s' % codes.ROUTE_REFRESH.SHORT: 'refresh',
  163. 'receive-%s' % codes.OPERATIONAL.SHORT: 'operational',
  164. }
  165. _extension_send = {
  166. 'send-packets': 'packets',
  167. 'send-parsed': 'parsed',
  168. 'send-consolidate': 'consolidate',
  169. 'send-%s' % codes.NOTIFICATION.SHORT: 'notification',
  170. 'send-%s' % codes.OPEN.SHORT: 'open',
  171. 'send-%s' % codes.KEEPALIVE.SHORT: 'keepalive',
  172. 'send-%s' % codes.UPDATE.SHORT: 'update',
  173. 'send-%s' % codes.ROUTE_REFRESH.SHORT: 'refresh',
  174. 'send-%s' % codes.OPERATIONAL.SHORT: 'operational',
  175. }
  176. _receive = []
  177. for api,name in _extension_receive.items():
  178. _receive.extend(['\t\t\t%s;\n' % name,] if self.api[api] else [])
  179. receive = ''.join(_receive)
  180. _send = []
  181. for api,name in _extension_send.items():
  182. _send.extend(['\t\t\t%s;\n' % name,] if self.api[api] else [])
  183. send = ''.join(_send)
  184. returned = \
  185. 'neighbor %s {\n' \
  186. '\tdescription "%s";\n' \
  187. '\trouter-id %s;\n' \
  188. '\thost-name %s;\n' \
  189. '\tdomain-name %s;\n' \
  190. '\tlocal-address %s;\n' \
  191. '\tlocal-as %s;\n' \
  192. '\tpeer-as %s;\n' \
  193. '\thold-time %s;\n' \
  194. '\tmanual-eor %s;\n' \
  195. '%s%s%s%s%s%s%s%s%s\n' \
  196. '\tcapability {\n' \
  197. '%s%s%s%s%s%s%s%s\t}\n' \
  198. '\tfamily {%s\n' \
  199. '\t}\n' \
  200. '\tprocess {\n' \
  201. '%s%s\t}%s\n' \
  202. '}' % (
  203. self.peer_address,
  204. self.description,
  205. self.router_id,
  206. self.host_name,
  207. self.domain_name,
  208. self.local_address,
  209. self.local_as,
  210. self.peer_as,
  211. self.hold_time,
  212. 'true' if self.manual_eor else 'false',
  213. '\n\tpassive;\n' if self.passive else '',
  214. '\n\tlisten %d;\n' % self.listen if self.listen else '',
  215. '\n\tconnect %d;\n' % self.connect if self.connect else '',
  216. '\tgroup-updates: %s;\n' % (self.group_updates if self.group_updates else ''),
  217. '\tauto-flush: %s;\n' % ('true' if self.flush else 'false'),
  218. '\tadj-rib-out: %s;\n' % ('true' if self.adjribout else 'false'),
  219. '\tmd5-password "%s";\n' % self.md5_password if self.md5_password else '',
  220. '\tmd5-ip "%s";\n' % self.md5_ip if self.md5_ip else '',
  221. '\toutgoing-ttl: %s;\n' % (self.ttl_out if self.ttl_out else ''),
  222. '\tincoming-ttl: %s;\n' % (self.ttl_in if self.ttl_in else ''),
  223. '\t\tasn4 %s;\n' % ('enable' if self.asn4 else 'disable'),
  224. '\t\troute-refresh %s;\n' % ('enable' if self.route_refresh else 'disable'),
  225. '\t\tgraceful-restart %s;\n' % (self.graceful_restart if self.graceful_restart else 'disable'),
  226. '\t\tadd-path %s;\n' % (AddPath.string[self.add_path] if self.add_path else 'disable'),
  227. '\t\tmulti-session %s;\n' % ('enable' if self.multisession else 'disable'),
  228. '\t\toperational %s;\n' % ('enable' if self.operational else 'disable'),
  229. '\t\taigp %s;\n' % ('enable' if self.aigp else 'disable'),
  230. families,
  231. '\t\treceive {\n%s\t\t}\n' % receive if receive else '',
  232. '\t\tsend {\n%s\t\t}\n' % send if send else '',
  233. changes
  234. )
  235. return returned.replace('\t',' ')
  236. def __str__ (self):
  237. return self.string(False)