# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import calendar, logging, math, pandas from datetime import datetime, timezone from typing import Dict from common.tools.object_factory.Context import json_context from common.tools.object_factory.Device import ( json_device_emulated_connect_rules, json_device_emulated_packet_router_disabled, json_device_id ) from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_descriptor, json_endpoint_id from common.tools.object_factory.Link import json_link from common.tools.object_factory.Topology import json_topology LOGGER = logging.getLogger(__name__) def time_datetime_to_int(dt_time : datetime) -> int: return int(calendar.timegm(dt_time.timetuple())) def time_datetime_to_float(dt_time : datetime) -> float: return time_datetime_to_int(dt_time) + (dt_time.microsecond / 1.e6) def time_utc_now_to_datetime() -> datetime: return datetime.now(tz=timezone.utc) def time_utc_now_to_float() -> float: return time_datetime_to_float(time_utc_now_to_datetime()) def read_csv(csv_file : str) -> pandas.DataFrame: LOGGER.info('Using Data File "{:s}"...'.format(csv_file)) LOGGER.info('Loading...') df = pandas.read_csv(csv_file) LOGGER.info(' DONE') LOGGER.info('Parsing and Adapting columns...') if 'dataset.csv' in csv_file or 'dataset-short.csv' in csv_file: df.rename(columns={'linkid': 'link_id', 'ds': 'timestamp', 'y': 'used_capacity_gbps'}, inplace=True) df[['source', 'destination']] = df['link_id'].str.split('_', expand=True) elif 'dataset2.csv' in csv_file: df.drop(columns=['Unnamed: 0'], inplace=True) df.rename(columns={ 'target': 'destination', 'id': 'link_id', 'ds': 'timestamp', 'demandValue': 'used_capacity_gbps' }, inplace=True) LOGGER.info(' DONE') LOGGER.info('Updating timestamps...') df['timestamp'] = pandas.to_datetime(df['timestamp']) max_timestamp = time_datetime_to_int(df['timestamp'].max()) now_timestamp = time_datetime_to_int(datetime.now(tz=timezone.utc)) df['timestamp'] = df['timestamp'] + pandas.offsets.Second(now_timestamp - max_timestamp) LOGGER.info(' DONE') LOGGER.info('Sorting...') df.sort_values('timestamp', ascending=True, inplace=True) LOGGER.info(' DONE') return df def compose_descriptors(df : pandas.DataFrame) -> Dict: devices = dict() links = dict() LOGGER.info('Discovering Devices and Links...') #df1.groupby(['A','B']).size().reset_index().rename(columns={0:'count'}) df_links = df[['link_id', 'source', 'destination']].drop_duplicates() for row in df_links.itertuples(index=False): #print(row) link_uuid = row.link_id src_device_uuid = row.source dst_device_uuid = row.destination src_port_uuid = row.destination dst_port_uuid = row.source if src_device_uuid not in devices: devices[src_device_uuid] = {'id': src_device_uuid, 'endpoints': set()} devices[src_device_uuid]['endpoints'].add(src_port_uuid) if dst_device_uuid not in devices: devices[dst_device_uuid] = {'id': dst_device_uuid, 'endpoints': set()} devices[dst_device_uuid]['endpoints'].add(dst_port_uuid) if link_uuid not in links: total_capacity_gbps = df[df.link_id==link_uuid]['used_capacity_gbps'].max() total_capacity_gbps = math.ceil(total_capacity_gbps / 100) * 100 # round up in steps of 100 used_capacity_gbps = df[df.link_id==link_uuid].used_capacity_gbps.iat[-1] # get last value links[link_uuid] = { 'id': link_uuid, 'src_dev': src_device_uuid, 'src_port': dst_device_uuid, 'dst_dev': dst_device_uuid, 'dst_port': src_device_uuid, 'total_capacity_gbps': total_capacity_gbps, 'used_capacity_gbps': used_capacity_gbps, } LOGGER.info(' Found {:d} devices and {:d} links...'.format(len(devices), len(links))) LOGGER.info('Composing Descriptors...') _context = json_context('admin', name='admin') _topology = json_topology('admin', name='admin', context_id=_context['context_id']) descriptor = { 'dummy_mode': True, # inject the descriptors directly into the Context component 'contexts': [_context], 'topologies': [_topology], 'devices': [ json_device_emulated_packet_router_disabled( device_uuid, name=device_uuid, endpoints=[ json_endpoint(json_device_id(device_uuid), endpoint_uuid, 'copper') for endpoint_uuid in device_data['endpoints'] ], config_rules=json_device_emulated_connect_rules([])) for device_uuid,device_data in devices.items() ], 'links': [ json_link(link_uuid, [ json_endpoint_id(json_device_id(link_data['src_dev']), link_data['src_port']), json_endpoint_id(json_device_id(link_data['dst_dev']), link_data['dst_port']), ], name=link_uuid, total_capacity_gbps=link_data['total_capacity_gbps'], used_capacity_gbps=link_data['used_capacity_gbps']) for link_uuid,link_data in links.items() ], } LOGGER.info(' DONE') return descriptor