Beispiel #1
0
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.watchers = {}
     self.config_watcher = ConfigWatcher()
     self.faucet_config_watchers = []
     self.prom_client = GaugePrometheusClient(reg=self._reg)
     self.thread_managers = (self.prom_client, )
Beispiel #2
0
    def _load_config(self):
        """Load Gauge config."""
        try:
            conf_hash, _faucet_config_files, faucet_conf_hashes, new_confs = watcher_parser(
                self.config_file, self.logname, self.prom_client)
            watchers = [
                watcher_factory(watcher_conf)(watcher_conf, self.logname,
                                              self.prom_client)
                for watcher_conf in new_confs
            ]
            self.prom_client.reregister_nonflow_vars()
        except InvalidConfigError as err:
            self.config_watcher.update(self.config_file)
            self.logger.error('invalid config: %s', err)
            return

        for old_watchers in self.watchers.values():
            self._stop_watchers(old_watchers)
        new_watchers = {}
        for watcher in watchers:
            watcher_dpid = watcher.dp.dp_id
            watcher_type = watcher.conf.type
            if watcher_dpid not in new_watchers:
                new_watchers[watcher_dpid] = {}
            if watcher_type not in new_watchers[watcher_dpid]:
                new_watchers[watcher_dpid][watcher_type] = []
            new_watchers[watcher_dpid][watcher_type].append(watcher)

        timestamp = time.time()
        for watcher_dpid, watchers in new_watchers.items():
            ryu_dp = self.dpset.get(watcher_dpid)
            if ryu_dp:
                self._start_watchers(ryu_dp, watchers, timestamp)

        self.watchers = new_watchers
        self.config_watcher.update(self.config_file,
                                   {self.config_file: conf_hash})
        self.faucet_config_watchers = []
        for faucet_config_file, faucet_conf_hash in faucet_conf_hashes.items():
            faucet_config_watcher = ConfigWatcher()
            faucet_config_watcher.update(faucet_config_file, faucet_conf_hash)
            self.faucet_config_watchers.append(faucet_config_watcher)
            self.logger.info('watching FAUCET config %s', faucet_config_file)
        self.logger.info('config complete')
Beispiel #3
0
class Gauge(OSKenAppBase):
    """Ryu app for polling Faucet controlled datapaths for stats/state.

    It can poll multiple datapaths. The configuration files for each datapath
    should be listed, one per line, in the file set as the environment variable
    GAUGE_CONFIG. It logs to the file set as the environment variable
    GAUGE_LOG,
    """
    logname = 'gauge'
    exc_logname = logname + '.exception'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.watchers = {}
        self.config_watcher = ConfigWatcher()
        self.faucet_config_watchers = []
        self.prom_client = GaugePrometheusClient(reg=self._reg)
        self.thread_managers = (self.prom_client, )

    @kill_on_exception(exc_logname)
    def _check_thread_exception(self):
        super()._check_thread_exception()

    def _get_watchers(self, ryu_event):
        """Get Watchers instances to response to an event.

        Args:
            ryu_event (ryu.controller.event.EventReplyBase): DP event.
        Returns:
        """
        return self._get_datapath_obj(self.watchers, ryu_event)

    @kill_on_exception(exc_logname)
    def _load_config(self):
        """Load Gauge config."""
        try:
            conf_hash, _faucet_config_files, faucet_conf_hashes, new_confs = watcher_parser(
                self.config_file, self.logname, self.prom_client)
            watchers = [
                watcher_factory(watcher_conf)(watcher_conf, self.logname,
                                              self.prom_client)
                for watcher_conf in new_confs
            ]
            self.prom_client.reregister_nonflow_vars()
        except InvalidConfigError as err:
            self.config_watcher.update(self.config_file)
            self.logger.error('invalid config: %s', err)
            return

        for old_watchers in self.watchers.values():
            self._stop_watchers(old_watchers)
        new_watchers = {}
        for watcher in watchers:
            watcher_dpid = watcher.dp.dp_id
            watcher_type = watcher.conf.type
            if watcher_dpid not in new_watchers:
                new_watchers[watcher_dpid] = {}
            if watcher_type not in new_watchers[watcher_dpid]:
                new_watchers[watcher_dpid][watcher_type] = []
            new_watchers[watcher_dpid][watcher_type].append(watcher)

        timestamp = time.time()
        for watcher_dpid, watchers in new_watchers.items():
            ryu_dp = self.dpset.get(watcher_dpid)
            if ryu_dp:
                self._start_watchers(ryu_dp, watchers, timestamp)

        self.watchers = new_watchers
        self.config_watcher.update(self.config_file,
                                   {self.config_file: conf_hash})
        self.faucet_config_watchers = []
        for faucet_config_file, faucet_conf_hash in faucet_conf_hashes.items():
            faucet_config_watcher = ConfigWatcher()
            faucet_config_watcher.update(faucet_config_file, faucet_conf_hash)
            self.faucet_config_watchers.append(faucet_config_watcher)
            self.logger.info('watching FAUCET config %s', faucet_config_file)
        self.logger.info('config complete')

    @kill_on_exception(exc_logname)
    def _update_watcher(self, name, ryu_event):
        """Call watcher with event data."""
        watchers, _ryu_dp, msg = self._get_watchers(ryu_event)
        if watchers is None:
            return
        if name in watchers:
            for watcher in watchers[name]:
                watcher.update(ryu_event.timestamp, msg)

    def _config_files_changed(self):
        for config_watcher in [self.config_watcher
                               ] + self.faucet_config_watchers:
            if config_watcher.files_changed():
                return True
        return False

    @set_ev_cls(EventReconfigure, MAIN_DISPATCHER)
    def reload_config(self, ryu_event):
        """Handle request for Gauge config reload."""
        super().reload_config(ryu_event)
        self._load_config()

    @staticmethod
    def _start_watchers(ryu_dp, watchers, timestamp):
        """Start watchers for DP if active."""
        for watchers_by_name in watchers.values():
            for i, watcher in enumerate(watchers_by_name):
                is_active = i == 0
                watcher.report_dp_status(1)
                watcher.start(ryu_dp, is_active)
                if isinstance(watcher, GaugePortStatePoller):
                    for port in ryu_dp.ports.values():
                        msg = parser.OFPPortStatus(ryu_dp,
                                                   desc=port,
                                                   reason=ofp.OFPPR_ADD)
                        watcher.update(timestamp, msg)

    @kill_on_exception(exc_logname)
    def _datapath_connect(self, ryu_event):
        """Handle DP up.

        Args:
            ryu_event (ryu.controller.event.EventReplyBase): DP event.
        """
        watchers, ryu_dp, _ = self._get_watchers(ryu_event)
        if watchers is None:
            return
        self.logger.info('%s up', dpid_log(ryu_dp.id))
        ryu_dp.send_msg(valve_of.faucet_config(datapath=ryu_dp))
        ryu_dp.send_msg(valve_of.faucet_async(datapath=ryu_dp,
                                              packet_in=False))
        self._start_watchers(ryu_dp, watchers, time.time())

    @staticmethod
    def _stop_watchers(watchers):
        """Stop watchers for DP."""
        for watchers_by_name in watchers.values():
            for watcher in watchers_by_name:
                watcher.report_dp_status(0)
                if watcher.is_active():
                    watcher.stop()

    @kill_on_exception(exc_logname)
    def _datapath_disconnect(self, ryu_event):
        """Handle DP down.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): DP event.
        """
        watchers, ryu_dp, _ = self._get_watchers(ryu_event)
        if watchers is None:
            return
        self.logger.info('%s down', dpid_log(ryu_dp.id))
        self._stop_watchers(watchers)

    _WATCHER_HANDLERS = {
        ofp_event.EventOFPPortStatus: 'port_state',  # pylint: disable=no-member
        ofp_event.EventOFPPortStatsReply: 'port_stats',  # pylint: disable=no-member
        ofp_event.EventOFPFlowStatsReply: 'flow_table',  # pylint: disable=no-member
        ofp_event.EventOFPMeterStatsReply: 'meter_stats',  # pylint: disable=no-member
    }

    @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)  # pylint: disable=no-member
    @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER)  # pylint: disable=no-member
    @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER)  # pylint: disable=no-member
    @set_ev_cls(ofp_event.EventOFPMeterStatsReply, MAIN_DISPATCHER)  # pylint: disable=no-member
    @kill_on_exception(exc_logname)
    def update_watcher_handler(self, ryu_event):
        """Handle any kind of stats/change event.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): stats/change event.
        """
        self._update_watcher(self._WATCHER_HANDLERS[type(ryu_event)],
                             ryu_event)
Beispiel #4
0
 def __init__(self, *args, **kwargs):
     super(Gauge, self).__init__(*args, **kwargs)
     self.prom_client = GaugePrometheusClient()
     self.watchers = {}
     self.config_watcher = ConfigWatcher()
Beispiel #5
0
class Gauge(RyuAppBase):
    """Ryu app for polling Faucet controlled datapaths for stats/state.

    It can poll multiple datapaths. The configuration files for each datapath
    should be listed, one per line, in the file set as the environment variable
    GAUGE_CONFIG. It logs to the file set as the environment variable
    GAUGE_LOG,
    """
    _CONTEXTS = {'dpset': dpset.DPSet}
    logname = 'gauge'
    exc_logname = logname + '.exception'

    def __init__(self, *args, **kwargs):
        super(Gauge, self).__init__(*args, **kwargs)
        self.prom_client = GaugePrometheusClient()
        self.watchers = {}
        self.config_watcher = ConfigWatcher()

    def start(self):
        super(Gauge, self).start()

        self._load_config()
        self.threads.extend([
            hub.spawn(thread) for thread in (self._config_file_stat,)])

    def _get_watchers(self, ryu_dp, handler_name):
        """Get Watchers instances to response to an event.

        Args:
            ryu_dp (ryu.controller.controller.Datapath): datapath.
            handler_name (string): handler name to log if datapath unknown.
        Returns:
            dict of Watchers instances or None.
        """
        dp_id = ryu_dp.id
        if dp_id in self.watchers:
            return self.watchers[dp_id]
        ryu_dp.close()
        self.logger.error(
            '%s: unknown datapath %s', handler_name, dpid_log(dp_id))
        return None

    @kill_on_exception(exc_logname)
    def _load_config(self):
        """Load Gauge config."""
        for watcher_dpid, old_watchers in list(self.watchers.items()):
            self._stop_watchers(watcher_dpid, old_watchers)

        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
            watcher_type = watcher.conf.type
            if watcher_dpid not in new_watchers:
                new_watchers[watcher_dpid] = {}
            if watcher_type not in new_watchers[watcher_dpid]:
                new_watchers[watcher_dpid][watcher_type] = []
            new_watchers[watcher_dpid][watcher_type].append(watcher)

        for watcher_dpid, watchers in list(new_watchers.items()):
            ryu_dp = self.dpset.get(watcher_dpid)
            if ryu_dp:
                self._start_watchers(ryu_dp, watcher_dpid, watchers)

        self.watchers = new_watchers
        self.config_watcher.update(self.config_file)
        self.logger.info('config complete')

    @kill_on_exception(exc_logname)
    def _update_watcher(self, ryu_dp, name, msg):
        """Call watcher with event data."""
        rcv_time = time.time()
        watchers = self._get_watchers(ryu_dp, '_update_watcher')
        if watchers is None:
            return
        if name in watchers:
            for watcher in watchers[name]:
                watcher.update(rcv_time, ryu_dp.id, msg)

    @kill_on_exception(exc_logname)
    def _config_file_stat(self):
        """Periodically stat config files for any changes."""
        while True:
            if self.config_watcher.files_changed():
                if self.stat_reload:
                    self.send_event('Gauge', EventReconfigure())
            self._thread_jitter(3)

    @set_ev_cls(EventReconfigure, MAIN_DISPATCHER)
    def reload_config(self, _):
        """Handle request for Gauge config reload."""
        self.logger.warning('reload config requested')
        self._load_config()

    def _start_watchers(self, ryu_dp, dp_id, watchers):
        """Start watchers for DP if active."""
        for watchers_by_name in list(watchers.values()):
            for i, watcher in enumerate(watchers_by_name):
                is_active = i == 0
                watcher.report_dp_status(1)
                watcher.start(ryu_dp, is_active)
                if is_active:
                    self.logger.info(
                        '%s %s watcher starting', dpid_log(dp_id), watcher.conf.type)

    @kill_on_exception(exc_logname)
    def _handler_datapath_up(self, ryu_dp):
        """Handle DP up.

        Args:
            ryu_dp (ryu.controller.controller.Datapath): datapath.
        """
        watchers = self._get_watchers(ryu_dp, '_handler_datapath_up')
        if watchers is None:
            return
        self.logger.info('%s up', dpid_log(ryu_dp.id))
        ryu_dp.send_msg(valve_of.faucet_config(datapath=ryu_dp))
        ryu_dp.send_msg(valve_of.gauge_async(datapath=ryu_dp))
        self._start_watchers(ryu_dp, ryu_dp.id, watchers)

    def _stop_watchers(self, dp_id, watchers):
        """Stop watchers for DP."""
        for watchers_by_name in list(watchers.values()):
            for watcher in watchers_by_name:
                watcher.report_dp_status(0)
                if watcher.is_active():
                    self.logger.info(
                        '%s %s watcher stopping', dpid_log(dp_id), watcher.conf.type)
                    watcher.stop()

    @kill_on_exception(exc_logname)
    def _handler_datapath_down(self, ryu_dp):
        """Handle DP down.

        Args:
            ryu_dp (ryu.controller.controller.Datapath): datapath.
        """
        watchers = self._get_watchers(ryu_dp, '_handler_datapath_down')
        if watchers is None:
            return
        self.logger.info('%s down', dpid_log(ryu_dp.id))
        self._stop_watchers(ryu_dp.id, watchers)

    @set_ev_cls(dpset.EventDP, dpset.DPSET_EV_DISPATCHER)
    @kill_on_exception(exc_logname)
    def handler_connect_or_disconnect(self, ryu_event):
        """Handle DP dis/connect.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): DP reconnection.
        """
        ryu_dp = ryu_event.dp
        if ryu_event.enter:
            self._handler_datapath_up(ryu_dp)
        else:
            self._handler_datapath_down(ryu_dp)

    @set_ev_cls(dpset.EventDPReconnected, dpset.DPSET_EV_DISPATCHER)
    @kill_on_exception(exc_logname)
    def handler_reconnect(self, ryu_event):
        """Handle a DP reconnection event.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): DP reconnection.
        """
        ryu_dp = ryu_event.dp
        self._handler_datapath_up(ryu_dp)

    @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) # pylint: disable=no-member
    @kill_on_exception(exc_logname)
    def port_status_handler(self, ryu_event):
        """Handle port status change event.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): port status change event.
        """
        self._update_watcher(
            ryu_event.msg.datapath, 'port_state', ryu_event.msg)

    @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) # pylint: disable=no-member
    @kill_on_exception(exc_logname)
    def port_stats_reply_handler(self, ryu_event):
        """Handle port stats reply event.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): port stats event.
        """
        self._update_watcher(
            ryu_event.msg.datapath, 'port_stats', ryu_event.msg)

    @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) # pylint: disable=no-member
    @kill_on_exception(exc_logname)
    def flow_stats_reply_handler(self, ryu_event):
        """Handle flow stats reply event.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): flow stats event.
        """
        self._update_watcher(
            ryu_event.msg.datapath, 'flow_table', ryu_event.msg)
Beispiel #6
0
 def __init__(self, *args, **kwargs):
     super(Gauge, self).__init__(*args, **kwargs)
     self.watchers = {}
     self.config_watcher = ConfigWatcher()
     self.prom_client = GaugePrometheusClient(reg=self._reg)
     self.thread_managers = (self.prom_client,)
Beispiel #7
0
class Gauge(RyuAppBase):
    """Ryu app for polling Faucet controlled datapaths for stats/state.

    It can poll multiple datapaths. The configuration files for each datapath
    should be listed, one per line, in the file set as the environment variable
    GAUGE_CONFIG. It logs to the file set as the environment variable
    GAUGE_LOG,
    """
    logname = 'gauge'
    exc_logname = logname + '.exception'
    prom_client = None

    def __init__(self, *args, **kwargs):
        super(Gauge, self).__init__(*args, **kwargs)
        self.watchers = {}
        self.config_watcher = ConfigWatcher()
        self.prom_client = GaugePrometheusClient(reg=self._reg)
        self.thread_managers = (self.prom_client,)

    @kill_on_exception(exc_logname)
    def _check_thread_exception(self):
        super(Gauge, self)._check_thread_exception()

    def _get_watchers(self, ryu_event):
        """Get Watchers instances to response to an event.

        Args:
            ryu_event (ryu.controller.event.EventReplyBase): DP event.
        Returns:
        """
        return self._get_datapath_obj(self.watchers, ryu_event)

    @kill_on_exception(exc_logname)
    def _load_config(self):
        """Load Gauge config."""
        try:
            new_confs = watcher_parser(self.config_file, self.logname, self.prom_client)
        except InvalidConfigError as err:
            self.logger.error('invalid config: %s', err)
            return

        for old_watchers in self.watchers.values():
            self._stop_watchers(old_watchers)

        new_watchers = {}

        for conf in new_confs:
            watcher = watcher_factory(conf)(conf, self.logname, self.prom_client)
            watcher_dpid = watcher.dp.dp_id
            watcher_type = watcher.conf.type
            if watcher_dpid not in new_watchers:
                new_watchers[watcher_dpid] = {}
            if watcher_type not in new_watchers[watcher_dpid]:
                new_watchers[watcher_dpid][watcher_type] = []
            new_watchers[watcher_dpid][watcher_type].append(watcher)

        timestamp = time.time()
        for watcher_dpid, watchers in new_watchers.items():
            ryu_dp = self.dpset.get(watcher_dpid)
            if ryu_dp:
                self._start_watchers(ryu_dp, watchers, timestamp)

        self.watchers = new_watchers
        self.config_watcher.update(self.config_file)
        self.logger.info('config complete')

    @kill_on_exception(exc_logname)
    def _update_watcher(self, name, ryu_event):
        """Call watcher with event data."""
        watchers, ryu_dp, msg = self._get_watchers(ryu_event)
        if watchers is None:
            return
        if name in watchers:
            for watcher in watchers[name]:
                watcher.update(ryu_event.timestamp, ryu_dp.id, msg)

    def _config_files_changed(self):
        return self.config_watcher.files_changed()

    @set_ev_cls(EventReconfigure, MAIN_DISPATCHER)
    def reload_config(self, ryu_event):
        """Handle request for Gauge config reload."""
        super(Gauge, self).reload_config(ryu_event)
        self._load_config()

    def _start_watchers(self, ryu_dp, watchers, timestamp):
        """Start watchers for DP if active."""
        for watchers_by_name in watchers.values():
            for i, watcher in enumerate(watchers_by_name):
                is_active = i == 0
                watcher.report_dp_status(1)
                if isinstance(watcher, GaugePortStatePoller):
                    for port in ryu_dp.ports.values():
                        msg = parser.OFPPortStatus(
                            ryu_dp, desc=port, reason=ofp.OFPPR_ADD)
                        watcher.update(timestamp, ryu_dp.id, msg)
                watcher.start(ryu_dp, is_active)

    @kill_on_exception(exc_logname)
    def _datapath_connect(self, ryu_event):
        """Handle DP up.

        Args:
            ryu_event (ryu.controller.event.EventReplyBase): DP event.
        """
        watchers, ryu_dp, _ = self._get_watchers(ryu_event)
        if watchers is None:
            return
        self.logger.info('%s up', dpid_log(ryu_dp.id))
        ryu_dp.send_msg(valve_of.faucet_config(datapath=ryu_dp))
        ryu_dp.send_msg(valve_of.faucet_async(datapath=ryu_dp, packet_in=False))
        self._start_watchers(ryu_dp, watchers, time.time())

    def _stop_watchers(self, watchers):
        """Stop watchers for DP."""
        for watchers_by_name in watchers.values():
            for watcher in watchers_by_name:
                watcher.report_dp_status(0)
                if watcher.is_active():
                    watcher.stop()

    @kill_on_exception(exc_logname)
    def _datapath_disconnect(self, ryu_event):
        """Handle DP down.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): DP event.
        """
        watchers, ryu_dp, _ = self._get_watchers(ryu_event)
        if watchers is None:
            return
        self.logger.info('%s down', dpid_log(ryu_dp.id))
        self._stop_watchers(watchers)

    _WATCHER_HANDLERS = {
        ofp_event.EventOFPPortStatus: 'port_state', # pylint: disable=no-member
        ofp_event.EventOFPPortStatsReply: 'port_stats', # pylint: disable=no-member
        ofp_event.EventOFPFlowStatsReply: 'flow_table', # pylint: disable=no-member
    }

    @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) # pylint: disable=no-member
    @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) # pylint: disable=no-member
    @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) # pylint: disable=no-member
    @kill_on_exception(exc_logname)
    def update_watcher_handler(self, ryu_event):
        """Handle port status change event.

        Args:
           ryu_event (ryu.controller.event.EventReplyBase): port status change event.
        """
        self._update_watcher(self._WATCHER_HANDLERS[type(ryu_event)], ryu_event)