def _watcher_parser_v2(conf, logname, prom_client): logger = config_parser_util.get_logger(logname) result = [] if conf is None: conf = {} dps = {} if 'faucet_configs' in conf: for faucet_file in conf['faucet_configs']: _, dp_list = dp_parser(faucet_file, logname) if dp_list: for dp in dp_list: dps[dp.name] = dp if 'faucet' in conf: faucet_conf = conf['faucet'] acls = faucet_conf.get('acls', {}) fct_dps = faucet_conf.get('dps', {}) meters = faucet_conf.get('meters', {}) routers = faucet_conf.get('routers', {}) vlans = faucet_conf.get('vlans', {}) for dp in _dp_parser_v2(acls, fct_dps, meters, routers, vlans): dps[dp.name] = dp if not dps: raise InvalidConfigError( 'Gauge configured without any FAUCET configuration') dbs = conf.pop('dbs') # pylint: disable=fixme for watcher_name, watcher_conf in list(conf['watchers'].items()): if watcher_conf.get('all_dps', False): watcher_dps = list(dps.keys()) else: watcher_dps = watcher_conf['dps'] # Watcher config has a list of DPs, but actually a WatcherConf is # created for each DP. # TODO: refactor watcher_conf as a container. for dp_name in watcher_dps: if dp_name not in dps: logger.error('DP %s in Gauge but not configured in FAUCET', dp_name) continue dp = dps[dp_name] if 'dbs' in watcher_conf: watcher_dbs = watcher_conf['dbs'] elif 'db' in watcher_conf: watcher_dbs = [watcher_conf['db']] else: raise InvalidConfigError('Watcher configured without DB') for db in watcher_dbs: watcher = WatcherConf(watcher_name, dp.dp_id, watcher_conf, prom_client) watcher.add_db(dbs[db]) watcher.add_dp(dp) result.append(watcher) return result
def build(self, meters, vid, port_num): """Check that ACL can be built from config.""" class NullRyuDatapath: """Placeholder Ryu Datapath.""" ofproto = valve_of.ofp self.matches = {} self.set_fields = set() self.meter = False if self.rules: try: ofmsgs = valve_acl.build_acl_ofmsgs( [self], wildcard_table, valve_of.goto_table(wildcard_table), valve_of.goto_table(wildcard_table), 2**16 - 1, meters, self.exact_match, vlan_vid=vid, port_num=port_num) except (netaddr.core.AddrFormatError, KeyError, ValueError) as err: raise InvalidConfigError(err) test_config_condition(not ofmsgs, 'OF messages is empty') for ofmsg in ofmsgs: ofmsg.datapath = NullRyuDatapath() ofmsg.set_xid(0) try: ofmsg.serialize() except (KeyError, ValueError) as err: raise InvalidConfigError(err) except Exception as err: print(ofmsg) raise err if valve_of.is_flowmod(ofmsg): apply_actions = [] for inst in ofmsg.instructions: if valve_of.is_apply_actions(inst): apply_actions.extend(inst.actions) elif valve_of.is_meter(inst): self.meter = True for action in apply_actions: if valve_of.is_set_field(action): self.set_fields.add(action.key) for match, value in list(ofmsg.match.items()): has_mask = isinstance(value, (tuple, list)) if has_mask or match not in self.matches: self.matches[match] = has_mask return (self.matches, self.set_fields, self.meter)
def watcher_factory(conf): """Return a Gauge object based on type. Args: conf (GaugeConf): object with the configuration for this valve. """ watcher_types = { 'port_state': { 'text': GaugePortStateLogger, 'influx': GaugePortStateInfluxDBLogger, 'prometheus': GaugePortStatePrometheusPoller, }, 'port_stats': { 'text': GaugePortStatsLogger, 'influx': GaugePortStatsInfluxDBLogger, 'prometheus': GaugePortStatsPrometheusPoller, }, 'flow_table': { 'text': GaugeFlowTableLogger, 'influx': GaugeFlowTableInfluxDBLogger, 'prometheus': GaugeFlowTablePrometheusPoller, }, 'meter_stats': { 'text': GaugeMeterStatsLogger, 'prometheus': GaugeMeterStatsPrometheusPoller, }, } w_type = conf.type db_type = conf.db_type try: return watcher_types[w_type][db_type] except KeyError as key_error: raise InvalidConfigError('invalid water config') from key_error
def build_acl(acl, vid=None): """Check that ACL can be built from config and mark mirror destinations.""" if acl.rules: null_dp = namedtuple('null_dp', 'ofproto') null_dp.ofproto = valve_of.ofp try: ofmsgs = valve_acl.build_acl_ofmsgs( [acl], self.wildcard_table, valve_of.goto_table(self.wildcard_table), valve_of.goto_table(self.wildcard_table), 2**16 - 1, self.meters, acl.exact_match, vlan_vid=vid) assert ofmsgs for ofmsg in ofmsgs: ofmsg.datapath = null_dp ofmsg.set_xid(0) ofmsg.serialize() except (AddrFormatError, KeyError, ValueError) as err: raise InvalidConfigError(err) for port_no in mirror_destinations: port = self.ports[port_no] port.output_only = True
def check_config(self): super(Port, self).check_config() test_config_condition(not (isinstance(self.number, int) and self.number > 0 and ( not valve_of.ignore_port(self.number))), ('Port number invalid: %s' % self.number)) test_config_condition( self.hairpin and self.hairpin_unicast, 'Cannot have both hairpin and hairpin_unicast enabled') if self.dot1x: test_config_condition(self.number > 65535, ( '802.1x not supported on ports > 65535')) if self.dot1x_acl: test_config_condition(not self.dot1x, ( '802.1x_ACL requires dot1x to be enabled also')) if self.mirror: test_config_condition(self.tagged_vlans or self.native_vlan, ( 'mirror port %s cannot have any VLANs assigned' % self)) if self.stack: self._check_conf_types(self.stack, self.stack_defaults_types) for stack_config in list(self.stack_defaults_types.keys()): test_config_condition(stack_config not in self.stack, ( 'stack %s must be defined' % stack_config)) # LLDP always enabled for stack ports. self.receive_lldp = True if not self.lldp_beacon_enabled(): self.lldp_beacon.update({'enable': True}) if self.lldp_beacon: self._check_conf_types( self.lldp_beacon, self.lldp_beacon_defaults_types) self.lldp_beacon = self._set_unknown_conf( self.lldp_beacon, self.lldp_beacon_defaults_types) if self.lldp_beacon_enabled(): if self.lldp_beacon['port_descr'] is None: self.lldp_beacon['port_descr'] = self.description org_tlvs = [] for org_tlv in self.lldp_beacon['org_tlvs']: self._check_conf_types(org_tlv, self.lldp_org_tlv_defaults_types) test_config_condition(len(org_tlv) != len(self.lldp_org_tlv_defaults_types), ( 'missing org_tlv config')) if not isinstance(org_tlv['info'], bytearray): try: org_tlv['info'] = bytearray.fromhex( org_tlv['info']) # pytype: disable=missing-parameter except ValueError: org_tlv['info'] = org_tlv['info'].encode('utf-8') if not isinstance(org_tlv['oui'], bytearray): org_tlv['oui'] = bytearray.fromhex( '%6.6x' % org_tlv['oui']) # pytype: disable=missing-parameter org_tlvs.append(org_tlv) self.lldp_beacon['org_tlvs'] = org_tlvs if self.acl_in and self.acls_in: raise InvalidConfigError('found both acl_in and acls_in, use only acls_in') if self.acl_in and not isinstance(self.acl_in, list): self.acls_in = [self.acl_in,] self.acl_in = None if self.acls_in: for acl in self.acls_in: test_config_condition(not isinstance(acl, (int, str)), 'ACL names must be int or str')
def _dp_parser_v2(dps_conf, acls_conf, meters_conf, routers_conf, vlans_conf, meta_dp_state): dps = [] for dp_key, dp_conf in dps_conf.items(): try: dps.append( _parse_dp(dp_key, dp_conf, acls_conf, meters_conf, routers_conf, vlans_conf)) except InvalidConfigError as err: raise InvalidConfigError('DP %s: %s' % (dp_key, err)) for dp in dps: dp.finalize_config(dps) for dp in dps: dp.resolve_stack_topology(dps, meta_dp_state) for dp in dps: dp.finalize() dpid_refs = set() for dp in dps: test_config_condition(dp.dp_id in dpid_refs, ('DPID %u is duplicated' % dp.dp_id)) dpid_refs.add(dp.dp_id) routers_referenced = set() for dp in dps: routers_referenced.update(dp.routers.keys()) for router in routers_conf: test_config_condition( router not in routers_referenced, ('router %s configured but not used by any DP' % router)) return dps
def _config_parser_v2(config_file, logname, meta_dp_state): config_path = config_parser_util.dp_config_path(config_file) top_confs = {top_conf: {} for top_conf in V2_TOP_CONFS} config_hashes = {} config_contents = {} dps = None if not config_parser_util.dp_include( config_hashes, config_contents, config_path, logname, top_confs): raise InvalidConfigError('Error found while loading config file: %s' % config_path) if not top_confs['dps']: raise InvalidConfigError('DPs not configured in file: %s' % config_path) dps = dp_preparsed_parser(top_confs, meta_dp_state) return (config_hashes, config_contents, dps, top_confs)
def __init__(self, _id, dp_id, conf): self.rules = [] self.exact_match = None self.meter = False self.matches = {} self.set_fields = set() for match_fields in (MATCH_FIELDS, OLD_MATCH_FIELDS): self.rule_types.update( {match: (str, int) for match in match_fields.keys()}) conf = copy.deepcopy(conf) if isinstance(conf, dict): rules = conf.get('rules', []) elif isinstance(conf, list): rules = conf conf = {} else: raise InvalidConfigError('ACL conf is an invalid type %s' % _id) conf['rules'] = [] for rule in rules: normalized_rule = rule if isinstance(rule, dict): normalized_rule = rule.get('rule', rule) if normalized_rule is None: normalized_rule = { k: v for k, v in rule.items() if v is not None } test_config_condition(not isinstance(normalized_rule, dict), ('ACL rule is %s not %s (%s)' % (type(normalized_rule), dict, rules))) conf['rules'].append(normalized_rule) super(ACL, self).__init__(_id, dp_id, conf)
def __init__(self, _id, dp_id, conf): self.rules = [] self.exact_match = None self.meter = False self.matches = {} self.set_fields = set() conf = copy.deepcopy(conf) if isinstance(conf, dict): rules = conf.get('rules', []) elif isinstance(conf, list): rules = conf conf = {} else: raise InvalidConfigError('ACL conf is an invalid type %s' % self._id) conf['rules'] = [] for rule in rules: test_config_condition(not isinstance(rule, dict), ('ACL rule is %s not %s' % (type(rule), dict))) if 'rule' in rule: conf['rules'].append(rule['rule']) else: conf['rules'].append(rule) super(ACL, self).__init__(_id, dp_id, conf)
def build_tunnel_ofmsgs(rule_conf, acl_table, priority, port_num=None, vlan_vid=None, flowdel=False, reverse=False): """Build a specific tunnel only ofmsgs""" ofmsgs = [] acl_inst = [] acl_match = [] acl_match_dict = {} _, output_actions, output_ofmsgs, output_inst = build_output_actions(acl_table, rule_conf) ofmsgs.extend(output_ofmsgs) acl_inst.extend(output_inst) acl_inst.append(valve_of.apply_actions(output_actions)) if port_num is not None: acl_match_dict['in_port'] = port_num if vlan_vid is not None: acl_match_dict['vlan_vid'] = valve_of.vid_present(vlan_vid) if reverse: acl_match_dict['vlan_pcp'] = valve_of.PCP_TUNNEL_REVERSE_DIRECTION_FLAG else: acl_match_dict['vlan_pcp'] = valve_of.PCP_TUNNEL_FLAG try: acl_match = valve_of.match_from_dict(acl_match_dict) except TypeError as type_error: raise InvalidConfigError('invalid match type in ACL') from type_error flowmod = acl_table.flowmod(acl_match, priority=priority, inst=tuple(acl_inst)) if flowdel: ofmsgs.append(acl_table.flowdel(match=acl_match, priority=priority, strict=False)) ofmsgs.append(flowmod) return ofmsgs
def check_config(self): super(VLAN, self).check_config() test_config_condition(not self.vid_valid(self.vid), 'invalid VID %s' % self.vid) test_config_condition(not netaddr.valid_mac(self.faucet_mac), ( 'invalid MAC address %s' % self.faucet_mac)) test_config_condition( self.acl_in and self.acls_in, 'found both acl_in and acls_in, use only acls_in') test_config_condition( self.acl_out and self.acls_out, 'found both acl_out and acls_out, use only acls_out') if self.acl_in and not isinstance(self.acl_in, list): self.acls_in = [self.acl_in,] self.acl_in = None if self.acl_out and not isinstance(self.acl_out, list): self.acls_out = [self.acl_out,] self.acl_out = None all_acls = [] if self.acls_in: all_acls.extend(self.acls_in) if self.acls_out: all_acls.extend(self.acls_out) for acl in all_acls: test_config_condition( not isinstance(acl, (int, str)), 'acl names must be int or str') if self.max_hosts: if not self.proactive_arp_limit: self.proactive_arp_limit = 2 * self.max_hosts if not self.proactive_nd_limit: self.proactive_nd_limit = 2 * self.max_hosts if self.faucet_vips: self.faucet_vips = frozenset([ self._check_ip_str(ip_str, ip_method=ipaddress.ip_interface) for ip_str in self.faucet_vips]) for faucet_vip in self.faucet_vips: test_config_condition( faucet_vip.network.prefixlen == faucet_vip.max_prefixlen, 'VIP cannot be a host address') if self.routes: test_config_condition(not isinstance(self.routes, list), 'invalid VLAN routes format') try: self.routes = [route['route'] for route in self.routes] except TypeError: raise InvalidConfigError('%s is not a valid routes value' % self.routes) except KeyError: pass for route in self.routes: test_config_condition(not isinstance(route, dict), 'invalid VLAN route format') test_config_condition('ip_gw' not in route, 'missing ip_gw in VLAN route') test_config_condition('ip_dst' not in route, 'missing ip_dst in VLAN route') ip_gw = self._check_ip_str(route['ip_gw']) ip_dst = self._check_ip_str(route['ip_dst'], ip_method=ipaddress.ip_network) test_config_condition( ip_gw.version != ip_dst.version, 'ip_gw version does not match the ip_dst version') self.add_route(ip_dst, ip_gw)
def build_acl(acl, vid): """Check that ACL can be built from config and mark mirror destinations.""" matches = {} set_fields = set() meter = False if acl.rules: try: ofmsgs = valve_acl.build_acl_ofmsgs( [acl], self.wildcard_table, valve_of.goto_table(self.wildcard_table), valve_of.goto_table(self.wildcard_table), 2**16 - 1, self.meters, acl.exact_match, vlan_vid=vid) except (netaddr.core.AddrFormatError, KeyError, ValueError) as err: raise InvalidConfigError(err) test_config_condition(not ofmsgs, 'OF messages is empty') for ofmsg in ofmsgs: ofmsg.datapath = NullRyuDatapath() ofmsg.set_xid(0) try: ofmsg.serialize() except (KeyError, ValueError) as err: raise InvalidConfigError(err) if valve_of.is_flowmod(ofmsg): apply_actions = [] for inst in ofmsg.instructions: if valve_of.is_apply_actions(inst): apply_actions.extend(inst.actions) elif valve_of.is_meter(inst): meter = True for action in apply_actions: if valve_of.is_set_field(action): set_fields.add(action.key) for match, value in list(ofmsg.match.items()): has_mask = isinstance(value, (tuple, list)) if has_mask or match not in matches: matches[match] = has_mask for port_no in mirror_destinations: port = self.ports[port_no] port.output_only = True return (matches, set_fields, meter)
def _map_port_num_to_port(ports_conf): port_num_to_port_conf = {} for port_key, port_conf in ports_conf.items(): test_config_condition(not isinstance(port_conf, dict), 'Invalid syntax in port config') port_num = port_conf.get('number', port_key) try: port_num_to_port_conf[port_num] = (port_key, port_conf) except TypeError as type_error: raise InvalidConfigError('Invalid syntax in port config') from type_error return port_num_to_port_conf
def check_config(self): super(Port, self).check_config() test_config_condition( not (isinstance(self.number, int) and self.number > 0 and (not valve_of.ignore_port(self.number))), ('Port number invalid: %s' % self.number)) if self.mirror: test_config_condition( self.tagged_vlans or self.native_vlan, ('mirror port %s cannot have any VLANs assigned' % self)) if self.stack: self._check_conf_types(self.stack, self.stack_defaults_types) for stack_config in list(self.stack_defaults_types.keys()): test_config_condition( stack_config not in self.stack, ('stack %s must be defined' % stack_config)) if self.lldp_beacon: self._check_conf_types(self.lldp_beacon, self.lldp_beacon_defaults_types) self.lldp_beacon = self._set_unknown_conf( self.lldp_beacon, self.lldp_beacon_defaults_types) if self.lldp_beacon_enabled(): if self.lldp_beacon['port_descr'] is None: self.lldp_beacon['port_descr'] = self.description org_tlvs = [] for org_tlv in self.lldp_beacon['org_tlvs']: self._check_conf_types(org_tlv, self.lldp_org_tlv_defaults_types) test_config_condition( len(org_tlv) != len(self.lldp_org_tlv_defaults_types), ('missing org_tlv config')) if not isinstance(org_tlv['info'], bytearray): try: org_tlv['info'] = bytearray.fromhex( org_tlv['info']) # pytype: disable=missing-parameter except ValueError: org_tlv['info'] = org_tlv['info'].encode('utf-8') if not isinstance(org_tlv['oui'], bytearray): org_tlv['oui'] = bytearray.fromhex( '%6.6x' % org_tlv['oui']) # pytype: disable=missing-parameter org_tlvs.append(org_tlv) self.lldp_beacon['org_tlvs'] = org_tlvs if self.acl_in and self.acls_in: raise InvalidConfigError( 'found both acl_in and acls_in, use only acls_in') if self.acl_in and not isinstance(self.acl_in, list): self.acls_in = [ self.acl_in, ] self.acl_in = None if self.acls_in: for acl in self.acls_in: test_config_condition(not isinstance(acl, (int, str)), 'acl names must be int or')
def _config_parser_v2(config_file, logname): config_path = config_parser_util.dp_config_path(config_file) top_confs = {} config_hashes = {} dps = None for top_conf in V2_TOP_CONFS: top_confs[top_conf] = {} if not config_parser_util.dp_include(config_hashes, config_path, logname, top_confs): raise InvalidConfigError('Error found while loading config file: %s' % config_path) elif not top_confs['dps']: raise InvalidConfigError('DPs not configured in file: %s' % config_path) else: dps = _dp_parser_v2(top_confs['acls'], top_confs['dps'], top_confs['meters'], top_confs['routers'], top_confs['vlans']) return (config_hashes, dps)
def match_from_dict(match_dict): kwargs = {} for of_match, field in match_dict.items(): of_match = OLD_MATCH_FIELDS.get(of_match, of_match) test_config_condition(of_match not in MATCH_FIELDS, 'Unknown match field: %s' % of_match) try: encoded_field = MATCH_FIELDS[of_match](field) except TypeError: raise InvalidConfigError('%s cannot be type %s' % (of_match, type(field))) kwargs[of_match] = encoded_field return parser.OFPMatch(**kwargs)
def __init__(self, _id, dp_id, conf): self.rules = [] self.exact_match = None self.dot1x_assigned = None self.meter = False self.matches = {} self.set_fields = set() self._ports_resolved = False #TODO: Would be possible to save the names instead of the DP and port objects # TUNNEL: # src_port: PORT object / port number # source port # src_dp: DP object # source dp # dst_port: PORT object / port number # final destination port # dst_dp: DP object # final destination dp # tunnel_id: int # ID to represent the tunnel # tunnel_type: str ('vlan') # tunnel type specification self.tunnel_info = {} for match_fields in (MATCH_FIELDS, OLD_MATCH_FIELDS): self.rule_types.update( {match: (str, int) for match in match_fields}) conf = copy.deepcopy(conf) if isinstance(conf, dict): rules = conf.get('rules', []) elif isinstance(conf, list): rules = conf conf = {} else: raise InvalidConfigError('ACL conf is an invalid type %s' % _id) conf['rules'] = [] for rule in rules: normalized_rule = rule if isinstance(rule, dict): normalized_rule = rule.get('rule', rule) if normalized_rule is None: normalized_rule = { k: v for k, v in rule.items() if v is not None } test_config_condition(not isinstance(normalized_rule, dict), ('ACL rule is %s not %s (%s)' % (type(normalized_rule), dict, rules))) conf['rules'].append(normalized_rule) super(ACL, self).__init__(_id, dp_id, conf)
def _dp_add_ports(dp, dp_conf, dp_id, vlans): ports_conf = dp_conf.get('interfaces', {}) port_ranges_conf = dp_conf.get('interface_ranges', {}) # as users can config port vlan by using vlan name, we store vid in # Port instance instead of vlan name for data consistency test_config_condition(not isinstance(ports_conf, dict), ('Invalid syntax in interface config')) test_config_condition(not isinstance(port_ranges_conf, dict), ('Invalid syntax in interface ranges config')) port_num_to_port_conf = {} for port_key, port_conf in list(ports_conf.items()): test_config_condition(not isinstance(port_conf, dict), 'Invalid syntax in port config') if 'number' in port_conf: port_num = port_conf['number'] else: port_num = port_key try: port_num_to_port_conf[port_num] = (port_key, port_conf) except TypeError: raise InvalidConfigError('Invalid syntax in port config') for port_range, port_conf in list(port_ranges_conf.items()): # port range format: 1-6 OR 1-6,8-9 OR 1-3,5,7-9 test_config_condition(not isinstance(port_conf, dict), 'Invalid syntax in port config') port_nums = set() if 'number' in port_conf: del port_conf['number'] for range_ in re.findall(r'(\d+-\d+)', str(port_range)): start_num, end_num = [int(num) for num in range_.split('-')] test_config_condition(start_num >= end_num, ('Incorrect port range (%d - %d)' % (start_num, end_num))) port_nums.update(list(range(start_num, end_num + 1))) port_range = re.sub(range_, '', port_range) other_nums = [int(p) for p in re.findall(r'\d+', str(port_range))] port_nums.update(other_nums) test_config_condition(not port_nums, 'interface-ranges contain invalid config') for port_num in port_nums: if port_num in port_num_to_port_conf: # port range config has lower priority than individual port config for attr, value in list(port_conf.items()): port_num_to_port_conf[port_num][1].setdefault( attr, value) else: port_num_to_port_conf[port_num] = (port_num, port_conf) for port_num, port_conf in list(port_num_to_port_conf.values()): port = _dp_parse_port(dp_id, port_num, port_conf, vlans) dp.add_port(port) dp.reset_refs(vlans=vlans)
def _get_vlan_by_key(dp_id, vlan_key, vlans): try: if vlan_key in vlans: return vlans[vlan_key] except TypeError as err: raise InvalidConfigError(err) from err for vlan in vlans.values(): if vlan_key == vlan.vid: return vlan test_config_condition(not isinstance(vlan_key, int), ( 'Implicitly created VLAN %s must be an int (not %s)' % ( vlan_key, type(vlan_key)))) # Create VLAN with VID, if not defined. return vlans.setdefault(vlan_key, VLAN(vlan_key, dp_id))
def _parse_dps_for_watchers(conf, logname, meta_dp_state=None): all_dps_list = [] faucet_conf_hashes = {} if not isinstance(conf, dict): raise InvalidConfigError('Gauge config not valid') faucet_config_files = conf.get('faucet_configs', []) for faucet_config_file in faucet_config_files: conf_hashes, _, dp_list, _ = dp_parser(faucet_config_file, logname) if dp_list: faucet_conf_hashes[faucet_config_file] = conf_hashes all_dps_list.extend(dp_list) faucet_config = conf.get('faucet', None) if faucet_config: all_dps_list.extend(dp_preparsed_parser(faucet_config, meta_dp_state)) dps = {dp.name: dp for dp in all_dps_list} if not dps: raise InvalidConfigError( 'Gauge configured without any FAUCET configuration') return faucet_config_files, faucet_conf_hashes, dps
def build_acl(acl, vid=None): """Check that ACL can be built from config and mark mirror destinations.""" if acl.rules: try: assert valve_acl.build_acl_ofmsgs( [acl], self.wildcard_table, valve_of.goto_table(self.wildcard_table), 2**16, self.meters, acl.exact_match, vlan_vid=vid) except (AddrFormatError, ValueError) as err: raise InvalidConfigError(err) for port_no in acl.mirror_destinations: port = self.ports[port_no] port.mirror_destination = True
def match_from_dict(match_dict): for old_match, new_match in list(OLD_MATCH_FIELDS.items()): if old_match in match_dict: match_dict[new_match] = match_dict[old_match] del match_dict[old_match] kwargs = {} for of_match, field in list(match_dict.items()): test_config_condition(of_match not in MATCH_FIELDS, 'Unknown match field: %s' % of_match) try: encoded_field = MATCH_FIELDS[of_match](field) except TypeError: raise InvalidConfigError('%s cannot be type %s' % (of_match, type(field))) kwargs[of_match] = encoded_field return parser.OFPMatch(**kwargs)
def _dp_parser_v2(dps_conf, acls_conf, meters_conf, routers_conf, vlans_conf, meta_dp_state): # pylint: disable=invalid-name dp_vlans = [] for dp_key, dp_conf in dps_conf.items(): try: dp, vlans = _parse_dp(dp_key, dp_conf, acls_conf, meters_conf, routers_conf, vlans_conf) dp_vlans.append((dp, vlans)) except InvalidConfigError as err: raise InvalidConfigError('DP %s: %s' % (dp_key, err)) # Some VLANs are created implicitly just by referencing them in tagged/native, # so we must make them available to all DPs. implicit_vids = set() for dp, vlans in dp_vlans: implicit_vids.update(set(vlans.keys()) - set(vlans_conf.keys())) dps = [] for dp, vlans in dp_vlans: for vlan_key in implicit_vids: if vlan_key not in vlans: vlans[vlan_key] = VLAN(vlan_key, dp.dp_id) dp.reset_refs(vlans=vlans) dps.append(dp) for dp in dps: dp.finalize_config(dps) for dp in dps: dp.resolve_stack_topology(dps, meta_dp_state) for dp in dps: dp.finalize() dpid_refs = set() for dp in dps: test_config_condition(dp.dp_id in dpid_refs, ('DPID %u is duplicated' % dp.dp_id)) dpid_refs.add(dp.dp_id) routers_referenced = set() for dp in dps: routers_referenced.update(dp.routers.keys()) for router in routers_conf: test_config_condition( router not in routers_referenced, ('router %s configured but not used by any DP' % router)) return dps
def dp_parser(config_file, logname): conf = config_parser_util.read_config(config_file, logname) config_hashes = None dps = None try: assert conf is not None, 'Config file is empty' assert isinstance(conf, dict), 'Config file does not have valid syntax' version = conf.pop('version', 2) assert version == 2, 'Only config version 2 is supported' config_hashes, dps = _config_parser_v2(config_file, logname) assert dps is not None, 'no DPs are not defined' except AssertionError as err: raise InvalidConfigError(err) return config_hashes, dps
def __init__(self, _id, dp_id, conf): self.rules = [] self.exact_match = None self.dot1x_assigned = None self.meter = False self.matches = {} self.set_fields = set() self._ports_resolved = False # Tunnel info maintains the tunnel output information for each tunnel rule self.tunnel_dests = {} # Tunnel sources is a list of the sources in the network for this ACL self.tunnel_sources = {} # Tunnel rules is the rules for each tunnel in the ACL for each source self.dyn_tunnel_rules = {} self.dyn_reverse_tunnel_rules = {} for match_fields in (MATCH_FIELDS, OLD_MATCH_FIELDS): self.rule_types.update( {match: (str, int) for match in match_fields}) conf = copy.deepcopy(conf) if isinstance(conf, dict): rules = conf.get('rules', []) elif isinstance(conf, list): rules = conf conf = {} else: raise InvalidConfigError('ACL conf is an invalid type %s' % _id) conf['rules'] = [] for rule in rules: normalized_rule = rule if isinstance(rule, dict): normalized_rule = rule.get('rule', rule) if normalized_rule is None: normalized_rule = { k: v for k, v in rule.items() if v is not None } test_config_condition(not isinstance(normalized_rule, dict), ('ACL rule is %s not %s (%s)' % (type(normalized_rule), dict, rules))) conf['rules'].append(normalized_rule) super().__init__(_id, dp_id, conf)
def _validate_faucet_config(self, config_dir): logname = os.devnull try: root_config = os.path.join(config_dir, self.default_config) _, _, dps, top_confs = dp_parser(root_config, logname) dps_conf = None valve_cls = None acls_conf = None if dps is not None: dps_conf = {dp.name: dp for dp in dps} valve_cls = [valve.valve_factory(dp) for dp in dps] acls_conf = top_confs.get('acls', {}) if dps_conf: if not valve_cls: raise InvalidConfigError('no valid DPs defined') else: dps_conf = {} return (dps_conf, acls_conf) except InvalidConfigError as err: raise _ServerError(f'Invalid config: {err}') # pylint: disable=raise-missing-from
def _parse_dps_for_watchers(conf, logname, meta_dp_state=None): dps = {} if 'faucet_configs' in conf: for faucet_file in conf['faucet_configs']: _, dp_list, _ = dp_parser(faucet_file, logname) if dp_list: for dp in dp_list: dps[dp.name] = dp if 'faucet' in conf: faucet_conf = conf['faucet'] dps = { dp.name: dp for dp in dp_preparsed_parser(faucet_conf, meta_dp_state) } if not dps: raise InvalidConfigError( 'Gauge configured without any FAUCET configuration') return dps
def _watcher_parser_v2(conf, logname, prom_client): logger = config_parser_util.get_logger(logname) result = [] dps = {} for faucet_file in conf['faucet_configs']: _, dp_list = dp_parser(faucet_file, logname) if dp_list: for dp in dp_list: dps[dp.name] = dp dbs = conf.pop('dbs') for watcher_name, watcher_conf in list(conf['watchers'].items()): if watcher_conf.get('all_dps', False): watcher_dps = list(dps.keys()) else: watcher_dps = watcher_conf['dps'] # Watcher config has a list of DPs, but actually a WatcherConf is # created for each DP. # TODO: refactor watcher_conf as a container. for dp_name in watcher_dps: if dp_name not in dps: logger.error('DP %s in Gauge but not configured in FAUCET', dp_name) continue dp = dps[dp_name] if 'dbs' in watcher_conf: watcher_dbs = watcher_conf['dbs'] elif 'db' in watcher_conf: watcher_dbs = [watcher_conf['db']] else: raise InvalidConfigError('Watcher configured without DB') for db in watcher_dbs: watcher = WatcherConf(watcher_name, dp.dp_id, watcher_conf, prom_client) watcher.add_db(dbs[db]) watcher.add_dp(dp) result.append(watcher) return result
def _watcher_parser_v2(conf, logname, prom_client): logger = config_parser_util.get_logger(logname) if conf is None: conf = {} faucet_config_files, faucet_conf_hashes, dps = _parse_dps_for_watchers( conf, logname) dbs = conf.pop('dbs') result = [] # pylint: disable=fixme for watcher_name, watcher_conf in conf['watchers'].items(): if watcher_conf.get('all_dps', False): watcher_dps = dps.keys() else: watcher_dps = watcher_conf['dps'] # Watcher config has a list of DPs, but actually a WatcherConf is # created for each DP. # TODO: refactor watcher_conf as a container. for dp_name in watcher_dps: if dp_name not in dps: logger.error('DP %s in Gauge but not configured in FAUCET', dp_name) continue dp = dps[dp_name] if 'dbs' in watcher_conf: watcher_dbs = watcher_conf['dbs'] elif 'db' in watcher_conf: watcher_dbs = [watcher_conf['db']] else: raise InvalidConfigError('Watcher configured without DB') for db in watcher_dbs: watcher = WatcherConf(watcher_name, dp.dp_id, watcher_conf, prom_client) watcher.add_db(dbs[db]) watcher.add_dp(dp) result.append(watcher) return faucet_config_files, faucet_conf_hashes, result
def _parse_dps_for_watchers(conf, logname): dps = {} if 'faucet_configs' in conf: for faucet_file in conf['faucet_configs']: _, dp_list = dp_parser(faucet_file, logname) if dp_list: for dp in dp_list: dps[dp.name] = dp if 'faucet' in conf: faucet_conf = conf['faucet'] acls = faucet_conf.get('acls', {}) fct_dps = faucet_conf.get('dps', {}) meters = faucet_conf.get('meters', {}) routers = faucet_conf.get('routers', {}) vlans = faucet_conf.get('vlans', {}) for dp in _dp_parser_v2(acls, fct_dps, meters, routers, vlans): dps[dp.name] = dp if not dps: raise InvalidConfigError( 'Gauge configured without any FAUCET configuration') return dps