Skip to content
Tools.py 5.77 KiB
Newer Older
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
# 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.

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
import calendar, logging, math, pandas
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
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
)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
from common.tools.object_factory.EndPoint import json_endpoint, json_endpoint_descriptor, json_endpoint_id
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
from common.tools.object_factory.Link import json_link
from common.tools.object_factory.Topology import json_topology

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
LOGGER = logging.getLogger(__name__)

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
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:
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('Using Data File "{:s}"...'.format(csv_file))

    LOGGER.info('Loading...')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    df = pandas.read_csv(csv_file)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('  DONE')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('Parsing and Adapting columns...')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    if 'dataset.csv' in csv_file or 'dataset-short.csv' in csv_file:
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        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)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('  DONE')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('Updating timestamps...')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    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)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('  DONE')

    LOGGER.info('Sorting...')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    df.sort_values('timestamp', ascending=True, inplace=True)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('  DONE')

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    return df

def compose_descriptors(df : pandas.DataFrame) -> Dict:
    devices = dict()
    links = dict()

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('Discovering Devices and Links...')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    #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
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            used_capacity_gbps  = df[df.link_id==link_uuid].used_capacity_gbps.iat[-1] # get last value
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            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,
            }
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('  Found {:d} devices and {:d} links...'.format(len(devices), len(links)))
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('Composing Descriptors...')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    _context  = json_context('admin', name='admin')
    _topology = json_topology('admin', name='admin', context_id=_context['context_id'])
    descriptor = {
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        'dummy_mode': True, # inject the descriptors directly into the Context component
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        'contexts': [_context],
        'topologies': [_topology],
        'devices': [
            json_device_emulated_packet_router_disabled(
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
                device_uuid, name=device_uuid, endpoints=[
                    json_endpoint(json_device_id(device_uuid), endpoint_uuid, 'copper')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
                    for endpoint_uuid in device_data['endpoints']
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
                ], config_rules=json_device_emulated_connect_rules([]))
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            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()
        ],
    }
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    LOGGER.info('  DONE')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    return descriptor