Example #1
0
def get_port_stats():
    """
    Collect stats on every port in the datapath.
    In every sampling iteration, these stats are stored
    in corresponding sampling slots.

    Raises
    ------
    OsCommandExc
        if the given OS command did not succeed for some reason.
    ObjModleExc
        if state of ports in switch differ.
    """

    nlog = Context.nlog

    # retrieve required data from the vswitch.
    cmd = "ovs-appctl dpctl/show -s"
    data = util.exec_host_command(cmd)
    if not data:
        raise OsCommandExc("unable to collect data")

    # current state of ports
    cur_port_l = sorted(Context.port_to_cls.keys())

    # current port object to be used in every line under parse.
    port = None

    for line in data.splitlines():
        if re.match(r'\s.*port\s(\d+):\s([A-Za-z0-9_-]+) *', line):
            # In below matching line, we retrieve port id and name.
            linesre = re.search(r'\s.*port\s(\d+):\s([A-Za-z0-9_-]+) *', line)
            (pid, pname) = linesre.groups()
            Context.port_to_id[pname] = int(pid)

            # If in mid of sampling, we should have port_to_cls having
            # entry for this port name.
            if pname in Context.port_to_cls:
                port = Context.port_to_cls[pname]
                assert (port.id == pid)

                # Store following stats in new sampling slot.
                port.cyc_idx = (port.cyc_idx + 1) % config.ncd_samples_max
                nlog.debug("port %s in iteration %d" %
                           (port.name, port.cyc_idx))
            else:
                # create new entry in port_to_cls for this port.
                port = make_dataif_port(pname)
                port.id = pid
                nlog.debug("added port %s stats.." % pname)

        elif re.match(r'\s.*RX packets:(\d+) .*? dropped:(\d+) *', line):
            # From other lines, we retrieve stats of the port.
            linesre = re.search(r'\s.*RX packets:(\d+) .*? dropped:(\d+) *',
                                line)
            (
                rx,
                drop,
            ) = linesre.groups()
            port.rx_cyc[port.cyc_idx] = int(rx)
            port.rx_drop_cyc[port.cyc_idx] = int(drop)

        elif re.match(r'\s.*TX packets:(\d+) .*? dropped:(\d+) *', line):
            # From other lines, we retrieve stats of the port.
            linesre = re.search(r'\s.*TX packets:(\d+) .*? dropped:(\d+) *',
                                line)
            (
                tx,
                drop,
            ) = linesre.groups()
            port.tx_cyc[port.cyc_idx] = int(tx)
            port.tx_drop_cyc[port.cyc_idx] = int(drop)

    # new state of ports.
    new_port_l = sorted(Context.port_to_cls.keys())

    # skip modelling this object if states differ.
    if len(cur_port_l) > 0 and cur_port_l != new_port_l:
        raise ObjModelExc("ports count differ")

    # current port object to be used in every line under parse.
    return None
Example #2
0
def get_pmd_rxqs(pmd_map):
    """
    Collect info on how rxq is pinned with pmd, from the vswitch.

    Parameters
    ----------
    pmd_map : dict
        mapping of pmd id and its Dataif_Pmd object.

    Raises
    ------
    OsCommandExc
        if the given OS command did not succeed for some reason.
    ObjConsistencyExc
        if state of pmds in ncd differ.
    ObjParseExc
        if unable to retrieve info from switch.
    ObjModleExc
        if state of pmds in switch differ.
    """

    nlog = Context.nlog

    # retrieve required data from the vswitch.
    cmd = "ovs-appctl dpif-netdev/pmd-rxq-show"
    data = util.exec_host_command(cmd)
    if not data:
        raise OsCommandExc("unable to collect data")

    # current state of pmds
    cur_pmd_l = sorted(pmd_map.keys())

    # sname and sval stores parsed string's key and value.
    sname, sval = None, None
    # current pmd object to be used in every line under parse.
    pmd = None

    for line in data.splitlines():
        if line.startswith('pmd thread'):
            # In below matching line, we retrieve core id (aka pmd id)
            # and core id.
            linesre = re.search(r'pmd thread numa_id (\d+) core_id (\d+):',
                                line)
            numa_id = int(linesre.groups()[0])
            core_id = int(linesre.groups()[1])
            if core_id not in pmd_map:
                raise ObjConsistencyExc(
                    "trying to add new pmd %d in mid of ncd!.. aborting! ")
            pmd = pmd_map[core_id]
            assert (pmd.numa_id == numa_id)
            nlog.debug("pmd %d in iteration %d" % (pmd.id, pmd.cyc_idx))

        elif re.match(r'\s.*port: .*', line):
            # From this line, we retrieve cpu usage of rxq.
            linesre = re.search(
                r'\s.*port:\s([A-Za-z0-9_-]+)\s*'
                r'queue-id:\s*(\d+)\s*(?=\((enabled)\))?.*'
                r'pmd usage:\s*(\d+|NOT AVAIL)\s*?', line)

            pname = linesre.groups()[0]
            qid = int(linesre.groups()[1])
            enabled_flag = linesre.groups()[2]
            try:
                qcpu = int(linesre.groups()[3])
            except ValueError:
                qcpu = linesre.groups()[3]
                if (qcpu == 'NOT AVAIL'):
                    raise ObjParseExc("pmd usage unavailable for now")
                else:
                    raise ObjParseExc("error parsing line %s" % line)

            # get the Dataif_Port owning this rxq.
            port = pmd.find_port_by_name(pname)
            if not port:
                port = pmd.add_port(pname)

            # update port attributes now.
            port.id = Context.port_to_id[pname]
            port.numa_id = pmd.numa_id

            port_cls = Context.port_to_cls[pname]
            port_cls.rebalance = True

            # check whether this rxq was being rebalanced.
            if qid in port.rxq_rebalanced:
                raise ObjConsistencyExc(
                    "stale %s object found while parsing rxq in pmd ..")
            else:
                # skip updating zero index as rxq counters works only
                # with the differences between pmd stats in every
                # sampling slot.
                if pmd.cyc_idx == 0:
                    continue

                # port not in rebalancing state, so update rxq for its
                # cpu cycles consumed by it.
                rxq = (port.find_rxq_by_id(qid) or port.add_rxq(qid))
                rxq.pmd = pmd
                rxq.port = port
                cur_idx = pmd.cyc_idx
                prev_idx = (cur_idx - 1) % config.ncd_samples_max
                rx_diff = pmd.rx_cyc[cur_idx] - pmd.rx_cyc[prev_idx]
                pcpu_diff = pmd.proc_cpu_cyc[cur_idx] - pmd.proc_cpu_cyc[
                    prev_idx]
                icpu_diff = pmd.idle_cpu_cyc[cur_idx] - pmd.idle_cpu_cyc[
                    prev_idx]
                cpu_diff = pcpu_diff + icpu_diff
                qcpu_diff = int((qcpu * cpu_diff) / 100)
                qrx_diff = int((qcpu * rx_diff) / 100)

            rxq.cpu_cyc[pmd.cyc_idx] = qcpu_diff
            rxq.rx_cyc[pmd.cyc_idx] = qrx_diff
            if (enabled_flag == "enabled"):
                rxq.enabled = True
            else:
                rxq.enabled = False
        else:
            # From other line, we retrieve isolated flag.
            (sname, sval) = line.split(":")
            sname = re.sub(r"^\s+", "", sname)
            assert (sname == 'isolated ')
            pmd.isolated = {'true': True, 'false': False}[sval[1:]]

    # new state of pmds.
    new_pmd_l = sorted(pmd_map.keys())

    # skip modelling this object if states differ.
    if len(cur_pmd_l) > 0 and cur_pmd_l != new_pmd_l:
        raise ObjModelExc("pmds count differ")

    return pmd_map
Example #3
0
def get_interface_stats():
    """
    Collect retry stats on every applicable port in the datapath.
    In every sampling iteration, these stats are stored
    in corresponding sampling slots.

    Raises
    ------
    OsCommandExc
        if the given OS command did not succeed for some reason.
    ObjModleExc
        if state of ports in switch differ.
    """

    nlog = Context.nlog

    # retrieve required data from the vswitch.
    cmd = "ovs-vsctl list interface"
    data = util.exec_host_command(cmd)
    if not data:
        raise OsCommandExc("unable to collect data")

    # current state of ports
    cur_port_l = sorted(Context.port_to_cls.keys())

    # current port object to be used in every line under parse.
    port = None

    for line in data.splitlines():
        if re.match(r'\s*name\s.*:\s"*([A-Za-z0-9_-]+)"*', line):
            # In below matching line, we retrieve port id and name.
            linesre = re.search(r'\s*name\s.*:\s"*([A-Za-z0-9_-]+)"*', line)
            (pname, ) = linesre.groups()

            # If in mid of sampling, we should have port_to_cls having
            # entry for this port name.
            if pname in Context.port_to_cls:
                port = Context.port_to_cls[pname]

                nlog.debug("port %s in iteration %d" %
                           (port.name, port.cyc_idx))

        elif re.match(r'\s*type\s.*:\s([a-z]+)', line):
            if not port:
                continue

            # From other lines, we retrieve stats of the port.
            linesre = re.search(r'\s*type\s.*:\s([a-z]+)', line)
            (type, ) = linesre.groups()
            port.type = type

            port = None

        elif re.match(r'\s*statistics\s.*:\s{(.*)}', line):
            if not port:
                continue

            # From other lines, we retrieve stats of the port.
            linesre = re.search(r'\s*statistics\s.*:\s{(.*)}', line)
            (sval, ) = linesre.groups()
            dval = {
                sub.split("=")[0]: sub.split("=")[1]
                for sub in sval.split(", ")
            }

            if 'tx_retries' in dval:
                port.tx_retry_cyc[port.cyc_idx] = int(dval['tx_retries'])

    # new state of ports.
    new_port_l = sorted(Context.port_to_cls.keys())

    # skip modelling this object if states differ.
    if len(cur_port_l) > 0 and cur_port_l != new_port_l:
        raise ObjModelExc("ports count differ")

    return None
Example #4
0
def get_pmd_stats(pmd_map):
    """
    Collect stats on every pmd running in the system and update
    pmd_map. In every sampling iteration, these stats are stored
    in corresponding sampling slots.

    Parameters
    ----------
    pmd_map : dict
        mapping of pmd id and its Dataif_Pmd object.

    Raises
    ------
    OsCommandExc
        if the given OS command did not succeed for some reason.
    ObjConsistencyExc
        if state of pmds in ncd differ.
    ObjModleExc
        if state of pmds in switch differ.
    """

    nlog = Context.nlog

    # retrieve required data from the vswitch.
    cmd = "ovs-appctl dpif-netdev/pmd-stats-show"
    data = util.exec_host_command(cmd)
    if not data:
        raise OsCommandExc("unable to collect data")

    # current state of pmds
    cur_pmd_l = sorted(pmd_map.keys())

    # sname and sval stores parsed string's key and value.
    sname, sval = None, None
    # current pmd object to be used in every line under parse.
    pmd = None

    for line in data.splitlines():
        if line.startswith("pmd thread"):
            # In below matching line, we retrieve core id (aka pmd id)
            # and core id.
            linesre = re.search(r'pmd thread numa_id (\d+) core_id (\d+):',
                                line)
            numa_id = int(linesre.groups()[0])
            core_id = int(linesre.groups()[1])

            # If in mid of sampling, we should have pmd_map having
            # entry for this core id.
            if core_id in pmd_map:
                pmd = pmd_map[core_id]

                # Check to ensure we are good to go as local should
                # always be used.
                assert (pmd.numa_id == numa_id)

                # Store following stats in new sampling slot.
                pmd.cyc_idx = (pmd.cyc_idx + 1) % config.ncd_samples_max
                nlog.debug("pmd %d in iteration %d" % (pmd.id, pmd.cyc_idx))
            else:
                # Very first sampling for each pmd occur in this
                # clause. Just ensure, no new pmd is added from system
                # reconfiguration.
                if len(pmd_map) != 0 and not pmd:
                    raise ObjConsistencyExc(
                        "trying to add new pmd %d in mid of ncd!.. aborting! ")

                # create new entry in pmd_map for this pmd.
                pmd = Dataif_Pmd(core_id)
                pmd_map[pmd.id] = pmd
                nlog.debug("added pmd %s stats.." % pmd.id)

                # numa id of pmd is of core's.
                pmd.numa_id = numa_id
        elif line.startswith("main thread"):
            # end of pmd stats
            break
        else:
            # From other lines, we retrieve stats of the pmd.
            (sname, sval) = line.split(":")
            sname = re.sub(r"^\s+", "", sname)
            sval = sval[1:].split()
            if sname == "packets received":
                pmd.rx_cyc[pmd.cyc_idx] = int(sval[0])
            elif sname == "idle cycles":
                pmd.idle_cpu_cyc[pmd.cyc_idx] = int(sval[0])
            elif sname == "processing cycles":
                pmd.proc_cpu_cyc[pmd.cyc_idx] = int(sval[0])

    # new state of pmds.
    new_pmd_l = sorted(pmd_map.keys())

    # skip modelling this object if states differ.
    if len(cur_pmd_l) > 0 and cur_pmd_l != new_pmd_l:
        raise ObjModelExc("pmds count differ")

    return pmd_map
Example #5
0
def get_pmd_rxqs(pmd_map):
    """
    Collect info on how rxq is pinned with pmd, from the vswitch.

    Parameters
    ----------
    pmd_map : dict
        mapping of pmd id and its Dataif_Pmd object.

    Raises
    ------
    OsCommandExc
        if the given OS command did not succeed for some reason.
    ObjConsistencyExc
        if state of pmds in ncd differ.
    ObjParseExc
        if unable to retrieve info from switch.
    ObjModleExc
        if state of pmds in switch differ.
    """

    nlog = Context.nlog

    # retrieve required data from the vswitch.
    cmd = "ovs-appctl dpif-netdev/pmd-rxq-show"
    data = util.exec_host_command(cmd)
    if not data:
        raise OsCommandExc("unable to collect data")

    # current state of pmds
    cur_pmd_l = sorted(pmd_map.keys())

    # sname and sval stores parsed string's key and value.
    sname, sval = None, None
    # current pmd object to be used in every line under parse.
    pmd = None

    for line in data.splitlines():
        if line.startswith('pmd thread'):
            # In below matching line, we retrieve core id (aka pmd id)
            # and core id.
            linesre = re.search(r'pmd thread numa_id (\d+) core_id (\d+):',
                                line)
            numa_id = int(linesre.groups()[0])
            core_id = int(linesre.groups()[1])
            if core_id not in pmd_map:
                raise ObjConsistencyExc(
                    "trying to add new pmd %d in mid of ncd!.. aborting! ")
            pmd = pmd_map[core_id]
            assert(pmd.numa_id == numa_id)
            nlog.debug("pmd %d in iteration %d" % (pmd.id, pmd.cyc_idx))

        elif re.match(r'\s.*port: .*', line):
            # From this line, we retrieve cpu usage of rxq.
            linesre = re.search(r'\s.*port:\s([A-Za-z0-9_-]+)\s*'
                                r'queue-id:\s*(\d+)\s*'
                                r'pmd usage:\s*(\d+|NOT AVAIL)\s*?',
                                line)

            pname = linesre.groups()[0]
            qid = int(linesre.groups()[1])
            try:
                qcpu = int(linesre.groups()[2])
            except ValueError:
                qcpu = linesre.groups()[2]
                if (qcpu == 'NOT AVAIL'):
                    raise ObjParseExc("pmd usage unavailable for now")
                else:
                    raise ObjParseExc("error parsing line %s" % line)

            # get the Dataif_Port owning this rxq.
            port = pmd.find_port_by_name(pname)
            if not port:
                port = pmd.add_port(pname)

            # update port attributes now.
            port.id = Context.port_to_id[pname]
            port.numa_id = pmd.numa_id

            port_cls = Context.port_to_cls[pname]
            port_cls.rebalance = True

            # check whether this rxq was being rebalanced.
            if qid in port.rxq_rebalanced:
                # In dry-run, we need to update cpu cycles consumed by
                # this rxq (through current pmd), into the processing
                # cycles of the rebalancing pmd. Then the load of the
                # rebalancing pmd could be estimated appropriately.
                reb_pmd_id = port.rxq_rebalanced[qid]
                reb_pmd = pmd_map[reb_pmd_id]
                reb_port = reb_pmd.find_port_by_name(port.name)
                rxq = reb_port.find_rxq_by_id(qid)
                # qcpu is in percentage in this data, so we convert it
                # into actual cycles using processing cycles that this
                # pmd consumed.
                # qrx is approximate count of packets that this rxq
                # received.
                cur_idx = pmd.cyc_idx
                prev_idx = (cur_idx - 1) % config.ncd_samples_max
                rx_diff = pmd.rx_cyc[cur_idx] - pmd.rx_cyc[prev_idx]
                cpu_diff = pmd.proc_cpu_cyc[
                    cur_idx] - pmd.proc_cpu_cyc[prev_idx]
                qrx = (qcpu * rx_diff) / 100
                qcpu = (qcpu * cpu_diff) / 100
                # update rebalancing pmd for cpu cycles and rx count.
                reb_pmd.proc_cpu_cyc[cur_idx] += qcpu
                reb_pmd.idle_cpu_cyc[cur_idx] -= qcpu
                reb_pmd.rx_cyc[pmd.cyc_idx] += qrx
                # update current pmd for cpu cycles and rx count.
                pmd.proc_cpu_cyc[pmd.cyc_idx] -= qcpu
                pmd.idle_cpu_cyc[pmd.cyc_idx] += qcpu
                pmd.rx_cyc[pmd.cyc_idx] -= qrx
            else:
                # port not in rebalancing state, so update rxq for its
                # cpu cycles consumed by it.
                rxq = (port.find_rxq_by_id(qid) or port.add_rxq(qid))
                rxq.pmd = pmd
                rxq.port = port
                cur_idx = pmd.cyc_idx
                prev_idx = (cur_idx - 1) % config.ncd_samples_max
                rx_diff = pmd.rx_cyc[cur_idx] - pmd.rx_cyc[prev_idx]
                cpu_diff = pmd.proc_cpu_cyc[
                    cur_idx] - pmd.proc_cpu_cyc[prev_idx]
                qcpu = (qcpu * cpu_diff) / 100
                qrx = (qcpu * rx_diff) / 100

            rxq.cpu_cyc[pmd.cyc_idx] = qcpu
        else:
            # From other line, we retrieve isolated flag.
            (sname, sval) = line.split(":")
            sname = re.sub("^\s+", "", sname)
            assert(sname == 'isolated ')
            pmd.isolated = {'true': True, 'false': False}[sval[1:]]

    # new state of pmds.
    new_pmd_l = sorted(pmd_map.keys())

    # skip modelling this object if states differ.
    if len(cur_pmd_l) > 0 and cur_pmd_l != new_pmd_l:
        raise ObjModelExc("pmds count differ")

    return pmd_map