def delete(self, *args, **kwargs): """Delete slice. Args: tenant_id: network name of a tenant dscp: the slice DSCP Example URLs: DELETE /api/v1/tenants/52313ecb-9d00-4b7d-b873-b55d3d9ada26/slice/ 0x42 """ try: if len(args) != 2: raise ValueError("Invalid url") tenant_id = UUID(args[0]) tenant = RUNTIME.tenants[tenant_id] dscp = DSCP(args[1]) if dscp == DSCP("0x00"): raise ValueError("Invalid Slice") tenant.del_slice(dscp) except ValueError as ex: self.send_error(400, message=ex) except KeyError as ex: self.send_error(404, message=ex) self.set_status(204, None)
def change_slices_configuration(self): # For each slice configuration for slice_descriptor_dscp in self.slices_descriptor: crr_dscp = DSCP(slice_descriptor_dscp) if crr_dscp in self.tenant.slices: # Check if values are the same if not self.same_slice_values( self.slices_descriptor[slice_descriptor_dscp], self. tenant.slices[crr_dscp].wifi['static-properties']): # Setting a new slice to replace the values from the existing one new_slice = format_slice_add_request( slice_descriptor_dscp, self.slices_descriptor[slice_descriptor_dscp] ['quantum'], self.slices_descriptor[slice_descriptor_dscp] ['amsdu_aggregation'], self.slices_descriptor[slice_descriptor_dscp] ['scheduler']) self.tenant.set_slice(DSCP(slice_descriptor_dscp), new_slice) else: # Add new ones new_slice = format_slice_add_request( slice_descriptor_dscp, self.slices_descriptor[slice_descriptor_dscp]['quantum'], self.slices_descriptor[slice_descriptor_dscp] ['amsdu_aggregation'], self.slices_descriptor[slice_descriptor_dscp]['scheduler']) self.tenant.add_slice(DSCP(slice_descriptor_dscp), new_slice)
def __init__(self, dscp, tenant, descriptor): self.log = empower.logger.get_logger() self.dscp = DSCP(dscp) self.tenant = tenant self.wifi = { 'static-properties': { 'amsdu_aggregation': False, 'quantum': 12000 }, 'wtps': {} } if 'wifi' in descriptor: self.__parse_wifi_descriptor(descriptor) self.lte = { 'static-properties': { 'sched_id': ROUND_ROBIN_UE_SCHED, 'rbgs': 6 }, 'vbses': {} } if 'lte' in descriptor: self.__parse_lte_descriptor(descriptor)
def reset_all(self, crr_wtp_addr): for dscp in self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices']: current_quantum = self.tenant.slices[DSCP( dscp)].wifi['static-properties']['quantum'] if self.__maximum_quantum != current_quantum: self.send_slice_config_to_wtp( dscp=dscp, new_quantum=self.__maximum_quantum)
def send_del_ran_mac_slice_request(self, cell, plmn_id, dscp): """Send an DEL_SLICE message. """ tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) # check if tenant is valid if not tenant: self.log.info("Unknown tenant %s", plmn_id) return # check if slice is valid if dscp in tenant.slices: # UEs already present in the slice must be moved to the default slice # before deleting the current slice for ue in list(RUNTIME.ues.values()): if self.vbs == ue.vbs and dscp == ue.slice: ue.slice = DSCP("0x00") else: self.log.warning("DSCP %s not found. Removing slice.", dscp) # Then proceed to remove the current slice msg = Container(plmn_id=plmn_id.to_raw(), dscp=dscp.to_raw(), padding=b'\x00\x00\x00', length=REM_RAN_MAC_SLICE_REQUEST.sizeof()) self.send_message(msg, E_TYPE_SINGLE, EP_ACT_RAN_MAC_SLICE, REM_RAN_MAC_SLICE_REQUEST, opcode=EP_OPERATION_REM, cellid=cell.pci)
def post(self, *args, **kwargs): """Add traffic rule. Args: tenant_id: network name of a tenant Example URLs: POST /api/v1/tenants/52313ecb-9d00-4b7d-b873-b55d3d9ada26/slices { "version" : 1.0, "dscp" : 0x40, "label" : "video traffic (high priority)", "match" : "dl_vlan=100,tp_dst=80", } """ try: if len(args) != 1: raise ValueError("Invalid url") request = tornado.escape.json_decode(self.request.body) if "version" not in request: raise ValueError("missing version element") if "dscp" not in request: raise ValueError("missing dscp element") if "label" not in request: raise ValueError("missing label element") if "match" not in request: raise ValueError("missing match element") tenant_id = UUID(args[0]) tenant = RUNTIME.tenants[tenant_id] dscp = DSCP(request["dscp"]) match = Match(request["match"]) if "priority" in request: tenant.add_traffic_rule(match, dscp, request["label"], request["priority"]) else: tenant.add_traffic_rule(match, dscp, request["label"]) url = "/api/v1/tenants/%s/trs/%s" % (tenant_id, match) self.set_header("Location", url) except TypeError as ex: self.send_error(400, message=ex) except ValueError as ex: self.send_error(400, message=ex) except KeyError as ex: self.send_error(404, message=ex) self.set_status(201, None)
def get(self, *args, **kwargs): """List slices. Args: tenant_id: network name of a tenant dscp: the slice DSCP Example URLs: GET /api/v1/tenants/52313ecb-9d00-4b7d-b873-b55d3d9ada26/slices GET /api/v1/tenants/52313ecb-9d00-4b7d-b873-b55d3d9ada26/slices/ \ 0x40 """ try: if len(args) < 1 or len(args) > 2: raise ValueError("Invalid url") tenant_id = UUID(args[0]) tenant = RUNTIME.tenants[tenant_id] if len(args) == 1: self.write_as_json(tenant.slices.values()) else: dscp = DSCP(args[1]) self.write_as_json(tenant.slices[dscp]) except ValueError as ex: self.send_error(400, message=ex) except KeyError as ex: self.send_error(404, message=ex)
def __init__(self, ue_id, rnti, imsi, tmsi, cell, tenant): # read only parameters self.ue_id = ue_id self.tenant = tenant self.imsi = imsi self.tmsi = tmsi # set on different situations, e.g. after an handover self.rnti = rnti # the current cell self._cell = cell # current slice to which the UE is subscribed self._slice = DSCP("0x00") # the ue state self._state = PROCESS_RUNNING # target cell to be used for handover self.target_cell = None # migration sats self._timer = None # the ue measurements, this is set by a ue_measurement module self.ue_measurements = {} # logger :) self.log = empower.logger.get_logger()
def slice(self, slice_id): """Assign a slice to the UE. Accepts as input an Slice ID Args: slice_id: An Slice ID """ slice_id = DSCP(slice_id) if slice_id not in self.tenant.slices: raise ValueError("Slice %u not found" % slice_id) self._slice = slice_id for slc in self.tenant.slices.values(): rntis = list() for ue in self.tenant.ues.values(): if self.vbs != ue.vbs: break if slc.dscp == ue.slice: rntis.append(ue.rnti) for cell in self.vbs.cells.values(): self.vbs.connection.\ send_add_set_ran_mac_slice_request(cell, slc, EP_OPERATION_SET, rntis)
def dscp(self, value): """Set DSCP""" try: self.__dscp = DSCP(value) except: raise ValueError("Invalid value for dscp!") self.__dscp = None
def __init__(self, dscp, tenant, descriptor): self.log = empower.logger.get_logger() self.dscp = DSCP(dscp) self.tenant = tenant self.wifi = { 'static-properties': { 'amsdu_aggregation': False, 'quantum': 12000, 'scheduler': WIFI_SLICE_SCHED['ROUND_ROBIN'] }, 'wtps': {} } if 'wifi' in descriptor: self.__parse_wifi_descriptor(descriptor) self.lte = { 'static-properties': { 'sched_id': LTE_SLICE_SCHED['ROUND_ROBIN_UE_SCHED'], 'rbgs': 6, 'window': 1, 'period': 1 }, 'vbses': {} } if 'lte' in descriptor: self.__parse_lte_descriptor(descriptor)
def _handle_status_slice(self, wtp, status): """Handle an incoming STATUS_SLICE message. Args: status, a STATUS_SLICE message Returns: None """ dscp = DSCP(status.dscp) ssid = SSID(status.ssid) tenant = RUNTIME.load_tenant(ssid) if not tenant: self.log.info("Slice status from unknown tenant %s", ssid) return # Check if block is valid valid = wtp.get_block(status.hwaddr, status.channel, status.band) if not valid: self.log.warning("No valid intersection found.") return # check if slice is valid if dscp not in tenant.slices: self.log.warning("DSCP %s not found. Removing slice.", dscp) self.send_del_slice(valid[0], ssid, dscp) return slc = tenant.slices[dscp] if slc.wifi['static-properties']['quantum'] != status.quantum: if wtp.addr not in slc.wifi['wtps']: slc.wifi['wtps'][wtp.addr] = {'static-properties': {}} slc.wifi['wtps'][wtp.addr]['static-properties']['quantum'] = \ status.quantum if slc.wifi['static-properties']['amsdu_aggregation'] != \ bool(status.flags.amsdu_aggregation): if wtp.addr not in slc.wifi['wtps']: slc.wifi['wtps'][wtp.addr] = {'static-properties': {}} slc.wifi['wtps'][wtp.addr]['static-properties'] \ ['amsdu_aggregation'] = bool(status.flags.amsdu_aggregation) if slc.wifi['static-properties']['scheduler'] != status.scheduler: if wtp.addr not in slc.wifi['wtps']: slc.wifi['wtps'][wtp.addr] = {'static-properties': {}} slc.wifi['wtps'][wtp.addr]['static-properties']['scheduler'] = \ status.scheduler self.log.info("Slice %s updated", slc)
def _handle_ran_mac_slice_response(self, vbs, hdr, event, msg): """Handle an incoming RAN_MAC_SLICE message. Args: status, a RAN_MAC_SLICE messagge Returns: None """ dscp = DSCP(msg.dscp) plmn_id = PLMNID(msg.plmn_id) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) # check if tenant is valid if not tenant: self.log.info("Unknown tenant %s", plmn_id) return # check if slice is valid if dscp not in tenant.slices: self.log.warning("DSCP %s not found. Removing slice.", dscp) # self.send_del_slice(valid[0], ssid, dscp) return slc = tenant.slices[dscp] if vbs.addr not in slc.lte['vbses']: slc.lte['vbses'][vbs.addr] = \ {'static-properties': {}, 'runtime-properties': {}, 'cells': {}} if hdr.cellid not in slc.lte['vbses'][vbs.addr]['cells']: slc.lte['vbses'][vbs.addr]['cells'][hdr.cellid] = {} for raw_cap in msg.options: if raw_cap.type not in RAN_MAC_SLICE_TYPES: self.log.warning("Unknown options %u", raw_cap.type) continue prop = RAN_MAC_SLICE_TYPES[raw_cap.type].name option = RAN_MAC_SLICE_TYPES[raw_cap.type].parse(raw_cap.data) self.log.warning("Processing options %s", prop) if raw_cap.type == EP_RAN_MAC_SLICE_SCHED_ID: slc.lte['vbses'][vbs.addr] \ ['static-properties']['sched_id'] = option.sched_id if raw_cap.type == EP_RAN_MAC_SLICE_RBGS: slc.lte['vbses'][vbs.addr]['static-properties']['rbgs'] = \ option.rbgs if raw_cap.type == EP_RAN_MAC_SLICE_RNTI_LIST: slc.lte['vbses'][vbs.addr]['runtime-properties']['rntis'] = \ option.rntis self.log.info("Slice %s updated", slc)
def __init__(self): super().__init__() # parameters self._block = None self._dscp = DSCP() # data structures self.slice_stats = {}
def send_slice_config_to_wtp(self): if self.__dscp is not None: if self.__quantum is None: # get current quantum self.__quantum = self.tenant.slices[DSCP( self.__dscp)].wifi['static-properties']['quantum'] if self.__amsdu is None: self.__amsdu = self.tenant.slices[DSCP( self.__dscp )].wifi['static-properties']['amsdu_aggregation'] if self.__wtp_addr is None: new_slice = format_slice_config_request( tenant_id=self.tenant_id, dscp=self.__dscp, default_quantum=self.__quantum, default_amsdu=self.__amsdu, default_scheduler=self.__scheduler, wtps=None) else: wtp = [{ 'addr': self.__wtp_addr, 'quantum': self.__quantum, 'amsdu_aggregation': self.__amsdu, 'scheduler': self.__scheduler, }] new_slice = format_slice_config_request( tenant_id=self.tenant_id, dscp=self.__dscp, default_quantum=self.tenant.slices[DSCP( self.__dscp)].wifi['static-properties']['quantum'], default_amsdu=self.tenant.slices[DSCP( self.__dscp)].wifi['static-properties'] ['amsdu_aggregation'], default_scheduler=self.tenant.slices[DSCP( self.__dscp)].wifi['static-properties']['scheduler'], wtps=wtp) self.log.debug("Sending new slice configurations to APs") self.tenant.set_slice(self.__dscp, new_slice) self.reset_slice_parameters() else: self.log.debug( "DSCP or quantum is not set, aborting configuration!")
def add_tenant(self, owner, desc, tenant_name, bssid_type, tenant_id=None, plmn_id=None): """Create new Tenant.""" if tenant_id in self.tenants: raise ValueError("Tenant %s exists" % tenant_id) plmn_ids = [tenant.plmn_id for tenant in self.tenants.values()] if plmn_id and plmn_id in plmn_ids: raise ValueError("PLMN ID %s exists" % plmn_id) if bssid_type not in T_TYPES: raise ValueError("Invalid bssid_type %s" % bssid_type) session = Session() if tenant_id: request = TblTenant(tenant_id=tenant_id, tenant_name=tenant_name, owner=owner, desc=desc, bssid_type=bssid_type, plmn_id=plmn_id) else: request = TblTenant(owner=owner, tenant_name=tenant_name, desc=desc, bssid_type=bssid_type, plmn_id=plmn_id) session.add(request) session.commit() self.tenants[request.tenant_id] = \ Tenant(request.tenant_id, request.tenant_name, self.accounts[owner].username, desc, request.bssid_type, request.plmn_id) # create default queue dscp = DSCP() descriptor = {} self.tenants[request.tenant_id].add_slice(dscp, descriptor) return request.tenant_id
def post(self, *args, **kwargs): """Add a new slice. Check Slice object documentation for descriptors examples. Args: tenant_id: network name of a tenant dscp: the slice DSCP (optional) Example URLs: POST /api/v1/tenants/52313ecb-9d00-4b7d-b873-b55d3d9ada26/slices """ try: if len(args) != 1: raise ValueError("Invalid url") request = tornado.escape.json_decode(self.request.body) if "version" not in request: raise ValueError("missing version element") if "dscp" not in request: raise ValueError("missing dscp element") tenant_id = UUID(args[0]) tenant = RUNTIME.tenants[tenant_id] dscp = DSCP(request["dscp"]) if dscp in tenant.slices: raise ValueError("slice already registered in this tenant") tenant.add_slice(dscp, request) url = "/api/v1/tenants/%s/slices/%s" % (tenant_id, dscp) self.set_header("Location", url) except TypeError as ex: self.send_error(400, message=ex) except ValueError as ex: self.send_error(400, message=ex) except KeyError as ex: self.send_error(404, message=ex) self.set_status(201, None)
def reconfigure(self, factor, crr_wtp_addr): for be_dscp in self.__active_flows_handler['be_slices']: if be_dscp in self.__slice_stats_handler['wtps'][crr_wtp_addr][ 'slices']: slice = self.__slice_stats_handler['wtps'][crr_wtp_addr][ 'slices'][be_dscp] # only if the slice active... if slice['tx_bytes'] > 0: current_quantum = self.tenant.slices[DSCP( be_dscp)].wifi['static-properties']['quantum'] adapted_quantum = int(current_quantum * factor) if adapted_quantum > self.__maximum_quantum: adapted_quantum = self.__maximum_quantum if adapted_quantum < self.__minimum_quantum: adapted_quantum = self.__minimum_quantum if adapted_quantum != current_quantum: self.send_slice_config_to_wtp( dscp=be_dscp, new_quantum=adapted_quantum)
def send_ran_mac_slice_request(self, cell_id, plmn_id=PLMNID(), dscp=DSCP()): """Send a STATUS_SLICE_REQUEST message. Args: None Returns: None """ msg = Container(length=RAN_MAC_SLICE_REQUEST.sizeof(), plmn_id=plmn_id.to_raw(), dscp=dscp.to_raw(), padding=b'\x00\x00\x00') self.send_message(msg, E_TYPE_SINGLE, EP_ACT_RAN_MAC_SLICE, RAN_MAC_SLICE_REQUEST, cellid=cell_id)
def put(self, *args, **kwargs): """Modify slice. Check Slice object documentation for descriptors examples. Args: tenant_id: network name of a tenant dscp: the slice DSCP (optional) Example URLs: PUT /api/v1/tenants/52313ecb-9d00-4b7d-b873-b55d3d9ada26/slices/ 0x42 """ try: if len(args) != 2: raise ValueError("Invalid url") request = tornado.escape.json_decode(self.request.body) if "version" not in request: raise ValueError("missing version element") tenant_id = UUID(args[0]) tenant = RUNTIME.tenants[tenant_id] dscp = DSCP(args[1]) tenant.set_slice(dscp, request) except TypeError as ex: self.send_error(400, message=ex) except ValueError as ex: self.send_error(400, message=ex) except KeyError as ex: self.send_error(404, message=ex) self.set_status(204, None)
def _handle_ran_mac_slice_response(self, vbs, hdr, event, msg): """Handle an incoming RAN_MAC_SLICE message. Args: status, a RAN_MAC_SLICE messagge Returns: None """ dscp = DSCP(msg.dscp) plmn_id = PLMNID(msg.plmn_id) tenant = RUNTIME.load_tenant_by_plmn_id(plmn_id) # check if tenant is valid if not tenant: self.log.info("Unknown tenant %s", plmn_id) return # check if slice is valid if dscp not in tenant.slices: self.log.warning("DSCP %s not found. Removing slice.", dscp) cell = vbs.cells[hdr.cellid] self.send_del_ran_mac_slice_request(cell, plmn_id, dscp) return slc = tenant.slices[dscp] for raw_cap in msg.options: if raw_cap.type not in RAN_MAC_SLICE_TYPES: self.log.warning("Unknown options %u", raw_cap.type) continue prop = RAN_MAC_SLICE_TYPES[raw_cap.type].name option = RAN_MAC_SLICE_TYPES[raw_cap.type].parse(raw_cap.data) self.log.warning("Processing options %s", prop) if raw_cap.type == EP_RAN_MAC_SLICE_SCHED_ID: if option.sched_id != slc.lte['static-properties']['sched_id']: if vbs.addr not in slc.lte['vbses']: slc.lte['vbses'][vbs.addr] = { 'static-properties': {} } slc.lte['vbses'][vbs.addr] \ ['static-properties']['sched_id'] = option.sched_id if raw_cap.type == EP_RAN_MAC_SLICE_RBGS: if option.rbgs != slc.lte['static-properties']['rbgs']: if vbs.addr not in slc.lte['vbses']: slc.lte['vbses'][vbs.addr] = { 'static-properties': {} } slc.lte['vbses'][vbs.addr] \ ['static-properties']['rbgs'] = option.rbgs if raw_cap.type == EP_RAN_MAC_SLICE_RNTI_LIST: rntis = option.rntis for ue in list(tenant.ues.values()): if ue.vbs != vbs: continue # if the UE was attached to this slice, but it is not # in the information given by the eNB, it should be # deleted. if slc.dscp in ue.slices and ue.rnti not in rntis: ue.remove_slice(slc.dscp) # if the UE was not attached to this slice, but its RNTI # is provided by the eNB for this slice, it should added. if slc.dscp not in ue.slices and ue.rnti in rntis: ue.add_slice(slc.dscp) self.log.info("Slice %s updated", slc)
def send_slice_config_to_wtp(self, dscp, new_quantum): new_slice = sliceconfigrequest.format_slice_config_request( tenant_id=self.tenant_id, dscp=dscp, default_quantum=new_quantum) self.log.debug("Sending new slice configurations to APs") self.tenant.set_slice(DSCP(dscp), new_slice)
def slices(self, slices): """Assign a list slices to the UE. Accepts as input either an Slice ID or a list of Slice IDs. Args: slices: A list of Slice IDs or a Slice ID """ if not slices: return if isinstance(slices, list): slice_pool = [DSCP(x) for x in slices] else: slice_pool = [DSCP(slices)] for slc in slice_pool: if not isinstance(slc, DSCP): raise TypeError("Invalid type: %s" % type(slc)) for slc_id in slice_pool: current_rntis = [] slc = self.tenant.slices[slc_id] # If an slice has been added to a UE, the VBS must be updated. if slc_id in self.slices: continue current_rntis = [self.rnti] for ue in list(self.tenant.ues.values()): if self.vbs == ue.vbs and slc_id in ue.slices: current_rntis.append(ue.rnti) for cell in self.vbs.cells.values(): self.vbs.connection.\ send_add_set_ran_mac_slice_request(cell, slc, EP_OPERATION_SET, current_rntis) # If it has been removed, the RNTI must not notified to the VBS. for slc_id in self.slices: if slc_id in slice_pool: continue current_rntis = [] slc = self.tenant.slices[slc_id] for ue in list(RUNTIME.ues.values()): if ue == self: continue if self.vbs == ue.vbs and slc_id in ue.slices: current_rntis.append(ue.rnti) for cell in self.vbs.cells.values(): self.vbs.connection.\ send_add_set_ran_mac_slice_request(cell, slc, EP_OPERATION_SET, current_rntis) self._slices = slice_pool
def dscp(self, value): self._dscp = DSCP(value)
def slice_stats_callback(self, slice_stats): """ New stats available. """ crr_wtp_addr = str(slice_stats.block.addr) crr_dscp = str(slice_stats.dscp) if crr_wtp_addr not in self.__slice_stats_handler['wtps']: self.__slice_stats_handler['wtps'][crr_wtp_addr] = { 'slices': {}, 'overall': { 'queue_delay_ms': { "values": [], "mean": None, "median": None, "stdev": None }, 'throughput_mbps': { "values": [], "mean": None, "median": None, "stdev": None } } } if crr_dscp not in self.__slice_stats_handler['wtps'][crr_wtp_addr][ 'slices']: self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp] = { 'tx_bytes': 0, 'tx_packets': 0, 'throughput_mbps': { "values": [], "mean": None, "median": None, "stdev": None }, 'deficit_used': None, 'deficit_avg': None, 'deficit': None, 'queue_delay_ms': { "values": [], "mean": None, "median": None, "stdev": None }, 'max_queue_length': None, 'crr_queue_length': None, 'tx_bytes_moving': [], 'tx_packets_moving': [] } # Computing TX metrics... for metric in self.__tx_metrics: self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp][metric + '_moving'].append( slice_stats.to_dict()['slice_stats'][metric]) if len(self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'] [crr_dscp][metric + '_moving']) >= 2: # Calculate diff self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][crr_dscp][metric] = \ self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][crr_dscp][ metric + '_moving'][1] - \ self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][crr_dscp][ metric + '_moving'][0] self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp][metric + '_moving'].pop(0) # Computing TX megabits crr_tx_megabits = self.__slice_stats_handler['wtps'][crr_wtp_addr][ 'slices'][crr_dscp]['tx_bytes'] / 125000 # from bytes to megabits # Computing throughput metric... crr_throughput_mbps = 0 if self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp]['tx_bytes'] > 0: crr_throughput_mbps = self.__slice_stats_handler['wtps'][ crr_wtp_addr]['slices'][crr_dscp][ 'tx_bytes'] / 1000 / 1000 * 8 # from bytes to Mbps self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][crr_dscp][ 'throughput_mbps']['values'].append(crr_throughput_mbps) crr_queue_delay_ms = 0 if self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp]['tx_bytes'] > 0: crr_queue_delay_sec = slice_stats.to_dict()['slice_stats'][ 'queue_delay_sec'] # getting seconds... crr_queue_delay_ms = slice_stats.to_dict( )['slice_stats']['queue_delay_usec'] / 1000 # from usec to ms crr_queue_delay_ms += crr_queue_delay_sec * 1000 # from sec to ms self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][crr_dscp][ 'queue_delay_ms']['values'].append(crr_queue_delay_ms) for metric in self.__moving_window_metrics: if len(self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'] [crr_dscp][metric]['values']) > 10: self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp][metric]['values'].pop(0) if len(self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'] [crr_dscp][metric]['values']) >= 2: # Mean self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp][metric]['mean'] = statistics.mean( self.__slice_stats_handler['wtps'][crr_wtp_addr] ['slices'][crr_dscp][metric]['values']) # Median self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp][metric]['median'] = statistics.median( self.__slice_stats_handler['wtps'][crr_wtp_addr] ['slices'][crr_dscp][metric]['values']) # STDEV self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp][metric]['stdev'] = statistics.stdev( self.__slice_stats_handler['wtps'][crr_wtp_addr] ['slices'][crr_dscp][metric]['values']) # Computing queue delay metric... for metric in self.__raw_metrics: if self.slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp]['tx_bytes'] == 0: self.slice_stats_handler['wtps'][crr_wtp_addr]['slices'][ crr_dscp][metric] = 0 else: self.slice_stats_handler['wtps'][crr_wtp_addr]['slices'][crr_dscp][metric] = \ slice_stats.to_dict()['slice_stats'][metric] # Update wtp counters self.update_wtp_overall_counters(crr_wtp_addr=crr_wtp_addr) # Saving slice stats into db if self.__db_monitor is not None: # If there is a specific slice configuration for this WTP if EtherAddress(crr_wtp_addr) in self.tenant.slices[DSCP( crr_dscp)].wifi['wtps']: crr_quantum = self.tenant.slices[DSCP(crr_dscp)].wifi['wtps'][ EtherAddress(crr_wtp_addr)]['static-properties']['quantum'] else: crr_quantum = self.tenant.slices[DSCP( crr_dscp)].wifi['static-properties']['quantum'] fields = [ 'WTP_ADDR', 'DSCP', 'WTP_DSCP', 'DEFICIT', 'DEFICIT_AVG', 'DEFICIT_USED', 'MAX_QUEUE_LENGTH', 'CRR_QUEUE_LENGTH', 'CURRENT_QUANTUM', 'QUEUE_DELAY_MSEC', 'TX_BYTES', 'TX_PACKETS', 'TX_MBITS', 'THROUGHPUT_MBPS' ] values = [ str(crr_wtp_addr), str(crr_dscp), 'WTP: ' + str(crr_wtp_addr) + ' - Slice: ' + str(crr_dscp), self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'] [crr_dscp]['deficit'], self.__slice_stats_handler['wtps'] [crr_wtp_addr]['slices'][crr_dscp]['deficit_avg'], self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'] [crr_dscp]['deficit_used'], self.__slice_stats_handler['wtps'] [crr_wtp_addr]['slices'][crr_dscp]['max_queue_length'], self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'] [crr_dscp]['crr_queue_length'], crr_quantum, crr_queue_delay_ms, self.__slice_stats_handler['wtps'] [crr_wtp_addr]['slices'][crr_dscp]['tx_bytes'], self.__slice_stats_handler['wtps'][crr_wtp_addr]['slices'] [crr_dscp]['tx_packets'], crr_tx_megabits, crr_throughput_mbps ] # Saving into db self.monitor.insert_into_db(table='slice_stats', fields=fields, values=values)