Ejemplo n.º 1
0
    def get(self, **kwargs) -> pd.DataFrame:
        """Class-specific to extract info from addnl tables"""

        drop_cols = []
        addnl_fields = kwargs.pop('addnl_fields', [])
        columns = kwargs.get('columns', [])
        if 'ifname' not in columns and 'ifname' not in addnl_fields:
            addnl_fields.append('ifname')
            drop_cols.append('ifname')

        if (columns == ['default'] or columns == ['*'] or
                'remoteVtepCnt' in columns):
            getVtepCnt = True
            if columns != ['*'] and 'remoteVtepList' not in columns:
                addnl_fields.append('remoteVtepList')
                drop_cols.append('remoteVtepList')
        else:
            getVtepCnt = False

        df = super().get(addnl_fields=addnl_fields, **kwargs)
        if df.empty:
            return df

        if getVtepCnt:
            df.insert(len(df.columns)-4, 'remoteVtepCnt',
                      df.remoteVtepList.str.len())

        # See if we can retrieve the info to fill out the rest of the data
        # Start with VLAN values
        if 'vlan' not in df.columns:
            return df.drop(columns=drop_cols, errors='ignore')

        iflist = df[df.vlan == 0]['ifname'].to_list()
        if iflist:
            ifdf = IfObj(context=self.ctxt).get(
                namespace=kwargs.get('namespace'), ifname=iflist,
                columns=['namespace', 'hostname', 'ifname', 'state', 'vlan',
                         'vni'])

            if not ifdf.empty:
                df = df.merge(ifdf, on=['namespace', 'hostname', 'ifname',
                                        'vni'], how='left',
                              suffixes=('', '_y')) \
                    .drop(columns=['timestamp_y', 'state_y'])

                df['vlan'] = np.where(df['vlan_y'], df['vlan_y'], df['vlan'])
                df.drop(columns=['vlan_y'], inplace=True)

        # Fill out the numMacs and numArps columns if we can
        if 'numMacs' in df.columns:
            vlanlist = list(set(df[df.numMacs == 0]['vlan'].to_list()))
        else:
            vlanlist = []
        if vlanlist:
            if 'numMacs' in df.columns:
                macdf = MacsObj(context=self.ctxt).get(
                    namespace=kwargs.get('namespace'))
                df['numMacs'] = df.apply(self._count_macs, axis=1,
                                         args=(macdf, ))
        return df.drop(columns=drop_cols, errors='ignore')
Ejemplo n.º 2
0
    def aver(self, **kwargs) -> pd.DataFrame:
        """Assert for EVPN Data"""

        assert_cols = [
            "namespace", "hostname", "vni", "remoteVtepList", "vrf",
            "mcastGroup", "type", "priVtepIp", "state", "l2VniList", "ifname",
            "secVtepIp"
        ]

        kwargs.pop("columns", None)  # Loose whatever's passed
        status = kwargs.pop('status', 'all')

        df = self.get(columns=assert_cols, **kwargs)
        if df.empty:
            df = pd.DataFrame(columns=assert_cols)
            if status != 'pass':
                df['assertReason'] = 'No data found'
                df['assert'] = 'fail'
            return df

        df["assertReason"] = [[] for _ in range(len(df))]

        # Gather the unique set of VTEPs per VNI
        vteps_df = df.explode(column='remoteVtepList') \
            .dropna(how='any') \
            .groupby(by=['vni', 'type'])['remoteVtepList'] \
            .aggregate(lambda x: x.unique().tolist()) \
            .reset_index() \
            .dropna(how='any') \
            .rename(columns={'remoteVtepList': 'allVteps'})

        if not vteps_df.empty:
            her_df = df.merge(vteps_df)
        else:
            her_df = pd.DataFrame()

        if (not her_df.empty and (her_df.remoteVtepList.str.len() != 0).any()):
            # Check if every VTEP we know is reachable
            rdf = RoutesObj(context=self.ctxt).get(
                namespace=kwargs.get('namespace'), vrf='default')
            if not rdf.empty:
                rdf['prefixlen'] = rdf['prefix'].str.split('/') \
                                                    .str[1].astype('int')
            self._routes_df = rdf

            her_df["assertReason"] += her_df.apply(self._is_vtep_reachable,
                                                   axis=1)

        mcast_df = df.query('mcastGroup != "0.0.0.0"')
        if not mcast_df.empty:
            # Ensure that all VNIs have at most one multicast group associated
            # per namespace

            mismatched_vni_df = mcast_df \
                .groupby(by=['namespace', 'vni'])['mcastGroup'] \
                .unique() \
                .reset_index() \
                .dropna() \
                .query('mcastGroup.str.len() != 1')

            if not mismatched_vni_df.empty:
                df['assertReason'] += df.apply(
                    lambda x, err_df: ['VNI has multiple mcast group']
                    if (x['namespace'] in err_df['namespace'] and x['vni'] in
                        err_df['vni']) else [],
                    axis=1,
                    args=(mismatched_vni_df, ))
        elif not her_df.empty:
            # Every VTEP has info about every other VTEP for a given VNI
            her_df["assertReason"] += her_df.apply(self._all_vteps_present,
                                                   axis=1)

        # State is up
        df["assertReason"] += df.apply(
            lambda x: ['interface is down']
            if x['type'] == "L2" and x['state'] != "up" else [],
            axis=1)

        devices = df["hostname"].unique().tolist()
        ifdf = IfObj(context=self.ctxt) \
            .get(namespace=kwargs.get("namespace", ""), hostname=devices,
                 type='vxlan')

        df = df.merge(
            ifdf[['namespace', 'hostname', 'ifname', 'master', 'vlan']],
            on=['namespace', 'hostname', 'ifname'],
            how='left')

        # vxlan interfaces, if defined, for every VNI is part of bridge
        # We ensure this is true artificially for NXOS, ignored for JunOS.
        df["assertReason"] += df.apply(
            lambda x: ['vni not in bridge'] if (x['type'] == "L2" and x[
                'ifname'] != '-' and x['master'] != "bridge") else [],
            axis=1)

        # mac_df = MacsObj(context=self.ctxt) \
        #     .get(namespace=kwargs.get("namespace", ""),
        #          macaddr=["00:00:00:00:00:00"])

        # # Assert that we have HER for every remote VTEP
        # df['assertReason'] += df.apply(self._is_her_good,
        #                                args=(mac_df, ), axis=1)

        # Fill out the assert column
        df['assert'] = df.apply(lambda x: 'pass'
                                if not len(x.assertReason) else 'fail',
                                axis=1)

        if status == 'fail':
            df = df.query('assertReason.str.len() != 0')
        elif status == "pass":
            df = df.query('assertReason.str.len() == 0')

        return df[['namespace', 'hostname', 'vni', 'type',
                   'assertReason', 'assert', 'timestamp']] \
            .explode(column='assertReason') \
            .fillna({'assertReason': '-'})
Ejemplo n.º 3
0
    def aver(self, **kwargs) -> pd.DataFrame:
        """BGP Assert"""

        assert_cols = [
            "namespace", "hostname", "vrf", "peer", "asn", "state", "peerAsn",
            "v4Enabled", "v6Enabled", "evpnEnabled", "v4Advertised",
            "v6Advertised", "evpnAdvertised", "v4Received", "v6Received",
            "evpnReceived", "bfdStatus", "reason", "notifcnReason"
        ]

        kwargs.pop("columns", None)  # Loose whatever's passed

        df = self.get(columns=assert_cols, **kwargs)
        if df.empty:
            return pd.DataFrame()

        if_df = IfObj(context=self.ctxt).get(namespace=kwargs.get("namespace", ""),
                                             columns=['namespace', 'hostname',
                                                      'ifname', 'state']) \
            .rename(columns={'state': 'ifState'})

        lldp_df = LldpObj(context=self.ctxt).get(
            namespace=kwargs.get("namespace", ""),
            columns=[
                'namespace', 'hostname', 'ifname', 'peerHostname', 'peerIfname'
            ])

        # Get the dataframes we need for processing
        df['cif'] = df.apply(self._get_connect_if, axis=1)
        df['ifname'] = df['cif'].str.split('.').str[0]

        df = df.merge(if_df, left_on=['namespace', 'hostname', 'cif'],
                      right_on=['namespace', 'hostname', 'ifname'],
                      how='left') \
            .drop(columns=['timestamp_y', 'ifname_y']) \
            .rename(columns={'ifname_x': 'ifname'})

        # We split off at this point to avoid merging mess because of lack of
        # LLDP info
        df = df.merge(lldp_df, on=['namespace', 'hostname', 'ifname'], how='left') \
               .drop(columns=['timestamp']) \
               .rename(columns={'timestamp_x': 'timestamp'})
        # Some munging to handle subinterfaces
        df['xx'] = df['peerIfname'] + '.' + df['cif'].str.split('.').str[1]

        df['peerIfname'] = df['xx'].where(df['xx'].notnull(), df['peerIfname'])
        df.drop(columns=['xx'], inplace=True)

        df = df.merge(df, left_on=['namespace', 'hostname', 'cif'],
                      right_on=['namespace', 'peerHostname',
                                'peerIfname'], how='left') \
            .drop(columns=['peerIfname_y', 'timestamp_y', 'cif_y',
                           'ifState_y', 'reason_y', 'notifcnReason_y']) \
            .rename(columns={'timestamp_x': 'timestamp',
                             'cif_x': 'cif', 'ifState_x': 'ifState',
                             'reason_x': 'reason',
                             'notifcnReason_x': 'notifcnReason'})
        df['peer_y'] = df['peer_y'].astype(str) \
                                   .where(df['peer_y'].notnull(), '')
        df["assertReason"] = [[] for _ in range(len(df))]
        # Now all sessions with NaN in the oif column have no connected route
        df['assertReason'] += df.apply(
            lambda x: ["outgoing link down"]
            if x['cif'] and x['ifState'] != "up" else [],
            axis=1)

        df['assertReason'] += df.apply(lambda x: ["no route to peer"]
                                       if not len(x['cif']) else [],
                                       axis=1)

        df['assertReason'] += df.apply(self._check_afi_safi, axis=1)

        df['assertReason'] += df.apply(
            lambda x: ["asn mismatch"]
            if x['peer_y'] and ((x["asn_x"] != x["peerAsn_y"]) or
                                (x['asn_y'] != x['peerAsn_x'])) else [],
            axis=1)

        df['assertReason'] += df.apply(
            lambda x: [f"{x['reason']}:{x['notifcnReason']}"]
            if (x['reason'] and x['state_x'] != 'Established') else [],
            axis=1)

        df['assert'] = df.apply(lambda x: 'pass'
                                if not len(x.assertReason) else 'fail',
                                axis=1)
        return (df[[
            'namespace', 'hostname_x', 'vrf_x', 'peer_x', 'asn_x', 'peerAsn_x',
            'state_x', 'peerHostname_x', 'vrf_y', 'peer_y', 'asn_y',
            'peerAsn_y', 'assert', 'assertReason', 'timestamp'
        ]].rename(columns={
            'hostname_x': 'hostname',
            'vrf_x': 'vrf',
            'peer_x': 'peer',
            'asn_x': 'asn',
            'peerAsn_x': 'peerAsn',
            'state_x': 'state',
            'peerHostname_x': 'hostnamePeer',
            'vrf_y': 'vrfPeer',
            'peer_y': 'peerPeer',
            'asn_y': 'asnPeer',
            'peerAsn_y': 'peerAsnPeer'
        },
                  copy=False).explode(column="assertReason").astype(
                      {
                          'asn': 'Int64',
                          'peerAsn': 'Int64',
                          'asnPeer': 'Int64',
                          'peerAsnPeer': 'Int64'
                      },
                      copy=False).fillna({'assertReason': '-'}))
Ejemplo n.º 4
0
    def aver(self, **kwargs) -> pd.DataFrame:
        """Assert for EVPN Data"""

        assert_cols = [
            "namespace", "hostname", "vni", "remoteVtepList", "vrf", "type",
            "srcVtepIp", "state", "l2VniList", "ifname"
        ]

        kwargs.pop("columns", None)  # Loose whatever's passed

        df = self.get(columns=assert_cols, **kwargs)
        if df.empty:
            return pd.DataFrame(columns=assert_cols)

        # Gather the unique set of VTEPs per VNI
        vteps_df = df.explode(column='remoteVtepList') \
            .dropna(how='any') \
            .groupby(by=['vni', 'type'])['remoteVtepList'] \
            .aggregate(lambda x: x.unique().tolist()) \
            .reset_index() \
            .dropna(how='any') \
            .rename(columns={'remoteVtepList': 'allVteps'})

        df = df.merge(vteps_df)

        df["assertReason"] = [[] for _ in range(len(df))]

        # Every VTEP has info about every other VTEP for a given VNI
        df["assertReason"] += df.apply(self._all_vteps_present, axis=1)

        # Every VTEP is reachable
        df["assertReason"] += df.apply(self._is_vtep_reachable, axis=1)

        # State is up
        df["assertReason"] += df.apply(
            lambda x: ['interface is down']
            if x['type'] == "L2" and x['state'] != "up" else [],
            axis=1)

        devices = df["hostname"].unique().tolist()
        ifdf = IfObj(context=self.ctxt) \
            .get(namespace=kwargs.get("namespace", ""), hostname=devices)

        df = df.merge(
            ifdf[['namespace', 'hostname', 'ifname', 'master', 'vlan']],
            on=['namespace', 'hostname', 'ifname'],
            how='left')

        # vxlan interfaces, if defined, for every VNI is part of bridge
        # We ensure this is true artificially for NXOS, ignored for JunOS.
        df["assertReason"] += df.apply(
            lambda x: ['vni not in bridge'] if (x['type'] == "L2" and x[
                'ifname'] != '-' and x['master'] != "bridge") else [],
            axis=1)

        # mac_df = MacsObj(context=self.ctxt) \
        #     .get(namespace=kwargs.get("namespace", ""),
        #          macaddr=["00:00:00:00:00:00"])

        # # Assert that we have HER for every remote VTEP
        # df['assertReason'] += df.apply(self._is_her_good,
        #                                args=(mac_df, ), axis=1)

        # Fill out the assert column
        df['assert'] = df.apply(lambda x: 'pass'
                                if not len(x.assertReason) else 'fail',
                                axis=1)

        return df[['namespace', 'hostname', 'vni', 'type',
                   'assertReason', 'assert', 'timestamp']].explode(column='assertReason') \
            .fillna({'assertReason': '-'})