def post (self): local = self.scope.pop_context(self.name) neighbor = Neighbor() # XXX: use the right class for the data type # XXX: we can use the scope.nlri interface ( and rename it ) to set some values neighbor.router_id = local.get('router-id',None) neighbor.peer_address = local.get('peer-address',None) neighbor.local_address = local.get('local-address',None) neighbor.local_as = local.get('local-as',None) neighbor.peer_as = local.get('peer-as',None) neighbor.passive = local.get('passive',False) neighbor.listen = local.get('listen',0) neighbor.hold_time = local.get('hold-time',HoldTime(180)) neighbor.host_name = local.get('host-name',_hostname()) neighbor.domain_name = local.get('domain-name',_domainname()) neighbor.md5 = local.get('md5',None) neighbor.description = local.get('description','') neighbor.flush = local.get('auto-flush',True) neighbor.adjribout = local.get('adj-rib-out',True) neighbor.aigp = local.get('aigp',None) neighbor.ttl = local.get('ttl-security',None) neighbor.group_updates = local.get('group-updates',True) neighbor.manual_eor = local.get('manual-eor', False) neighbor.api = ParseAPI.extract() # capabilities capability = local.get('capability',{}) neighbor.graceful_restart = capability.get('graceful-restart',0) or int(neighbor.hold_time) neighbor.add_path = capability.get('add-path',0) neighbor.asn4 = capability.get('asn4',True) neighbor.multisession = capability.get('multi-session',False) neighbor.operational = capability.get('operational',False) neighbor.route_refresh = capability.get('route-refresh',0) families = [] for family in ParseFamily.convert.keys(): for pair in local.get('family',{}).get(family,[]): families.append(pair) families = families or NLRI.known_families() if (AFI.ipv4,SAFI.unicast) not in families: families.append((AFI(AFI.ipv4),SAFI(SAFI.unicast))) for family in families: neighbor.add_family(family) neighbor.changes = [] for section in ('static','l2vpn','flow'): routes = local.get(section,{}).get('routes',[]) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) messages = local.get('operational',{}).get('routes',[]) if not neighbor.router_id: neighbor.router_id = neighbor.local_address if neighbor.graceful_restart is None: neighbor.graceful_restart = int(neighbor.hold_time) if neighbor.route_refresh: if neighbor.adjribout: self.logger.configuration('route-refresh requested, enabling adj-rib-out') missing = neighbor.missing() if missing: return self.error.set('incomplete neighbor, missing %s' % missing) if neighbor.local_address.afi != neighbor.peer_address.afi: return self.error.set('local-address and peer-address must be of the same family') if neighbor.peer_address.top() in self._neighbors: return self.error.set('duplicate peer definition %s' % neighbor.peer_address.top()) self._neighbors.append(neighbor.peer_address.top()) # check we are not trying to announce routes without the right MP announcement for change in neighbor.changes: family = change.nlri.family() if family not in families and family != (AFI.ipv4,SAFI.unicast): return self.error.set('Trying to announce a route of type %s,%s when we are not announcing the family to our peer' % change.nlri.family()) def _init_neighbor (neighbor): families = neighbor.families() for change in neighbor.changes: if change.nlri.family() in families: # This add the family to neighbor.families() neighbor.rib.outgoing.insert_announced_watchdog(change) for message in messages: if message.family() in families: if message.name == 'ASM': neighbor.asm[message.family()] = message else: neighbor.messages.append(message) self.neighbors[neighbor.name()] = neighbor # create one neighbor object per family for multisession if neighbor.multisession and len(neighbor.families()) > 1: for family in neighbor.families(): # XXX: FIXME: Ok, it works but it takes LOTS of memory .. m_neighbor = deepcopy(neighbor) m_neighbor.make_rib() m_neighbor.rib.outgoing.families = [family] _init_neighbor(m_neighbor) else: neighbor.make_rib() _init_neighbor(neighbor) return True
def __init__(self, configurations, text=False): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self._text = text self.error = Error() self.scope = Scope() self.tokeniser = Tokeniser(self.scope, self.error, self.logger) params = (self.tokeniser, self.scope, self.error, self.logger) self.section = Section(*params) self.process = ParseProcess(*params) self.template = ParseTemplate(*params) self.template_neighbor = ParseTemplateNeighbor(*params) self.neighbor = ParseNeighbor(*params) self.family = ParseFamily(*params) self.addpath = ParseAddPath(*params) self.capability = ParseCapability(*params) self.api = ParseAPI(*params) self.api_send = ParseSend(*params) self.api_receive = ParseReceive(*params) self.static = ParseStatic(*params) self.static_route = ParseStaticRoute(*params) self.announce = SectionAnnounce(*params) self.announce_ipv4 = AnnounceIPv4(*params) self.announce_ipv6 = AnnounceIPv6(*params) self.announce_l2vpn = AnnounceL2VPN(*params) self.flow = ParseFlow(*params) self.flow_route = ParseFlowRoute(*params) self.flow_match = ParseFlowMatch(*params) self.flow_then = ParseFlowThen(*params) self.flow_scope = ParseFlowScope(*params) self.l2vpn = ParseL2VPN(*params) self.vpls = ParseVPLS(*params) self.operational = ParseOperational(*params) # We should check if name are unique when running Section.__init__ self._structure = { 'root': { 'class': self.section, 'commands': [], 'sections': { 'process': self.process.name, 'neighbor': self.neighbor.name, 'template': self.template.name, }, }, self.process.name: { 'class': self.process, 'commands': self.process.known.keys(), 'sections': {}, }, self.template.name: { 'class': self.template, 'commands': self.template.known.keys(), 'sections': { 'neighbor': self.template_neighbor.name, }, }, self.template_neighbor.name: { 'class': self.template_neighbor, 'commands': self.template_neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'add-path': self.addpath.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, 'announce': self.announce.name, }, }, self.neighbor.name: { 'class': self.neighbor, 'commands': self.neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'add-path': self.addpath.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, 'announce': self.announce.name, }, }, self.family.name: { 'class': self.family, 'commands': self.family.known.keys(), 'sections': {}, }, self.capability.name: { 'class': self.capability, 'commands': self.capability.known.keys(), 'sections': {}, }, self.addpath.name: { 'class': self.addpath, 'commands': self.addpath.known.keys(), 'sections': {}, }, self.api.name: { 'class': self.api, 'commands': self.api.known.keys(), 'sections': { 'send': self.api_send.name, 'receive': self.api_receive.name, }, }, self.api_send.name: { 'class': self.api_send, 'commands': self.api_send.known.keys(), 'sections': {}, }, self.api_receive.name: { 'class': self.api_receive, 'commands': self.api_receive.known.keys(), 'sections': {}, }, self.announce.name: { 'class': self.announce, 'commands': self.announce.known.keys(), 'sections': { 'ipv4': self.announce_ipv4.name, 'ipv6': self.announce_ipv6.name, 'l2vpn': self.announce_l2vpn.name, }, }, self.announce_ipv4.name: { 'class': self.announce_ipv4, 'commands': [ 'unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn' ], 'sections': {}, }, self.announce_ipv6.name: { 'class': self.announce_ipv6, 'commands': [ 'unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn' ], 'sections': {}, }, self.announce_l2vpn.name: { 'class': self.announce_l2vpn, 'commands': [ 'vpls', ], 'sections': {}, }, self.static.name: { 'class': self.static, 'commands': ['route', 'attributes'], 'sections': { 'route': self.static_route.name, }, }, self.static_route.name: { 'class': self.static_route, 'commands': self.static_route.known.keys(), 'sections': {}, }, self.flow.name: { 'class': self.flow, 'commands': self.flow.known.keys(), 'sections': { 'route': self.flow_route.name, }, }, self.flow_route.name: { 'class': self.flow_route, 'commands': self.flow_route.known.keys(), 'sections': { 'match': self.flow_match.name, 'then': self.flow_then.name, 'scope': self.flow_scope.name, }, }, self.flow_match.name: { 'class': self.flow_match, 'commands': self.flow_match.known.keys(), 'sections': {}, }, self.flow_then.name: { 'class': self.flow_then, 'commands': self.flow_then.known.keys(), 'sections': {}, }, self.flow_scope.name: { 'class': self.flow_scope, 'commands': self.flow_scope.known.keys(), 'sections': {} }, self.l2vpn.name: { 'class': self.l2vpn, 'commands': self.l2vpn.known.keys(), 'sections': { 'vpls': self.vpls.name, }, }, self.vpls.name: { 'class': self.vpls, 'commands': self.l2vpn.known.keys(), 'sections': {}, }, self.operational.name: { 'class': self.operational, 'commands': self.operational.known.keys(), 'sections': {} }, } self._neighbors = {} self._previous_neighbors = {}
def post(self): for inherit in self.scope.pop('inherit', []): data = self.scope.template('neighbor', inherit) self.scope.inherit(data) local = self.scope.get() neighbor = Neighbor() # XXX: use the right class for the data type # XXX: we can use the scope.nlri interface ( and rename it ) to set some values neighbor.router_id = local.get('router-id', None) neighbor.peer_address = local.get('peer-address', None) neighbor.local_address = local.get('local-address', None) neighbor.local_as = local.get('local-as', None) neighbor.peer_as = local.get('peer-as', None) neighbor.passive = local.get('passive', False) neighbor.listen = local.get('listen', 0) neighbor.connect = local.get('connect', 0) neighbor.hold_time = local.get('hold-time', HoldTime(180)) neighbor.host_name = local.get('host-name', host()) neighbor.domain_name = local.get('domain-name', domain()) neighbor.md5_password = local.get('md5-password', None) neighbor.md5_base64 = local.get('md5-base64', None) neighbor.md5_ip = local.get('md5-ip', neighbor.local_address) neighbor.description = local.get('description', '') neighbor.flush = local.get('auto-flush', True) neighbor.adj_rib_out = local.get('adj-rib-out', True) neighbor.adj_rib_in = local.get('adj-rib-in', True) neighbor.aigp = local.get('aigp', None) neighbor.ttl_out = local.get('outgoing-ttl', None) neighbor.ttl_in = local.get('incoming-ttl', None) neighbor.group_updates = local.get('group-updates', True) neighbor.manual_eor = local.get('manual-eor', False) capability = local.get('capability', {}) neighbor.add_path = capability.get('add-path', 0) neighbor.asn4 = capability.get('asn4', True) neighbor.extended_message = capability.get('extended-message', True) neighbor.multisession = capability.get('multi-session', False) neighbor.operational = capability.get('operational', False) neighbor.route_refresh = capability.get('route-refresh', 0) if capability.get('graceful-restart', False) is not False: neighbor.graceful_restart = capability.get( 'graceful-restart', 0) or int(neighbor.hold_time) neighbor.api = ParseAPI.flatten(local.pop('api', {})) families = [] for family in ParseFamily.convert: for pair in local.get('family', {}).get(family, []): families.append(pair) families = families or NLRI.known_families() for family in families: neighbor.add_family(family) if neighbor.add_path: add_path = local.get('add-path', {}) if add_path: for family in ParseAddPath.convert: for pair in add_path.get(family, []): if pair not in families: self.logger.debug( 'skipping add-path family %s as it is not negotiated' % pair, 'configuration') continue neighbor.add_addpath(pair) else: for family in families: neighbor.add_addpath(family) neighbor.changes = [] neighbor.changes.extend(self.scope.pop_routes()) # old format for section in ('static', 'l2vpn', 'flow'): routes = local.get(section, {}).get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) routes = local.get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) messages = local.get('operational', {}).get('routes', []) if neighbor.local_address is None: neighbor.auto_discovery = True neighbor.local_address = None neighbor.md5_ip = None if not neighbor.router_id and neighbor.peer_address.afi == AFI.ipv4 and not neighbor.auto_discovery: neighbor.router_id = neighbor.local_address if neighbor.route_refresh: if neighbor.adj_rib_out: self.logger.debug( 'route-refresh requested, enabling adj-rib-out', 'configuration') missing = neighbor.missing() if missing: return self.error.set('incomplete neighbor, missing %s' % missing) if not neighbor.auto_discovery and neighbor.local_address.afi != neighbor.peer_address.afi: return self.error.set( 'local-address and peer-address must be of the same family') neighbor.range_size = neighbor.peer_address.mask.size() if neighbor.range_size > 1 and not neighbor.passive: return self.error.set( 'can only use ip ranges for the peer address with passive neighbors' ) if neighbor.peer_address.top() in self._neighbors: return self.error.set('duplicate peer definition %s' % neighbor.peer_address.top()) self._neighbors.append(neighbor.peer_address.top()) if neighbor.md5_password: try: md5 = base64.b64decode( neighbor.md5_password ) if neighbor.md5_base64 else neighbor.md5_password except TypeError as e: return self.error.set( "Invalid base64 encoding of MD5 password.") else: if len(md5) > 80: return self.error.set( 'MD5 password must be no larger than 80 characters') # check we are not trying to announce routes without the right MP announcement for change in neighbor.changes: family = change.nlri.family() if family not in families and family != (AFI.ipv4, SAFI.unicast): return self.error.set( 'Trying to announce a route of type %s,%s when we are not announcing the family to our peer' % change.nlri.family()) def _init_neighbor(neighbor): families = neighbor.families() for change in neighbor.changes: if change.nlri.family() in families: # This add the family to neighbor.families() neighbor.rib.outgoing.add_to_rib_watchdog(change) for message in messages: if message.family() in families: if message.name == 'ASM': neighbor.asm[message.family()] = message else: neighbor.messages.append(message) self.neighbors[neighbor.name()] = neighbor # create one neighbor object per family for multisession if neighbor.multisession and len(neighbor.families()) > 1: for family in neighbor.families(): # XXX: FIXME: Ok, it works but it takes LOTS of memory .. m_neighbor = deepcopy(neighbor) m_neighbor.make_rib() m_neighbor.rib.outgoing.families = [family] _init_neighbor(m_neighbor) else: neighbor.make_rib() _init_neighbor(neighbor) return True
def post(self): local = self.scope.pop_context(self.name) neighbor = Neighbor() # XXX: use the right class for the data type # XXX: we can use the scope.nlri interface ( and rename it ) to set some values neighbor.router_id = local.get('router-id', None) neighbor.peer_address = local.get('peer-address', None) neighbor.local_address = local.get('local-address', None) neighbor.local_as = local.get('local-as', None) neighbor.peer_as = local.get('peer-as', None) neighbor.passive = local.get('passive', False) neighbor.listen = local.get('listen', 0) neighbor.hold_time = local.get('hold-time', HoldTime(180)) neighbor.host_name = local.get('host-name', _hostname()) neighbor.domain_name = local.get('domain-name', _domainname()) neighbor.md5 = local.get('md5', None) neighbor.description = local.get('description', '') neighbor.flush = local.get('auto-flush', True) neighbor.adjribout = local.get('adj-rib-out', True) neighbor.aigp = local.get('aigp', None) neighbor.ttl = local.get('ttl-security', None) neighbor.group_updates = local.get('group-updates', True) neighbor.manual_eor = local.get('manual-eor', False) neighbor.api = ParseAPI.extract() # capabilities capability = local.get('capability', {}) neighbor.add_path = capability.get('add-path', 0) neighbor.asn4 = capability.get('asn4', True) neighbor.multisession = capability.get('multi-session', False) neighbor.operational = capability.get('operational', False) neighbor.route_refresh = capability.get('route-refresh', 0) if capability.get('graceful-restart', False) is not False: neighbor.graceful_restart = capability.get( 'graceful-restart', 0) or int(neighbor.hold_time) families = [] for family in ParseFamily.convert.keys(): for pair in local.get('family', {}).get(family, []): families.append(pair) families = families or NLRI.known_families() if (AFI.ipv4, SAFI.unicast) not in families: families.append((AFI(AFI.ipv4), SAFI(SAFI.unicast))) for family in families: neighbor.add_family(family) neighbor.changes = [] for section in ('static', 'l2vpn', 'flow'): routes = local.get(section, {}).get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) messages = local.get('operational', {}).get('routes', []) if not neighbor.router_id: neighbor.router_id = neighbor.local_address if neighbor.route_refresh: if neighbor.adjribout: self.logger.configuration( 'route-refresh requested, enabling adj-rib-out') missing = neighbor.missing() if missing: return self.error.set('incomplete neighbor, missing %s' % missing) if neighbor.local_address.afi != neighbor.peer_address.afi: return self.error.set( 'local-address and peer-address must be of the same family') if neighbor.peer_address.top() in self._neighbors: return self.error.set('duplicate peer definition %s' % neighbor.peer_address.top()) self._neighbors.append(neighbor.peer_address.top()) # check we are not trying to announce routes without the right MP announcement for change in neighbor.changes: family = change.nlri.family() if family not in families and family != (AFI.ipv4, SAFI.unicast): return self.error.set( 'Trying to announce a route of type %s,%s when we are not announcing the family to our peer' % change.nlri.family()) def _init_neighbor(neighbor): families = neighbor.families() for change in neighbor.changes: if change.nlri.family() in families: # This add the family to neighbor.families() neighbor.rib.outgoing.insert_announced_watchdog(change) for message in messages: if message.family() in families: if message.name == 'ASM': neighbor.asm[message.family()] = message else: neighbor.messages.append(message) self.neighbors[neighbor.name()] = neighbor # create one neighbor object per family for multisession if neighbor.multisession and len(neighbor.families()) > 1: for family in neighbor.families(): # XXX: FIXME: Ok, it works but it takes LOTS of memory .. m_neighbor = deepcopy(neighbor) m_neighbor.make_rib() m_neighbor.rib.outgoing.families = [family] _init_neighbor(m_neighbor) else: neighbor.make_rib() _init_neighbor(neighbor) return True
class Configuration(_Configuration): def __init__(self, configurations, text=False): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self._text = text self.error = Error() self.scope = Scope() self.tokeniser = Tokeniser(self.scope, self.error, self.logger) params = (self.tokeniser, self.scope, self.error, self.logger) self.section = Section(*params) self.process = ParseProcess(*params) self.template = ParseTemplate(*params) self.template_neighbor = ParseTemplateNeighbor(*params) self.neighbor = ParseNeighbor(*params) self.family = ParseFamily(*params) self.addpath = ParseAddPath(*params) self.capability = ParseCapability(*params) self.api = ParseAPI(*params) self.api_send = ParseSend(*params) self.api_receive = ParseReceive(*params) self.static = ParseStatic(*params) self.static_route = ParseStaticRoute(*params) self.announce = SectionAnnounce(*params) self.announce_ipv4 = AnnounceIPv4(*params) self.announce_ipv6 = AnnounceIPv6(*params) self.announce_l2vpn = AnnounceL2VPN(*params) self.flow = ParseFlow(*params) self.flow_route = ParseFlowRoute(*params) self.flow_match = ParseFlowMatch(*params) self.flow_then = ParseFlowThen(*params) self.flow_scope = ParseFlowScope(*params) self.l2vpn = ParseL2VPN(*params) self.vpls = ParseVPLS(*params) self.operational = ParseOperational(*params) # We should check if name are unique when running Section.__init__ self._structure = { 'root': { 'class': self.section, 'commands': [], 'sections': { 'process': self.process.name, 'neighbor': self.neighbor.name, 'template': self.template.name, }, }, self.process.name: { 'class': self.process, 'commands': self.process.known.keys(), 'sections': {}, }, self.template.name: { 'class': self.template, 'commands': self.template.known.keys(), 'sections': { 'neighbor': self.template_neighbor.name, }, }, self.template_neighbor.name: { 'class': self.template_neighbor, 'commands': self.template_neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'add-path': self.addpath.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, 'announce': self.announce.name, }, }, self.neighbor.name: { 'class': self.neighbor, 'commands': self.neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'add-path': self.addpath.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, 'announce': self.announce.name, }, }, self.family.name: { 'class': self.family, 'commands': self.family.known.keys(), 'sections': {}, }, self.capability.name: { 'class': self.capability, 'commands': self.capability.known.keys(), 'sections': {}, }, self.addpath.name: { 'class': self.addpath, 'commands': self.addpath.known.keys(), 'sections': {}, }, self.api.name: { 'class': self.api, 'commands': self.api.known.keys(), 'sections': { 'send': self.api_send.name, 'receive': self.api_receive.name, }, }, self.api_send.name: { 'class': self.api_send, 'commands': self.api_send.known.keys(), 'sections': {}, }, self.api_receive.name: { 'class': self.api_receive, 'commands': self.api_receive.known.keys(), 'sections': {}, }, self.announce.name: { 'class': self.announce, 'commands': self.announce.known.keys(), 'sections': { 'ipv4': self.announce_ipv4.name, 'ipv6': self.announce_ipv6.name, 'l2vpn': self.announce_l2vpn.name, }, }, self.announce_ipv4.name: { 'class': self.announce_ipv4, 'commands': [ 'unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn' ], 'sections': {}, }, self.announce_ipv6.name: { 'class': self.announce_ipv6, 'commands': [ 'unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn' ], 'sections': {}, }, self.announce_l2vpn.name: { 'class': self.announce_l2vpn, 'commands': [ 'vpls', ], 'sections': {}, }, self.static.name: { 'class': self.static, 'commands': ['route', 'attributes'], 'sections': { 'route': self.static_route.name, }, }, self.static_route.name: { 'class': self.static_route, 'commands': self.static_route.known.keys(), 'sections': {}, }, self.flow.name: { 'class': self.flow, 'commands': self.flow.known.keys(), 'sections': { 'route': self.flow_route.name, }, }, self.flow_route.name: { 'class': self.flow_route, 'commands': self.flow_route.known.keys(), 'sections': { 'match': self.flow_match.name, 'then': self.flow_then.name, 'scope': self.flow_scope.name, }, }, self.flow_match.name: { 'class': self.flow_match, 'commands': self.flow_match.known.keys(), 'sections': {}, }, self.flow_then.name: { 'class': self.flow_then, 'commands': self.flow_then.known.keys(), 'sections': {}, }, self.flow_scope.name: { 'class': self.flow_scope, 'commands': self.flow_scope.known.keys(), 'sections': {} }, self.l2vpn.name: { 'class': self.l2vpn, 'commands': self.l2vpn.known.keys(), 'sections': { 'vpls': self.vpls.name, }, }, self.vpls.name: { 'class': self.vpls, 'commands': self.l2vpn.known.keys(), 'sections': {}, }, self.operational.name: { 'class': self.operational, 'commands': self.operational.known.keys(), 'sections': {} }, } self._neighbors = {} self._previous_neighbors = {} def _clear(self): self.processes = {} self._previous_neighbors = self.neighbors self.neighbors = {} self._neighbors = {} # clear the parser data (ie: free memory) def _cleanup(self): self.error.clear() self.tokeniser.clear() self.scope.clear() self.process.clear() self.template.clear() self.template_neighbor.clear() self.neighbor.clear() self.family.clear() self.capability.clear() self.api.clear() self.api_send.clear() self.api_receive.clear() self.announce_ipv6.clear() self.announce_ipv4.clear() self.announce_l2vpn.clear() self.announce.clear() self.static.clear() self.static_route.clear() self.flow.clear() self.flow_route.clear() self.flow_match.clear() self.flow_then.clear() self.flow_scope.clear() self.l2vpn.clear() self.vpls.clear() self.operational.clear() def _rollback_reload(self): self.neighbors = self._previous_neighbors self.processes = self.process.processes self._neighbors = {} self._previous_neighbors = {} def _commit_reload(self): self.neighbors = self.neighbor.neighbors # XXX: Yes, we do not detect changes in processes and restart anything .. # XXX: This is a bug .. self.processes = self.process.processes self._neighbors = {} # Add the changes prior to the reload to the neighbor to correct handling of deleted routes for neighbor in self.neighbors: if neighbor in self._previous_neighbors: self.neighbors[ neighbor].backup_changes = self._previous_neighbors[ neighbor].changes self._previous_neighbors = {} self._cleanup() def reload(self): try: return self._reload() except KeyboardInterrupt: return self.error.set( 'configuration reload aborted by ^C or SIGINT') except Error as exc: if environment.settings().debug.configuration: raise return self.error.set( 'problem parsing configuration file line %d\n' 'error message: %s' % (self.tokeniser.index_line, exc)) except StandardError as exc: if environment.settings().debug.configuration: raise return self.error.set( 'problem parsing configuration file line %d\n' 'error message: %s' % (self.tokeniser.index_line, exc)) def _reload(self): # taking the first configuration available (FIFO buffer) fname = self._configurations.pop(0) self._configurations.append(fname) # clearing the current configuration to be able to re-parse it self._clear() if self._text: if not self.tokeniser.set_text(fname): return False else: if not self.tokeniser.set_file(fname): return False if self.parseSection('root') is not True: # XXX: Should it be in neighbor ? self.process.add_api() self._rollback_reload() return self.error.set( "\n" "syntax error in section %s\n" "line %d: %s\n" "\n%s" % (self.scope.location(), self.tokeniser.number, ' '.join(self.tokeniser.line), str(self.error))) self.process.add_api() self._commit_reload() self._link() self.debug_check_route() self.debug_self_check() return True def _link(self): for neighbor in six.itervalues(self.neighbors): api = neighbor.api for process in api.get('processes', []): self.processes.setdefault( process, {})['neighbor-changes'] = api['neighbor-changes'] self.processes.setdefault(process, {})['negotiated'] = api['negotiated'] self.processes.setdefault(process, {})['fsm'] = api['fsm'] self.processes.setdefault(process, {})['signal'] = api['signal'] for way in ('send', 'receive'): for name in ('parsed', 'packets', 'consolidate'): key = "%s-%s" % (way, name) if api[key]: self.processes[process].setdefault(key, []).append( neighbor.router_id) for name in ('open', 'update', 'notification', 'keepalive', 'refresh', 'operational'): key = "%s-%s" % (way, name) if api[key]: self.processes[process].setdefault(key, []).append( neighbor.router_id) def partial(self, section, text): self._cleanup( ) # this perform a big cleanup (may be able to be smarter) self._clear() self.tokeniser.set_api( text if text.endswith(';') or text.endswith('}') else text + ' ;') if self.parseSection(section) is not True: self._rollback_reload() self.logger.debug( "\n" "syntax error in api command %s\n" "line %d: %s\n" "\n%s" % (self.scope.location(), self.tokeniser.number, ' '.join(self.tokeniser.line), str(self.error)), 'configuration') return False return True def _enter(self, name): location = self.tokeniser.iterate() self.logger.debug("> %-16s | %s" % (location, self.tokeniser.params()), 'configuration') if location not in self._structure[name]['sections']: return self.error.set('section %s is invalid in %s, %s' % (location, name, self.scope.location())) self.scope.enter(location) self.scope.to_context() class_name = self._structure[name]['sections'][location] instance = self._structure[class_name].get('class', None) if not instance: raise RuntimeError('This should not be happening, debug time !') if not instance.pre(): return False if not self.dispatch(self._structure[name]['sections'][location]): return False if not instance.post(): return False left = self.scope.leave() if not left: return self.error.set('closing too many parenthesis') self.scope.to_context() self.logger.debug("< %-16s | %s" % (left, self.tokeniser.params()), 'configuration') return True def _run(self, name): command = self.tokeniser.iterate() self.logger.debug(". %-16s | %s" % (command, self.tokeniser.params()), 'configuration') if not self.run(name, command): return False return True def dispatch(self, name): while True: self.tokeniser() if self.tokeniser.end == ';': if self._run(name): continue return False if self.tokeniser.end == '{': if self._enter(name): continue return False if self.tokeniser.end == '}': return True if not self.tokeniser.end: # finished return True return self.error.set('invalid syntax line %d' % self.tokeniser.index_line) return False def parseSection(self, name): if name not in self._structure: return self.error.set('option %s is not allowed here' % name) return self.dispatch(name) def run(self, name, command): # restore 'anounce attribute' to provide backward 3.4 compatibility if name == 'static' and command == 'attribute': command = 'attributes' if command not in self._structure[name]['commands']: return self.error.set('invalid keyword "%s"' % command) return self._structure[name]['class'].parse(name, command) def debug_check_route(self): # we are not really running the program, just want to .... if environment.settings().debug.route: from exabgp.configuration.check import check_message if check_message(self.neighbors, environment.settings().debug.route): sys.exit(0) sys.exit(1) def debug_self_check(self): # we are not really running the program, just want check the configuration validity if environment.settings().debug.selfcheck: from exabgp.configuration.check import check_neighbor if check_neighbor(self.neighbors): sys.exit(0) sys.exit(1)
def __init__(self, configurations, text=False): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self._text = text self.error = Error() self.scope = Scope() self.tokeniser = Tokeniser(self.scope, self.error, self.logger) generic = Section(self.tokeniser, self.scope, self.error, self.logger) self.process = ParseProcess(self.tokeniser, self.scope, self.error, self.logger) self.template = ParseTemplate(self.tokeniser, self.scope, self.error, self.logger) self.neighbor = ParseNeighbor(self.tokeniser, self.scope, self.error, self.logger) self.family = ParseFamily(self.tokeniser, self.scope, self.error, self.logger) self.capability = ParseCapability(self.tokeniser, self.scope, self.error, self.logger) self.api = ParseAPI(self.tokeniser, self.scope, self.error, self.logger) self.api_send = ParseSend(self.tokeniser, self.scope, self.error, self.logger) self.api_receive = ParseReceive(self.tokeniser, self.scope, self.error, self.logger) self.static = ParseStatic(self.tokeniser, self.scope, self.error, self.logger) self.static_route = ParseStaticRoute(self.tokeniser, self.scope, self.error, self.logger) self.flow = ParseFlow(self.tokeniser, self.scope, self.error, self.logger) self.flow_route = ParseFlowRoute(self.tokeniser, self.scope, self.error, self.logger) self.flow_match = ParseFlowMatch(self.tokeniser, self.scope, self.error, self.logger) self.flow_then = ParseFlowThen(self.tokeniser, self.scope, self.error, self.logger) self.l2vpn = ParseL2VPN(self.tokeniser, self.scope, self.error, self.logger) self.vpls = ParseVPLS(self.tokeniser, self.scope, self.error, self.logger) self.operational = ParseOperational(self.tokeniser, self.scope, self.error, self.logger) # We should check if name are unique when running Section.__init__ self._structure = { "root": { "class": generic, "commands": [], "sections": { "process": self.process.name, "neighbor": self.neighbor.name, "template": self.template.name, }, }, self.process.name: {"class": self.process, "commands": self.process.known.keys(), "sections": {}}, self.template.name: { "class": self.template, "commands": self.template.known.keys(), "sections": { "family": self.family.name, "capability": self.capability.name, "api": self.api.name, "static": self.static.name, "flow": self.flow.name, "l2vpn": self.l2vpn.name, "operational": self.operational.name, }, }, self.neighbor.name: { "class": self.neighbor, "commands": self.neighbor.known.keys(), "sections": { "family": self.family.name, "capability": self.capability.name, "api": self.api.name, "static": self.static.name, "flow": self.flow.name, "l2vpn": self.l2vpn.name, "operational": self.operational.name, }, }, self.family.name: {"class": self.family, "commands": self.family.known.keys(), "sections": {}}, self.capability.name: {"class": self.capability, "commands": self.capability.known.keys(), "sections": {}}, self.api.name: { "class": self.api, "commands": self.api.known.keys(), "sections": {"send": self.api_send.name, "receive": self.api_receive.name}, }, self.api_send.name: {"class": self.api_send, "commands": self.api_send.known.keys(), "sections": {}}, self.api_receive.name: { "class": self.api_receive, "commands": self.api_receive.known.keys(), "sections": {}, }, self.static.name: { "class": self.static, "commands": ["route", "attributes"], "sections": {"route": self.static_route.name}, }, self.static_route.name: { "class": self.static_route, "commands": self.static_route.known.keys(), "sections": {}, }, self.flow.name: { "class": self.flow, "commands": self.flow.known.keys(), "sections": {"route": self.flow_route.name}, }, self.flow_route.name: { "class": self.flow_route, "commands": self.flow_route.known.keys(), "sections": {"match": self.flow_match.name, "then": self.flow_then.name}, }, self.flow_match.name: {"class": self.flow_match, "commands": self.flow_match.known.keys(), "sections": {}}, self.flow_then.name: {"class": self.flow_then, "commands": self.flow_then.known.keys(), "sections": {}}, self.l2vpn.name: { "class": self.l2vpn, "commands": self.l2vpn.known.keys(), "sections": {"vpls": self.vpls.name}, }, self.vpls.name: {"class": self.vpls, "commands": self.l2vpn.known.keys(), "sections": {}}, self.operational.name: { "class": self.operational, "commands": self.operational.known.keys(), "sections": {}, }, } self._neighbors = {} self._previous_neighbors = {}
def post (self): for inherit in self.scope.pop('inherit',[]): data = self.scope.template('neighbor',inherit) self.scope.inherit(data) local = self.scope.get() neighbor = Neighbor() # XXX: use the right class for the data type # XXX: we can use the scope.nlri interface ( and rename it ) to set some values neighbor.router_id = local.get('router-id',None) neighbor.peer_address = local.get('peer-address',None) neighbor.local_address = local.get('local-address',None) neighbor.local_as = local.get('local-as',None) neighbor.peer_as = local.get('peer-as',None) neighbor.passive = local.get('passive',False) neighbor.listen = local.get('listen',0) neighbor.connect = local.get('connect',0) neighbor.hold_time = local.get('hold-time',HoldTime(180)) neighbor.host_name = local.get('host-name',host()) neighbor.domain_name = local.get('domain-name',domain()) neighbor.md5_password = local.get('md5-password',None) neighbor.md5_base64 = local.get('md5-base64', None) neighbor.md5_ip = local.get('md5-ip',neighbor.local_address) neighbor.description = local.get('description','') neighbor.flush = local.get('auto-flush',True) neighbor.adj_rib_out = local.get('adj-rib-out',True) neighbor.adj_rib_in = local.get('adj-rib-in',True) neighbor.aigp = local.get('aigp',None) neighbor.ttl_out = local.get('outgoing-ttl',None) neighbor.ttl_in = local.get('incoming-ttl',None) neighbor.group_updates = local.get('group-updates',True) neighbor.manual_eor = local.get('manual-eor', False) capability = local.get('capability',{}) neighbor.add_path = capability.get('add-path',0) neighbor.asn4 = capability.get('asn4',True) neighbor.extended_message = capability.get('extended-message',True) neighbor.multisession = capability.get('multi-session',False) neighbor.operational = capability.get('operational',False) neighbor.route_refresh = capability.get('route-refresh',0) if capability.get('graceful-restart',False) is not False: neighbor.graceful_restart = capability.get('graceful-restart',0) or int(neighbor.hold_time) neighbor.api = ParseAPI.flatten(local.pop('api',{})) families = [] for family in ParseFamily.convert: for pair in local.get('family',{}).get(family,[]): families.append(pair) families = families or NLRI.known_families() for family in families: neighbor.add_family(family) if neighbor.add_path: add_path = local.get('add-path',{}) if add_path: for family in ParseAddPath.convert: for pair in add_path.get(family,[]): if pair not in families: self.logger.debug('skipping add-path family %s as it is not negotiated' % pair,'configuration') continue neighbor.add_addpath(pair) else: for family in families: neighbor.add_addpath(family) neighbor.changes = [] neighbor.changes.extend(self.scope.pop_routes()) # old format for section in ('static','l2vpn','flow'): routes = local.get(section,{}).get('routes',[]) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) routes = local.get('routes',[]) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) messages = local.get('operational',{}).get('routes',[]) if neighbor.local_address is None: neighbor.auto_discovery = True neighbor.local_address = None neighbor.md5_ip = None if not neighbor.router_id: if neighbor.peer_address.afi == AFI.ipv4 and not neighbor.auto_discovery: neighbor.router_id = neighbor.local_address else: return self.error.set('missing router-id for the peer, it can not be set using the local-ip') if neighbor.route_refresh: if neighbor.adj_rib_out: self.logger.debug('route-refresh requested, enabling adj-rib-out','configuration') missing = neighbor.missing() if missing: return self.error.set('incomplete neighbor, missing %s' % missing) if not neighbor.auto_discovery and neighbor.local_address.afi != neighbor.peer_address.afi: return self.error.set('local-address and peer-address must be of the same family') neighbor.range_size = neighbor.peer_address.mask.size() if neighbor.range_size > 1 and not neighbor.passive: return self.error.set('can only use ip ranges for the peer address with passive neighbors') if neighbor.peer_address.top() in self._neighbors: return self.error.set('duplicate peer definition %s' % neighbor.peer_address.top()) self._neighbors.append(neighbor.peer_address.top()) if neighbor.md5_password: try: md5 = base64.b64decode(neighbor.md5_password) if neighbor.md5_base64 else neighbor.md5_password except TypeError as e: return self.error.set("Invalid base64 encoding of MD5 password.") else: if len(md5) > 80: return self.error.set('MD5 password must be no larger than 80 characters') # check we are not trying to announce routes without the right MP announcement for change in neighbor.changes: family = change.nlri.family() if family not in families and family != (AFI.ipv4,SAFI.unicast): return self.error.set('Trying to announce a route of type %s,%s when we are not announcing the family to our peer' % change.nlri.family()) def _init_neighbor (neighbor): families = neighbor.families() for change in neighbor.changes: if change.nlri.family() in families: # This add the family to neighbor.families() neighbor.rib.outgoing.add_to_rib_watchdog(change) for message in messages: if message.family() in families: if message.name == 'ASM': neighbor.asm[message.family()] = message else: neighbor.messages.append(message) self.neighbors[neighbor.name()] = neighbor # create one neighbor object per family for multisession if neighbor.multisession and len(neighbor.families()) > 1: for family in neighbor.families(): # XXX: FIXME: Ok, it works but it takes LOTS of memory .. m_neighbor = deepcopy(neighbor) m_neighbor.make_rib() m_neighbor.rib.outgoing.families = [family] _init_neighbor(m_neighbor) else: neighbor.make_rib() _init_neighbor(neighbor) return True
def __init__ (self, configurations, text=False): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self._text = text self.error = Error () self.scope = Scope () self.tokeniser = Tokeniser(self.scope,self.error,self.logger) params = (self.tokeniser,self.scope,self.error,self.logger) generic = Section (*params) self.process = ParseProcess (*params) self.template = ParseTemplate (*params) self.template_neighbor = ParseTemplateNeighbor (*params) self.neighbor = ParseNeighbor (*params) self.family = ParseFamily (*params) self.addpath = ParseAddPath (*params) self.capability = ParseCapability (*params) self.api = ParseAPI (*params) self.api_send = ParseSend (*params) self.api_receive = ParseReceive (*params) self.static = ParseStatic (*params) self.static_route = ParseStaticRoute (*params) self.announce = SectionAnnounce (*params) self.announce_ipv4 = AnnounceIPv4 (*params) self.announce_ipv6 = AnnounceIPv6 (*params) self.announce_l2vpn = AnnounceL2VPN (*params) self.flow = ParseFlow (*params) self.flow_route = ParseFlowRoute (*params) self.flow_match = ParseFlowMatch (*params) self.flow_then = ParseFlowThen (*params) self.flow_scope = ParseFlowScope (*params) self.l2vpn = ParseL2VPN (*params) self.vpls = ParseVPLS (*params) self.operational = ParseOperational (*params) # We should check if name are unique when running Section.__init__ self._structure = { 'root': { 'class': generic, 'commands': [], 'sections': { 'process': self.process.name, 'neighbor': self.neighbor.name, 'template': self.template.name, }, }, self.process.name: { 'class': self.process, 'commands': self.process.known.keys(), 'sections': {}, }, self.template.name: { 'class': self.template, 'commands': self.template.known.keys(), 'sections': { 'neighbor': self.template_neighbor.name, }, }, self.template_neighbor.name: { 'class': self.template_neighbor, 'commands': self.template_neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'add-path': self.addpath.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, 'announce': self.announce.name, }, }, self.neighbor.name: { 'class': self.neighbor, 'commands': self.neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'add-path': self.addpath.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, 'announce': self.announce.name, }, }, self.family.name: { 'class': self.family, 'commands': self.family.known.keys(), 'sections': { }, }, self.capability.name: { 'class': self.capability, 'commands': self.capability.known.keys(), 'sections': { }, }, self.addpath.name: { 'class': self.addpath, 'commands': self.addpath.known.keys(), 'sections': { }, }, self.api.name: { 'class': self.api, 'commands': self.api.known.keys(), 'sections': { 'send': self.api_send.name, 'receive': self.api_receive.name, }, }, self.api_send.name: { 'class': self.api_send, 'commands': self.api_send.known.keys(), 'sections': { }, }, self.api_receive.name: { 'class': self.api_receive, 'commands': self.api_receive.known.keys(), 'sections': { }, }, self.announce.name: { 'class': self.announce, 'commands': self.announce.known.keys(), 'sections': { 'ipv4': self.announce_ipv4.name, 'ipv6': self.announce_ipv6.name, 'l2vpn': self.announce_l2vpn.name, }, }, self.announce_ipv4.name: { 'class': self.announce_ipv4, 'commands': ['unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn'], 'sections': { }, }, self.announce_ipv6.name: { 'class': self.announce_ipv6, 'commands': ['unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn'], 'sections': { }, }, self.announce_l2vpn.name: { 'class': self.announce_l2vpn, 'commands': ['vpls',], 'sections': { }, }, self.static.name: { 'class': self.static, 'commands': ['route','attributes'], 'sections': { 'route': self.static_route.name, }, }, self.static_route.name: { 'class': self.static_route, 'commands': self.static_route.known.keys(), 'sections': { }, }, self.flow.name: { 'class': self.flow, 'commands': self.flow.known.keys(), 'sections': { 'route': self.flow_route.name, }, }, self.flow_route.name: { 'class': self.flow_route, 'commands': self.flow_route.known.keys(), 'sections': { 'match': self.flow_match.name, 'then': self.flow_then.name, 'scope': self.flow_scope.name, }, }, self.flow_match.name: { 'class': self.flow_match, 'commands': self.flow_match.known.keys(), 'sections': { }, }, self.flow_then.name: { 'class': self.flow_then, 'commands': self.flow_then.known.keys(), 'sections': { }, }, self.flow_scope.name: { 'class': self.flow_scope, 'commands': self.flow_scope.known.keys(), 'sections': { } }, self.l2vpn.name: { 'class': self.l2vpn, 'commands': self.l2vpn.known.keys(), 'sections': { 'vpls': self.vpls.name, }, }, self.vpls.name: { 'class': self.vpls, 'commands': self.l2vpn.known.keys(), 'sections': { }, }, self.operational.name: { 'class': self.operational, 'commands': self.operational.known.keys(), 'sections': { } }, } self._neighbors = {} self._previous_neighbors = {}
class Configuration(_Configuration): def __init__(self, configurations, text=False): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self._text = text self.error = Error() self.scope = Scope() self.tokeniser = Tokeniser(self.scope, self.error, self.logger) generic = Section(self.tokeniser, self.scope, self.error, self.logger) self.process = ParseProcess(self.tokeniser, self.scope, self.error, self.logger) self.template = ParseTemplate(self.tokeniser, self.scope, self.error, self.logger) self.neighbor = ParseNeighbor(self.tokeniser, self.scope, self.error, self.logger) self.family = ParseFamily(self.tokeniser, self.scope, self.error, self.logger) self.capability = ParseCapability(self.tokeniser, self.scope, self.error, self.logger) self.api = ParseAPI(self.tokeniser, self.scope, self.error, self.logger) self.api_send = ParseSend(self.tokeniser, self.scope, self.error, self.logger) self.api_receive = ParseReceive(self.tokeniser, self.scope, self.error, self.logger) self.static = ParseStatic(self.tokeniser, self.scope, self.error, self.logger) self.static_route = ParseStaticRoute(self.tokeniser, self.scope, self.error, self.logger) self.flow = ParseFlow(self.tokeniser, self.scope, self.error, self.logger) self.flow_route = ParseFlowRoute(self.tokeniser, self.scope, self.error, self.logger) self.flow_match = ParseFlowMatch(self.tokeniser, self.scope, self.error, self.logger) self.flow_then = ParseFlowThen(self.tokeniser, self.scope, self.error, self.logger) self.l2vpn = ParseL2VPN(self.tokeniser, self.scope, self.error, self.logger) self.vpls = ParseVPLS(self.tokeniser, self.scope, self.error, self.logger) self.operational = ParseOperational(self.tokeniser, self.scope, self.error, self.logger) # We should check if name are unique when running Section.__init__ self._structure = { "root": { "class": generic, "commands": [], "sections": { "process": self.process.name, "neighbor": self.neighbor.name, "template": self.template.name, }, }, self.process.name: {"class": self.process, "commands": self.process.known.keys(), "sections": {}}, self.template.name: { "class": self.template, "commands": self.template.known.keys(), "sections": { "family": self.family.name, "capability": self.capability.name, "api": self.api.name, "static": self.static.name, "flow": self.flow.name, "l2vpn": self.l2vpn.name, "operational": self.operational.name, }, }, self.neighbor.name: { "class": self.neighbor, "commands": self.neighbor.known.keys(), "sections": { "family": self.family.name, "capability": self.capability.name, "api": self.api.name, "static": self.static.name, "flow": self.flow.name, "l2vpn": self.l2vpn.name, "operational": self.operational.name, }, }, self.family.name: {"class": self.family, "commands": self.family.known.keys(), "sections": {}}, self.capability.name: {"class": self.capability, "commands": self.capability.known.keys(), "sections": {}}, self.api.name: { "class": self.api, "commands": self.api.known.keys(), "sections": {"send": self.api_send.name, "receive": self.api_receive.name}, }, self.api_send.name: {"class": self.api_send, "commands": self.api_send.known.keys(), "sections": {}}, self.api_receive.name: { "class": self.api_receive, "commands": self.api_receive.known.keys(), "sections": {}, }, self.static.name: { "class": self.static, "commands": ["route", "attributes"], "sections": {"route": self.static_route.name}, }, self.static_route.name: { "class": self.static_route, "commands": self.static_route.known.keys(), "sections": {}, }, self.flow.name: { "class": self.flow, "commands": self.flow.known.keys(), "sections": {"route": self.flow_route.name}, }, self.flow_route.name: { "class": self.flow_route, "commands": self.flow_route.known.keys(), "sections": {"match": self.flow_match.name, "then": self.flow_then.name}, }, self.flow_match.name: {"class": self.flow_match, "commands": self.flow_match.known.keys(), "sections": {}}, self.flow_then.name: {"class": self.flow_then, "commands": self.flow_then.known.keys(), "sections": {}}, self.l2vpn.name: { "class": self.l2vpn, "commands": self.l2vpn.known.keys(), "sections": {"vpls": self.vpls.name}, }, self.vpls.name: {"class": self.vpls, "commands": self.l2vpn.known.keys(), "sections": {}}, self.operational.name: { "class": self.operational, "commands": self.operational.known.keys(), "sections": {}, }, } self._neighbors = {} self._previous_neighbors = {} def _clear(self): self.processes = {} self.neighbors = {} self._neighbors = {} self._previous_neighbors = {} # clear the parser data (ie: free memory) def _cleanup(self): self.error.clear() self.tokeniser.clear() self.scope.clear() self.process.clear() self.template.clear() self.neighbor.clear() self.family.clear() self.capability.clear() self.api.clear() self.api_send.clear() self.api_receive.clear() self.static.clear() self.static_route.clear() self.flow.clear() self.flow_route.clear() self.flow_match.clear() self.flow_then.clear() self.l2vpn.clear() self.vpls.clear() self.operational.clear() def _rollback_reload(self): self.neighbors = self._previous_neighbors self._neighbors = {} self._previous_neighbors = {} def _commit_reload(self): self.neighbors = self.neighbor.neighbors # XXX: Yes, we do not detect changes in processes and restart anything .. # XXX: This is a bug .. self.processes = self.process.processes self._neighbors = {} # installing in the neighbor the API routes for neighbor in self.neighbors: if neighbor in self._previous_neighbors: self.neighbors[neighbor].changes = self._previous_neighbors[neighbor].changes self._previous_neighbors = {} self._cleanup() def reload(self): try: return self._reload() except KeyboardInterrupt: return self.error.set("configuration reload aborted by ^C or SIGINT") except Error, exc: if environment.settings().debug.configuration: raise return self.error.set( "problem parsing configuration file line %d\n" "error message: %s" % (self.tokeniser.index_line, exc) ) except StandardError, exc: if environment.settings().debug.configuration: raise return self.error.set( "problem parsing configuration file line %d\n" "error message: %s" % (self.tokeniser.index_line, exc) )
def post(self): self.scope.merge('api', ParseAPI.extract()) return True
class Configuration (_Configuration): def __init__ (self, configurations, text=False): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self._text = text self.error = Error () self.scope = Scope () self.tokeniser = Tokeniser(self.scope,self.error,self.logger) params = (self.tokeniser,self.scope,self.error,self.logger) generic = Section (*params) self.process = ParseProcess (*params) self.template = ParseTemplate (*params) self.template_neighbor = ParseTemplateNeighbor (*params) self.neighbor = ParseNeighbor (*params) self.family = ParseFamily (*params) self.addpath = ParseAddPath (*params) self.capability = ParseCapability (*params) self.api = ParseAPI (*params) self.api_send = ParseSend (*params) self.api_receive = ParseReceive (*params) self.static = ParseStatic (*params) self.static_route = ParseStaticRoute (*params) self.announce = SectionAnnounce (*params) self.announce_ipv4 = AnnounceIPv4 (*params) self.announce_ipv6 = AnnounceIPv6 (*params) self.announce_l2vpn = AnnounceL2VPN (*params) self.flow = ParseFlow (*params) self.flow_route = ParseFlowRoute (*params) self.flow_match = ParseFlowMatch (*params) self.flow_then = ParseFlowThen (*params) self.flow_scope = ParseFlowScope (*params) self.l2vpn = ParseL2VPN (*params) self.vpls = ParseVPLS (*params) self.operational = ParseOperational (*params) # We should check if name are unique when running Section.__init__ self._structure = { 'root': { 'class': generic, 'commands': [], 'sections': { 'process': self.process.name, 'neighbor': self.neighbor.name, 'template': self.template.name, }, }, self.process.name: { 'class': self.process, 'commands': self.process.known.keys(), 'sections': {}, }, self.template.name: { 'class': self.template, 'commands': self.template.known.keys(), 'sections': { 'neighbor': self.template_neighbor.name, }, }, self.template_neighbor.name: { 'class': self.template_neighbor, 'commands': self.template_neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'add-path': self.addpath.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, 'announce': self.announce.name, }, }, self.neighbor.name: { 'class': self.neighbor, 'commands': self.neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'add-path': self.addpath.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, 'announce': self.announce.name, }, }, self.family.name: { 'class': self.family, 'commands': self.family.known.keys(), 'sections': { }, }, self.capability.name: { 'class': self.capability, 'commands': self.capability.known.keys(), 'sections': { }, }, self.addpath.name: { 'class': self.addpath, 'commands': self.addpath.known.keys(), 'sections': { }, }, self.api.name: { 'class': self.api, 'commands': self.api.known.keys(), 'sections': { 'send': self.api_send.name, 'receive': self.api_receive.name, }, }, self.api_send.name: { 'class': self.api_send, 'commands': self.api_send.known.keys(), 'sections': { }, }, self.api_receive.name: { 'class': self.api_receive, 'commands': self.api_receive.known.keys(), 'sections': { }, }, self.announce.name: { 'class': self.announce, 'commands': self.announce.known.keys(), 'sections': { 'ipv4': self.announce_ipv4.name, 'ipv6': self.announce_ipv6.name, 'l2vpn': self.announce_l2vpn.name, }, }, self.announce_ipv4.name: { 'class': self.announce_ipv4, 'commands': ['unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn'], 'sections': { }, }, self.announce_ipv6.name: { 'class': self.announce_ipv6, 'commands': ['unicast', 'multicast', 'nlri-mpls', 'mpls-vpn', 'flow', 'flow-vpn'], 'sections': { }, }, self.announce_l2vpn.name: { 'class': self.announce_l2vpn, 'commands': ['vpls',], 'sections': { }, }, self.static.name: { 'class': self.static, 'commands': ['route','attributes'], 'sections': { 'route': self.static_route.name, }, }, self.static_route.name: { 'class': self.static_route, 'commands': self.static_route.known.keys(), 'sections': { }, }, self.flow.name: { 'class': self.flow, 'commands': self.flow.known.keys(), 'sections': { 'route': self.flow_route.name, }, }, self.flow_route.name: { 'class': self.flow_route, 'commands': self.flow_route.known.keys(), 'sections': { 'match': self.flow_match.name, 'then': self.flow_then.name, 'scope': self.flow_scope.name, }, }, self.flow_match.name: { 'class': self.flow_match, 'commands': self.flow_match.known.keys(), 'sections': { }, }, self.flow_then.name: { 'class': self.flow_then, 'commands': self.flow_then.known.keys(), 'sections': { }, }, self.flow_scope.name: { 'class': self.flow_scope, 'commands': self.flow_scope.known.keys(), 'sections': { } }, self.l2vpn.name: { 'class': self.l2vpn, 'commands': self.l2vpn.known.keys(), 'sections': { 'vpls': self.vpls.name, }, }, self.vpls.name: { 'class': self.vpls, 'commands': self.l2vpn.known.keys(), 'sections': { }, }, self.operational.name: { 'class': self.operational, 'commands': self.operational.known.keys(), 'sections': { } }, } self._neighbors = {} self._previous_neighbors = {} def _clear (self): self.processes = {} self._previous_neighbors = self.neighbors self.neighbors = {} self._neighbors = {} # clear the parser data (ie: free memory) def _cleanup (self): self.error.clear() self.tokeniser.clear() self.scope.clear() self.process.clear() self.template.clear() self.template_neighbor.clear() self.neighbor.clear() self.family.clear() self.capability.clear() self.api.clear() self.api_send.clear() self.api_receive.clear() self.announce_ipv6.clear() self.announce_ipv4.clear() self.announce_l2vpn.clear() self.announce.clear() self.static.clear() self.static_route.clear() self.flow.clear() self.flow_route.clear() self.flow_match.clear() self.flow_then.clear() self.flow_scope.clear() self.l2vpn.clear() self.vpls.clear() self.operational.clear() def _rollback_reload (self): self.neighbors = self._previous_neighbors self.processes = self.process.processes self._neighbors = {} self._previous_neighbors = {} def _commit_reload (self): self.neighbors = self.neighbor.neighbors # XXX: Yes, we do not detect changes in processes and restart anything .. # XXX: This is a bug .. self.processes = self.process.processes self._neighbors = {} # Add the changes prior to the reload to the neighbor to correct handling of deleted routes for neighbor in self.neighbors: if neighbor in self._previous_neighbors: self.neighbors[neighbor].backup_changes = self._previous_neighbors[neighbor].changes self._previous_neighbors = {} self._cleanup() def reload (self): try: return self._reload() except KeyboardInterrupt: return self.error.set('configuration reload aborted by ^C or SIGINT') except Error as exc: if environment.settings().debug.configuration: raise return self.error.set( 'problem parsing configuration file line %d\n' 'error message: %s' % (self.tokeniser.index_line, exc) ) except StandardError as exc: if environment.settings().debug.configuration: raise return self.error.set( 'problem parsing configuration file line %d\n' 'error message: %s' % (self.tokeniser.index_line, exc) ) def _reload (self): # taking the first configuration available (FIFO buffer) fname = self._configurations.pop(0) self._configurations.append(fname) # clearing the current configuration to be able to re-parse it self._clear() if self._text: if not self.tokeniser.set_text(fname): return False else: if not self.tokeniser.set_file(fname): return False if self.section('root') is not True: # XXX: Should it be in neighbor ? self.process.add_api() self._rollback_reload() return self.error.set( "\n" "syntax error in section %s\n" "line %d: %s\n" "\n%s" % ( self.scope.location(), self.tokeniser.number, ' '.join(self.tokeniser.line), str(self.error) ) ) self.process.add_api() self._commit_reload() self._link() self.debug_check_route() self.debug_self_check() return True def _link (self): for neighbor in six.itervalues(self.neighbors): api = neighbor.api for process in api.get('processes',[]): self.processes.setdefault(process,{})['neighbor-changes'] = api['neighbor-changes'] self.processes.setdefault(process,{})['negotiated'] = api['negotiated'] self.processes.setdefault(process,{})['fsm'] = api['fsm'] self.processes.setdefault(process,{})['signal'] = api['signal'] for way in ('send','receive'): for name in ('parsed','packets','consolidate'): key = "%s-%s" % (way,name) if api[key]: self.processes[process].setdefault(key,[]).append(neighbor.router_id) for name in ('open', 'update', 'notification', 'keepalive', 'refresh', 'operational'): key = "%s-%s" % (way,name) if api[key]: self.processes[process].setdefault(key,[]).append(neighbor.router_id) def partial (self, section, text): self._cleanup() # this perform a big cleanup (may be able to be smarter) self._clear() self.tokeniser.set_api(text if text.endswith(';') or text.endswith('}') else text + ' ;') if self.section(section) is not True: self._rollback_reload() self.logger.debug( "\n" "syntax error in api command %s\n" "line %d: %s\n" "\n%s" % ( self.scope.location(), self.tokeniser.number, ' '.join(self.tokeniser.line), str(self.error) ), 'configuration' ) return False return True def _enter (self,name): location = self.tokeniser.iterate() self.logger.debug("> %-16s | %s" % (location,self.tokeniser.params()),'configuration') if location not in self._structure[name]['sections']: return self.error.set('section %s is invalid in %s, %s' % (location,name,self.scope.location())) self.scope.enter(location) self.scope.to_context() class_name = self._structure[name]['sections'][location] instance = self._structure[class_name].get('class',None) if not instance: raise RuntimeError('This should not be happening, debug time !') if not instance.pre(): return False if not self.dispatch(self._structure[name]['sections'][location]): return False if not instance.post(): return False left = self.scope.leave() if not left: return self.error.set('closing too many parenthesis') self.scope.to_context() self.logger.debug("< %-16s | %s" % (left,self.tokeniser.params()),'configuration') return True def _run (self,name): command = self.tokeniser.iterate() self.logger.debug(". %-16s | %s" % (command,self.tokeniser.params()),'configuration') if not self.run(name,command): return False return True def dispatch (self,name): while True: self.tokeniser() if self.tokeniser.end == ';': if self._run(name): continue return False if self.tokeniser.end == '{': if self._enter(name): continue return False if self.tokeniser.end == '}': return True if not self.tokeniser.end: # finished return True return self.error.set('invalid syntax line %d' % self.tokeniser.index_line) return False def section (self, name): if name not in self._structure: return self.error.set('option %s is not allowed here' % name) return self.dispatch(name) def run (self, name, command): # restore 'anounce attribute' to provide backward 3.4 compatibility if name == 'static' and command == 'attribute': command = 'attributes' if command not in self._structure[name]['commands']: return self.error.set('invalid keyword "%s"' % command) return self._structure[name]['class'].parse(name,command) def debug_check_route (self): # we are not really running the program, just want to .... if environment.settings().debug.route: from exabgp.configuration.check import check_message if check_message(self.neighbors,environment.settings().debug.route): sys.exit(0) sys.exit(1) def debug_self_check (self): # we are not really running the program, just want check the configuration validity if environment.settings().debug.selfcheck: from exabgp.configuration.check import check_neighbor if check_neighbor(self.neighbors): sys.exit(0) sys.exit(1)
def __init__ (self, configurations): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self.error = Error () self.scope = Scope () self.tokeniser = Tokeniser(self.scope,self.error,self.logger) generic = Section (self.tokeniser,self.scope,self.error,self.logger) self.process = ParseProcess (self.tokeniser,self.scope,self.error,self.logger) self.template = ParseTemplate (self.tokeniser,self.scope,self.error,self.logger) self.neighbor = ParseNeighbor (self.tokeniser,self.scope,self.error,self.logger) self.family = ParseFamily (self.tokeniser,self.scope,self.error,self.logger) self.capability = ParseCapability (self.tokeniser,self.scope,self.error,self.logger) self.api = ParseAPI (self.tokeniser,self.scope,self.error,self.logger) self.api_send = ParseSend (self.tokeniser,self.scope,self.error,self.logger) self.api_receive = ParseReceive (self.tokeniser,self.scope,self.error,self.logger) self.static = ParseStatic (self.tokeniser,self.scope,self.error,self.logger) self.static_route = ParseStaticRoute (self.tokeniser,self.scope,self.error,self.logger) self.flow = ParseFlow (self.tokeniser,self.scope,self.error,self.logger) self.flow_route = ParseFlowRoute (self.tokeniser,self.scope,self.error,self.logger) self.flow_match = ParseFlowMatch (self.tokeniser,self.scope,self.error,self.logger) self.flow_then = ParseFlowThen (self.tokeniser,self.scope,self.error,self.logger) self.l2vpn = ParseL2VPN (self.tokeniser,self.scope,self.error,self.logger) self.vpls = ParseVPLS (self.tokeniser,self.scope,self.error,self.logger) self.operational = ParseOperational (self.tokeniser,self.scope,self.error,self.logger) # We should check if name are unique when running Section.__init__ self._structure = { 'root': { 'class': generic, 'commands': [], 'sections': { 'process': self.process.name, 'neighbor': self.neighbor.name, 'template': self.template.name, }, }, self.process.name: { 'class': self.process, 'commands': self.process.known.keys(), 'sections': {}, }, self.template.name: { 'class': self.template, 'commands': self.template.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, }, }, self.neighbor.name: { 'class': self.neighbor, 'commands': self.neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, }, }, self.family.name: { 'class': self.family, 'commands': self.family.known.keys(), 'sections': { }, }, self.capability.name: { 'class': self.capability, 'commands': self.capability.known.keys(), 'sections': { }, }, self.api.name: { 'class': self.api, 'commands': self.api.known.keys(), 'sections': { 'send': self.api_send.name, 'receive': self.api_receive.name, }, }, self.api_send.name: { 'class': self.api_send, 'commands': self.api_send.known.keys(), 'sections': { }, }, self.api_receive.name: { 'class': self.api_receive, 'commands': self.api_receive.known.keys(), 'sections': { }, }, self.static.name: { 'class': self.static, 'commands': 'route', 'sections': { 'route': self.static_route.name, }, }, self.static_route.name: { 'class': self.static_route, 'commands': self.static_route.known.keys(), 'sections': { }, }, self.flow.name: { 'class': self.flow, 'commands': self.flow.known.keys(), 'sections': { 'route': self.flow_route.name, }, }, self.flow_route.name: { 'class': self.flow_route, 'commands': self.flow_route.known.keys(), 'sections': { 'match': self.flow_match.name, 'then': self.flow_then.name, }, }, self.flow_match.name: { 'class': self.flow_match, 'commands': self.flow_match.known.keys(), 'sections': { }, }, self.flow_then.name: { 'class': self.flow_then, 'commands': self.flow_then.known.keys(), 'sections': { }, }, self.l2vpn.name: { 'class': self.l2vpn, 'commands': self.l2vpn.known.keys(), 'sections': { 'vpls': self.vpls.name, }, }, self.vpls.name: { 'class': self.vpls, 'commands': self.l2vpn.known.keys(), 'sections': { }, }, self.operational.name: { 'class': self.operational, 'commands': self.operational.known.keys(), 'sections': { } }, } self._neighbors = {} self._previous_neighbors = {}
class Configuration (_Configuration): def __init__ (self, configurations): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self.error = Error () self.scope = Scope () self.tokeniser = Tokeniser(self.scope,self.error,self.logger) generic = Section (self.tokeniser,self.scope,self.error,self.logger) self.process = ParseProcess (self.tokeniser,self.scope,self.error,self.logger) self.template = ParseTemplate (self.tokeniser,self.scope,self.error,self.logger) self.neighbor = ParseNeighbor (self.tokeniser,self.scope,self.error,self.logger) self.family = ParseFamily (self.tokeniser,self.scope,self.error,self.logger) self.capability = ParseCapability (self.tokeniser,self.scope,self.error,self.logger) self.api = ParseAPI (self.tokeniser,self.scope,self.error,self.logger) self.api_send = ParseSend (self.tokeniser,self.scope,self.error,self.logger) self.api_receive = ParseReceive (self.tokeniser,self.scope,self.error,self.logger) self.static = ParseStatic (self.tokeniser,self.scope,self.error,self.logger) self.static_route = ParseStaticRoute (self.tokeniser,self.scope,self.error,self.logger) self.flow = ParseFlow (self.tokeniser,self.scope,self.error,self.logger) self.flow_route = ParseFlowRoute (self.tokeniser,self.scope,self.error,self.logger) self.flow_match = ParseFlowMatch (self.tokeniser,self.scope,self.error,self.logger) self.flow_then = ParseFlowThen (self.tokeniser,self.scope,self.error,self.logger) self.l2vpn = ParseL2VPN (self.tokeniser,self.scope,self.error,self.logger) self.vpls = ParseVPLS (self.tokeniser,self.scope,self.error,self.logger) self.operational = ParseOperational (self.tokeniser,self.scope,self.error,self.logger) # We should check if name are unique when running Section.__init__ self._structure = { 'root': { 'class': generic, 'commands': [], 'sections': { 'process': self.process.name, 'neighbor': self.neighbor.name, 'template': self.template.name, }, }, self.process.name: { 'class': self.process, 'commands': self.process.known.keys(), 'sections': {}, }, self.template.name: { 'class': self.template, 'commands': self.template.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, }, }, self.neighbor.name: { 'class': self.neighbor, 'commands': self.neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, }, }, self.family.name: { 'class': self.family, 'commands': self.family.known.keys(), 'sections': { }, }, self.capability.name: { 'class': self.capability, 'commands': self.capability.known.keys(), 'sections': { }, }, self.api.name: { 'class': self.api, 'commands': self.api.known.keys(), 'sections': { 'send': self.api_send.name, 'receive': self.api_receive.name, }, }, self.api_send.name: { 'class': self.api_send, 'commands': self.api_send.known.keys(), 'sections': { }, }, self.api_receive.name: { 'class': self.api_receive, 'commands': self.api_receive.known.keys(), 'sections': { }, }, self.static.name: { 'class': self.static, 'commands': 'route', 'sections': { 'route': self.static_route.name, }, }, self.static_route.name: { 'class': self.static_route, 'commands': self.static_route.known.keys(), 'sections': { }, }, self.flow.name: { 'class': self.flow, 'commands': self.flow.known.keys(), 'sections': { 'route': self.flow_route.name, }, }, self.flow_route.name: { 'class': self.flow_route, 'commands': self.flow_route.known.keys(), 'sections': { 'match': self.flow_match.name, 'then': self.flow_then.name, }, }, self.flow_match.name: { 'class': self.flow_match, 'commands': self.flow_match.known.keys(), 'sections': { }, }, self.flow_then.name: { 'class': self.flow_then, 'commands': self.flow_then.known.keys(), 'sections': { }, }, self.l2vpn.name: { 'class': self.l2vpn, 'commands': self.l2vpn.known.keys(), 'sections': { 'vpls': self.vpls.name, }, }, self.vpls.name: { 'class': self.vpls, 'commands': self.l2vpn.known.keys(), 'sections': { }, }, self.operational.name: { 'class': self.operational, 'commands': self.operational.known.keys(), 'sections': { } }, } self._neighbors = {} self._previous_neighbors = {} def _clear (self): self.processes = {} self.neighbors = {} self._neighbors = {} self._previous_neighbors = {} # clear the parser data (ie: free memory) def _cleanup (self): self.error.clear() self.tokeniser.clear() self.scope.clear() self.process.clear() self.template.clear() self.neighbor.clear() self.family.clear() self.capability.clear() self.api.clear() self.api_send.clear() self.api_receive.clear() self.static.clear() self.static_route.clear() self.flow.clear() self.flow_route.clear() self.flow_match.clear() self.flow_then.clear() self.l2vpn.clear() self.vpls.clear() self.operational.clear() def _rollback_reload (self): self.neighbors = self._previous_neighbors self._neighbors = {} self._previous_neighbors = {} def _commit_reload (self): self.neighbors = self.neighbor.neighbors # XXX: Yes, we do not detect changes in processes and restart anything .. # XXX: This is a bug .. self.processes = self.process.processes self._neighbors = {} # installing in the neighbor the API routes for neighbor in self.neighbors: if neighbor in self._previous_neighbors: self.neighbors[neighbor].changes = self._previous_neighbors[neighbor].changes self._previous_neighbors = {} self._cleanup() def reload (self): try: return self._reload() except KeyboardInterrupt: return self.error.set('configuration reload aborted by ^C or SIGINT') except Error, exc: return self.error.set( 'problem parsing configuration file line %d\n' 'error message: %s' % (self.tokeniser.index_line, exc) ) except StandardError, exc: if environment.settings().debug.configuration: raise return self.error.set( 'problem parsing configuration file line %d\n' 'error message: %s' % (self.tokeniser.index_line, exc) )
def __init__(self, configurations, text=False): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self._text = text self.error = Error() self.scope = Scope() self.tokeniser = Tokeniser(self.scope, self.error, self.logger) generic = Section(self.tokeniser, self.scope, self.error, self.logger) self.process = ParseProcess(self.tokeniser, self.scope, self.error, self.logger) self.template = ParseTemplate(self.tokeniser, self.scope, self.error, self.logger) self.neighbor = ParseNeighbor(self.tokeniser, self.scope, self.error, self.logger) self.family = ParseFamily(self.tokeniser, self.scope, self.error, self.logger) self.capability = ParseCapability(self.tokeniser, self.scope, self.error, self.logger) self.api = ParseAPI(self.tokeniser, self.scope, self.error, self.logger) self.api_send = ParseSend(self.tokeniser, self.scope, self.error, self.logger) self.api_receive = ParseReceive(self.tokeniser, self.scope, self.error, self.logger) self.static = ParseStatic(self.tokeniser, self.scope, self.error, self.logger) self.static_route = ParseStaticRoute(self.tokeniser, self.scope, self.error, self.logger) self.flow = ParseFlow(self.tokeniser, self.scope, self.error, self.logger) self.flow_route = ParseFlowRoute(self.tokeniser, self.scope, self.error, self.logger) self.flow_match = ParseFlowMatch(self.tokeniser, self.scope, self.error, self.logger) self.flow_then = ParseFlowThen(self.tokeniser, self.scope, self.error, self.logger) self.l2vpn = ParseL2VPN(self.tokeniser, self.scope, self.error, self.logger) self.vpls = ParseVPLS(self.tokeniser, self.scope, self.error, self.logger) self.operational = ParseOperational(self.tokeniser, self.scope, self.error, self.logger) # We should check if name are unique when running Section.__init__ self._structure = { 'root': { 'class': generic, 'commands': [], 'sections': { 'process': self.process.name, 'neighbor': self.neighbor.name, 'template': self.template.name, }, }, self.process.name: { 'class': self.process, 'commands': self.process.known.keys(), 'sections': {}, }, self.template.name: { 'class': self.template, 'commands': self.template.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, }, }, self.neighbor.name: { 'class': self.neighbor, 'commands': self.neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, }, }, self.family.name: { 'class': self.family, 'commands': self.family.known.keys(), 'sections': {}, }, self.capability.name: { 'class': self.capability, 'commands': self.capability.known.keys(), 'sections': {}, }, self.api.name: { 'class': self.api, 'commands': self.api.known.keys(), 'sections': { 'send': self.api_send.name, 'receive': self.api_receive.name, }, }, self.api_send.name: { 'class': self.api_send, 'commands': self.api_send.known.keys(), 'sections': {}, }, self.api_receive.name: { 'class': self.api_receive, 'commands': self.api_receive.known.keys(), 'sections': {}, }, self.static.name: { 'class': self.static, 'commands': ['route', 'attributes'], 'sections': { 'route': self.static_route.name, }, }, self.static_route.name: { 'class': self.static_route, 'commands': self.static_route.known.keys(), 'sections': {}, }, self.flow.name: { 'class': self.flow, 'commands': self.flow.known.keys(), 'sections': { 'route': self.flow_route.name, }, }, self.flow_route.name: { 'class': self.flow_route, 'commands': self.flow_route.known.keys(), 'sections': { 'match': self.flow_match.name, 'then': self.flow_then.name, }, }, self.flow_match.name: { 'class': self.flow_match, 'commands': self.flow_match.known.keys(), 'sections': {}, }, self.flow_then.name: { 'class': self.flow_then, 'commands': self.flow_then.known.keys(), 'sections': {}, }, self.l2vpn.name: { 'class': self.l2vpn, 'commands': self.l2vpn.known.keys(), 'sections': { 'vpls': self.vpls.name, }, }, self.vpls.name: { 'class': self.vpls, 'commands': self.l2vpn.known.keys(), 'sections': {}, }, self.operational.name: { 'class': self.operational, 'commands': self.operational.known.keys(), 'sections': {} }, } self._neighbors = {} self._previous_neighbors = {}
class Configuration(_Configuration): def __init__(self, configurations, text=False): _Configuration.__init__(self) self.api_encoder = environment.settings().api.encoder self._configurations = configurations self._text = text self.error = Error() self.scope = Scope() self.tokeniser = Tokeniser(self.scope, self.error, self.logger) generic = Section(self.tokeniser, self.scope, self.error, self.logger) self.process = ParseProcess(self.tokeniser, self.scope, self.error, self.logger) self.template = ParseTemplate(self.tokeniser, self.scope, self.error, self.logger) self.neighbor = ParseNeighbor(self.tokeniser, self.scope, self.error, self.logger) self.family = ParseFamily(self.tokeniser, self.scope, self.error, self.logger) self.capability = ParseCapability(self.tokeniser, self.scope, self.error, self.logger) self.api = ParseAPI(self.tokeniser, self.scope, self.error, self.logger) self.api_send = ParseSend(self.tokeniser, self.scope, self.error, self.logger) self.api_receive = ParseReceive(self.tokeniser, self.scope, self.error, self.logger) self.static = ParseStatic(self.tokeniser, self.scope, self.error, self.logger) self.static_route = ParseStaticRoute(self.tokeniser, self.scope, self.error, self.logger) self.flow = ParseFlow(self.tokeniser, self.scope, self.error, self.logger) self.flow_route = ParseFlowRoute(self.tokeniser, self.scope, self.error, self.logger) self.flow_match = ParseFlowMatch(self.tokeniser, self.scope, self.error, self.logger) self.flow_then = ParseFlowThen(self.tokeniser, self.scope, self.error, self.logger) self.l2vpn = ParseL2VPN(self.tokeniser, self.scope, self.error, self.logger) self.vpls = ParseVPLS(self.tokeniser, self.scope, self.error, self.logger) self.operational = ParseOperational(self.tokeniser, self.scope, self.error, self.logger) # We should check if name are unique when running Section.__init__ self._structure = { 'root': { 'class': generic, 'commands': [], 'sections': { 'process': self.process.name, 'neighbor': self.neighbor.name, 'template': self.template.name, }, }, self.process.name: { 'class': self.process, 'commands': self.process.known.keys(), 'sections': {}, }, self.template.name: { 'class': self.template, 'commands': self.template.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, }, }, self.neighbor.name: { 'class': self.neighbor, 'commands': self.neighbor.known.keys(), 'sections': { 'family': self.family.name, 'capability': self.capability.name, 'api': self.api.name, 'static': self.static.name, 'flow': self.flow.name, 'l2vpn': self.l2vpn.name, 'operational': self.operational.name, }, }, self.family.name: { 'class': self.family, 'commands': self.family.known.keys(), 'sections': {}, }, self.capability.name: { 'class': self.capability, 'commands': self.capability.known.keys(), 'sections': {}, }, self.api.name: { 'class': self.api, 'commands': self.api.known.keys(), 'sections': { 'send': self.api_send.name, 'receive': self.api_receive.name, }, }, self.api_send.name: { 'class': self.api_send, 'commands': self.api_send.known.keys(), 'sections': {}, }, self.api_receive.name: { 'class': self.api_receive, 'commands': self.api_receive.known.keys(), 'sections': {}, }, self.static.name: { 'class': self.static, 'commands': ['route', 'attributes'], 'sections': { 'route': self.static_route.name, }, }, self.static_route.name: { 'class': self.static_route, 'commands': self.static_route.known.keys(), 'sections': {}, }, self.flow.name: { 'class': self.flow, 'commands': self.flow.known.keys(), 'sections': { 'route': self.flow_route.name, }, }, self.flow_route.name: { 'class': self.flow_route, 'commands': self.flow_route.known.keys(), 'sections': { 'match': self.flow_match.name, 'then': self.flow_then.name, }, }, self.flow_match.name: { 'class': self.flow_match, 'commands': self.flow_match.known.keys(), 'sections': {}, }, self.flow_then.name: { 'class': self.flow_then, 'commands': self.flow_then.known.keys(), 'sections': {}, }, self.l2vpn.name: { 'class': self.l2vpn, 'commands': self.l2vpn.known.keys(), 'sections': { 'vpls': self.vpls.name, }, }, self.vpls.name: { 'class': self.vpls, 'commands': self.l2vpn.known.keys(), 'sections': {}, }, self.operational.name: { 'class': self.operational, 'commands': self.operational.known.keys(), 'sections': {} }, } self._neighbors = {} self._previous_neighbors = {} def _clear(self): self.processes = {} self.neighbors = {} self._neighbors = {} self._previous_neighbors = {} # clear the parser data (ie: free memory) def _cleanup(self): self.error.clear() self.tokeniser.clear() self.scope.clear() self.process.clear() self.template.clear() self.neighbor.clear() self.family.clear() self.capability.clear() self.api.clear() self.api_send.clear() self.api_receive.clear() self.static.clear() self.static_route.clear() self.flow.clear() self.flow_route.clear() self.flow_match.clear() self.flow_then.clear() self.l2vpn.clear() self.vpls.clear() self.operational.clear() def _rollback_reload(self): self.neighbors = self._previous_neighbors self._neighbors = {} self._previous_neighbors = {} def _commit_reload(self): self.neighbors = self.neighbor.neighbors # XXX: Yes, we do not detect changes in processes and restart anything .. # XXX: This is a bug .. self.processes = self.process.processes self._neighbors = {} # installing in the neighbor the API routes for neighbor in self.neighbors: if neighbor in self._previous_neighbors: self.neighbors[neighbor].changes = self._previous_neighbors[ neighbor].changes self._previous_neighbors = {} self._cleanup() def reload(self): try: return self._reload() except KeyboardInterrupt: return self.error.set( 'configuration reload aborted by ^C or SIGINT') except Error, exc: if environment.settings().debug.configuration: raise return self.error.set( 'problem parsing configuration file line %d\n' 'error message: %s' % (self.tokeniser.index_line, exc)) except StandardError, exc: if environment.settings().debug.configuration: raise return self.error.set( 'problem parsing configuration file line %d\n' 'error message: %s' % (self.tokeniser.index_line, exc))
def post(self): for inherited in self.scope.pop('inherit', []): data = self.scope.template('neighbor', inherited) self.scope.inherit(data) local = self.scope.get() neighbor = Neighbor() for option in neighbor.defaults: conf = local.get(option, None) if conf is not None: neighbor[option] = conf # XXX: use the right class for the data type # XXX: we can use the scope.nlri interface ( and rename it ) to set some values capability = local.get('capability', {}) for option in neighbor.Capability.defaults: conf = capability.get(option, None) if conf is not None: neighbor['capability'][option] = conf neighbor.api = ParseAPI.flatten(local.pop('api', {})) missing = neighbor.missing() if missing: return self.error.set(missing) neighbor.infer() families = [] for family in ParseFamily.convert: for pair in local.get('family', {}).get(family, []): families.append(pair) families = families or NLRI.known_families() for family in families: neighbor.add_family(family) if neighbor['capability']['add-path']: add_path = local.get('add-path', {}) if add_path: for family in ParseAddPath.convert: for pair in add_path.get(family, []): if pair not in families: log.debug( 'skipping add-path family ' + str(pair) + ' as it is not negotiated', 'configuration') continue neighbor.add_addpath(pair) else: for family in families: neighbor.add_addpath(family) # The default is to auto-detect by the presence of the nexthop block # if this is manually set, then we honor it nexthop = local.get('nexthop', {}) if neighbor['capability']['nexthop'] is None and nexthop: neighbor['capability']['nexthop'] = True if neighbor['capability']['nexthop']: nexthops = [] for family in nexthop: nexthops.extend(nexthop[family]) if nexthops: for afi, safi, nhafi in nexthops: if (afi, safi) not in neighbor.families(): log.debug( 'skipping nexthop afi,safi ' + str(afi) + '/' + str(safi) + ' as it is not negotiated', 'configuration', ) continue if (nhafi, safi) not in neighbor.families(): log.debug( 'skipping nexthop afi ' + str(nhafi) + '/' + str(safi) + ' as it is not negotiated', 'configuration', ) continue neighbor.add_nexthop(afi, safi, nhafi) neighbor.changes = [] neighbor.changes.extend(self.scope.pop_routes()) # old format for section in ('static', 'l2vpn', 'flow'): routes = local.get(section, {}).get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) routes = local.get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) messages = local.get('operational', {}).get('routes', []) if neighbor['local-address'] is None: neighbor.auto_discovery = True neighbor['local-address'] = None neighbor['md5-ip'] = None if not neighbor['router-id']: if neighbor[ 'peer-address'].afi == AFI.ipv4 and not neighbor.auto_discovery: neighbor['router-id'] = neighbor['local-address'] else: return self.error.set( 'missing router-id for the peer, it can not be set using the local-ip' ) if neighbor['capability']['route-refresh']: if neighbor['adj-rib-out']: log.debug('route-refresh requested, enabling adj-rib-out', 'configuration') missing = neighbor.missing() if missing: return self.error.set('incomplete neighbor, missing %s' % missing) if not neighbor.auto_discovery and neighbor[ 'local-address'].afi != neighbor['peer-address'].afi: return self.error.set( 'local-address and peer-address must be of the same family') neighbor.range_size = neighbor['peer-address'].mask.size() if neighbor.range_size > 1 and not (neighbor['passive'] or getenv().bgp.passive): return self.error.set( 'can only use ip ranges for the peer address with passive neighbors' ) if neighbor.index() in self._neighbors: return self.error.set('duplicate peer definition %s' % neighbor['peer-address'].top()) self._neighbors.append(neighbor.index()) if neighbor['md5-password']: try: md5 = base64.b64decode( neighbor['md5-password'] ) if neighbor['md5-base64'] else neighbor['md5-password'] except TypeError as e: return self.error.set( f"Invalid base64 encoding of MD5 password ({e})") else: if len(md5) > 80: return self.error.set( 'MD5 password must be no larger than 80 characters') # check we are not trying to announce routes without the right MP announcement for change in neighbor.changes: family = change.nlri.family() if family not in families and family != (AFI.ipv4, SAFI.unicast): return self.error.set( 'Trying to announce a route of type %s,%s when we are not announcing the family to our peer' % change.nlri.family()) def _init_neighbor(neighbor): families = neighbor.families() for change in neighbor.changes: if change.nlri.family() in families: # This add the family to neighbor.families() neighbor.rib.outgoing.add_to_rib_watchdog(change) for message in messages: if message.family() in families: if message.name == 'ASM': neighbor.asm[message.family()] = message else: neighbor.messages.append(message) self.neighbors[neighbor.name()] = neighbor # create one neighbor object per family for multisession if neighbor['capability']['multi-session'] and len( neighbor.families()) > 1: for family in neighbor.families(): # XXX: FIXME: Ok, it works but it takes LOTS of memory .. m_neighbor = deepcopy(neighbor) m_neighbor.make_rib() m_neighbor.rib.outgoing.families = [family] _init_neighbor(m_neighbor) else: neighbor.make_rib() _init_neighbor(neighbor) local.clear() return True