def _send_flow_msgs(self, dp_id, flow_msgs, ryu_dp=None): """Send OpenFlow messages to a connected datapath. Args: dp_id (int): datapath ID. flow_msgs (list): OpenFlow messages to send. ryu_dp: Override datapath from DPSet. """ if ryu_dp is None: ryu_dp = self.dpset.get(dp_id) if not ryu_dp: self.logger.error('send_flow_msgs: %s not up', dpid_log(dp_id)) return if dp_id not in self.valves: self.logger.error('send_flow_msgs: unknown %s', dpid_log(dp_id)) return valve = self.valves[dp_id] reordered_flow_msgs = valve_of.valve_flowreorder(flow_msgs) valve.ofchannel_log(reordered_flow_msgs) for flow_msg in reordered_flow_msgs: # pylint: disable=no-member self.metrics.of_flowmsgs_sent.labels(dp_id=hex(dp_id)).inc() flow_msg.datapath = ryu_dp ryu_dp.send_msg(flow_msg)
def handler_connect_or_disconnect(self, ryu_event): """Handle connection or disconnection of a datapath. Args: ryu_event (ryu.controller.dpset.EventDP): trigger. """ ryu_dp = ryu_event.dp dp_id = ryu_dp.id # Datapath down message if not ryu_event.enter: if dp_id in self.valves: # pylint: disable=no-member self.metrics.of_dp_disconnections.labels(dpid=hex(dp_id)).inc() self.logger.debug('%s disconnected', dpid_log(dp_id)) self.valves[dp_id].datapath_disconnect(dp_id) else: self.logger.error('handler_connect_or_disconnect: unknown %s', dpid_log(dp_id)) return # pylint: disable=no-member self.metrics.of_dp_connections.labels(dpid=hex(dp_id)).inc() self.logger.debug('%s connected', dpid_log(dp_id)) self.handler_datapath(ryu_dp)
def _packet_in_handler(self, ryu_event): """Handle a packet in event from the dataplane. Args: ryu_event (ryu.controller.event.EventReplyBase): packet in message. """ msg = ryu_event.msg ryu_dp = msg.datapath dp_id = ryu_dp.id if not dp_id in self.valves: self.logger.error('_packet_in_handler: unknown %s', dpid_log(dp_id)) return valve = self.valves[dp_id] valve.ofchannel_log([msg]) in_port = msg.match['in_port'] # eth/VLAN header only pkt, vlan_vid = valve_packet.parse_packet_in_pkt(msg.data, max_len=(14 + 4)) if pkt is None or vlan_vid is None: self.logger.info('unparseable packet from %s port %s', dpid_log(dp_id), in_port) return pkt_meta = valve.parse_rcv_packet(in_port, vlan_vid, msg.data, pkt) # pylint: disable=no-member self.metrics.of_packet_ins.labels(dpid=hex(dp_id)).inc() flowmods = valve.rcv_packet(dp_id, self.valves, pkt_meta) self._send_flow_msgs(dp_id, flowmods) valve.update_metrics(self.metrics)
def handler_connect_or_disconnect(self, ryu_event): """Handle connection or disconnection of a datapath. Args: ryu_event (ryu.controller.dpset.EventDP): trigger. """ ryu_dp = ryu_event.dp dp_id = ryu_dp.id if dp_id in self.valves: valve = self.valves[dp_id] # pylint: disable=no-member if ryu_event.enter: self.metrics.of_dp_connections.labels(dpid=hex(dp_id)).inc() self.logger.debug('%s connected', dpid_log(dp_id)) self._handler_datapath(ryu_dp) # pylint: disable=no-member self.metrics.dp_status.labels(dpid=hex(dp_id)).set(1) else: self.metrics.of_dp_disconnections.labels(dpid=hex(dp_id)).inc() # pylint: disable=no-member self.metrics.dp_status.labels(dpid=hex(dp_id)).set(0) self.logger.debug('%s disconnected', dpid_log(dp_id)) valve.datapath_disconnect(dp_id) else: self.logger.error('handler_connect_or_disconnect: unknown %s', dpid_log(dp_id)) ryu_dp.close()
def _handler_datapath_up(self, ryu_dp): """Handle DP up. Args: ryu_dp (ryu.controller.controller.Datapath): datapath. """ dp_id = ryu_dp.id if dp_id in self.watchers: self.logger.info('%s up', dpid_log(dp_id)) for watcher in list(self.watchers[dp_id].values()): watcher.start(ryu_dp) else: self.logger.info('%s up, unknown', dpid_log(dp_id))
def port_status_handler(self, ryu_event): """Handle a port status change event. Args: ryu_event (ryu.controller.ofp_event.EventOFPPortStatus): trigger. """ msg = ryu_event.msg ryu_dp = msg.datapath dp_id = ryu_dp.id ofp = msg.datapath.ofproto reason = msg.reason port_no = msg.desc.port_no port_down = msg.desc.state & ofp.OFPPS_LINK_DOWN port_status = not port_down if dp_id in self.valves: valve = self.valves[dp_id] flowmods = valve.port_status_handler(dp_id, port_no, reason, port_status) self._send_flow_msgs(dp_id, flowmods) # pylint: disable=no-member self.metrics.port_status.labels(dpid=hex(dp_id), port=port_no).set(port_status) else: self.logger.error('port_status_handler: unknown %s', dpid_log(dp_id)) ryu_dp.close()
def _load_configs(self, new_config_file): self.config_file = new_config_file self.config_hashes, new_dps = dp_parser( new_config_file, self.logname) deleted_valve_dpids = ( set(list(self.valves.keys())) - set([valve.dp_id for valve in new_dps])) for new_dp in new_dps: cold_start = True if new_dp.dp_id in self.valves: valve = self.valves[new_dp.dp_id] cold_start, flowmods = valve.reload_config(new_dp) self._send_flow_msgs(new_dp.dp_id, flowmods) else: # pylint: disable=no-member valve_cl = valve_factory(new_dp) if valve_cl is None: self.logger.fatal('Could not configure %s', new_dp.name) else: valve = valve_cl(new_dp, self.logname) self.valves[new_dp.dp_id] = valve # pylint: disable=no-member if cold_start: self.metrics.faucet_config_reload_cold.labels( dpid=hex(new_dp.dp_id)).inc() else: self.metrics.faucet_config_reload_warm.labels( dpid=hex(new_dp.dp_id)).inc() valve.update_config_metrics(self.metrics) for deleted_valve_dpid in deleted_valve_dpids: self.logger.info( 'Deleting de-configured %s', dpid_log(deleted_valve_dpid)) del self.valves[deleted_valve_dpid] self._bgp.reset(self.valves, self.metrics)
def packet_in_handler(self, ryu_event): """Handle a packet in event from the dataplane. Args: ryu_event (ryu.controller.event.EventReplyBase): packet in message. """ msg = ryu_event.msg ryu_dp = msg.datapath dp_id = ryu_dp.id valve = self._get_valve(ryu_dp, 'packet_in_handler', msg) if valve is None: return in_port = msg.match['in_port'] # eth/VLAN header only pkt, eth_pkt, vlan_vid, eth_type = valve_packet.parse_packet_in_pkt( msg.data, max_len=valve_packet.ETH_VLAN_HEADER_SIZE) if pkt is None or vlan_vid is None: self.logger.info('unparseable packet from %s port %s', dpid_log(dp_id), in_port) return pkt_meta = valve.parse_rcv_packet(in_port, vlan_vid, eth_type, msg.data, pkt, eth_pkt) # pylint: disable=no-member self.metrics.of_packet_ins.labels(dp_id=hex(dp_id)).inc() flowmods = valve.rcv_packet(dp_id, self.valves, pkt_meta) self._send_flow_msgs(dp_id, flowmods) valve.update_metrics(self.metrics)
def update(self, rcv_time, dp_id, msg): super(GaugeFlowTableInfluxDBLogger, self).update(rcv_time, dp_id, msg) jsondict = msg.to_jsondict() points = [] for stats_reply in jsondict['OFPFlowStatsReply']['body']: stats = stats_reply['OFPFlowStats'] packet_count = int(stats['packet_count']) byte_count = int(stats['byte_count']) instructions = stats['instructions'] tags = { 'dp_name': self.dp.name, 'table_id': int(stats['table_id']), 'priority': int(stats['priority']), 'inst_count': len(instructions), } oxm_matches = stats['match']['OFPMatch']['oxm_fields'] for oxm_match in oxm_matches: oxm_tlv = oxm_match['OXMTlv'] mask = oxm_tlv['mask'] val = oxm_tlv['value'] field = oxm_tlv['field'] if mask is not None: val = '/'.join((val, mask)) tags[field] = val if field == 'vlan_vid' and mask is None: tags['vlan'] = devid_present(int(val)) points.append( self.make_point(tags, rcv_time, 'flow_packet_count', packet_count)) points.append( self.make_point(tags, rcv_time, 'flow_byte_count', byte_count)) if not self.ship_points(points): self.logger.warning('%s error shipping flow_table points', dpid_log(dp_id))
def port_status_handler(self, ryu_event): """Handle a port status change event. Args: ryu_event (ryu.controller.ofp_event.EventOFPPortStatus): trigger. """ msg = ryu_event.msg ryu_dp = msg.datapath dp_id = ryu_dp.id ofp = msg.datapath.ofproto reason = msg.reason port_no = msg.desc.port_no if dp_id not in self.valves: self.logger.error('port_status_handler: unknown %s', dpid_log(dp_id)) return valve = self.valves[dp_id] flowmods = [] if reason == ofp.OFPPR_ADD: flowmods = valve.port_add(dp_id, port_no) elif reason == ofp.OFPPR_DELETE: flowmods = valve.port_delete(dp_id, port_no) elif reason == ofp.OFPPR_MODIFY: port_down = msg.desc.state & ofp.OFPPS_LINK_DOWN if port_down: flowmods = valve.port_delete(dp_id, port_no) else: flowmods = valve.port_add(dp_id, port_no) else: self.logger.warning('Unhandled port status %s for port %u', reason, port_no) self._send_flow_msgs(ryu_dp, flowmods)
def _handler_datapath_down(self, ryu_dp): """Handle DP down. Args: ryu_dp (ryu.controller.controller.Datapath): datapath. """ dp_id = ryu_dp.id if dp_id in self.watchers: self.logger.info('%s down', dpid_log(dp_id)) self.prom_client.dp_status.labels(dp_id=hex(dp_id)).set(0) for watcher in list(self.watchers[dp_id].values()): self.logger.info('%s %s watcher stopping', dpid_log(dp_id), watcher.conf.type) watcher.stop() else: self.logger.info('%s down, unknown', dpid_log(dp_id))
def connect_or_disconnect_handler(self, ryu_event): """Handle connection or disconnection of a datapath. Args: ryu_event (ryu.controller.dpset.EventDP): trigger. """ ryu_dp = ryu_event.dp dp_id = ryu_dp.id valve = self._get_valve(ryu_dp, 'handler_connect_or_disconnect') if valve is None: return if ryu_event.enter: self.logger.debug('%s connected', dpid_log(dp_id)) self._datapath_connect(ryu_dp) else: self.logger.debug('%s disconnected', dpid_log(dp_id)) self._datapath_disconnect(ryu_dp)
def _update_watcher(self, dp_id, name, msg): """Call watcher with event data.""" rcv_time = time.time() if dp_id in self.watchers: if name in self.watchers[dp_id]: self.watchers[dp_id][name].update(rcv_time, dp_id, msg) else: self.logger.info('%s event, unknown', dpid_log(dp_id))
def handler_connect_or_disconnect(self, ryu_event): ryu_dp = ryu_event.dp dp_id = ryu_dp.id if dp_id not in self.watchers: self.logger.info('no watcher configured for %s', dpid_log(dp_id)) return if ryu_event.enter: # DP is connecting self.logger.info('%s up', dpid_log(dp_id)) for watcher in list(self.watchers[dp_id].values()): watcher.start(ryu_dp) else: # DP is disconnecting if ryu_dp.id in self.watchers: for watcher in list(self.watchers[dp_id].values()): watcher.stop() del self.watchers[dp_id] self.logger.info('%s down', dpid_log(dp_id))
def handler_reconnect(self, ryu_event): """Handle reconnection of a datapath. Args: ryu_event (ryu.controller.dpset.EventDPReconnected): trigger. """ ryu_dp = ryu_event.dp self.logger.debug('%s reconnected', dpid_log(ryu_dp.id)) self.handler_datapath(ryu_dp)
def _load_configs(self, new_config_file): self.config_file = new_config_file self.config_hashes, new_dps = dp_parser(new_config_file, self.logname) if new_dps is None: self.logger.error('new config bad - rejecting') return deleted_valve_dpids = (set(list(self.valves.keys())) - set([valve.dp_id for valve in new_dps])) for new_dp in new_dps: dp_id = new_dp.dp_id if dp_id in self.valves: valve = self.valves[dp_id] cold_start, flowmods = valve.reload_config(new_dp) # pylint: disable=no-member if flowmods: self._send_flow_msgs(new_dp.dp_id, flowmods) if cold_start: self.metrics.faucet_config_reload_cold.labels( dp_id=hex(dp_id)).inc() else: self.metrics.faucet_config_reload_warm.labels( dp_id=hex(dp_id)).inc() else: # pylint: disable=no-member valve_cl = valve_factory(new_dp) if valve_cl is None: self.logger.error('%s hardware %s must be one of %s', new_dp.name, new_dp.hardware, sorted(list(SUPPORTED_HARDWARE.keys()))) continue else: valve = valve_cl(new_dp, self.logname) self.valves[dp_id] = valve self.logger.info('Add new datapath %s', dpid_log(dp_id)) self.metrics.reset_dpid(dp_id) valve.update_config_metrics(self.metrics) for deleted_valve_dpid in deleted_valve_dpids: self.logger.info('Deleting de-configured %s', dpid_log(deleted_valve_dpid)) del self.valves[deleted_valve_dpid] ryu_dp = self.dpset.get(deleted_valve_dpid) if ryu_dp is not None: ryu_dp.close() self._bgp.reset(self.valves, self.metrics)
def _load_config(self): """Load Gauge config.""" self.config_file = os.getenv('GAUGE_CONFIG', self.config_file) new_confs = watcher_parser(self.config_file, self.logname, self.prom_client) new_watchers = {} for conf in new_confs: watcher = watcher_factory(conf)(conf, self.logname, self.prom_client) watcher_dpid = watcher.dp.dp_id ryu_dp = self.dpset.get(watcher_dpid) watcher_type = watcher.conf.type watcher_msg = '%s %s watcher' % (dpid_log(watcher_dpid), watcher_type) if watcher_dpid not in new_watchers: new_watchers[watcher_dpid] = {} if (watcher_dpid in self.watchers and watcher_type in self.watchers[watcher_dpid]): old_watcher = self.watchers[watcher_dpid][watcher_type] if old_watcher.running(): self.logger.info('%s stopped', watcher_msg) old_watcher.stop() del self.watchers[watcher_dpid][watcher_type] new_watchers[watcher_dpid][watcher_type] = watcher if ryu_dp is None: self.logger.info('%s added but DP currently down', watcher_msg) else: new_watchers[watcher_dpid][watcher_type].start(ryu_dp) self.logger.info('%s started', watcher_msg) for watcher_dpid, leftover_watchers in list(self.watchers.items()): for watcher_type, watcher in list(leftover_watchers.items()): if watcher.running(): self.logger.info('%s %s deconfigured', dpid_log(watcher_dpid), watcher_type) watcher.stop() self.watchers = new_watchers self.logger.info('config complete')
def _stat_port_name(self, msg, stat, dp_id): if stat.port_no == msg.datapath.ofproto.OFPP_CONTROLLER: return 'CONTROLLER' elif stat.port_no == msg.datapath.ofproto.OFPP_LOCAL: return 'LOCAL' elif stat.port_no in self.dp.ports: return self.dp.ports[stat.port_no].name self.logger.info('%s stats for unknown port %u', dpid_log(dp_id), stat.port_no) return None
def update(self, rcv_time, dp_id, msg): super(GaugePortStatsInfluxDBLogger, self).update(rcv_time, dp_id, msg) points = [] for stat in msg.body: port_name = self._stat_port_name(msg, stat, dp_id) for stat_name, stat_val in self._format_port_stats('_', stat): points.append( self.make_port_point(self.dp.name, port_name, rcv_time, stat_name, stat_val)) if not self.ship_points(points): self.logger.warning('%s error shipping port_stats points', dpid_log(dp_id))
def handler_reconnect(self, ryu_event): """Handle reconnection of a datapath. Args: ryu_event (ryu.controller.dpset.EventDPReconnected): trigger. """ ryu_dp = ryu_event.dp dp_id = ryu_dp.id self.logger.debug('%s reconnected', dpid_log(dp_id)) # pylint: disable=no-member self.metrics.dp_status.labels(dpid=hex(dp_id)).set(1) self._handler_datapath(ryu_dp)
def reconnect_handler(self, ryu_event): """Handle reconnection of a datapath. Args: ryu_event (ryu.controller.dpset.EventDPReconnected): trigger. """ ryu_dp = ryu_event.dp dp_id = ryu_dp.id valve = self._get_valve(ryu_dp, 'reconnect_handler') if valve is None: return self.logger.debug('%s reconnected', dpid_log(dp_id)) self._datapath_connect(ryu_dp)
def update(self, rcv_time, dp_id, msg): # TODO: it may be worth while verifying this is the correct stats # response before doing this self.reply_pending = False points = [] for stat in msg.body: port_name = self._stat_port_name(msg, stat, dp_id) for stat_name, stat_val in self._format_port_stats('_', stat): points.append( self.make_point(self.dp.name, port_name, rcv_time, stat_name, stat_val)) if not self.ship_points(points): self.logger.warn('%s error shipping port_stats points', dpid_log(dp_id))
def handler_features(self, ryu_event): """Handle receiving a switch features message from a datapath. Args: ryu_event (ryu.controller.ofp_event.EventOFPStateChange): trigger. """ msg = ryu_event.msg ryu_dp = msg.datapath dp_id = ryu_dp.id if dp_id in self.valves: flowmods = self.valves[dp_id].switch_features(dp_id, msg) self._send_flow_msgs(ryu_dp, flowmods) else: self.logger.error('handler_features: unknown %s', dpid_log(dp_id))
def update(self, rcv_time, dp_id, msg): super(GaugePortStateInfluxDBLogger, self).update(rcv_time, dp_id, msg) reason = msg.reason port_no = msg.desc.port_no if port_no in self.dp.ports: port_name = self.dp.ports[port_no].name points = [ self.make_point(self.dp.name, port_name, rcv_time, 'port_state_reason', reason) ] if not self.ship_points(points): self.logger.warning( '%s error shipping port_state_reason points', dpid_log(dp_id))
def _error_handler(self, ryu_event): """Handle an OFPError from a datapath. Args: ryu_event (ryu.controller.ofp_event.EventOFPErrorMsg): trigger """ msg = ryu_event.msg ryu_dp = msg.datapath dp_id = ryu_dp.id if dp_id in self.valves: # pylint: disable=no-member self.metrics.of_errors.labels(dpid=hex(dp_id)).inc() self.valves[dp_id].ofchannel_log([msg]) self.logger.error('OFError %s from %s', msg, dpid_log(dp_id))
def error_handler(self, ryu_event): """Handle an OFPError from a datapath. Args: ryu_event (ryu.controller.ofp_event.EventOFPErrorMsg): trigger """ msg = ryu_event.msg ryu_dp = msg.datapath dp_id = ryu_dp.id valve = self._get_valve(ryu_dp, 'error_handler', msg) if valve is None: return # pylint: disable=no-member self.metrics.of_errors.labels(dp_id=hex(dp_id)).inc() self.logger.error('OFError %s from %s', msg, dpid_log(dp_id))
def _handler_datapath(self, ryu_dp): """Handle any/all re/dis/connection of a datapath. Args: ryu_dp (ryu.controller.controller.Datapath): datapath. """ dp_id = ryu_dp.id if dp_id in self.valves: discovered_up_port_nums = [ port.port_no for port in list(ryu_dp.ports.values()) if port.state == 0] valve = self.valves[dp_id] flowmods = valve.datapath_connect( dp_id, discovered_up_port_nums) self._send_flow_msgs(dp_id, flowmods) else: self.logger.error('handler_datapath: unknown %s', dpid_log(dp_id))
def _send_flow_msgs(self, ryu_dp, flow_msgs): """Send OpenFlow messages to a connected datapath. Args: ryu_db (ryu.controller.controller.Datapath): datapath. flow_msgs (list): OpenFlow messages to send. """ dp_id = ryu_dp.id if dp_id not in self.valves: self.logger.error('send_flow_msgs: unknown %s', dpid_log(dp_id)) return valve = self.valves[dp_id] reordered_flow_msgs = valve.valve_flowreorder(flow_msgs) valve.ofchannel_log(reordered_flow_msgs) for flow_msg in reordered_flow_msgs: # pylint: disable=no-member self.metrics.of_flowmsgs_sent.labels(dpid=hex(dp_id)).inc() flow_msg.datapath = ryu_dp ryu_dp.send_msg(flow_msg)
def _get_valve(self, ryu_dp, handler_name, msg=None): """Get Valve instance to response to an event. Args: ryu_dp (ryu.controller.controller.Datapath): datapath. handler_name (string): handler name to log if datapath unknown. msg (ryu.controller.ofp_event.EventOFPMsgBase): message from datapath. Returns: Valve instance or None. """ dp_id = ryu_dp.id if dp_id in self.valves: valve = self.valves[dp_id] if msg: valve.ofchannel_log([msg]) return valve ryu_dp.close() self.logger.error('%s: unknown datapath %s', handler_name, dpid_log(dp_id)) return None
def update(self, rcv_time, dp_id, msg): rcv_time_str = _rcv_time(rcv_time) reason = msg.reason port_no = msg.desc.port_no ofp = msg.datapath.ofproto log_msg = 'port %s unknown state %s' % (port_no, reason) if reason == ofp.OFPPR_ADD: log_msg = 'port %s added' % port_no elif reason == ofp.OFPPR_DELETE: log_msg = 'port %s deleted' % port_no elif reason == ofp.OFPPR_MODIFY: link_down = (msg.desc.state & ofp.OFPPS_LINK_DOWN) if link_down: log_msg = 'port %s down' % port_no else: log_msg = 'port %s up' % port_no log_msg = '%s %s' % (dpid_log(dp_id), log_msg) self.logger.info(log_msg) if self.conf.file: with open(self.conf.file, 'a') as logfile: logfile.write('\t'.join((rcv_time_str, log_msg)) + '\n')