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
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
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
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
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