def finalize_config(self, dps): """Perform consistency checks after initial config parsing.""" dp_by_name = {} vlan_by_name = {} def resolve_ports(port_names): """Resolve list of ports, by port by name or number.""" resolved_ports = [] for port_name in port_names: port = self.resolve_port(port_name) if port is not None: resolved_ports.append(port) return resolved_ports def resolve_vlan(vlan_name): """Resolve VLAN by name or VID.""" test_config_condition(not isinstance(vlan_name, (str, int)), ('VLAN must be type %s or %s not %s' % (str, int, type(vlan_name)))) if vlan_name in vlan_by_name: return vlan_by_name[vlan_name] if vlan_name in self.vlans: return self.vlans[vlan_name] return None def resolve_stack_dps(): """Resolve DP references in stacking config.""" port_stack_dp = {} for port in self.stack_ports: stack_dp = port.stack['dp'] test_config_condition(stack_dp not in dp_by_name, ('stack DP %s not defined' % stack_dp)) port_stack_dp[port] = dp_by_name[stack_dp] for port, dp in port_stack_dp.items(): port.stack['dp'] = dp stack_port = dp.resolve_port(port.stack['port']) test_config_condition(stack_port is None, ('stack port %s not defined in DP %s' % (port.stack['port'], dp.name))) port.stack['port'] = stack_port def resolve_mirror_destinations(): """Resolve mirror port references and destinations.""" mirror_from_port = defaultdict(list) for mirror_port in self.ports.values(): if mirror_port.mirror is not None: mirrored_ports = resolve_ports(mirror_port.mirror) test_config_condition( len(mirrored_ports) != len(mirror_port.mirror), ('port mirror not defined in DP %s' % self.name)) for mirrored_port in mirrored_ports: mirror_from_port[mirrored_port].append(mirror_port) # TODO: confusingly, mirror at config time means what ports to mirror from. # But internally we use as a list of ports to mirror to. for mirrored_port, mirror_ports in mirror_from_port.items(): mirrored_port.mirror = [] for mirror_port in mirror_ports: mirrored_port.mirror.append(mirror_port.number) mirror_port.output_only = True def resolve_override_output_ports(): """Resolve override output ports.""" for port_no, port in self.ports.items(): if port.override_output_port: port.override_output_port = self.resolve_port( port.override_output_port) test_config_condition( not port.override_output_port, ('override_output_port port not defined')) self.ports[port_no] = port def resolve_acl(acl_in, vid=None, port_num=None): """Resolve an individual ACL.""" test_config_condition(acl_in not in self.acls, ('missing ACL %s in DP: %s' % (acl_in, self.name))) acl = self.acls[acl_in] def resolve_port_cb(port_name): port = self.resolve_port(port_name) if port: return port.number return port acl.resolve_ports(resolve_port_cb) for meter_name in acl.get_meters(): test_config_condition( meter_name not in self.meters, ('meter %s is not configured' % meter_name)) for port_no in acl.get_mirror_destinations(): port = self.ports[port_no] port.output_only = True return acl.build(self.meters, vid, port_num) def verify_acl_exact_match(acls): for acl in acls: test_config_condition( acl.exact_match != acls[0].exact_match, ('ACLs when used together must have consistent exact_match' )) def resolve_acls(): """Resolve config references in ACLs.""" # TODO: move this config validation to ACL object. for vlan in self.vlans.values(): if vlan.acls_in: acls = [] for acl in vlan.acls_in: resolve_acl(acl, vlan.vid) acls.append(self.acls[acl]) vlan.acls_in = acls verify_acl_exact_match(acls) for port in self.ports.values(): if port.acls_in: test_config_condition( self.dp_acls, ('dataplane ACLs cannot be used with port ACLs.')) acls = [] for acl in port.acls_in: resolve_acl(acl, port_num=port.number) acls.append(self.acls[acl]) port.acls_in = acls verify_acl_exact_match(acls) if self.dp_acls: acls = [] for acl in self.acls: resolve_acl(acl, None) acls.append(self.acls[acl]) self.dp_acls = acls def resolve_vlan_names_in_routers(): """Resolve VLAN references in routers.""" dp_routers = {} for router_name, router in self.routers.items(): vlans = [] for vlan_name in router.vlans: vlan = resolve_vlan(vlan_name) if vlan is not None: vlans.append(vlan) if len(vlans) > 1: dp_router = copy.copy(router) dp_router.vlans = vlans dp_routers[router_name] = dp_router vips = set() for vlan in vlans: for vip in vlan.faucet_vips: if vip.ip.is_link_local: continue vips.add(vip) for vip in vips: for other_vip in vips - set([vip]): test_config_condition( vip.ip in other_vip.network, 'VIPs %s and %s overlap in router %s' % (vip, other_vip, router_name)) self.routers = dp_routers test_config_condition( not self.vlans, 'no VLANs referenced by interfaces in %s' % self.name) valve_cl = SUPPORTED_HARDWARE.get(self.hardware, None) test_config_condition( not valve_cl, 'hardware %s must be in %s' % (self.hardware, SUPPORTED_HARDWARE.keys())) for dp in dps: dp_by_name[dp.name] = dp for vlan in self.vlans.values(): vlan_by_name[vlan.name] = vlan if self.global_vlan: test_config_condition( self.global_vlan == vlan.vid, 'VLAN %u is reserved by global_vlan' % vlan.vid) resolve_stack_dps() resolve_mirror_destinations() resolve_override_output_ports() resolve_vlan_names_in_routers() resolve_acls() self._configure_tables(valve_cl) bgp_vlans = self.bgp_vlans() if bgp_vlans: for vlan in bgp_vlans: vlan_dps = [dp for dp in dps if vlan.vid in dp.vlans] test_config_condition( len(vlan_dps) != 1, ('DPs %s sharing a BGP speaker VLAN is unsupported')) router_ids = {vlan.bgp_routerid for vlan in bgp_vlans} test_config_condition( len(router_ids) != 1, 'BGP router IDs must all be the same') bgp_ports = {vlan.bgp_port for vlan in bgp_vlans} test_config_condition( len(bgp_ports) != 1, 'BGP ports must all be the same') for vlan in bgp_vlans: test_config_condition( vlan.bgp_server_addresses != (bgp_vlans[0].bgp_server_addresses), ('BGP server addresses must all be the same')) for port in self.ports.values(): port.finalize() for vlan in self.vlans.values(): vlan.finalize() for acl in self.acls.values(): acl.finalize() for router in self.routers.values(): router.finalize() self.finalize()
def finalize_config(self, dps): """Perform consistency checks after initial config parsing.""" dp_by_name = {} vlan_by_name = {} def resolve_ports(port_names): """Resolve list of ports, by port by name or number.""" resolved_ports = [] for port_name in port_names: port = self.resolve_port(port_name) if port is not None: resolved_ports.append(port) return resolved_ports def resolve_vlan(vlan_name): """Resolve VLAN by name or VID.""" test_config_condition(not isinstance(vlan_name, (str, int)), ('VLAN must be type %s or %s not %s' % (str, int, type(vlan_name)))) if vlan_name in vlan_by_name: return vlan_by_name[vlan_name] if vlan_name in self.vlans: return self.vlans[vlan_name] return None def resolve_stack_dps(): """Resolve DP references in stacking config.""" port_stack_dp = {} for port in self.stack_ports: stack_dp = port.stack['dp'] test_config_condition(stack_dp not in dp_by_name, ('stack DP %s not defined' % stack_dp)) port_stack_dp[port] = dp_by_name[stack_dp] for port, dp in list(port_stack_dp.items()): port.stack['dp'] = dp stack_port = dp.resolve_port(port.stack['port']) test_config_condition(stack_port is None, ('stack port %s not defined in DP %s' % (port.stack['port'], dp.name))) port.stack['port'] = stack_port def resolve_mirror_destinations(): """Resolve mirror port references and destinations.""" mirror_from_port = defaultdict(list) for mirror_port in list(self.ports.values()): if mirror_port.mirror is not None: mirrored_ports = resolve_ports(mirror_port.mirror) test_config_condition( len(mirrored_ports) != len(mirror_port.mirror), ('port mirror not defined in DP %s' % self.name)) for mirrored_port in mirrored_ports: mirror_from_port[mirrored_port].append(mirror_port) # TODO: confusingly, mirror at config time means what ports to mirror from. # But internally we use as a list of ports to mirror to. for mirrored_port, mirror_ports in list(mirror_from_port.items()): mirrored_port.mirror = [] for mirror_port in mirror_ports: mirrored_port.mirror.append(mirror_port.number) mirror_port.output_only = True def resolve_override_output_ports(): """Resolve override output ports.""" for port_no, port in list(self.ports.items()): if port.override_output_port: port.override_output_port = self.resolve_port( port.override_output_port) test_config_condition( not port.override_output_port, ('override_output_port port not defined')) self.ports[port_no] = port def resolve_acl(acl_in, vid): """Resolve an individual ACL.""" test_config_condition(acl_in not in self.acls, ('missing ACL %s in DP: %s' % (acl_in, self.name))) acl = self.acls[acl_in] def resolve_port_cb(port_name): port = self.resolve_port(port_name) if port: return port.number return port acl.resolve_ports(resolve_port_cb) for meter_name in acl.get_meters(): test_config_condition( meter_name not in self.meters, ('meter %s is not configured' % meter_name)) for port_no in acl.get_mirror_destinations(): port = self.ports[port_no] port.output_only = True return acl.build(vid, self.meters) def verify_acl_exact_match(acls): for acl in acls: test_config_condition( acl.exact_match != acls[0].exact_match, ('ACLs when used together must have consistent exact_match' )) return acls[0].exact_match def resolve_acls(valve_cl): """Resolve config references in ACLs.""" # TODO: move this config validation to ACL object. port_acl_enabled = valve_cl.STATIC_TABLE_IDS port_acl_matches = {} port_acl_set_fields = set() port_acl_exact_match = False port_acl_meter = False vlan_acl_matches = {} vlan_acl_exact_match = False vlan_acl_set_fields = set() vlan_acl_meter = False def merge_matches(matches, new_matches): for field, has_mask in list(new_matches.items()): if has_mask or field not in matches: matches[field] = has_mask for vlan in list(self.vlans.values()): if vlan.acls_in: acls = [] for acl in vlan.acls_in: matches, set_fields, meter = resolve_acl(acl, vlan.vid) merge_matches(vlan_acl_matches, matches) vlan_acl_set_fields = vlan_acl_set_fields.union( set_fields) if meter: vlan_acl_meter = True acls.append(self.acls[acl]) vlan.acls_in = acls vlan_acl_exact_match = verify_acl_exact_match(acls) for port in list(self.ports.values()): if port.acls_in: test_config_condition( self.dp_acls, ('dataplane ACLs cannot be used with port ACLs.')) acls = [] for acl in port.acls_in: matches, set_fields, meter = resolve_acl(acl, None) merge_matches(port_acl_matches, matches) port_acl_set_fields = port_acl_set_fields.union( set_fields) if meter: port_acl_meter = True acls.append(self.acls[acl]) port.acls_in = acls port_acl_exact_match = verify_acl_exact_match(acls) port_acl_enabled = True if self.dp_acls: acls = [] for acl in self.acls: matches, set_fields, meter = resolve_acl(acl, None) merge_matches(port_acl_matches, matches) port_acl_set_fields = port_acl_set_fields.union(set_fields) if meter: port_acl_meter = True acls.append(self.acls[acl]) self.dp_acls = acls port_acl_enabled = True if port_acl_enabled: port_acl_matches.update({'in_port': False}) port_acl_matches = { (field, mask) for field, mask in list(port_acl_matches.items()) } vlan_acl_matches = { (field, mask) for field, mask in list(vlan_acl_matches.items()) } # TODO: skip port_acl table if not configured. # TODO: dynamically configure output attribue override_table_config = { 'port_acl': ValveTableConfig('port_acl', exact_match=port_acl_exact_match, meter=port_acl_meter, output=True, match_types=port_acl_matches, set_fields=tuple(port_acl_set_fields)), 'vlan_acl': ValveTableConfig('vlan_acl', exact_match=vlan_acl_exact_match, meter=vlan_acl_meter, output=True, match_types=vlan_acl_matches, set_fields=tuple(vlan_acl_set_fields)), } return override_table_config def resolve_vlan_names_in_routers(): """Resolve VLAN references in routers.""" dp_routers = {} for router_name, router in list(self.routers.items()): vlans = [] for vlan_name in router.vlans: vlan = resolve_vlan(vlan_name) if vlan is not None: vlans.append(vlan) if len(vlans) > 1: dp_router = copy.copy(router) dp_router.vlans = vlans dp_routers[router_name] = dp_router vips = set() for vlan in vlans: for vip in vlan.faucet_vips: if vip.ip.is_link_local: continue vips.add(vip) for vip in vips: for other_vip in vips - set([vip]): test_config_condition( vip.ip in other_vip.network, 'VIPs %s and %s overlap in router %s' % (vip, other_vip, router_name)) self.routers = dp_routers test_config_condition( not self.vlans, 'no VLANs referenced by interfaces in %s' % self.name) valve_cl = SUPPORTED_HARDWARE.get(self.hardware, None) test_config_condition( not valve_cl, 'hardware %s must be in %s' % (self.hardware, list(SUPPORTED_HARDWARE.keys()))) for dp in dps: dp_by_name[dp.name] = dp for vlan in list(self.vlans.values()): vlan_by_name[vlan.name] = vlan if self.global_vlan: test_config_condition( self.global_vlan == vlan.vid, 'VLAN %u is reserved by global_vlan' % vlan.vid) resolve_stack_dps() resolve_mirror_destinations() resolve_override_output_ports() resolve_vlan_names_in_routers() override_table_config = resolve_acls(valve_cl) # Only configure IP routing tables if enabled. ipvs = set() for vlan in list(self.vlans.values()): ipvs = ipvs.union(vlan.ipvs()) for ipv in (4, 6): if ipv not in ipvs: table_name = 'ipv%u_fib' % ipv override_table_config[table_name] = ValveTableConfig( table_name) if not ipvs: override_table_config['vip'] = ValveTableConfig('vip') vlan_port_factor = len(self.vlans) * len(self.ports) self._configure_tables(override_table_config, valve_cl, vlan_port_factor) bgp_vlans = self.bgp_vlans() if bgp_vlans: for vlan in bgp_vlans: vlan_dps = [dp for dp in dps if vlan.vid in dp.vlans] test_config_condition( len(vlan_dps) != 1, ('DPs %s sharing a BGP speaker VLAN is unsupported')) router_ids = {vlan.bgp_routerid for vlan in bgp_vlans} test_config_condition( len(router_ids) != 1, 'BGP router IDs must all be the same') bgp_ports = {vlan.bgp_port for vlan in bgp_vlans} test_config_condition( len(bgp_ports) != 1, 'BGP ports must all be the same') for vlan in bgp_vlans: test_config_condition( vlan.bgp_server_addresses != (bgp_vlans[0].bgp_server_addresses), ('BGP server addresses must all be the same')) for port in list(self.ports.values()): port.finalize() for vlan in list(self.vlans.values()): vlan.finalize() for acl in list(self.acls.values()): acl.finalize() for router in list(self.routers.values()): router.finalize() self.finalize()
def finalize_config(self, dps): """Perform consistency checks after initial config parsing.""" dp_by_name = {} vlan_by_name = {} def resolve_ports(port_names): """Resolve list of ports, by port by name or number.""" resolved_ports = [] for port_name in port_names: port = self.resolve_port(port_name) if port is not None: resolved_ports.append(port) return resolved_ports def resolve_vlan(vlan_name): """Resolve VLAN by name or VID.""" test_config_condition(not isinstance(vlan_name, (str, int)), ( 'VLAN must be type %s or %s not %s' % (str, int, type(vlan_name)))) if vlan_name in vlan_by_name: return vlan_by_name[vlan_name] if vlan_name in self.vlans: return self.vlans[vlan_name] return None def resolve_stack_dps(): """Resolve DP references in stacking config.""" port_stack_dp = {} for port in self.stack_ports: stack_dp = port.stack['dp'] test_config_condition(stack_dp not in dp_by_name, ( 'stack DP %s not defined' % stack_dp)) port_stack_dp[port] = dp_by_name[stack_dp] for port, dp in list(port_stack_dp.items()): port.stack['dp'] = dp stack_port = dp.resolve_port(port.stack['port']) test_config_condition(stack_port is None, ( 'stack port %s not defined in DP %s' % (port.stack['port'], dp.name))) port.stack['port'] = stack_port def resolve_mirror_destinations(): """Resolve mirror port references and destinations.""" mirror_from_port = defaultdict(list) for mirror_port in list(self.ports.values()): if mirror_port.mirror is not None: mirrored_ports = resolve_ports(mirror_port.mirror) test_config_condition(len(mirrored_ports) != len(mirror_port.mirror), ( 'port mirror not defined in DP %s' % self.name)) for mirrored_port in mirrored_ports: mirror_from_port[mirrored_port].append(mirror_port) # TODO: confusingly, mirror at config time means what ports to mirror from. # But internally we use as a list of ports to mirror to. for mirrored_port, mirror_ports in list(mirror_from_port.items()): mirrored_port.mirror = [] for mirror_port in mirror_ports: mirrored_port.mirror.append(mirror_port.number) mirror_port.output_only = True def resolve_override_output_ports(): """Resolve override output ports.""" for port_no, port in list(self.ports.items()): if port.override_output_port: port.override_output_port = self.resolve_port(port.override_output_port) test_config_condition(not port.override_output_port, ( 'override_output_port port not defined')) self.ports[port_no] = port def resolve_acl(acl_in, vid=None, port_num=None): """Resolve an individual ACL.""" test_config_condition(acl_in not in self.acls, ( 'missing ACL %s in DP: %s' % (acl_in, self.name))) acl = self.acls[acl_in] def resolve_port_cb(port_name): port = self.resolve_port(port_name) if port: return port.number return port acl.resolve_ports(resolve_port_cb) for meter_name in acl.get_meters(): test_config_condition(meter_name not in self.meters, ( 'meter %s is not configured' % meter_name)) for port_no in acl.get_mirror_destinations(): port = self.ports[port_no] port.output_only = True return acl.build(self.meters, vid, port_num) def verify_acl_exact_match(acls): for acl in acls: test_config_condition(acl.exact_match != acls[0].exact_match, ( 'ACLs when used together must have consistent exact_match')) def resolve_acls(): """Resolve config references in ACLs.""" # TODO: move this config validation to ACL object. for vlan in list(self.vlans.values()): if vlan.acls_in: acls = [] for acl in vlan.acls_in: resolve_acl(acl, vlan.vid) acls.append(self.acls[acl]) vlan.acls_in = acls verify_acl_exact_match(acls) for port in list(self.ports.values()): if port.acls_in: test_config_condition(self.dp_acls, ( 'dataplane ACLs cannot be used with port ACLs.')) acls = [] for acl in port.acls_in: resolve_acl(acl, port_num=port.number) acls.append(self.acls[acl]) port.acls_in = acls verify_acl_exact_match(acls) if self.dp_acls: acls = [] for acl in self.acls: resolve_acl(acl, None) acls.append(self.acls[acl]) self.dp_acls = acls def resolve_vlan_names_in_routers(): """Resolve VLAN references in routers.""" dp_routers = {} for router_name, router in list(self.routers.items()): vlans = [] for vlan_name in router.vlans: vlan = resolve_vlan(vlan_name) if vlan is not None: vlans.append(vlan) if len(vlans) > 1: dp_router = copy.copy(router) dp_router.vlans = vlans dp_routers[router_name] = dp_router vips = set() for vlan in vlans: for vip in vlan.faucet_vips: if vip.ip.is_link_local: continue vips.add(vip) for vip in vips: for other_vip in vips - set([vip]): test_config_condition( vip.ip in other_vip.network, 'VIPs %s and %s overlap in router %s' % ( vip, other_vip, router_name)) self.routers = dp_routers test_config_condition(not self.vlans, 'no VLANs referenced by interfaces in %s' % self.name) valve_cl = SUPPORTED_HARDWARE.get(self.hardware, None) test_config_condition( not valve_cl, 'hardware %s must be in %s' % ( self.hardware, list(SUPPORTED_HARDWARE.keys()))) for dp in dps: dp_by_name[dp.name] = dp for vlan in list(self.vlans.values()): vlan_by_name[vlan.name] = vlan if self.global_vlan: test_config_condition( self.global_vlan == vlan.vid, 'VLAN %u is reserved by global_vlan' % vlan.vid) resolve_stack_dps() resolve_mirror_destinations() resolve_override_output_ports() resolve_vlan_names_in_routers() resolve_acls() self._configure_tables(valve_cl) bgp_vlans = self.bgp_vlans() if bgp_vlans: for vlan in bgp_vlans: vlan_dps = [dp for dp in dps if vlan.vid in dp.vlans] test_config_condition(len(vlan_dps) != 1, ( 'DPs %s sharing a BGP speaker VLAN is unsupported')) router_ids = {vlan.bgp_routerid for vlan in bgp_vlans} test_config_condition(len(router_ids) != 1, 'BGP router IDs must all be the same') bgp_ports = {vlan.bgp_port for vlan in bgp_vlans} test_config_condition(len(bgp_ports) != 1, 'BGP ports must all be the same') for vlan in bgp_vlans: test_config_condition(vlan.bgp_server_addresses != ( bgp_vlans[0].bgp_server_addresses), ( 'BGP server addresses must all be the same')) for port in list(self.ports.values()): port.finalize() for vlan in list(self.vlans.values()): vlan.finalize() for acl in list(self.acls.values()): acl.finalize() for router in list(self.routers.values()): router.finalize() self.finalize()