Commit 83a03497 authored by Waleed Akbar's avatar Waleed Akbar
Browse files

feat(pluggables): Add complete flow tests and logging for pluggables service

- Implemented logging for various services in dump_logs.sh.
- Activated pluggables component in ecoc26_deploy.sh.
- Added PluggablesClient fixture in Fixtuers.py for test sessions.
- Updated topology descriptor to use ecoc26_topo_devices_DSCM.json.
- Created Kubernetes job for pluggables complete-flow tests in pluggables_test_job.yaml.
- Developed script to run pluggables tests in Kubernetes environment.
- Added comprehensive tests for pluggables functionality in test_pluggable_complete_flow.py.
- Included real configuration XML templates for hub and leaf devices.
- Updated todo list with pluggables testing status and next steps.
parent 2a866649
Loading
Loading
Loading
Loading
+0 −241
Original line number Diff line number Diff line
#!/bin/bash
# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (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.


# ----- TeraFlowSDN ------------------------------------------------------------

# Set the URL of the internal MicroK8s Docker registry where the images will be uploaded to.
export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/"

# Set the list of components, separated by spaces, you want to build images for, and deploy.
# export TFS_COMPONENTS="context device pathcomp opticalcontroller service nbi webui"
export TFS_COMPONENTS="context device pathcomp service nbi webui"

# Uncomment to activate Monitoring (old)
#export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring"

# Uncomment to activate Monitoring Framework (new)
# export TFS_COMPONENTS="${TFS_COMPONENTS} kpi_manager kpi_value_writer kpi_value_api telemetry analytics automation"

# Uncomment to activate QoS Profiles
#export TFS_COMPONENTS="${TFS_COMPONENTS} qos_profile"

# Uncomment to activate BGP-LS Speaker
#export TFS_COMPONENTS="${TFS_COMPONENTS} bgpls_speaker"

# Uncomment to activate Optical Controller
#   To manage optical connections, "service" requires "opticalcontroller" to be deployed
#   before "service", thus we "hack" the TFS_COMPONENTS environment variable prepending the
#   "opticalcontroller" only if "service" is already in TFS_COMPONENTS, and re-export it.
#if [[ "$TFS_COMPONENTS" == *"service"* ]]; then
#    BEFORE="${TFS_COMPONENTS% service*}"
#    AFTER="${TFS_COMPONENTS#* service}"
#    export TFS_COMPONENTS="${BEFORE} opticalcontroller service ${AFTER}"
#fi

# Uncomment to activate ZTP
#export TFS_COMPONENTS="${TFS_COMPONENTS} ztp"

# Uncomment to activate Policy Manager
# export TFS_COMPONENTS="${TFS_COMPONENTS} policy"

# Uncomment to activate Optical CyberSecurity
#export TFS_COMPONENTS="${TFS_COMPONENTS} dbscanserving opticalattackmitigator opticalattackdetector opticalattackmanager"

# Uncomment to activate L3 CyberSecurity
#export TFS_COMPONENTS="${TFS_COMPONENTS} l3_attackmitigator l3_centralizedattackdetector"

# Uncomment to activate TE
#export TFS_COMPONENTS="${TFS_COMPONENTS} te"

# Uncomment to activate Forecaster
#export TFS_COMPONENTS="${TFS_COMPONENTS} forecaster"

# Uncomment to activate E2E Orchestrator
#export TFS_COMPONENTS="${TFS_COMPONENTS} e2e_orchestrator"

# Uncomment to activate VNT Manager
#export TFS_COMPONENTS="${TFS_COMPONENTS} vnt_manager"

# Uncomment to activate OSM Client
#export TFS_COMPONENTS="${TFS_COMPONENTS} osm_client"

# Uncomment to activate DLT and Interdomain
#export TFS_COMPONENTS="${TFS_COMPONENTS} interdomain dlt"
#if [[ "$TFS_COMPONENTS" == *"dlt"* ]]; then
#    export KEY_DIRECTORY_PATH="src/dlt/gateway/keys/priv_sk"
#    export CERT_DIRECTORY_PATH="src/dlt/gateway/keys/cert.pem"
#    export TLS_CERT_PATH="src/dlt/gateway/keys/ca.crt"
#fi

# Uncomment to activate QKD App
#   To manage QKD Apps, "service" requires "qkd_app" to be deployed
#   before "service", thus we "hack" the TFS_COMPONENTS environment variable prepending the
#   "qkd_app" only if "service" is already in TFS_COMPONENTS, and re-export it.
#if [[ "$TFS_COMPONENTS" == *"service"* ]]; then
#    BEFORE="${TFS_COMPONENTS% service*}"
#    AFTER="${TFS_COMPONENTS#* service}"
#    export TFS_COMPONENTS="${BEFORE} qkd_app service ${AFTER}"
#fi

# Uncomment to activate SIMAP Connector
#export TFS_COMPONENTS="${TFS_COMPONENTS} simap_connector"

# Uncomment to activate Load Generator
#export TFS_COMPONENTS="${TFS_COMPONENTS} load_generator"

# Uncomment to activate Pluggables Component
export TFS_COMPONENTS="${TFS_COMPONENTS} pluggables"


# Set the tag you want to use for your images.
export TFS_IMAGE_TAG="dev"

# Set the name of the Kubernetes namespace to deploy TFS to.
export TFS_K8S_NAMESPACE="tfs"

# Set additional manifest files to be applied after the deployment
export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml"

# Uncomment to monitor performance of components
#export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/servicemonitors.yaml"

# Uncomment when deploying Optical CyberSecurity
#export TFS_EXTRA_MANIFESTS="${TFS_EXTRA_MANIFESTS} manifests/cachingservice.yaml"

# Set the new Grafana admin password
export TFS_GRAFANA_PASSWORD="admin123+"

# Disable skip-build flag to rebuild the Docker images.
export TFS_SKIP_BUILD=""


# ----- CockroachDB ------------------------------------------------------------

# Set the namespace where CockroackDB will be deployed.
export CRDB_NAMESPACE="crdb"

# Set the external port CockroackDB Postgre SQL interface will be exposed to.
export CRDB_EXT_PORT_SQL="26257"

# Set the external port CockroackDB HTTP Mgmt GUI interface will be exposed to.
export CRDB_EXT_PORT_HTTP="8081"

# Set the database username to be used by Context.
export CRDB_USERNAME="tfs"

# Set the database user's password to be used by Context.
export CRDB_PASSWORD="tfs123"

# Set CockroachDB installation mode to 'single'. This option is convenient for development and testing.
# See ./deploy/all.sh or ./deploy/crdb.sh for additional details
export CRDB_DEPLOY_MODE="single"

# Disable flag for dropping database, if it exists.
export CRDB_DROP_DATABASE_IF_EXISTS=""

# Disable flag for re-deploying CockroachDB from scratch.
export CRDB_REDEPLOY=""


# ----- NATS -------------------------------------------------------------------

# Set the namespace where NATS will be deployed.
export NATS_NAMESPACE="nats"

# Set the external port NATS Client interface will be exposed to.
export NATS_EXT_PORT_CLIENT="4222"

# Set the external port NATS HTTP Mgmt GUI interface will be exposed to.
export NATS_EXT_PORT_HTTP="8222"

# Set NATS installation mode to 'single'. This option is convenient for development and testing.
# See ./deploy/all.sh or ./deploy/nats.sh for additional details
export NATS_DEPLOY_MODE="single"

# Disable flag for re-deploying NATS from scratch.
export NATS_REDEPLOY=""


# ----- Apache Kafka -----------------------------------------------------------

# Set the namespace where Apache Kafka will be deployed.
export KFK_NAMESPACE="kafka"

# Set the port Apache Kafka server will be exposed to.
export KFK_EXT_PORT_CLIENT="9092"

# Set Kafka installation mode to 'single'. This option is convenient for development and testing.
# See ./deploy/all.sh or ./deploy/kafka.sh for additional details
export KFK_DEPLOY_MODE="single"

# Disable flag for re-deploying Kafka from scratch.
export KFK_REDEPLOY=""


# ----- QuestDB ----------------------------------------------------------------

# Set the namespace where QuestDB will be deployed.
export QDB_NAMESPACE="qdb"

# Set the external port QuestDB Postgre SQL interface will be exposed to.
export QDB_EXT_PORT_SQL="8812"

# Set the external port QuestDB Influx Line Protocol interface will be exposed to.
export QDB_EXT_PORT_ILP="9009"

# Set the external port QuestDB HTTP Mgmt GUI interface will be exposed to.
export QDB_EXT_PORT_HTTP="9000"

# Set the database username to be used for QuestDB.
export QDB_USERNAME="admin"

# Set the database user's password to be used for QuestDB.
export QDB_PASSWORD="quest"

# Set the table name to be used by Monitoring for KPIs.
export QDB_TABLE_MONITORING_KPIS="tfs_monitoring_kpis"

# Set the table name to be used by Slice for plotting groups.
export QDB_TABLE_SLICE_GROUPS="tfs_slice_groups"

# Disable flag for dropping tables if they exist.
export QDB_DROP_TABLES_IF_EXIST=""

# Disable flag for re-deploying QuestDB from scratch.
export QDB_REDEPLOY=""


# ----- Time Series Storage - Prometheus / Grafana Mimir -----------------------

# Set Time Series Storage installation mode to 'single' (i.e., Prometheus only).
# This option is convenient for development and testing. See ./deploy/all.sh or
# ./deploy/monitoring.sh for additional details.
export TSDB_DEPLOY_MODE="single"


# ----- K8s Observability ------------------------------------------------------

# Set the external port Prometheus Mgmt HTTP GUI interface will be exposed to.
export PROM_EXT_PORT_HTTP="9090"

# Set the external port Grafana HTTP Dashboards will be exposed to.
export GRAF_EXT_PORT_HTTP="3000"


# ----- Telemetry Config ------------------------------------------------------

# Define a Load Balancer IP for Telemetry Collector components
export LOAD_BALANCER_IP="192.168.5.250" # <-- Change this to match your network
+363 −0
Original line number Diff line number Diff line
# Copyright 2022-2026 ETSI SDG TeraFlowSDN (TFS) (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 uuid
from typing import Optional, List, Dict, Any
from common.proto import pluggables_pb2 

###########################
# CreatePluggableRequest
###########################

def create_pluggable_request(
    device_uuid: Optional[str] = None,
    preferred_pluggable_index: Optional[int] = None,
    with_initial_config: bool = False
) -> pluggables_pb2.CreatePluggableRequest:  # pyright: ignore[reportInvalidTypeForm]
    """
    Create a CreatePluggableRequest message.
    
    Args:
        device_uuid: UUID of the device. If None, generates a random UUID.
        preferred_pluggable_index: Preferred index for the pluggable. If None, not set.
        with_initial_config: If True, includes initial configuration.
    
    Returns:
        CreatePluggableRequest message
    """
    _request = pluggables_pb2.CreatePluggableRequest()
    
    # Set device ID
    if device_uuid is None:
        device_uuid = str(uuid.uuid4())
    _request.device.device_uuid.uuid = device_uuid
    
    # Set preferred pluggable index if provided
    if preferred_pluggable_index is not None:
        _request.preferred_pluggable_index = preferred_pluggable_index
    
    # Add initial configuration if requested
    if with_initial_config:
        _request.initial_config.id.device.device_uuid.uuid = device_uuid
        _request.initial_config.id.pluggable_index = preferred_pluggable_index or 0
        
        # Set top-level PluggableConfig fields
        _request.initial_config.center_frequency_mhz    = 193100000  # 193.1 THz in MHz
        _request.initial_config.operational_mode        = 1  # Operational mode
        _request.initial_config.target_output_power_dbm = -10.0  # Target output power
        _request.initial_config.line_port               = 1  # Line port number
        _request.initial_config.channel_name            = "channel-1"  # Channel name for component
        
        # Add sample DSC group configuration
        dsc_group = _request.initial_config.dsc_groups.add()
        dsc_group.id.pluggable.device.device_uuid.uuid  = device_uuid
        dsc_group.id.pluggable.pluggable_index          = preferred_pluggable_index or 0
        dsc_group.id.group_index                        = 0
        dsc_group.group_size                            = 4
        dsc_group.group_capacity_gbps                   = 400.0
        dsc_group.subcarrier_spacing_mhz                = 75.0
        
        # Add sample subcarrier configurations
        for i in range(2):
            subcarrier = dsc_group.subcarriers.add()
            subcarrier.id.group.pluggable.device.device_uuid.uuid = device_uuid
            subcarrier.id.group.pluggable.pluggable_index         = preferred_pluggable_index or 0
            subcarrier.id.group.group_index                       = 0
            subcarrier.id.subcarrier_index                        = i
            subcarrier.active                                     = True
            subcarrier.target_output_power_dbm                    = -10.0
            subcarrier.center_frequency_hz                        = 193100000000000 + (i * 75000000)  # 193.1 THz + spacing
            subcarrier.symbol_rate_baud                           = 64000000000  # 64 GBaud
    
    return _request


###########################
# ListPluggablesRequest
###########################

def create_list_pluggables_request(
    device_uuid: Optional[str] = None,
    view_level: pluggables_pb2.View = pluggables_pb2.VIEW_FULL  # pyright: ignore[reportInvalidTypeForm]
) -> pluggables_pb2.ListPluggablesRequest:  # pyright: ignore[reportInvalidTypeForm]
    """
    Create a ListPluggablesRequest message.
    
    Args:
        device_uuid: UUID of the device to filter by. If None, generates a random UUID.
        view_level: View level (VIEW_CONFIG, VIEW_STATE, VIEW_FULL, VIEW_UNSPECIFIED)
    
    Returns:
        ListPluggablesRequest message
    """
    _request = pluggables_pb2.ListPluggablesRequest()
    
    if device_uuid is None:
        device_uuid = "9bbf1937-db9e-45bc-b2c6-3214a9d42157"
        # device_uuid = str(uuid.uuid4())
    _request.device.device_uuid.uuid = device_uuid
    _request.view_level = view_level
    
    return _request


###########################
# GetPluggableRequest
###########################

def create_get_pluggable_request(
    device_uuid: str,
    pluggable_index: int,
    view_level: pluggables_pb2.View = pluggables_pb2.VIEW_FULL  # pyright: ignore[reportInvalidTypeForm]
) -> pluggables_pb2.GetPluggableRequest:  # pyright: ignore[reportInvalidTypeForm]
    """
    Create a GetPluggableRequest message.
    
    Args:
        device_uuid: UUID of the device
        pluggable_index: Index of the pluggable
        view_level: View level (VIEW_CONFIG, VIEW_STATE, VIEW_FULL, VIEW_UNSPECIFIED)
    
    Returns:
        GetPluggableRequest message
    """
    _request = pluggables_pb2.GetPluggableRequest()
    _request.id.device.device_uuid.uuid = device_uuid
    _request.id.pluggable_index = pluggable_index
    _request.view_level = view_level
    return _request


###########################
# DeletePluggableRequest
###########################

# NOTE: Both leaf and hub use the same Jinja template for deleting pluggable config.
# The difference lies in the component name: channel-5 for hub, channel-5/channel-7 for leaf.

def create_delete_pluggable_request(
    device_uuid: str,
    pluggable_index: int
) -> pluggables_pb2.DeletePluggableRequest:  # pyright: ignore[reportInvalidTypeForm]
    """
    Create a DeletePluggableRequest message.
    
    Args:
        device_uuid: UUID of the device
        pluggable_index: Index of the pluggable
    
    Returns:
        DeletePluggableRequest message
    """
    _request = pluggables_pb2.DeletePluggableRequest()
    _request.id.device.device_uuid.uuid = device_uuid
    _request.id.pluggable_index = pluggable_index
    
    return _request


###########################
# ConfigurePluggableRequest
###########################

def create_configure_pluggable_request(
    device_uuid: str,
    pluggable_index: int,
    update_mask_paths: Optional[list] = None,
    view_level: pluggables_pb2.View = pluggables_pb2.VIEW_FULL,  # pyright: ignore[reportInvalidTypeForm]
    apply_timeout_seconds: int = 30,
    parameters: Optional[List[Dict[str, Any]]] = [],
) -> pluggables_pb2.ConfigurePluggableRequest:                      # pyright: ignore[reportInvalidTypeForm]
    """
    Create a ConfigurePluggableRequest message.
    
    Args:
        device_uuid: UUID of the device
        pluggable_index: Index of the pluggable
        update_mask_paths: List of field paths to update. If None, updates all fields.
        view_level: View level for response
        apply_timeout_seconds: Timeout in seconds for applying configuration
    
    Returns:
        ConfigurePluggableRequest message
    """
    _request = pluggables_pb2.ConfigurePluggableRequest()
    
    # Set pluggable configuration
    _request.config.id.device.device_uuid.uuid = device_uuid
    _request.config.id.pluggable_index         = pluggable_index
    
    # Set top-level PluggableConfig fields
    _request.config.center_frequency_mhz       = 193100000  # 193.1 THz in MHz
    _request.config.operational_mode           = 1  # Operational mode
    _request.config.target_output_power_dbm    = -10.0  # Target output power
    _request.config.line_port                  = 1  # Line port number
    _request.config.channel_name               = "channel-1"  # Channel name for component
    
    # Add DSC group configuration
    group_1 = _request.config.dsc_groups.add()
    group_1.id.pluggable.device.device_uuid.uuid = device_uuid
    group_1.id.pluggable.pluggable_index         = pluggable_index
    group_1.id.group_index                       = 0
    group_1.group_size                           = 2
    group_1.group_capacity_gbps                  = 400.0
    group_1.subcarrier_spacing_mhz               = 75.0
    
    # Add digital-subcarrier configuration (to group group_1)
    subcarrier_1 = group_1.subcarriers.add()
    subcarrier_1.id.group.pluggable.device.device_uuid.uuid = device_uuid
    subcarrier_1.id.group.pluggable.pluggable_index         = pluggable_index
    subcarrier_1.id.group.group_index                       = 0
    subcarrier_1.id.subcarrier_index                        = 0
    subcarrier_1.active                                     = True
    subcarrier_1.target_output_power_dbm                    = -10.0
    subcarrier_1.center_frequency_hz                        = 193100000000000  # 193.1 THz
    subcarrier_1.symbol_rate_baud                           = 64000000000  # 64 GBaud
    
    # Add another digital-subcarrier configuration (to group group_1)
    subcarrier_2 = group_1.subcarriers.add()
    subcarrier_2.id.group.pluggable.device.device_uuid.uuid = device_uuid
    subcarrier_2.id.group.pluggable.pluggable_index         = pluggable_index
    subcarrier_2.id.group.group_index                       = 0
    subcarrier_2.id.subcarrier_index                        = 1
    subcarrier_2.active                                     = True
    subcarrier_2.target_output_power_dbm                    = -10.0
    subcarrier_2.center_frequency_hz                        = 193100075000000  # 193.175 THz
    subcarrier_2.symbol_rate_baud                           = 64000000000  # 64 GBaud
   
    _request.view_level = view_level
    _request.apply_timeout_seconds = apply_timeout_seconds
    
    return _request


# ─── Real-device constants ────────────────────────────────────────────────────
# Values derived from edit_dscm_hub_template.xml and edit_dscm_leaves_template.xml

_HUB_FREQ_MHZ               = 194000000   # 194.000 THz
_HUB_TARGET_POWER_DBM       = 0.0
_HUB_SUBCARRIER_SPACING_MHZ = 50.0
_HUB_OPERATIONAL_MODE       = 1

# pluggable_index → center frequency for leaf channels
_LEAF_FREQ_MAP: Dict[int, int] = {
    5: 194006250,   # channel-5 → 194.00625 THz
    7: 194018750,   # channel-7 → 194.01875 THz
}
_LEAF_TARGET_POWER_DBM       = -99.0
_LEAF_SUBCARRIER_SPACING_MHZ = 50.0
_LEAF_OPERATIONAL_MODE       = 1


###########################
# Hub — ConfigurePluggableRequest (real device values)
###########################

def create_configure_hub_pluggable_request(
    device_uuid: str,
    pluggable_index: int = 5,
    view_level: pluggables_pb2.View = pluggables_pb2.VIEW_FULL,   # pyright: ignore[reportInvalidTypeForm]
    apply_timeout_seconds: int = 30,
) -> pluggables_pb2.ConfigurePluggableRequest:                     # pyright: ignore[reportInvalidTypeForm]
    """ConfigurePluggableRequest matching edit_dscm_hub_template.xml.

    4 groups x 4 subcarriers; groups 1-2 active, groups 3-4 inactive.
    channel_name and line_port are derived from pluggable_index.
    Subcarrier IDs are 1-based and contiguous across groups (1-4, 5-8, 9-12, 13-16).
    """
    _request = pluggables_pb2.ConfigurePluggableRequest()
    _request.config.id.device.device_uuid.uuid = device_uuid
    _request.config.id.pluggable_index         = pluggable_index
    _request.config.channel_name               = f"channel-{pluggable_index}"
    _request.config.line_port                  = pluggable_index
    _request.config.center_frequency_mhz       = _HUB_FREQ_MHZ
    _request.config.target_output_power_dbm    = _HUB_TARGET_POWER_DBM
    _request.config.operational_mode           = _HUB_OPERATIONAL_MODE

    _GROUP_ACTIVE = {1: True, 2: True, 3: False, 4: False}
    for grp_idx in range(1, 5):
        group = _request.config.dsc_groups.add()
        group.id.pluggable.device.device_uuid.uuid = device_uuid
        group.id.pluggable.pluggable_index         = pluggable_index
        group.id.group_index                       = grp_idx
        group.group_size                           = 4
        group.subcarrier_spacing_mhz               = _HUB_SUBCARRIER_SPACING_MHZ

        is_active  = _GROUP_ACTIVE[grp_idx]
        base_sc_id = (grp_idx - 1) * 4
        for sc_offset in range(4):
            sc_id = base_sc_id + sc_offset + 1   # 1-based IDs: 1-4, 5-8, 9-12, 13-16
            sc = group.subcarriers.add()
            sc.id.group.pluggable.device.device_uuid.uuid = device_uuid
            sc.id.group.pluggable.pluggable_index         = pluggable_index
            sc.id.group.group_index                       = grp_idx
            sc.id.subcarrier_index                        = sc_id
            sc.active                                     = is_active

    _request.view_level            = view_level
    _request.apply_timeout_seconds = apply_timeout_seconds
    return _request


###########################
# Leaf — ConfigurePluggableRequest (real device values)
###########################

def create_configure_leaf_pluggable_request(
    device_uuid: str,
    pluggable_index: int = 5,
    view_level: pluggables_pb2.View = pluggables_pb2.VIEW_FULL,   # pyright: ignore[reportInvalidTypeForm]
    apply_timeout_seconds: int = 30,
) -> pluggables_pb2.ConfigurePluggableRequest:                     # pyright: ignore[reportInvalidTypeForm]
    """ConfigurePluggableRequest matching edit_dscm_leaves_template.xml.

    1 group x 4 subcarriers (IDs 1-4), all active.
    pluggable_index must be 5 (194.00625 THz) or 7 (194.01875 THz).
    channel_name and line_port are derived from pluggable_index.
    """
    if pluggable_index not in _LEAF_FREQ_MAP:
        raise ValueError(
            f"pluggable_index must be one of {list(_LEAF_FREQ_MAP)}, got {pluggable_index}"
        )

    _request = pluggables_pb2.ConfigurePluggableRequest()
    _request.config.id.device.device_uuid.uuid = device_uuid
    _request.config.id.pluggable_index         = pluggable_index
    _request.config.channel_name               = f"channel-{pluggable_index}"
    _request.config.line_port                  = pluggable_index
    _request.config.center_frequency_mhz       = _LEAF_FREQ_MAP[pluggable_index]
    _request.config.target_output_power_dbm    = _LEAF_TARGET_POWER_DBM
    _request.config.operational_mode           = _LEAF_OPERATIONAL_MODE

    group = _request.config.dsc_groups.add()
    group.id.pluggable.device.device_uuid.uuid = device_uuid
    group.id.pluggable.pluggable_index         = pluggable_index
    group.id.group_index                       = 1   # 1-based
    group.group_size                           = 4
    group.subcarrier_spacing_mhz               = _LEAF_SUBCARRIER_SPACING_MHZ

    for sc_id in range(1, 5):   # subcarrier IDs 1-4
        sc = group.subcarriers.add()
        sc.id.group.pluggable.device.device_uuid.uuid = device_uuid
        sc.id.group.pluggable.pluggable_index         = pluggable_index
        sc.id.group.group_index                       = 1
        sc.id.subcarrier_index                        = sc_id
        if sc_id in [1, 2]:  # subcarriers 1 and 2 are active
            sc.active = True
        else:
            sc.active = False

    _request.view_level            = view_level
    _request.apply_timeout_seconds = apply_timeout_seconds
    return _request
+50 −0

File added.

Preview size limit exceeded, changes collapsed.

+10 −1

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -97,7 +97,7 @@ export TFS_COMPONENTS="${TFS_COMPONENTS} kpi_manager telemetry analytics automat
#export TFS_COMPONENTS="${TFS_COMPONENTS} load_generator"

# Uncomment to activate Pluggables Component
#export TFS_COMPONENTS="${TFS_COMPONENTS} pluggables"
export TFS_COMPONENTS="${TFS_COMPONENTS} pluggables"


# Set the tag you want to use for your images.
Loading