Skip to content
Snippets Groups Projects
Commit 1844c608 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Tool load-gen:

- added support for L3NM and TAPI services
parent c71777a4
No related branches found
No related tags found
2 merge requests!54Release 2.0.0,!36Performance Evaluation Framework + Helper Tools
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# 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.
SERVICE_TYPE_L2NM = 'l2nm'
SERVICE_TYPE_L3NM = 'l3nm'
SERVICE_TYPE_TAPI = 'tapi'
......@@ -12,14 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Optional
from typing import List, Optional
class Parameters:
def __init__(
self, num_services : int, offered_load : Optional[float] = None, inter_arrival_time : Optional[float] = None,
holding_time : Optional[float] = None, dry_mode : bool = False
self, num_services : int, service_types : List[str], offered_load : Optional[float] = None,
inter_arrival_time : Optional[float] = None, holding_time : Optional[float] = None,
dry_mode : bool = False
) -> None:
self._num_services = num_services
self._service_types = service_types
self._offered_load = offered_load
self._inter_arrival_time = inter_arrival_time
self._holding_time = holding_time
......@@ -35,6 +37,9 @@ class Parameters:
@property
def num_services(self): return self._num_services
@property
def service_types(self): return self._service_types
@property
def offered_load(self): return self._offered_load
......
......@@ -19,17 +19,28 @@ from common.tools.object_factory.Constraint import json_constraint_custom
from common.tools.object_factory.ConfigRule import json_config_rule_set
from common.tools.object_factory.Device import json_device_id
from common.tools.object_factory.EndPoint import json_endpoint_id
from common.tools.object_factory.Service import json_service_l2nm_planned
from common.tools.object_factory.Service import (
json_service_l2nm_planned, json_service_l3nm_planned, json_service_tapi_planned)
from context.client.ContextClient import ContextClient
from .Constants import SERVICE_TYPE_L2NM, SERVICE_TYPE_L3NM, SERVICE_TYPE_TAPI
from .Parameters import Parameters
LOGGER = logging.getLogger(__name__)
ENDPOINT_COMPATIBILITY = {
'PHOTONIC_MEDIA:FLEX:G_6_25GHZ:INPUT': 'PHOTONIC_MEDIA:FLEX:G_6_25GHZ:OUTPUT',
'PHOTONIC_MEDIA:DWDM:G_50GHZ:INPUT' : 'PHOTONIC_MEDIA:DWDM:G_50GHZ:OUTPUT',
}
class ServiceGenerator:
def __init__(self) -> None:
def __init__(self, parameters : Parameters) -> None:
self._parameters = parameters
self._lock = threading.Lock()
self._num_services = 0
self._available_device_endpoints : Dict[str, Set[str]] = dict()
self._used_device_endpoints : Dict[str, Dict[str, str]] = dict()
self._endpoint_ids_to_types : Dict[Tuple[str, str], str] = dict()
self._endpoint_types_to_ids : Dict[str, Set[Tuple[str, str]]] = dict()
def initialize(self) -> None:
with self._lock:
......@@ -44,7 +55,10 @@ class ServiceGenerator:
_endpoints = self._available_device_endpoints.setdefault(device_uuid, set())
for endpoint in device.device_endpoints:
endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid
endpoint_type = endpoint.endpoint_type
_endpoints.add(endpoint_uuid)
self._endpoint_ids_to_types.setdefault((device_uuid, endpoint_uuid), endpoint_type)
self._endpoint_types_to_ids.setdefault(endpoint_type, set()).add((device_uuid, endpoint_uuid))
links = context_client.ListLinks(Empty())
for link in links.links:
......@@ -55,6 +69,15 @@ class ServiceGenerator:
_endpoints.discard(endpoint_uuid)
if len(_endpoints) == 0: self._available_device_endpoints.pop(device_uuid, None)
endpoint_type = self._endpoint_ids_to_types.pop((device_uuid, endpoint_uuid), None)
if endpoint_type is None: continue
if endpoint_type not in self._endpoint_types_to_ids: continue
endpoints_for_type = self._endpoint_types_to_ids[endpoint_type]
endpoint_key = (device_uuid, endpoint_uuid)
if endpoint_key not in endpoints_for_type: continue
endpoints_for_type.discard(endpoint_key)
@property
def num_services_generated(self): return self._num_services
......@@ -68,15 +91,42 @@ class ServiceGenerator:
LOGGER.info('[dump_state] used_device_endpoints = {:s}'.format(json.dumps(self._used_device_endpoints)))
def _use_device_endpoint(
self, service_uuid : str, exclude_device_uuids : Set[str] = set()
self, service_uuid : str, endpoint_types : Optional[Set[str]] = None, exclude_device_uuids : Set[str] = set()
) -> Optional[Tuple[str, str]]:
with self._lock:
elegible_device_endpoints = {
device_uuid:device_endpoint_uuids
for device_uuid,device_endpoint_uuids in self._available_device_endpoints.items()
if device_uuid not in exclude_device_uuids and len(device_endpoint_uuids) > 0
}
if len(elegible_device_endpoints) == 0: return None
compatible_endpoints : Set[Tuple[str, str]] = set()
elegible_device_endpoints : Dict[str, Set[str]] = {}
if endpoint_types is None:
# allow all
elegible_device_endpoints : Dict[str, Set[str]] = {
device_uuid:device_endpoint_uuids
for device_uuid,device_endpoint_uuids in self._available_device_endpoints.items()
if device_uuid not in exclude_device_uuids and len(device_endpoint_uuids) > 0
}
else:
# allow only compatible endpoints
for endpoint_type in endpoint_types:
if endpoint_type not in self._endpoint_types_to_ids: continue
compatible_endpoints.update(self._endpoint_types_to_ids[endpoint_type])
for device_uuid,device_endpoint_uuids in self._available_device_endpoints.items():
if device_uuid in exclude_device_uuids or len(device_endpoint_uuids) == 0: continue
for endpoint_uuid in device_endpoint_uuids:
endpoint_key = (device_uuid,endpoint_uuid)
if endpoint_key not in compatible_endpoints: continue
elegible_device_endpoints.setdefault(device_uuid, set()).add(endpoint_uuid)
if len(elegible_device_endpoints) == 0:
LOGGER.warning(' '.join([
'>> No endpoint is available:',
'endpoint_types={:s}'.format(str(endpoint_types)),
'exclude_device_uuids={:s}'.format(str(exclude_device_uuids)),
'self._endpoint_types_to_ids={:s}'.format(str(self._endpoint_types_to_ids)),
'self._available_device_endpoints={:s}'.format(str(self._available_device_endpoints)),
'compatible_endpoints={:s}'.format(str(compatible_endpoints)),
]))
return None
device_uuid = random.choice(list(elegible_device_endpoints.keys()))
device_endpoint_uuids = elegible_device_endpoints.get(device_uuid)
endpoint_uuid = random.choice(list(device_endpoint_uuids))
......@@ -95,47 +145,124 @@ class ServiceGenerator:
num_service = self._num_services
#service_uuid = str(uuid.uuid4())
service_uuid = 'svc_{:d}'.format(num_service)
src = self._use_device_endpoint(service_uuid)
if src is None: return None
# choose service type
service_type = random.choice(self._parameters.service_types)
# choose source endpoint
src_endpoint_types = set(ENDPOINT_COMPATIBILITY.keys()) if service_type in {SERVICE_TYPE_TAPI} else None
src = self._use_device_endpoint(service_uuid, endpoint_types=src_endpoint_types)
if src is None:
LOGGER.warning('>> No source endpoint is available')
return None
src_device_uuid,src_endpoint_uuid = src
dst = self._use_device_endpoint(service_uuid, exclude_device_uuids={src_device_uuid})
# identify compatible destination endpoint types
src_endpoint_type = self._endpoint_ids_to_types.get((src_device_uuid,src_endpoint_uuid))
dst_endpoint_type = ENDPOINT_COMPATIBILITY.get(src_endpoint_type)
# identify expluded destination devices
exclude_device_uuids = {} if service_type in {SERVICE_TYPE_TAPI} else {src_device_uuid}
# choose feasible destination endpoint
dst = self._use_device_endpoint(
service_uuid, endpoint_types={dst_endpoint_type}, exclude_device_uuids=exclude_device_uuids)
# if destination endpoint not found, release source, and terminate current service generation
if dst is None:
LOGGER.warning('>> No destination endpoint is available')
self._release_device_endpoint(src_device_uuid, src_endpoint_uuid)
return None
# compose endpoints
dst_device_uuid,dst_endpoint_uuid = dst
endpoint_ids = [
json_endpoint_id(json_device_id(src_device_uuid), src_endpoint_uuid),
json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid),
]
constraints = [
json_constraint_custom('bandwidth[gbps]', '10.0'),
json_constraint_custom('latency[ms]', '20.0'),
]
vlan_id = num_service % 1000
circuit_id = '{:03d}'.format(vlan_id)
src_router_id = '.'.join([src_device_uuid.replace('R', ''), '0'] + src_endpoint_uuid.split('/'))
dst_router_id = '.'.join([dst_device_uuid.replace('R', ''), '0'] + dst_endpoint_uuid.split('/'))
config_rules = [
json_config_rule_set('/settings', {
'mtu': 1512
}),
json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), {
'router_id': src_router_id,
'sub_interface_index': vlan_id,
'vlan_id': vlan_id,
'remote_router': dst_router_id,
'circuit_id': circuit_id,
}),
json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), {
'router_id': dst_router_id,
'sub_interface_index': vlan_id,
'vlan_id': vlan_id,
'remote_router': src_router_id,
'circuit_id': circuit_id,
}),
]
return json_service_l2nm_planned(
service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules)
if service_type == SERVICE_TYPE_L2NM:
constraints = [
json_constraint_custom('bandwidth[gbps]', '10.0'),
json_constraint_custom('latency[ms]', '20.0'),
]
vlan_id = num_service % 1000
circuit_id = '{:03d}'.format(vlan_id)
src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', '')))
dst_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', '')))
config_rules = [
json_config_rule_set('/settings', {
'mtu': 1512
}),
json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), {
'router_id': src_router_id,
'sub_interface_index': vlan_id,
'vlan_id': vlan_id,
'remote_router': dst_router_id,
'circuit_id': circuit_id,
}),
json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), {
'router_id': dst_router_id,
'sub_interface_index': vlan_id,
'vlan_id': vlan_id,
'remote_router': src_router_id,
'circuit_id': circuit_id,
}),
]
return json_service_l2nm_planned(
service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules)
elif service_type == SERVICE_TYPE_L3NM:
constraints = [
json_constraint_custom('bandwidth[gbps]', '10.0'),
json_constraint_custom('latency[ms]', '20.0'),
]
vlan_id = num_service % 1000
bgp_as = 60000 + (num_service % 10000)
bgp_route_target = '{:5d}:{:03d}'.format(bgp_as, 333)
route_distinguisher = '{:5d}:{:03d}'.format(bgp_as, vlan_id)
src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', '')))
dst_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', '')))
src_address_ip = '.'.join([src_device_uuid.replace('R', ''), '0'] + src_endpoint_uuid.split('/'))
dst_address_ip = '.'.join([dst_device_uuid.replace('R', ''), '0'] + dst_endpoint_uuid.split('/'))
config_rules = [
json_config_rule_set('/settings', {
'mtu' : 1512,
'bgp_as' : bgp_as,
'bgp_route_target': bgp_route_target,
}),
json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), {
'router_id' : src_router_id,
'route_distinguisher': route_distinguisher,
'sub_interface_index': vlan_id,
'vlan_id' : vlan_id,
'address_ip' : src_address_ip,
'address_prefix' : 16,
}),
json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), {
'router_id' : dst_router_id,
'route_distinguisher': route_distinguisher,
'sub_interface_index': vlan_id,
'vlan_id' : vlan_id,
'address_ip' : dst_address_ip,
'address_prefix' : 16,
}),
]
return json_service_l3nm_planned(
service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules)
elif service_type == SERVICE_TYPE_TAPI:
config_rules = [
json_config_rule_set('/settings', {
'capacity_value' : 50.0,
'capacity_unit' : 'GHz',
'layer_proto_name': 'PHOTONIC_MEDIA',
'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC',
'direction' : 'UNIDIRECTIONAL',
}),
]
return json_service_tapi_planned(
service_uuid, endpoint_ids=endpoint_ids, constraints=[], config_rules=config_rules)
def release_service(self, json_service : Dict) -> None:
for endpoint_id in json_service['service_endpoint_ids']:
......
......@@ -13,6 +13,7 @@
# limitations under the License.
import logging, sys
from .Constants import SERVICE_TYPE_L2NM, SERVICE_TYPE_L3NM, SERVICE_TYPE_TAPI
from .Parameters import Parameters
from .ServiceGenerator import ServiceGenerator
from .ServiceScheduler import ServiceScheduler
......@@ -23,14 +24,19 @@ LOGGER = logging.getLogger(__name__)
def main():
LOGGER.info('Starting...')
parameters = Parameters(
num_services = 100,
offered_load = 50,
holding_time = 10,
dry_mode = False, # in dry mode, no request is sent to TeraFlowSDN
num_services = 100,
service_types = [
#SERVICE_TYPE_L2NM,
#SERVICE_TYPE_L3NM,
SERVICE_TYPE_TAPI,
],
offered_load = 50,
holding_time = 10,
dry_mode = False, # in dry mode, no request is sent to TeraFlowSDN
)
LOGGER.info('Initializing Generator...')
service_generator = ServiceGenerator()
service_generator = ServiceGenerator(parameters)
service_generator.initialize()
LOGGER.info('Running Schedule...')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment