Exemple #1
0
    def _chart_ospf(self, layout: dict, ns_list: List[str]) -> None:
        '''Chart OSPF status'''

        ospf_df = gui_get_df('ospf', namespace=ns_list, columns=['default'])
        if ospf_df.empty:
            return

        ospf_df['state'] = np.where(ospf_df.ifState == "adminDown",
                                    "adminDown", ospf_df.adjState)

        ospf_status = ospf_df.groupby(by=['namespace', 'state'])['hostname'] \
                             .count() \
                             .reset_index() \
                             .rename({'hostname': 'count'}, axis=1)

        ospf_chart = alt.Chart(ospf_status, title='OSPF') \
                        .mark_bar(tooltip=True) \
                        .encode(y='state', x='count:Q', row='namespace',
                                color=alt.Color(
                                    'state',
                                    scale=alt.Scale(
                                        domain=['full', 'fail',
                                                'adminDown', 'passive'],
                                        range=['green', 'red', 'orange',
                                               'peach']))
                                )
        layout['ospf_chart'].altair_chart(ospf_chart)
Exemple #2
0
    def _get_failed_data(self, namespace: str, pgbar) -> None:
        '''Get interface/mlag/routing protocol states that are failed'''

        progress = 40
        for i, entry in enumerate([{
                'name': 'device',
                'query': 'status == "dead"'
        }, {
                'name': 'interfaces',
                'query': 'state == "down"'
        }, {
                'name':
                'mlag',
                'query':
                'mlagSinglePortsCnt != 0 or mlagErrorPortsCnt != 0'
        }, {
                'name': 'ospf',
                'query': 'adjState == "other"'
        }, {
                'name': 'bgp',
                'query': 'state == "NotEstd"'
        }]):
            df = gui_get_df(entry['name'], namespace=[namespace])
            if not df.empty and (entry.get('query', '')):
                df = df.query(entry['query']).reset_index(drop=True)
                self._failed_dfs[entry['name']] = df
            pgbar.progress(progress + i * 10)
Exemple #3
0
    def _chart_interface(self, layout: dict, ns_list: List[str]) -> None:
        '''Chart the interfaces table status'''

        if_df = gui_get_df('interfaces', namespace=ns_list,
                           columns=['default'])
        if if_df.empty:
            layout['if_chart'].warning('No Interface info found')
            return

        if_df['state'] = np.where((if_df.state == "down") & (
            if_df.adminState == "down"), "adminDown", if_df.state)

        if_status = if_df.groupby(by=['namespace', 'state'])['hostname'] \
                         .count() \
                         .reset_index() \
                         .rename({'hostname': 'count'}, axis=1)

        if_chart = alt.Chart(if_status, title='Interfaces') \
                      .mark_bar(tooltip=True) \
                      .encode(y='state', x='count:Q', row='namespace',
                              color=alt.Color(
                                  'state',
                                  scale=alt.Scale(domain=['up', 'adminDown',
                                                          'down'],
                                                  range=['green', 'orange',
                                                         'red']))
                              )
        layout['if_chart'].altair_chart(if_chart)
Exemple #4
0
    def _create_sidebar(self) -> None:

        state = self._state
        devdf = gui_get_df('device', columns=['namespace', 'hostname'])
        if devdf.empty:
            st.error('Unable to retrieve any namespace info')
            st.stop()

        namespaces = [''] + sorted(devdf.namespace.unique().tolist())
        if not state.namespace:
            nsidx = 0
        else:
            nsidx = namespaces.index(state.namespace)
        namespace = st.sidebar.selectbox('Namespace',
                                         namespaces,
                                         key='search_ns',
                                         index=nsidx,
                                         on_change=self._sync_state)

        st.sidebar.markdown("""Displays last 5 search results.

You can use search to find specific objects. You can qualify what you're
searching for by qualifying the search term with the type. We support:
- __addresses__: You can qualify a specific table to look for the address.
                 The search string can start with one of the following
                 keywords: __route, mac, arpnd__, to specify which table you
                 want the search to be performed in . If you don't specify a
                 table name, we assume ```network find``` to search for the
                 network attach point for the address. For example,
                 ```arpnd 172.16.1.101``` searches for entries with
                 172.16.1.101 in the IP address column of the arpnd table.
                 Similarly, ```10.0.0.21``` searches for where in the
                 network that IP address is attached to.
- __ASN__: Start the search with the string ```asn``` followed by the ASN
           number. Typing ```asns``` will show you the list of unique ASNs
               across the specified namespaces.
- __VTEP__: Start the search with the string ```vtep``` followed by the VTEP
            IP address. Typing ```vteps``` will show you the list of unique
            VTEPs across the specified namespaces.
- __VNI__: Start the search with the string ```vni``` followed by the VNI
           number.
Typing ```mtus``` will show you the list of unique MTUs across the
specified namespaces.

When specifying a table, you can specify multiple addresses to look for by
 providing the addresses as a space separated values such as
 ```"172.16.1.101 10.0.0.11"``` or
  ```mac "00:01:02:03:04:05 00:21:22:23:24:25"```
  and so on. A combination of mac and IP address can also be specified with
   the address table. Support for more sophisticated search will be added in
    the next few releases.
    """)

        if namespace != state.namespace:
            state.namespace = namespace
Exemple #5
0
    def _create_sidebar(self) -> None:

        state = self._state
        devdf = gui_get_df('device', columns=['namespace'])
        if devdf.empty:
            st.error('Unable to retrieve any namespace info')
            st.stop()

        namespaces = [''] + sorted(devdf.namespace.unique().tolist())
        if self._state.namespace:
            nsidx = namespaces.index(self._state.namespace)
        else:
            nsidx = 0

        url = '&'.join([
            f'{get_base_url()}?page=Help&session={get_session_id()}',
            'help=yes',
            'help_on=Path',
        ])
        display_help_icon(url)
        with st.sidebar:
            with st.form(key='trace'):
                state.namespace = st.selectbox('Namespace',
                                               namespaces,
                                               key='path_namespace',
                                               index=nsidx)
                state.source = st.text_input('Source IP',
                                             key='path_source',
                                             value=state.source)
                state.dest = st.text_input('Dest IP',
                                           value=state.dest,
                                           key='path_dest')

                state.vrf = st.text_input('VRF',
                                          value=state.vrf,
                                          key='path_vrf')
                state.start_time = st.text_input('Start Time',
                                                 value=state.start_time,
                                                 key='path_start_time')
                state.end_time = st.text_input('End Time',
                                               value=state.end_time,
                                               key='path_end_time')

                _ = st.form_submit_button('Trace', on_click=self._sync_state)

            state.show_ifnames = st.checkbox('Show in/out interface names',
                                             value=state.show_ifnames,
                                             key='path_show_ifnames',
                                             on_change=self._sync_state)
            _ = st.button('Source <-> Dest',
                          key='path_swap',
                          on_click=self._sync_state)
Exemple #6
0
    def _render(self, layout: dict) -> None:

        state = self._state

        if not state.table:
            return
        sqobj = get_sqobject(state.table)
        try:
            df = gui_get_df(state.table,
                            namespace=state.namespace.split(),
                            start_time=state.start_time,
                            end_time=state.end_time,
                            view=state.view, columns=state.columns)
        except Exception as e:  # pylint: disable=broad-except
            st.error(e)
            st.stop()

        if not df.empty:
            if 'error' in df.columns:
                st.error(df.iloc[0].error)
                st.experimental_set_query_params(**asdict(state))
                st.stop()
                return
        else:
            st.info('No data returned by the table')
            st.experimental_set_query_params(**asdict(state))
            st.stop()
            return

        if state.query:
            try:
                show_df = df.query(state.query).reset_index(drop=True)
                query_str = state.query
            except Exception as ex:  # pylint: disable=broad-except
                st.error(f'Invalid query string: {ex}')
                st.stop()
                query_str = ''
        else:
            show_df = df
            query_str = ''

        if not show_df.empty:
            self._draw_summary_df(layout, sqobj, query_str)
            self._draw_assert_df(layout, sqobj)

        self._draw_table_df(layout, show_df)
Exemple #7
0
    def _chart_poller(self, layout: dict, ns_list: List[str]) -> None:
        '''Display poller status'''

        sqdf = gui_get_df('sqPoller',
                          columns=['namespace', 'hostname', 'timestamp'],
                          service='device', namespace=ns_list)
        if sqdf.empty:
            st.warning('No Poller information found')
            return

        hosts = sqdf.groupby(by=['namespace'])['hostname'] \
                    .nunique() \
                    .reset_index() \
                    .rename({'hostname': '#devices'}, axis=1)
        times = sqdf.groupby(by=['namespace'])['timestamp'] \
                    .max().reset_index() \
                          .rename({'timestamp': 'lastPolledTime'}, axis=1)
        pstats = times.merge(hosts, on=['namespace'])

        layout['poller'].subheader('Poller Status')
        layout['poller'].dataframe(pstats)
Exemple #8
0
    def _create_sidebar(self) -> None:
        '''Draw appropriate sidebar for the page'''

        do_refresh = st.sidebar.button('Refresh')

        devdf = gui_get_df('device', columns=['namespace', 'hostname'])
        if devdf.empty:
            st.error('Unable to retrieve any namespace info')
            st.stop()

        namespaces = [''] + sorted(devdf.namespace.unique().tolist())
        if self._state.namespace:
            nsidx = namespaces.index(self._state.namespace)
        else:
            nsidx = 0
        namespace = st.sidebar.selectbox('Namespace', namespaces, index=nsidx,
                                         key='status_ns',
                                         on_change=self._sync_state)

        if do_refresh:
            st.caching.clear_memo_cache()

        st.sidebar.markdown(
            '''This page provides an overview of the overall network status

Select one of the following pages from the Page menu to investigate further.
* __Xplore__: Look at all the data, look at summaries, run asserts, queries
              and more
* __Path__: Trace the paths between destinations in a given namespace
* __Search__: Search for addresses in various tables. Just type in any address
              you want to search. You can specify multiple addresses, space
               separated. See the search page for more help.

__Caching is enabled by default for 90 secs on all pages__. You can clear the
 cache by hitting the refresh button on this page or selecting "Clear Cache"
 option from the drop down menu on the top right hand corner
        ''')

        if namespace != self._state.namespace:
            self._state.namespace = namespace
Exemple #9
0
    def _chart_bgp(self, layout: dict, ns_list: List[str]) -> None:
        '''Chart the BGP table status'''

        bgp_df = gui_get_df('bgp', namespace=ns_list, columns=['default'])

        if bgp_df.empty:
            return

        bgp_status = bgp_df.groupby(by=['namespace', 'state'])['hostname'] \
                           .count() \
                           .reset_index() \
                           .rename({'hostname': 'count'}, axis=1)

        bgp_chart = alt.Chart(bgp_status, title='BGP') \
                       .mark_bar(tooltip=True) \
                       .encode(y='state', x='count:Q', row='namespace',
                               color=alt.Color(
                                   'state',
                                   scale=alt.Scale(
                                       domain=['Established',
                                               'NotEstd', 'dynamic'],
                                       range=['green', 'red', 'orange']))
                               )
        layout['bgp_chart'].altair_chart(bgp_chart)
Exemple #10
0
    def _chart_dev(self, layout: dict, ns_list: List[str]) -> None:
        '''Chart the devices table status'''

        dev_df = gui_get_df('device', namespace=ns_list, columns=['*'])
        if dev_df.empty:
            layout['dev_chart'].warning('No device info found')
            return

        dev_status = dev_df \
            .groupby(by=['namespace', 'status'])['hostname'].count() \
            .reset_index() \
            .rename({'hostname': 'count'}, axis=1)

        dev_chart = alt.Chart(dev_status, title='Devices') \
                       .mark_bar(tooltip=True) \
                       .encode(y='status', x='count:Q', row='namespace',
                               color=alt.Color(
                                   'status',
                                   scale=alt.Scale(
                                       domain=[
                                           'alive', 'dead', 'neverpoll'],
                                       range=['green', 'red', 'darkred']))
                               )
        layout['dev_chart'].altair_chart(dev_chart)
Exemple #11
0
    def _render(self, layout) -> None:

        state = self._state
        search_text = st.session_state.search or state.search_text
        query_str, uniq_dict, columns = '', {}, []
        df = DataFrame()
        try:
            query_str, uniq_dict, columns = self._build_query(search_text)
        except ValueError as ve:
            expander = layout['current'].expander(f'Search for {search_text}',
                                                  expanded=True)
            df = DataFrame({'error': [ve]})
            self._draw_aggrid_df(expander, df)

        if state.namespace:
            query_ns = [state.namespace]
        else:
            query_ns = []

        if query_str:
            if state.table == "network":
                df = gui_get_df(state.table,
                                verb='find',
                                namespace=query_ns,
                                view="latest",
                                columns=columns,
                                address=query_str.split())
            else:
                df = gui_get_df(state.table,
                                namespace=query_ns,
                                query_str=query_str,
                                view="latest",
                                columns=columns)
                if not df.empty:
                    df = df.query(query_str) \
                        .drop_duplicates() \
                        .reset_index(drop=True)

            expander = layout['current'].expander(f'Search for {search_text}',
                                                  expanded=True)
            self._draw_aggrid_df(expander, df)

        elif uniq_dict:
            columns = ['namespace'] + uniq_dict['column']
            df = gui_get_df(uniq_dict['table'],
                            namespace=query_ns,
                            view='latest',
                            columns=columns)
            if not df.empty:
                df = df.groupby(by=columns).first().reset_index()

            expander = layout['current'].expander(f'Search for {search_text}',
                                                  expanded=True)
            self._draw_aggrid_df(expander, df)
        elif len(state.prev_results) == 0:
            st.info('Enter a search string to see results, '
                    'see sidebar for examples')

        prev_searches = [search_text]
        for psrch, prev_df in reversed(state.prev_results):
            if psrch in prev_searches:
                continue
            prev_searches.append(psrch)
            expander = st.expander(f'Search for {psrch}', expanded=True)
            self._draw_aggrid_df(expander, prev_df)

        is_error = (not df.empty) and all(df.columns == 'error')
        if ((query_str or uniq_dict) and st.session_state.search
                and (st.session_state.search != state.search_text)
                and not is_error):
            state.prev_results.append((search_text, df))
            state.search_text = st.session_state.search
Exemple #12
0
    def _create_sidebar(self) -> None:

        state = self._state
        stime = state.start_time
        etime = state.end_time

        tables = filter(
            lambda x: x not in ['path', 'tables', 'ospfIf', 'ospfNbr',
                                'devconfig', 'topmem', 'topcpu', 'ifCounters',
                                'time'],
            get_tables()
        )
        table_vals = [''] + sorted(tables)
        if state.table:
            if isinstance(state.table, list):
                tblidx = table_vals.index(state.table[0])
            else:
                tblidx = table_vals.index(state.table)
        else:
            tblidx = table_vals.index('network')  # Default starting table
        view_idx = 1 if state.view == 'all' else 0

        devdf = gui_get_df('device', columns=['namespace', 'hostname'])
        if devdf.empty:
            st.error('Unable to retrieve any namespace info')
            st.stop()

        namespaces = [""]
        namespaces.extend(sorted(devdf.namespace.unique().tolist()))
        if state.namespace:
            nsidx = namespaces.index(state.namespace)
        else:
            nsidx = 0
        with st.sidebar:
            with st.form('Xplore'):
                namespace = st.selectbox('Namespace',
                                         namespaces, key='xplore_namespace',
                                         index=nsidx)
                state.start_time = st.text_input('Start time',
                                                 value=stime,
                                                 key='xplore_stime')
                state.end_time = st.text_input('End time',
                                               value=etime,
                                               key='xplore_etime')

                table = st.selectbox(
                    'Select Table to View', tuple(table_vals),
                    key='xplore_table', index=tblidx)
                if table != state.table:
                    # We need to reset the specific variables
                    state.query = ''
                    state.assert_clicked = False
                    state.uniq_clicked = '-'
                    state.columns = ['default']
                    state.table = table

                view_vals = ('latest', 'all')
                if state.start_time and state.end_time:
                    # Show everything thats happened if both times are given
                    view_idx = 1
                state.view = st.radio("View of Data", view_vals,
                                      index=view_idx, key='xplore_view')
                st.form_submit_button('Get', on_click=self._fetch_data)

        if namespace != state.namespace:
            state.namespace = namespace

        if state.table:
            tables_obj = get_sqobject('tables')(start_time=state.start_time,
                                                end_time=state.end_time,
                                                view=state.view)
            fields = tables_obj.describe(table=state.table)
            colist = sorted((filter(lambda x: x not in ['index', 'sqvers'],
                                    fields.name.tolist())))
            columns = st.sidebar.multiselect('Pick columns',
                                             ['default', 'all'] + colist,
                                             key='xplore_columns',
                                             default=state.columns)
            col_sel_val = (('default' in columns or 'all' in columns)
                           and len(columns) == 1)

            col_ok = st.sidebar.checkbox('Column Selection Done',
                                         key='xplore_col_done',
                                         value=col_sel_val)
            if not col_ok:
                columns = ['default']
        else:
            col_ok = True
            columns = ['default']

        if not columns:
            columns = ['default']

        state.columns = columns
        state.experimental_ok = st.sidebar.checkbox(
            'Enable Experimental Features',
            key='xplore_exp',
            on_change=self._sync_state)
        if state.table in ['interfaces', 'ospf', 'bgp', 'evpnVni']:
            state.assert_clicked = st.sidebar.checkbox(
                'Run Assert',  key='xplore_assert',
                on_change=self._sync_state)
        else:
            state.assert_clicked = False

        state.query = st.sidebar.text_input(
            'Filter results with pandas query', value=state.query,
            key='xplore_query', on_change=self._sync_state)

        st.sidebar.markdown(
            "[query syntax help]"
            "(https://suzieq.readthedocs.io/en/latest/pandas-query-examples/)")

        if columns == ['all']:
            columns = ['*']

        if state.table:
            col_expander = st.sidebar.expander('Column Names', expanded=False)
            with col_expander:
                st.subheader(f'{state.table} column names')
                st.table(tables_obj.describe(table=state.table)
                         .query('name != "sqvers"')
                         .reset_index(drop=True).astype(str).style)

        if not col_ok:
            st.experimental_set_query_params(**asdict(state))
            st.stop()
        if ('default' in columns or 'all' in columns) and len(columns) != 1:
            st.error('Cannot select default/all with any other columns')
            st.experimental_set_query_params(**asdict(state))
            st.stop()
        elif not columns:
            st.error('Columns cannot be empty')
            st.experimental_set_query_params(**asdict(state))
            st.stop()