def static_routes(self, node): node.ipv4_static_routes = [] if self.anm.has_overlay("ipv4"): ipv4_node = self.anm['ipv4'].node(node) if ipv4_node and ipv4_node.static_routes: for route in ipv4_node.static_routes: stanza = ConfigStanza( prefix=route.prefix, netmask=route.netmask, nexthop=route.nexthop, metric=route.metric, ) node.ipv4_static_routes.append(stanza) node.ipv6_static_routes = [] if self.anm.has_overlay("ipv6"): ipv6_node = self.anm['ipv6'].node(node) if ipv6_node and ipv6_node.static_routes: for route in ipv6_node.static_routes: stanza = ConfigStanza( prefix=route.prefix, nexthop=route.nexthop, metric=route.metric, ) node.ipv6_static_routes.append(stanza)
def eigrp(self, node): super(IosXrCompiler, self).eigrp(node) g_eigrp = self.anm['eigrp'] ipv4_interfaces = [] ipv6_interfaces = [] for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface eigrp_int = g_eigrp.interface(interface) if eigrp_int and eigrp_int.is_bound: # TODO: for here and below use stanza directly data = {'id': interface.id, 'passive': False} stanza = ConfigStanza(**data) if node.eigrp.use_ipv4: ipv4_interfaces.append(stanza) if node.eigrp.use_ipv6: ipv6_interfaces.append(stanza) loopback_zero = node.loopback_zero data = {'id': node.loopback_zero.id, 'passive': True} stanza = ConfigStanza(**data) if node.eigrp.use_ipv4: ipv4_interfaces.append(stanza) if node.eigrp.use_ipv6: ipv6_interfaces.append(stanza) node.eigrp.ipv4_interfaces = ipv4_interfaces node.eigrp.ipv6_interfaces = ipv6_interfaces
def nailed_up_routes(self, node): log.debug('Configuring nailed up routes') phy_node = self.anm['phy'].node(node) if node.is_ebgp_v4 and node.ip.use_ipv4: infra_blocks = self.anm['ipv4'].data['infra_blocks'].get( phy_node.asn) or [] for infra_route in infra_blocks: stanza = ConfigStanza( prefix=str(infra_route.network), netmask=str(infra_route.netmask), nexthop="Null0", metric=254, ) node.ipv4_static_routes.append(stanza) if node.is_ebgp_v6 and node.ip.use_ipv6: infra_blocks = self.anm['ipv6'].data['infra_blocks'].get( phy_node.asn) or [] # TODO: setup schema with defaults for infra_route in infra_blocks: stanza = ConfigStanza( prefix=str(infra_route), nexthop="Null0", metric=254, ) node.ipv6_static_routes.append(stanza)
def gre(self, node): node.gre_tunnels = [] if not self.anm.has_overlay('gre_tunnel'): return g_gre_tunnel = self.anm['gre_tunnel'] if node not in g_gre_tunnel: return # no gre tunnel for node gre_node = g_gre_tunnel.node(node) neighbors = gre_node.neighbors() for index, neigh in enumerate(neighbors, start=1): stanza = ConfigStanza(id=index, endpoint=neigh) # TODO: try/except here # TODO: Explain logic here src_int = g_gre_tunnel.edge(node, neigh).src_int tunnel_source = node.interface(src_int).id stanza.source = tunnel_source stanza.destination = "0.0.0.0" # placeholder for user to replace if neigh.tunnel_enabled_ipv4: ip_address = neigh.tunnel_ipv4_address cidr = neigh.tunnel_ipv4_cidr stanza.ipv4_address = ip_address stanza.ipv4_subnet = cidr stanza.use_ipv4 = True if neigh.tunnel_enabled_ipv6: cidr = neigh.tunnel_ipv6_cidr stanza.ipv4_subnet = cidr stanza.use_ipv6 = True node.gre_tunnels.append(stanza)
def lab_topology(self): # TODO: replace name/label and use attribute from subgraph lab_topology = self.nidb.topology(self.host) lab_topology.render_template = os.path.join("templates", "netkit_lab_conf.mako") lab_topology.render_dst_folder = os.path.join("rendered", self.host, "netkit") lab_topology.render_dst_file = "lab.conf" lab_topology.description = "AutoNetkit Lab" lab_topology.author = "AutoNetkit" lab_topology.web = "www.autonetkit.org" host_nodes = list(self.nidb.nodes(host=self.host, platform="netkit")) if not len(host_nodes): log.debug("No Netkit hosts for %s" % self.host) # also need collision domains for this host cd_nodes = self.nidb.nodes("broadcast_domain", host=self.host) host_nodes += cd_nodes subgraph = self.nidb.subgraph(host_nodes, self.host) lab_topology.machines = " ".join( alpha_sort( naming.network_hostname(phy_node) for phy_node in subgraph.l3devices())) lab_topology.config_items = [] for node in sorted(subgraph.l3devices()): for interface in node.physical_interfaces(): broadcast_domain = str(interface.ipv4_subnet).replace("/", ".") #netkit lab.conf uses 1 instead of eth1 numeric_id = interface.numeric_id stanza = ConfigStanza( device=naming.network_hostname(node), key=numeric_id, value=broadcast_domain, ) lab_topology.config_items.append(stanza) lab_topology.tap_ips = [] for node in subgraph: if node.tap: stanza = ConfigStanza( device=naming.network_hostname(node), id=node.tap.id.replace("eth", ""), # strip ethx -> x ip=node.tap.ip, ) lab_topology.tap_ips.append(stanza) lab_topology.tap_ips = sorted(lab_topology.tap_ips, key=lambda x: x.ip) lab_topology.config_items = sorted(lab_topology.config_items, key=lambda x: x.device)
def isis(self, node): super(IosXrCompiler, self).isis(node) node.isis.isis_links = [] for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface # print interface.isis.dump() # copy across attributes from the IosBaseCompiler setting step isis_int = self.anm['isis'].interface(interface) if isis_int and isis_int.is_bound: data = { 'id': interface.id, 'metric': isis_int.metric, 'multipoint': isis_int.multipoint } if interface.isis.hello_padding_disable is not None: data['hello_padding_disable'] = \ interface.isis.hello_padding_disable if interface.isis.mtu is not None: data['mtu'] = interface.isis.hello_padding_disable # TODO: make stanza stanza = ConfigStanza(**data) node.isis.isis_links.append(stanza)
def vrf(self, node): g_vrf = self.anm['vrf'] vrf_node = self.anm['vrf'].node(node) node.add_stanza("vrf") node.add_stanza("mpls") node.vrf.vrfs = [] if vrf_node and vrf_node.vrf_role is 'PE': # TODO: check if mpls ldp already set elsewhere for vrf in vrf_node.node_vrf_names: route_target = g_vrf.data.route_targets[node.asn][vrf] rd_index = vrf_node.rd_indices[vrf] rd = '%s:%s' % (node.asn, rd_index) stanza = ConfigStanza(vrf=vrf, rd=rd, route_target=route_target) node.vrf.vrfs.append(stanza) for interface in node.interfaces: vrf_int = self.anm['vrf'].interface(interface) if vrf_int.vrf_name: # mark interface as being part of vrf interface.vrf = vrf_int.vrf_name if interface.physical: interface.description += ' vrf %s' \ % vrf_int.vrf_name if vrf_node and vrf_node.vrf_role in ('P', 'PE'): # Add PE -> P, PE -> PE interfaces to MPLS LDP node.mpls.ldp_interfaces = [] for interface in node.physical_interfaces(): mpls_ldp_int = self.anm['mpls_ldp'].interface(interface) if mpls_ldp_int.is_bound: node.mpls.ldp_interfaces.append(interface.id) interface.use_mpls = True if vrf_node and vrf_node.vrf_role is 'P': node.mpls.ldp_interfaces = [] for interface in node.physical_interfaces(): node.mpls.ldp_interfaces.append(interface.id) vrf_node = self.anm['vrf'].node(node) node.vrf.use_ipv4 = node.ip.use_ipv4 node.vrf.use_ipv6 = node.ip.use_ipv6 node.vrf.vrfs = sorted(node.vrf.vrfs, key=lambda x: x.vrf) if self.anm.has_overlay('mpls_ldp') and node \ in self.anm['mpls_ldp']: node.mpls.enabled = True node.mpls.router_id = node.loopback_zero.id
def ospf(self, node): """Quagga ospf compiler""" super(QuaggaCompiler, self).ospf(node) # add eBGP link subnets node.ospf.passive_interfaces = [] for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface if self.anm.has_overlay('ebgp_v4'): bgp_int = self.anm['ebgp_v4'].interface(interface) if bgp_int.is_bound: # ebgp interface node.ospf.passive_interfaces.append( ConfigStanza(id=interface.id)) subnet = bgp_int['ipv4'].subnet default_ebgp_area = 0 node.ospf.ospf_links.append( ConfigStanza(network=subnet, area=default_ebgp_area))
def snmp(self, node): g_snmp = self.anm['snmp'] snmp_node = g_snmp.node(node) snmp_stanza = node.add_stanza("snmp") if snmp_node.snmp is None: node.snmp.enabled = False return else: node.snmp.enabled = True if 'servers' in snmp_node.snmp: node.snmp.server = [] for server in snmp_node.snmp['servers']: snmp_server_stanza = ConfigStanza( ip=server['ip'], version=server['version'], udp_port=server['udp_port'], community=server['community']) node.snmp.server.append(snmp_server_stanza) if 'users' in snmp_node.snmp: node.snmp.users = [] for user_prof in snmp_node.snmp['users']: snmp_user_stanza = ConfigStanza( user=user_prof['user'], auth=user_prof['auth'], pwd=user_prof['pwd'], priv_passphrase=user_prof['priv_passphrase'], engine_id=user_prof['engineID']) node.snmp.users.append(snmp_user_stanza) if 'traps' in snmp_node.snmp: node.snmp.traps = [] for trap in snmp_node.snmp['traps']: snmp_trap_stanza = ConfigStanza(id=trap['id'], enabled=trap['enabled']) node.snmp.traps.append(snmp_trap_stanza)
def rip(self, node): super(IosXrCompiler, self).rip(node) g_rip = self.anm['rip'] ipv4_interfaces = [] for interface in node.physical_interfaces(): if interface.exclude_igp: continue # discontinue configuring IGP for this interface rip_int = g_rip.interface(interface) if rip_int and rip_int.is_bound: data = {'id': interface.id, 'passive': False} stanza = ConfigStanza(**data) if node.rip.use_ipv4: ipv4_interfaces.append(stanza) loopback_zero = node.loopback_zero data = {'id': node.loopback_zero.id, 'passive': True} stanza = ConfigStanza(**data) if node.rip.use_ipv4: ipv4_interfaces.append(stanza) node.rip.ipv4_interfaces = ipv4_interfaces
def interfaces(self, node): phy_loopback_zero = self.anm['phy'].interface(node.loopback_zero) if node.ip.use_ipv4: ipv4_loopback_subnet = netaddr.IPNetwork('0.0.0.0/32') ipv4_loopback_zero = phy_loopback_zero['ipv4'] ipv4_address = ipv4_loopback_zero.ip_address node.loopback_zero.use_ipv4 = True node.loopback_zero.ipv4_address = ipv4_address node.loopback_zero.ipv4_subnet = ipv4_loopback_subnet node.loopback_zero.ipv4_cidr = \ sn_preflen_to_network(ipv4_address, ipv4_loopback_subnet.prefixlen) if node.ip.use_ipv6: # TODO: clean this up so can set on router_base: call cidr not # address and update templates node.loopback_zero.use_ipv6 = True ipv6_loopback_zero = phy_loopback_zero['ipv6'] node.loopback_zero.ipv6_address = \ sn_preflen_to_network(ipv6_loopback_zero.ip_address, 128) super(IosBaseCompiler, self).interfaces(node) for interface in node.physical_interfaces(): interface.use_cdp = node.use_cdp # use node value for interface in node.interfaces: interface.sub_ints = [] # temporary until full subinterfaces for interface in node.physical_interfaces(): g_ext_conn = self.anm['ext_conn'] if node not in g_ext_conn: continue node_ext_conn = g_ext_conn.node(node) ext_int = node_ext_conn.interface(interface) for sub_int in ext_int.sub_int or []: stanza = ConfigStanza( id=sub_int['id'], ipv4_address=sub_int['ipv4_address'], ipv4_prefixlen=sub_int['ipv4_prefixlen'], ipv4_subnet=sub_int['ipv4_subnet'], dot1q=sub_int['dot1q'], ) interface.sub_ints.append(stanza)
def mpls_te(self, node): super(IosXrCompiler, self).mpls_te(node) g_mpls_te = self.anm['mpls_te'] if node not in g_mpls_te: return # no mpls te configured rsvp_interfaces = [] mpls_te_interfaces = [] mpls_te_node = g_mpls_te.node(node) for interface in mpls_te_node.physical_interfaces(): nidb_interface = self.nidb.interface(interface) stanza = ConfigStanza(id=nidb_interface.id, bandwidth_percent=100) rsvp_interfaces.append(stanza) mpls_te_interfaces.append(nidb_interface.id) node.add_stanza("rsvp") node.rsvp.interfaces = rsvp_interfaces node.mpls.te_interfaces = mpls_te_interfaces
def vxlan(self, node): g_input = self.anm['input'] node_profiles = g_input.data['profiles'] g_vxlan = self.anm['vxlan'] vxlan_global_config = g_vxlan.data['vxlan_global_config'] g_ipv4 = self.anm['ipv4'] vxlan_node = g_vxlan.node(node) vxlan_stanza = node.add_stanza("vxlan") l2_vni_info = vxlan_global_config['vni_info']['l2_vni_info'] l3_vni_info = vxlan_global_config['vni_info']['l3_vni_info'] if 'ingress_replication' in vxlan_global_config and vxlan_global_config[ 'ingress_replication'] == True: node.vxlan.ingress_replication = True if vxlan_node.vxlan_vni_configured is not None: ### if vni is configured on this device then we can assume that it will be a VTEP device ### To be precise, a leaf for ACI architecture node.vxlan.vtep = True node.vxlan.vrf_vni_vlan = [] node.vxlan.vlan_vni_l2 = [] node.vxlan.vni_mcast = [] node.vxlan.svi = [] if 'anycast_gateway_mac' in vxlan_global_config and vxlan_global_config[ 'anycast_gateway_mac'] is not None: node.vxlan.anycast_gateway_mac = vxlan_global_config[ 'anycast_gateway_mac'] for tenant in vxlan_node.vxlan_vni_configured: flag_first_time = True ## we will use this flag to fetch tenant info only once l2_vnis_configured = tenant['vni'] for l2_vni in l2_vnis_configured: vlan = l2_vni_info[l2_vni][1] vlan_vni_l2_stanza = ConfigStanza(vlan=vlan, vni=l2_vni) node.vxlan.vlan_vni_l2.append(vlan_vni_l2_stanza) mcast_group = l2_vni_info[l2_vni][2] if mcast_group is not None: vni_mcast_stanza = ConfigStanza(vni=l2_vni, mcast_grp=mcast_group) node.vxlan.vni_mcast.append(vni_mcast_stanza) if flag_first_time == True: flag_first_time = False l3_vni_data = l3_vni_info[tenant['tenant_id']] if l3_vni_data is not None: l3_vni = l3_vni_data[0] control_vlan = l3_vni_data[1] vrf = l3_vni_data[2] vrf_vni_vlan_stanza = ConfigStanza( vrf_context=vrf, vni=l3_vni, vlan=control_vlan) node.vxlan.vrf_vni_vlan.append(vrf_vni_vlan_stanza) if l3_vni is not None: anycast_address = l2_vni_info[l2_vni][0] vlan_svi_stanza = ConfigStanza( vlan=vlan, vrf_context=vrf, anycast_gateway_ip=anycast_address) node.vxlan.svi.append(vlan_svi_stanza) else: ###otherwise it will be a non vtep node node.vxlan.nonvtep = True if 'route_reflectors' in vxlan_global_config and vxlan_global_config[ 'route_reflectors'] is not None: for rr in vxlan_global_config['route_reflectors']: if vxlan_node.id == rr: node.vxlan.rr = True break if 'rendezvous_point' in vxlan_global_config and vxlan_global_config[ 'rendezvous_point'] is not None: node.vxlan.rp = [] for rp in vxlan_global_config['rendezvous_point']: rp_stanza = ConfigStanza(ip=rp['node_id'], mcastgrp=rp['mcast_group']) node.vxlan.rp.append(rp_stanza)
def ospf(self, node): """Returns OSPF links, also sets process_id """ g_ospf = self.anm['ospf'] g_ipv4 = self.anm['ipv4'] ospf_node = g_ospf.node(node) ospf_stanza = node.add_stanza("ospf") ospf_stanza.custom_config = ospf_node.custom_config # default, inherited enable if necessary node.ospf.ipv4_mpls_te = False node.ospf.loopback_area = g_ospf.node(node).area or 0 node.ospf.process_id = ospf_node.process_id node.ospf.lo_interface = self.lo_interface node.ospf.ospf_links = [] # aggregate by area from collections import defaultdict interfaces_by_area = defaultdict(list) for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface ospf_int = g_ospf.interface(interface) if ospf_int and ospf_int.is_bound: area = ospf_int.area # TODO: can we remove the next line? area = str(area) # can't serialize IPAddress object to JSON # TODO: put in interface rather than interface.id for # consistency try: cost = int(ospf_int.cost) except TypeError: #TODO: log warning here cost = 1 stanza = ConfigStanza(id=interface.id, cost=cost, passive=False) if node.ip.use_ipv4: stanza.ipv4_address = ospf_int['ipv4'].ip_address stanza.ipv4_subnet = ospf_int['ipv4'].subnet if node.ip.use_ipv6: stanza.ipv6_address = ospf_int['ipv6'].ip_address stanza.ipv6_subnet = ospf_int['ipv6'].subnet interfaces_by_area[area].append(stanza) loopback_zero = node.loopback_zero ospf_loopback_zero = g_ospf.interface(loopback_zero) router_area = ospf_loopback_zero.area # area assigned to router # can't serialize IPAddress object to JSON router_area = str(router_area) stanza = ConfigStanza(id=node.loopback_zero.id, cost=0, passive=True) interfaces_by_area[router_area].append(stanza) node.ospf.interfaces_by_area = ConfigStanza(**interfaces_by_area) # TODO: split this into a generic IGP function added_networks = set() for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface if not interface.use_ipv4: continue ipv4_int = g_ipv4.interface(interface) ospf_int = g_ospf.interface(interface) if not ospf_int.is_bound: continue # not an OSPF interface try: ospf_cost = int(ospf_int.cost) except TypeError: try: ospf_cost = netaddr.IPAddress(ospf_int.cost) except (TypeError, netaddr.AddrFormatError): log.debug('Using default OSPF cost of 1 for %s on %s', ospf_int, node) ospf_cost = 1 # default interface.ospf_cost = ospf_cost network = ipv4_int.subnet #TODO: refactor to allow injecting loopback IPs, etc into IGP if ospf_int and ospf_int.is_bound and network \ not in added_networks: # don't add more than once added_networks.add(network) link_stanza = ConfigStanza( network=network, interface=interface, area=ospf_int.area) node.ospf.ospf_links.append(link_stanza) for interface in node.loopback_interfaces(): phy_int = self.anm['phy'].interface(interface) ipv4_int = g_ipv4.interface(interface) if not phy_int.inject_to_igp: #TODO: need to confirm which area to use continue network = ipv4_int.subnet if network in added_networks: #TODO: may want to warn here continue # already advertised ("how? - warn if so!) # Use the same area as Loopback Zero area = node.ospf.loopback_area if not network: log.info("Not injecting unset network on loopback %s " "to IGP", interface) continue link_stanza = ConfigStanza( network=network, interface=interface, area=area) node.ospf.ospf_links.append(link_stanza)
def bgp(self, node): phy_node = self.anm['phy'].node(node) g_ipv4 = self.anm['ipv4'] if node.ip.use_ipv6: g_ipv6 = self.anm['ipv6'] asn = phy_node.asn node.asn = asn bgp_stanza = node.add_stanza("bgp") if self.anm.has_overlay("bgp"): bgp_stanza.custom_config = phy_node['bgp'].custom_config else: bgp_stanza.custom_config = "" node.bgp.ipv4_advertise_subnets = [] if node.ip.use_ipv4: node.bgp.ipv4_advertise_subnets = \ g_ipv4.data.infra_blocks.get( asn) or [] # could be none (one-node AS) default empty list node.bgp.ipv6_advertise_subnets = [] if node.ip.use_ipv6: g_ipv6 = self.anm['ipv6'] node.bgp.ipv6_advertise_subnets = \ g_ipv6.data.infra_blocks.get(asn) or [] ibgp_neighbors = [] ibgp_rr_clients = [] ibgp_rr_parents = [] g_ibgp_v4 = self.anm['ibgp_v4'] bgp_node = g_ibgp_v4.node(node) if bgp_node.route_reflector == True: node.bgp.route_reflector = True for session in sort_sessions(g_ibgp_v4.edges(phy_node)): if session.exclude: log.debug('Skipping excluded ibgp session %s', session) # exclude from regular ibgp config (eg VRF, VPLS, etc) continue data = self.ibgp_session_data(session, ip_version=4) bgp_stanza = ConfigStanza(**data) direction = session.direction if direction == 'down': ibgp_rr_clients.append(bgp_stanza) elif direction == 'up': ibgp_rr_parents.append(bgp_stanza) else: ibgp_neighbors.append(bgp_stanza) # TODO: check v6 hierarchy only created if node set to being v4 or v6 if node.ip.use_ipv6: g_ibgp_v6 = self.anm['ibgp_v6'] for session in sort_sessions(g_ibgp_v6.edges(phy_node)): if session.exclude: log.debug('Skipping excluded ibgp session %s', session) # exclude from regular ibgp config (eg VRF, VPLS, etc) continue data = self.ibgp_session_data(session, ip_version=6) bgp_stanza = ConfigStanza(**data) direction = session.direction if direction == 'down': ibgp_rr_clients.append(bgp_stanza) elif direction == 'up': ibgp_rr_parents.append(bgp_stanza) else: ibgp_neighbors.append(bgp_stanza) # TODO: update this to use ibgp_v4 and ibgp_v6 overlays node.bgp.ibgp_neighbors = ibgp_neighbors node.bgp.ibgp_rr_clients = ibgp_rr_clients node.bgp.ibgp_rr_parents = ibgp_rr_parents # ebgp ebgp_neighbors = [] g_ebgp_v4 = self.anm['ebgp_v4'] for session in sort_sessions(g_ebgp_v4.edges(phy_node)): if session.exclude: log.debug('Skipping excluded ebgp session %s', session) # exclude from regular ibgp config (eg VRF, VPLS, etc) continue data = self.ebgp_session_data(session, ip_version=4) bgp_stanza = ConfigStanza(**data) ebgp_neighbors.append(bgp_stanza) if node.ip.use_ipv6: g_ebgp_v6 = self.anm['ebgp_v6'] for session in sort_sessions(g_ebgp_v6.edges(phy_node)): if session.exclude: log.debug('Skipping excluded ebgp session %s', session) # exclude from regular ibgp config (eg VRF, VPLS, etc) continue data = self.ebgp_session_data(session, ip_version=6) bgp_stanza = ConfigStanza(**data) ebgp_neighbors.append(bgp_stanza) ebgp_neighbors = sorted(ebgp_neighbors, key=lambda x: x.asn) node.bgp.ebgp_neighbors = ebgp_neighbors return
def static_routes(self, node): node.static_routes_v4 = [ ] # initialise for case of no routes -> simplifies template logic node.host_routes_v4 = [ ] # initialise for case of no routes -> simplifies template logic node.static_routes_v6 = [ ] # initialise for case of no routes -> simplifies template logic node.host_routes_v6 = [ ] # initialise for case of no routes -> simplifies template logic if not self.anm['phy'].data.enable_routing: log.info( 'Routing disabled, not configuring static routes for Ubuntu server %s' % node) return if self.anm['phy'].node(node).dont_configure_static_routing: log.info('Static routing disabled for server %s' % node) return l3_node = self.anm['layer3'].node(node) gateway_list = [n for n in l3_node.neighbors() if n.is_router()] if not len(gateway_list): log.warning('Server %s is not directly connected to any routers' % node) return elif len(gateway_list) > 1: log.info('Server %s is multi-homed: using gateways %s' % (node, sorted(gateway_list))) # TODO: warn if server has no neighbors in same ASN (either in design or verification steps) # TODO: need to check that servers don't have any direct ebgp connections cloud_init_static_routes = [] for gateway in sorted(gateway_list): gateway_edge_l3 = self.anm['layer3'].edge(node, gateway) server_interface = gateway_edge_l3.src_int server_interface_id = self.nidb.interface(server_interface).id gateway_interface = gateway_edge_l3.dst_int gateway_ipv4 = gateway_ipv6 = None node.add_stanza("ip") if node.ip.use_ipv4: gateway_ipv4 = gateway_interface['ipv4'].ip_address if node.ip.use_ipv6: gateway_ipv6 = gateway_interface['ipv6'].ip_address # TODO: look at aggregation # TODO: catch case of ip addressing being disabled # TODO: handle both ipv4 and ipv6 # IGP advertised infrastructure pool from same AS static_routes_v4 = [] host_routes_v4 = [] for (asn, asn_routes) in self.anm['ipv4'].data['infra_blocks'].items(): # host_routes_v4 for infra_route in asn_routes: route_entry = { 'network': infra_route, 'prefix': infra_route.network, 'gw': gateway_ipv4, 'interface': server_interface_id, 'description': 'Route to infra subnet in AS %s via %s' \ % (asn, gateway), } route_entry = ConfigStanza(**route_entry) if infra_route.prefixlen == 32: host_routes_v4.append(route_entry) else: static_routes_v4.append(route_entry) # eBGP advertised loopbacks in all (same + other) ASes for (asn, asn_routes ) in self.anm['ipv4'].data['loopback_blocks'].items(): for asn_route in asn_routes: route_entry = { 'network': asn_route, 'prefix': asn_route.network, 'gw': gateway_ipv4, 'interface': server_interface_id, 'description': 'Route to loopback subnet in AS %s via %s' \ % (asn, gateway), } route_entry = ConfigStanza(**route_entry) if asn_route.prefixlen == 32: host_routes_v4.append(route_entry) else: static_routes_v4.append(route_entry) # TODO: combine the above logic into single step rather than creating dict then formatting with it for entry in static_routes_v4: formatted = 'route add -net %s gw %s dev %s' \ % (entry.network, entry.gw, entry.interface) cloud_init_static_routes.append(formatted) for entry in host_routes_v4: formatted = 'route add -host %s gw %s dev %s' \ % (entry.prefix, entry.gw, entry.interface) cloud_init_static_routes.append(formatted) node.add_stanza("cloud_init") node.cloud_init.static_routes = cloud_init_static_routes
def bgp(self, node): super(IosClassicCompiler, self).bgp(node) node.bgp.use_ipv4 = node.ip.use_ipv4 node.bgp.use_ipv6 = node.ip.use_ipv6 # Seperate by address family ipv4_peers = [] ipv6_peers = [] # Note cast to dict - #TODO revisit this requirement # TODO: revisit and tidy up the logic here: split iBGP and eBGP # TODO: sort the peer list by peer IP for peer in node.bgp.ibgp_neighbors: peer = ConfigStanza(peer) peer.remote_ip = peer.loopback if peer.use_ipv4: if node.is_ebgp_v4: peer.next_hop_self = True ipv4_peers.append(peer) if peer.use_ipv6: if node.is_ebgp_v6: peer.next_hop_self = True ipv6_peers.append(peer) for peer in node.bgp.ibgp_rr_parents: peer = ConfigStanza(peer) peer.remote_ip = peer.loopback if peer.use_ipv4: if node.is_ebgp_v4: peer.next_hop_self = True ipv4_peers.append(peer) if peer.use_ipv6: if node.is_ebgp_v6: peer.next_hop_self = True ipv6_peers.append(peer) for peer in node.bgp.ibgp_rr_clients: peer = ConfigStanza(peer) peer.rr_client = True peer.remote_ip = peer.loopback if peer.use_ipv4: if node.is_ebgp_v4: peer.next_hop_self = True ipv4_peers.append(peer) if peer.use_ipv6: if node.is_ebgp_v6: peer.next_hop_self = True ipv6_peers.append(peer) for peer in node.bgp.ebgp_neighbors: peer = ConfigStanza(peer) peer.is_ebgp = True peer.remote_ip = peer.dst_int_ip if peer.use_ipv4: peer.next_hop_self = True ipv4_peers.append(peer) if peer.use_ipv6: peer.next_hop_self = True ipv6_peers.append(peer) node.bgp.ipv4_peers = ipv4_peers node.bgp.ipv6_peers = ipv6_peers vpnv4_neighbors = [] if node.bgp.vpnv4: for neigh in node.bgp.ibgp_neighbors: if not neigh.use_ipv4: continue neigh_data = ConfigStanza(neigh) vpnv4_neighbors.append(neigh_data) for neigh in node.bgp.ibgp_rr_clients: if not neigh.use_ipv4: continue neigh_data = ConfigStanza(neigh) neigh_data.rr_client = True vpnv4_neighbors.append(neigh_data) for neigh in node.bgp.ibgp_rr_parents: if not neigh.use_ipv4: continue neigh_data = ConfigStanza(neigh) vpnv4_neighbors.append(neigh_data) vpnv4_neighbors = sorted(vpnv4_neighbors, key=lambda x: x['loopback']) node.bgp.vpnv4_neighbors = vpnv4_neighbors
def ospf(self, node): """Returns OSPF links, also sets process_id """ g_ospf = self.anm['ospf'] g_ipv4 = self.anm['ipv4'] ospf_node = g_ospf.node(node) ospf_stanza = node.add_stanza("ospf") ospf_stanza.custom_config = ospf_node.custom_config # default, inherited enable if necessary node.ospf.ipv4_mpls_te = False node.ospf.loopback_area = g_ospf.node(node).area or 0 node.ospf.process_id = ospf_node.process_id node.ospf.lo_interface = self.lo_interface node.ospf.ospf_links = [] # aggregate by area from collections import defaultdict interfaces_by_area = defaultdict(list) for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface ospf_int = g_ospf.interface(interface) if ospf_int and ospf_int.is_bound: area = ospf_int.area # TODO: can we remove the next line? area = str(area) # can't serialize IPAddress object to JSON # TODO: put in interface rather than interface.id for # consistency try: cost = int(ospf_int.cost) except TypeError: #TODO: log warning here cost = 1 stanza = ConfigStanza(id=interface.id, cost=cost, passive=False) if node.ip.use_ipv4: stanza.ipv4_address = ospf_int['ipv4'].ip_address stanza.ipv4_subnet = ospf_int['ipv4'].subnet if node.ip.use_ipv6: stanza.ipv6_address = ospf_int['ipv6'].ip_address stanza.ipv6_subnet = ospf_int['ipv6'].subnet interfaces_by_area[area].append(stanza) for interface in node.portchannel_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface ospf_int = g_ospf.interface(interface) if ospf_int and ospf_int.is_bound: area = ospf_int.area # TODO: can we remove the next line? area = str(area) # can't serialize IPAddress object to JSON # TODO: put in interface rather than interface.id for # consistency try: cost = int(ospf_int.cost) except TypeError: #TODO: log warning here cost = 1 stanza = ConfigStanza(id=interface.id, cost=cost, passive=False) if node.ip.use_ipv4: stanza.ipv4_address = ospf_int['ipv4'].ip_address stanza.ipv4_subnet = ospf_int['ipv4'].subnet if node.ip.use_ipv6: stanza.ipv6_address = ospf_int['ipv6'].ip_address stanza.ipv6_subnet = ospf_int['ipv6'].subnet interfaces_by_area[area].append(stanza) loopback_zero = node.loopback_zero ospf_loopback_zero = g_ospf.interface(loopback_zero) router_area = ospf_loopback_zero.area # area assigned to router # can't serialize IPAddress object to JSON router_area = str(router_area) stanza = ConfigStanza(id=node.loopback_zero.id, cost=0, passive=True) interfaces_by_area[router_area].append(stanza) node.ospf.interfaces_by_area = ConfigStanza(**interfaces_by_area) # TODO: split this into a generic IGP function added_networks = set() for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface if not interface.use_ipv4: continue ipv4_int = g_ipv4.interface(interface) ospf_int = g_ospf.interface(interface) if not ospf_int.is_bound: continue # not an OSPF interface try: ospf_cost = int(ospf_int.cost) except TypeError: try: ospf_cost = netaddr.IPAddress(ospf_int.cost) except (TypeError, netaddr.AddrFormatError): log.debug('Using default OSPF cost of 1 for %s on %s', ospf_int, node) ospf_cost = 1 # default interface.ospf_cost = ospf_cost network = ipv4_int.subnet #TODO: refactor to allow injecting loopback IPs, etc into IGP if ospf_int and ospf_int.is_bound and network \ not in added_networks: # don't add more than once added_networks.add(network) link_stanza = ConfigStanza(network=network, interface=interface, area=ospf_int.area) node.ospf.ospf_links.append(link_stanza) for interface in node.portchannel_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface if not interface.use_ipv4: continue ipv4_int = g_ipv4.interface(interface) ospf_int = g_ospf.interface(interface) if not ospf_int.is_bound: continue # not an OSPF interface try: ospf_cost = int(ospf_int.cost) except TypeError: try: ospf_cost = netaddr.IPAddress(ospf_int.cost) except (TypeError, netaddr.AddrFormatError): log.debug('Using default OSPF cost of 1 for %s on %s', ospf_int, node) ospf_cost = 1 # default interface.ospf_cost = ospf_cost network = ipv4_int.subnet #TODO: refactor to allow injecting loopback IPs, etc into IGP if ospf_int and ospf_int.is_bound and network \ not in added_networks: # don't add more than once added_networks.add(network) link_stanza = ConfigStanza(network=network, interface=interface, area=ospf_int.area) node.ospf.ospf_links.append(link_stanza) for interface in node.loopback_interfaces(): phy_int = self.anm['phy'].interface(interface) ipv4_int = g_ipv4.interface(interface) if not phy_int.inject_to_igp: #TODO: need to confirm which area to use continue network = ipv4_int.subnet if network in added_networks: #TODO: may want to warn here continue # already advertised ("how? - warn if so!) # Use the same area as Loopback Zero area = node.ospf.loopback_area if not network: log.info( "Not injecting unset network on loopback %s " "to IGP", interface) continue link_stanza = ConfigStanza(network=network, interface=interface, area=area) node.ospf.ospf_links.append(link_stanza)
def l2tp_v3(self, node): node.l2tp_classes = [] node.pseudowire_classes = [] if not self.anm.has_overlay('l2tp_v3'): return g_l2tp_v3 = self.anm['l2tp_v3'] g_phy = self.anm['phy'] if node not in g_l2tp_v3: return # no l2tp_v3 for node l2tp_v3_node = g_l2tp_v3.node(node) if l2tp_v3_node.role != "tunnel": return # nothing to configure node.l2tp_classes = list(l2tp_v3_node.l2tp_classes) for l2tp_class in l2tp_v3_node.l2tp_classes: node.l2tp_classes node.pseudowire_classes = [] for pwc in l2tp_v3_node.pseudowire_classes: stanza = ConfigStanza() stanza.name = pwc['name'] stanza.encapsulation = pwc['encapsulation'] stanza.protocol = pwc['protocol'] stanza.l2tp_class_name = pwc['l2tp_class_name'] local_interface = pwc['local_interface'] # Lookup the interface ID allocated for this loopback by compiler local_interface_id = node.interface(local_interface).id stanza.local_interface = local_interface_id node.pseudowire_classes.append(stanza) for interface in node.physical_interfaces(): phy_int = g_phy.interface(interface) if phy_int.xconnect_encapsulation != "l2tpv3": continue # no l2tpv3 encap, no need to do anything tunnel_int = l2tp_v3_node.interface(interface) stanza = ConfigStanza() stanza.remote_ip = tunnel_int.xconnect_remote_ip stanza.vc_id = tunnel_int.xconnect_vc_id stanza.encapsulation = "l2tpv3" #TODO: need to be conscious of support for other xconnect types # in templates since pw_class may not apply if not l2tpv3, et stanza.pw_class = tunnel_int.xconnect_pw_class interface.xconnect = stanza
def compile_devices(self): import re g_phy = self.anm['phy'] to_memory, use_mgmt_interfaces, dst_folder = self._parameters() if use_mgmt_interfaces: log.debug("Allocating VIRL management interfaces") else: log.debug("Not allocating VIRL management interfaces") pc_only_config = False vxlan_global_config = None if g_phy.data.vxlan_global_config is not None: vxlan_global_config = g_phy.data.vxlan_global_config if g_phy.data.pc_only is not None: pc_only_config = g_phy.data.pc_only use_ignite_pool = False if g_phy.data.mgmt_block is not None: mgmt_address_block = netaddr.IPNetwork(g_phy.data.mgmt_block).iter_hosts() mgmt_address_mask = (netaddr.IPNetwork(g_phy.data.mgmt_block)).netmask else: pool = g_phy.data.ignite if pool is not None: if 'mgmt_pool_id' in pool and pool['mgmt_pool_id'] is not None: mgmt_pool_id = pool['mgmt_pool_id'] use_ignite_pool = True if g_phy.data.vpcid_block is not None: vpc_re = "([0-9]+)(-)([0-9]+)" #vpc_id_start = int(re.search(vpc_re, g_phy.data.vpcid_block).group(1)) #vpc_id_end = int(re.search(vpc_re, g_phy.data.vpcid_block).group(3)) #vpc_id_range = vpc_id_end-vpc_id_start vpc_id_range = 1000 # TODO: need to copy across the interface name from edge to the interface # TODO: merge common router code, so end up with three loops: routers, ios # routers, ios_xr routers # TODO: Split out each device compiler into own function # TODO: look for unused code paths here - especially for interface # allocation # store autonetkit_cisco version log.debug("Generating device configurations") from pkg_resources import get_distribution # Copy across indices for external connectors (e.g may want to copy # configs) external_connectors = [n for n in g_phy if n.host == self.host and n.device_type == "external_connector"] for phy_node in external_connectors: DmNode = self.nidb.node(phy_node) DmNode.indices = phy_node.indices g_input = self.anm['input'] managed_switches = [n for n in g_phy.switches() if n.host == self.host and n.device_subtype == "managed"] for phy_node in managed_switches: DmNode = self.nidb.node(phy_node) DmNode.indices = phy_node.indices for phy_node in g_phy.l3devices(host=self.host): loopback_ids = self.loopback_interface_ids() # allocate loopbacks to routes (same for all ios variants) DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.indices = phy_node.indices DmNode.add_stanza("syslog") DmNode.add_stanza('mgmt') if use_ignite_pool == True: mgmt_ip = allocate_pool_entry(mgmt_pool_id,phy_node.name, None) pos_mask = mgmt_ip.find('/') if pos_mask != -1: network = mgmt_ip[:pos_mask] mask = int(mgmt_ip[pos_mask+1:]) else: network = mgmt_ip[:pos_mask] mask = 32 DmNode.mgmt.ip = network + '/' + str(mask) DmNode.mgmt.mask = "" elif g_phy.data.mgmt_block is not None: DmNode.mgmt.ip = mgmt_address_block.next() DmNode.mgmt.mask = mgmt_address_mask # for node_data in phy_node._graph.node: for interface in DmNode.loopback_interfaces(): if interface != DmNode.loopback_zero: interface.id = loopback_ids.next() # numeric ids numeric_int_ids = self.numeric_interface_ids() for interface in DmNode.physical_interfaces(): phy_numeric_id = phy_node.interface(interface).numeric_id if phy_numeric_id is None: # TODO: remove numeric ID code interface.numeric_id = numeric_int_ids.next() else: interface.numeric_id = int(phy_numeric_id) phy_specified_id = phy_node.interface(interface).specified_id if phy_specified_id is not None: interface.id = phy_specified_id # numeric ids numeric_po_ids = self.numeric_portchannel_ids() for interface in DmNode.portchannel_interfaces(): po_interface = phy_node.interface(interface) interface.numeric_id = int(po_interface.id[po_interface.id.rfind('_')+1:]) po_specified_id = phy_node.interface(interface).specified_id if po_specified_id is not None: interface.id = po_specified_id interface.pc = True for po_mem_int in DmNode.physical_interfaces(): po_mem_interface = phy_node.interface(po_mem_int) if po_mem_interface.id in po_interface.members: po_mem_int.channel_group = interface.numeric_id po_interface_int = po_interface._interface if po_interface_int.has_key('subcat_prot'): if po_interface.subcat_prot == "vpc": po_mem_int.keepalive_port_vpc = True# is a member port of VPC peer link interface.virt_port_channel = True# is a VPC interface DmNode.add_stanza('vpc') if po_interface.subcat_prot == "vpc-member": po_mem_int.member_port_vpc = True interface.vpc_member_id = interface.numeric_id interface.member_vpc = True ##adding rp's if vxlan_global_config is not None and 'rendezvous_point' in vxlan_global_config: for rp in vxlan_global_config['rendezvous_point']: for phy_node in g_phy.l3devices(host=self.host): DmNode = self.nidb.node(phy_node) if phy_node.id == rp['node_id']: rp['node_id'] = DmNode.interfaces[0]._port['ipv4_address'] #from autonetkit.compilers.device.ubuntu import UbuntuCompiler #from autonetkit_cisco.compilers.device.ubuntu import UbuntuCompiler #ubuntu_compiler = UbuntuCompiler(self.nidb, self.anm) for phy_node in g_phy.servers(host=self.host): DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.add_stanza("ip") #interface.id = self.numeric_to_interface_label_linux(interface.numeric_id) # print "numeric", interface.numeric_id, interface.id DmNode.ip.use_ipv4 = phy_node.use_ipv4 DmNode.ip.use_ipv6 = phy_node.use_ipv6 # TODO: clean up interface handling numeric_int_ids = self.numeric_interface_ids() for interface in DmNode.physical_interfaces(): phy_numeric_id = phy_node.interface(interface).numeric_id if phy_numeric_id is None: # TODO: remove numeric ID code interface.numeric_id = numeric_int_ids.next() else: interface.numeric_id = int(phy_numeric_id) phy_specified_id = phy_node.interface(interface).specified_id if phy_specified_id is not None: interface.id = phy_specified_id # numeric ids numeric_po_ids = self.numeric_portchannel_ids() for interface in DmNode.portchannel_interfaces(): phy_numeric_id = phy_node.interface(interface).numeric_id if phy_numeric_id is None: # TODO: remove numeric ID code interface.numeric_id = numeric_po_ids.next() else: interface.numeric_id = int(phy_numeric_id) phy_specified_id = phy_node.interface(interface).specified_id if phy_specified_id is not None: interface.id = phy_specified_id # TODO: make this part of the base device compiler, which # server/router inherits # not these are physical interfaces; configure after previous # config steps if use_mgmt_interfaces: mgmt_int = DmNode.add_interface( management=True, description="eth0") mgmt_int_id = "eth0" mgmt_int.id = mgmt_int_id # render route config DmNode = self.nidb.node(phy_node) #ubuntu_compiler.compile(DmNode) if not phy_node.dont_configure_static_routing: DmNode.render.template = os.path.join( "templates", "linux", "static_route.mako") if to_memory: DmNode.render.to_memory = True else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % naming.network_hostname( phy_node) # TODO: refactor out common logic ios_compiler = IosClassicCompiler(self.nidb, self.anm) host_routers = g_phy.routers(host=self.host) ios_nodes = (n for n in host_routers if n.syntax in ("ios", "ios_xe")) for phy_node in ios_nodes: if (phy_node.devsubtype == "core" or phy_node.devsubtype == "border"): continue DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.render.template = os.path.join("templates", "ios.mako") to_memory = False if to_memory: DmNode.render.to_memory = True else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % naming.network_hostname( phy_node) # TODO: write function that assigns interface number excluding # those already taken # Assign interfaces if phy_node.device_subtype == "IOSv": int_ids = self.interface_ids_ios() numeric_to_interface_label = self.numeric_to_interface_label_ios elif phy_node.device_subtype == "CSR1000v": int_ids = self.interface_ids_csr1000v() numeric_to_interface_label = self.numeric_to_interface_label_ra else: # default if no subtype specified # TODO: need to set default in the load module log.warning("Unexpected subtype %s for %s" % (phy_node.device_subtype, phy_node)) int_ids = self.interface_ids_ios() numeric_to_interface_label = self.numeric_to_interface_label_ios numeric_to_portchannel_interface_label = self.numeric_to_portchannel_interface_label_ios if use_mgmt_interfaces: if phy_node.device_subtype == "IOSv": # TODO: make these configured in the internal config file # for platform/device_subtype keying mgmt_int_id = "GigabitEthernet0/0" if phy_node.device_subtype == "CSR1000v": mgmt_int_id = "GigabitEthernet1" for interface in DmNode.physical_interfaces(): # TODO: use this code block once for all routers if not interface.id: interface.id = numeric_to_interface_label( interface.numeric_id) else: if interface.id[0] == 'e' or interface.id[0] == 'E': #import re port_re = "([a-zA-Z]+)([0-9]+/[0-9]+)" interface.id = "%s %s"%((re.search(port_re,interface.id)).group(1), (re.search(port_re,interface.id)).group(2)) for interface in DmNode.portchannel_interfaces(): # TODO: use this code block once for all routers #if not interface.id: interface.id = numeric_to_portchannel_interface_label( interface.numeric_id) ios_compiler.compile(DmNode) if use_mgmt_interfaces: mgmt_int = DmNode.add_interface(management=True) mgmt_int.id = mgmt_int_id nxos_compiler = NxOsCompiler(self.nidb, self.anm) for phy_node in g_phy.routers(host=self.host, syntax='nx_os'): if (phy_node.devsubtype == "core" or phy_node.devsubtype == "border"): continue DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") if pc_only_config == True: DmNode.render.template = os.path.join("templates", "nexus_os_pc_only.mako") else: DmNode.render.template = os.path.join("templates", "nexus_os.mako") #if to_memory: # DmNode.render.to_memory = True #else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % phy_node.name # Assign interfaces int_ids = self.interface_ids_nxos() numeric_to_interface_label = self.numeric_to_interface_label_nxos numeric_to_portchannel_interface_label = self.numeric_to_portchannel_interface_label_nxos for interface in DmNode.physical_interfaces(): if not interface.id: interface.id = self.numeric_to_interface_label_nxos( interface.numeric_id) elif interface.id[0] == 'e' or interface.id[0] == 'E': import re port_re = "([a-zA-Z]+)([0-9]+/[0-9]+)" interface.id = "%s %s"%((re.search(port_re,interface.id)).group(1), (re.search(port_re,interface.id)).group(2)) else: interface.id = 'Ethernet ' + interface.id for interface in DmNode.portchannel_interfaces(): # TODO: use this code block once for all routers #if not interface.id: interface.id = numeric_to_portchannel_interface_label( interface.numeric_id) DmNode.supported_features = ConfigStanza( mpls_te=False, mpls_oam=False, vrf=False) nxos_compiler.compile(DmNode) # TODO: make this work other way around if use_mgmt_interfaces: mgmt_int_id = "mgmt0" mgmt_int = DmNode.add_interface(management=True) mgmt_int.id = mgmt_int_id staros_compiler = StarOsCompiler(self.nidb, self.anm) for phy_node in g_phy.routers(host=self.host, syntax='StarOS'): DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.render.template = os.path.join("templates", "staros.mako") if to_memory: DmNode.render.to_memory = True else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % naming.network_hostname( phy_node) # Assign interfaces int_ids = self.interface_ids_nxos() for interface in DmNode.physical_interfaces(): if not interface.id: interface.id = self.numeric_to_interface_label_star_os( interface.numeric_id) staros_compiler.compile(DmNode) # TODO: make this work other way around if use_mgmt_interfaces: mgmt_int_id = "ethernet 1/1" mgmt_int = DmNode.add_interface(management=True) mgmt_int.id = mgmt_int_id
def ospf(self, node): """Returns OSPF links, also sets process_id """ g_ospf = self.anm['ospf'] g_ipv4 = self.anm['ipv4'] ospf_node = g_ospf.node(node) ospf_stanza = node.add_stanza("ospf") ospf_stanza.custom_config = ospf_node.custom_config node.ospf.ipv4_mpls_te = False # default, inherited enable if necessary node.ospf.loopback_area = g_ospf.node(node).area or 0 node.ospf.process_id = ospf_node.process_id node.ospf.lo_interface = self.lo_interface node.ospf.ospf_links = [] # aggregate by area from collections import defaultdict interfaces_by_area = defaultdict(list) for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface ospf_int = g_ospf.interface(interface) if ospf_int and ospf_int.is_bound: area = ospf_int.area #TODO: can we remove the next line? area = str(area) # can't serialize IPAddress object to JSON #TODO: put in interface rather than interface.id for consistency stanza = ConfigStanza(id=interface.id, cost=int(ospf_int.cost), passive=False) if node.ip.use_ipv4: stanza.ipv4_address = ospf_int['ipv4'].ip_address stanza.ipv4_subnet = ospf_int['ipv4'].subnet if node.ip.use_ipv6: stanza.ipv6_address = ospf_int['ipv6'].ip_address stanza.ipv6_subnet = ospf_int['ipv6'].subnet interfaces_by_area[area].append(stanza) loopback_zero = node.loopback_zero ospf_loopback_zero = g_ospf.interface(loopback_zero) router_area = ospf_loopback_zero.area # area assigned to router router_area = str( router_area) # can't serialize IPAddress object to JSON stanza = ConfigStanza(id=node.loopback_zero.id, cost=0, passive=True) interfaces_by_area[router_area].append(stanza) node.ospf.interfaces_by_area = ConfigStanza(**interfaces_by_area) added_networks = set() for interface in node.physical_interfaces(): if interface.exclude_igp: continue # don't configure IGP for this interface ipv4_int = g_ipv4.interface(interface) ospf_int = g_ospf.interface(interface) if not ospf_int.is_bound: continue # not an OSPF interface try: ospf_cost = int(ospf_int.cost) except TypeError: try: ospf_cost = netaddr.IPAddress(ospf_int.cost) except (TypeError, netaddr.AddrFormatError): log.debug('Using default OSPF cost of 1 for %s on %s' % (ospf_int, node)) ospf_cost = 1 # default interface.ospf_cost = ospf_cost network = ipv4_int.subnet if ospf_int and ospf_int.is_bound and network \ not in added_networks: # don't add more than once added_networks.add(network) link_stanza = ConfigStanza(network=network, interface=interface, area=ospf_int.area) node.ospf.ospf_links.append(link_stanza)
def compile_devices(self): g_phy = self.anm['phy'] to_memory, use_mgmt_interfaces, dst_folder = self._parameters() if use_mgmt_interfaces: log.debug("Allocating VIRL management interfaces") else: log.debug("Not allocating VIRL management interfaces") # TODO: need to copy across the interface name from edge to the interface # TODO: merge common router code, so end up with three loops: routers, ios # routers, ios_xr routers # TODO: Split out each device compiler into own function # TODO: look for unused code paths here - especially for interface # allocation # store autonetkit_cisco version log.debug("Generating device configurations") from pkg_resources import get_distribution # Copy across indices for external connectors (e.g may want to copy # configs) external_connectors = [ n for n in g_phy if n.host == self.host and n.device_type == "external_connector" ] for phy_node in external_connectors: DmNode = self.nidb.node(phy_node) DmNode.indices = phy_node.indices managed_switches = [ n for n in g_phy.switches() if n.host == self.host and n.device_subtype == "managed" ] for phy_node in managed_switches: DmNode = self.nidb.node(phy_node) DmNode.indices = phy_node.indices for phy_node in g_phy.l3devices(host=self.host): loopback_ids = self.loopback_interface_ids() # allocate loopbacks to routes (same for all ios variants) DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.indices = phy_node.indices for interface in DmNode.loopback_interfaces(): if interface != DmNode.loopback_zero: interface.id = loopback_ids.next() # numeric ids numeric_int_ids = self.numeric_interface_ids() for interface in DmNode.physical_interfaces(): phy_numeric_id = phy_node.interface(interface).numeric_id if phy_numeric_id is None: # TODO: remove numeric ID code interface.numeric_id = numeric_int_ids.next() else: interface.numeric_id = int(phy_numeric_id) phy_specified_id = phy_node.interface(interface).specified_id if phy_specified_id is not None: interface.id = phy_specified_id #from autonetkit.compilers.device.ubuntu import UbuntuCompiler from autonetkit_cisco.compilers.device.ubuntu import UbuntuCompiler ubuntu_compiler = UbuntuCompiler(self.nidb, self.anm) for phy_node in g_phy.servers(host=self.host): DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.add_stanza("ip") # TODO: look at server syntax also, same as for routers for interface in DmNode.physical_interfaces(): phy_specified_id = phy_node.interface(interface).specified_id if phy_specified_id is not None: interface.id = phy_specified_id #interface.id = self.numeric_to_interface_label_linux(interface.numeric_id) # print "numeric", interface.numeric_id, interface.id DmNode.ip.use_ipv4 = phy_node.use_ipv4 DmNode.ip.use_ipv6 = phy_node.use_ipv6 # TODO: clean up interface handling numeric_int_ids = self.numeric_interface_ids() for interface in DmNode.physical_interfaces(): phy_int = phy_node.interface(interface) phy_numeric_id = phy_node.interface(interface).numeric_id if phy_numeric_id is None: # TODO: remove numeric ID code interface.numeric_id = numeric_int_ids.next() else: interface.numeric_id = int(phy_numeric_id) phy_specified_id = phy_node.interface(interface).specified_id if phy_specified_id is not None: interface.id = phy_specified_id # TODO: make this part of the base device compiler, which # server/router inherits # not these are physical interfaces; configure after previous # config steps if use_mgmt_interfaces: mgmt_int = DmNode.add_interface(management=True, description="eth0") mgmt_int_id = "eth0" mgmt_int.id = mgmt_int_id # render route config DmNode = self.nidb.node(phy_node) ubuntu_compiler.compile(DmNode) if not phy_node.dont_configure_static_routing: DmNode.render.template = os.path.join("templates", "linux", "static_route.mako") if to_memory: DmNode.render.to_memory = True else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % naming.network_hostname( phy_node) # TODO: refactor out common logic ios_compiler = IosClassicCompiler(self.nidb, self.anm) host_routers = g_phy.routers(host=self.host) ios_nodes = (n for n in host_routers if n.syntax in ("ios", "ios_xe")) for phy_node in ios_nodes: DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.render.template = os.path.join("templates", "ios.mako") if to_memory: DmNode.render.to_memory = True else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % naming.network_hostname( phy_node) # TODO: write function that assigns interface number excluding # those already taken # Assign interfaces if phy_node.device_subtype == "IOSv": int_ids = self.interface_ids_ios() numeric_to_interface_label = self.numeric_to_interface_label_ios elif phy_node.device_subtype == "CSR1000v": int_ids = self.interface_ids_csr1000v() numeric_to_interface_label = self.numeric_to_interface_label_ra else: # default if no subtype specified # TODO: need to set default in the load module log.warning("Unexpected subtype %s for %s" % (phy_node.device_subtype, phy_node)) int_ids = self.interface_ids_ios() numeric_to_interface_label = self.numeric_to_interface_label_ios if use_mgmt_interfaces: if phy_node.device_subtype == "IOSv": # TODO: make these configured in the internal config file # for platform/device_subtype keying mgmt_int_id = "GigabitEthernet0/0" if phy_node.device_subtype == "CSR1000v": mgmt_int_id = "GigabitEthernet1" for interface in DmNode.physical_interfaces(): # TODO: use this code block once for all routers if not interface.id: interface.id = numeric_to_interface_label( interface.numeric_id) ios_compiler.compile(DmNode) if use_mgmt_interfaces: mgmt_int = DmNode.add_interface(management=True) mgmt_int.id = mgmt_int_id try: from autonetkit_cisco.compilers.device.cisco import IosXrCompiler ios_xr_compiler = IosXrCompiler(self.nidb, self.anm) except ImportError: ios_xr_compiler = IosXrCompiler(self.nidb, self.anm) for phy_node in g_phy.routers(host=self.host, syntax='ios_xr'): DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.render.template = os.path.join("templates", "ios_xr", "router.conf.mako") if to_memory: DmNode.render.to_memory = True else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % naming.network_hostname( phy_node) # Assign interfaces int_ids = self.interface_ids_ios_xr() for interface in DmNode.physical_interfaces(): if not interface.id: interface.id = self.numeric_to_interface_label_ios_xr( interface.numeric_id) ios_xr_compiler.compile(DmNode) if use_mgmt_interfaces: mgmt_int_id = "mgmteth0/0/CPU0/0" mgmt_int = DmNode.add_interface(management=True) mgmt_int.id = mgmt_int_id nxos_compiler = NxOsCompiler(self.nidb, self.anm) for phy_node in g_phy.routers(host=self.host, syntax='nx_os'): DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.render.template = os.path.join("templates", "nx_os.mako") if to_memory: DmNode.render.to_memory = True else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % naming.network_hostname( phy_node) # Assign interfaces int_ids = self.interface_ids_nxos() for interface in DmNode.physical_interfaces(): if not interface.id: interface.id = self.numeric_to_interface_label_nxos( interface.numeric_id) DmNode.supported_features = ConfigStanza(mpls_te=False, mpls_oam=False, vrf=False) nxos_compiler.compile(DmNode) # TODO: make this work other way around if use_mgmt_interfaces: mgmt_int_id = "mgmt0" mgmt_int = DmNode.add_interface(management=True) mgmt_int.id = mgmt_int_id staros_compiler = StarOsCompiler(self.nidb, self.anm) for phy_node in g_phy.routers(host=self.host, syntax='StarOS'): DmNode = self.nidb.node(phy_node) DmNode.add_stanza("render") DmNode.render.template = os.path.join("templates", "staros.mako") if to_memory: DmNode.render.to_memory = True else: DmNode.render.dst_folder = dst_folder DmNode.render.dst_file = "%s.conf" % naming.network_hostname( phy_node) # Assign interfaces int_ids = self.interface_ids_nxos() for interface in DmNode.physical_interfaces(): if not interface.id: interface.id = self.numeric_to_interface_label_star_os( interface.numeric_id) staros_compiler.compile(DmNode) # TODO: make this work other way around if use_mgmt_interfaces: mgmt_int_id = "ethernet 1/1" mgmt_int = DmNode.add_interface(management=True) mgmt_int.id = mgmt_int_id
def ospf(self, node): """Returns OSPF links, also sets process_id """ g_ospf = self.anm['ospf'] g_ipv4 = self.anm['ipv4'] ospf_node = g_ospf.node(node) ospf_stanza = node.add_stanza("ospf") ospf_stanza.custom_config = ospf_node.custom_config node.ospf.ipv4_mpls_te = False # default, inherited enable if necessary node.ospf.loopback_area = g_ospf.node(node).area or 0 node.ospf.process_id = ospf_node.process_id node.ospf.lo_interface = self.lo_interface node.ospf.ospf_links = [] # aggregate by area from collections import defaultdict interfaces_by_area = defaultdict(list) for interface in node.physical_interfaces: if interface.exclude_igp: continue # don't configure IGP for this interface ospf_int = g_ospf.interface(interface) if ospf_int and ospf_int.is_bound: area = ospf_int.area #TODO: can we remove the next line? area = str(area) # can't serialize IPAddress object to JSON #TODO: put in interface rather than interface.id for consistency stanza = ConfigStanza(id = interface.id, cost = int(ospf_int.cost), passive = False) if node.ip.use_ipv4: stanza.ipv4_address = ospf_int['ipv4'].ip_address stanza.ipv4_subnet = ospf_int['ipv4'].subnet if node.ip.use_ipv6: stanza.ipv6_address = ospf_int['ipv6'].ip_address stanza.ipv6_subnet = ospf_int['ipv6'].subnet interfaces_by_area[area].append(stanza) loopback_zero = node.loopback_zero ospf_loopback_zero = g_ospf.interface(loopback_zero) router_area = ospf_loopback_zero.area # area assigned to router router_area = str(router_area) # can't serialize IPAddress object to JSON stanza = ConfigStanza(id = node.loopback_zero.id, cost = 0, passive = True) interfaces_by_area[router_area].append(stanza) node.ospf.interfaces_by_area = ConfigStanza(**interfaces_by_area) added_networks = set() for interface in node.physical_interfaces: if interface.exclude_igp: continue # don't configure IGP for this interface ipv4_int = g_ipv4.interface(interface) ospf_int = g_ospf.interface(interface) if not ospf_int.is_bound: continue # not an OSPF interface try: ospf_cost = int(ospf_int.cost) except TypeError: try: ospf_cost = netaddr.IPAddress(ospf_int.cost) except (TypeError, netaddr.AddrFormatError): log.debug('Using default OSPF cost of 1 for %s on %s' % (ospf_int, node)) ospf_cost = 1 # default interface.ospf_cost = ospf_cost network = ipv4_int.subnet if ospf_int and ospf_int.is_bound and network \ not in added_networks: # don't add more than once added_networks.add(network) link_stanza = ConfigStanza(network = network, interface = interface, area = ospf_int.area) node.ospf.ospf_links.append(link_stanza)
def bgp(self, node): node.add_stanza("bgp") node.bgp.lo_interface = self.lo_interface super(IosBaseCompiler, self).bgp(node) phy_node = self.anm['phy'].node(node) asn = phy_node.asn g_ebgp_v4 = self.anm['ebgp_v4'] g_ebgp_v6 = self.anm['ebgp_v6'] if node in g_ebgp_v4 \ and len(list(g_ebgp_v4.node(node).edges())) > 0: node.is_ebgp_v4 = True else: node.is_ebgp_v4 = False if node in g_ebgp_v6 \ and len(list(g_ebgp_v6.node(node).edges())) > 0: node.is_ebgp_v6 = True else: node.is_ebgp_v6 = False node.bgp.ipv4_advertise_subnets = [] node.bgp.ipv6_advertise_subnets = [] # Advertise loopbacks into BGP if node.ip.use_ipv4: node.bgp.ipv4_advertise_subnets = \ [node.loopback_zero.ipv4_cidr] if node.ip.use_ipv6: node.bgp.ipv6_advertise_subnets = \ [node.loopback_zero.ipv6_address] # Advertise infrastructure into eBGP if node.ip.use_ipv4 and node.is_ebgp_v4: infra_blocks = self.anm['ipv4'].data['infra_blocks'].get(asn) or [] for infra_route in infra_blocks: node.bgp.ipv4_advertise_subnets.append(infra_route) if node.ip.use_ipv6 and node.is_ebgp_v6: infra_blocks = self.anm['ipv6'].data['infra_blocks'].get(asn) or [] for infra_route in infra_blocks: node.bgp.ipv6_advertise_subnets.append(infra_route) self.nailed_up_routes(node) # vrf # TODO: this should be inside vrf section? node.bgp.vrfs = [] vrf_node = self.anm['vrf'].node(node) if vrf_node and vrf_node.vrf_role is 'PE': # iBGP sessions for this VRF vrf_ibgp_neighbors = defaultdict(list) g_ibgp_v4 = self.anm['ibgp_v4'] for session in sort_sessions(g_ibgp_v4.edges(vrf_node)): if session.exclude and session.vrf: data = self.ibgp_session_data(session, ip_version=4) stanza = ConfigStanza(data) vrf_ibgp_neighbors[session.vrf].append(stanza) g_ibgp_v6 = self.anm['ibgp_v6'] for session in sort_sessions(g_ibgp_v6.edges(vrf_node)): if session.exclude and session.vrf: data = self.ibgp_session_data(session, ip_version=6) stanza = ConfigStanza(data) vrf_ibgp_neighbors[session.vrf].append(stanza) # eBGP sessions for this VRF vrf_ebgp_neighbors = defaultdict(list) for session in sort_sessions(g_ebgp_v4.edges(vrf_node)): if session.exclude and session.vrf: data = self.ebgp_session_data(session, ip_version=4) stanza = ConfigStanza(data) vrf_ebgp_neighbors[session.vrf].append(stanza) for session in sort_sessions(g_ebgp_v6.edges(vrf_node)): if session.exclude and session.vrf: data = self.ebgp_session_data(session, ip_version=6) stanza = ConfigStanza(data) vrf_ebgp_neighbors[session.vrf].append(stanza) for vrf in vrf_node.node_vrf_names: rd_index = vrf_node.rd_indices[vrf] rd = '%s:%s' % (node.asn, rd_index) stanza = ConfigStanza( vrf=vrf, rd=rd, use_ipv4=node.ip.use_ipv4, use_ipv6=node.ip.use_ipv6, vrf_ebgp_neighbors=vrf_ebgp_neighbors[vrf], vrf_ibgp_neighbors=vrf_ibgp_neighbors[vrf], ) node.bgp.vrfs.append(stanza) # Retain route_target if in ibgp_vpn_v4 and RR or HRR (set in design) vpnv4_node = self.anm['ibgp_vpn_v4'].node(node) if vpnv4_node: retain = False if vpnv4_node.retain_route_target: retain = True node.bgp.vpnv4 = ConfigStanza(retain_route_target=retain)