Commit 5a6a58d4 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Device-Service component - MikrotikRouterOS

- Added Handlers - still not complete/Confirmed
parent 508c78f5
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
# 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.

from typing import Any, Dict, List, Tuple
from ._Handler import _Handler

class InterfaceHandler(_Handler):
    def get_resource_key(self) -> str:
        return '/interfaces'

    def compose(self, resource_key: str, resource_value: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]:
        if resource_key.startswith('/interfaces/ip'):
            endpoint = '/ip/address'
            payload = {
                'address': str(resource_value['address']),
                'interface': str(resource_value['interface']),
                'comment': str(resource_value.get('comment', '')),
            }
            return endpoint, payload

        if resource_key.startswith('/interfaces/vxlan'):
            endpoint = '/interface/vxlan'
            payload: Dict[str, Any] = {}
            if 'name' in resource_value:
                payload['name'] = str(resource_value['name'])
            if 'vni' in resource_value:
                payload['vni'] = int(resource_value['vni'])
            if 'local-address' in resource_value:
                payload['local-address'] = str(resource_value['local-address'])
            if 'port' in resource_value:
                payload['port'] = int(resource_value.get('port', 4789))
            return endpoint, payload

        if resource_key.startswith('/interfaces/bridge/port'):
            endpoint = '/interface/bridge/port'
            payload = {
                'bridge': str(resource_value['bridge']),
                'interface': str(resource_value['interface']),
            }
            return endpoint, payload

        raise NotImplementedError(f'Resource not implemented: {resource_key}')

    def parse(self, raw_payload: List[Dict[str, Any]]) -> List[Tuple[str, Dict[str, Any]]]:
        return []
+103 −0
Original line number Diff line number Diff line
# 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.

from typing import Any, Dict, List, Tuple
from ._Handler import _Handler

class NetworkInstanceHandler(_Handler):
    def get_resource_key(self) -> str:
        return '/network_instances'

    def compose(self, resource_key: str, resource_value: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]:
        if resource_key.startswith('/network_instances/bgp_instance'):
            return '/routing/bgp/instance', {
                'name': str(resource_value['name']),
                'as': int(resource_value['as']),
                'router-id': str(resource_value['router_id']),
            }

        if resource_key.startswith('/network_instances/bgp_session'):
            endpoint = '/routing/bgp/connection'
            payload: Dict[str, Any] = {}
            if 'name' in resource_value:
                payload['name'] = str(resource_value['name'])
            if 'instance' in resource_value:
                payload['instance'] = resource_value['instance']
            if 'local.address' in resource_value:
                payload['local.address'] = resource_value['local.address']
            if 'remote.address' in resource_value:
                payload['remote.address'] = resource_value['remote.address']
            if 'remote.as' in resource_value:
                payload['remote.as'] = resource_value['remote.as']
            if 'routing-table' in resource_value:
                payload['routing-table'] = resource_value['routing-table']
            if 'local.role' in resource_value:
                payload['local.role'] = resource_value['local.role']
            if 'multihop' in resource_value:
                payload['multihop'] = resource_value['multihop']
            if 'afi' in resource_value:
                payload['afi'] = resource_value['afi']
            return endpoint, payload

        if resource_key.startswith('/network_instances/bgp_evpn'):
            endpoint = '/routing/bgp/evpn'
            payload: Dict[str, Any] = {}
            if 'name' in resource_value:
                payload['name'] = str(resource_value['name'])
            if 'instance' in resource_value:
                payload['instance'] = str(resource_value['instance'])
            if 'vni' in resource_value:
                payload['vni'] = int(resource_value['vni'])
            return endpoint, payload

        if resource_key.startswith('/network_instances/ospf_instance'):
            return '/routing/ospf/instance', {
                'name': str(resource_value['name']),
                'router-id': str(resource_value['router-id']),
                'version': int(resource_value.get('version', 2)),
            }

        if resource_key.startswith('/network_instances/ospf_area'):
            return '/routing/ospf/area', {
                'name': str(resource_value['name']),
                'instance': str(resource_value['instance']),
                'area-id': str(resource_value.get('area-id', '0.0.0.0')),
            }

        if resource_key.startswith('/network_instances/ospf_interface'):
            return '/routing/ospf/interface-template', {
                'interfaces': [str(resource_value['interface'])],
                'area': str(resource_value.get('area', 'backbone')),
            }

        if resource_key.startswith('/network_instances/isis_instance'):
            return '/routing/isis/instance', {
                'name': str(resource_value['name']),
                'areas': str(resource_value['areas']),
                'system-id': str(resource_value['system-id']),
            }

        if resource_key.startswith('/network_instances/isis_interface'):
            level_val = resource_value.get('levels', 'l2')
            return '/routing/isis/interface-template', {
                'instance': str(resource_value['instance']),
                'interfaces': [str(resource_value['interface'])],
                'levels': [str(level_val)],
            }

        raise NotImplementedError(f'Resource not implemented: {resource_key}')

    def parse(self, raw_payload: List[Dict[str, Any]]) -> List[Tuple[str, Dict[str, Any]]]:
        # Placeholder: no behavior wiring yet.
        return []
+20 −0
Original line number Diff line number Diff line
# 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.

import re

RE_REMOVE_FILTERS = re.compile(r'\[[^\]]+\]')

def get_schema(resource_key: str) -> str:
    return RE_REMOVE_FILTERS.sub('', resource_key)
+25 −0
Original line number Diff line number Diff line
# 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.

from typing import Any, Dict, List, Tuple

class _Handler:
    def get_resource_key(self) -> str:
        raise NotImplementedError()

    def compose(self, resource_key: str, resource_value: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]:
        raise NotImplementedError()

    def parse(self, raw_payload: List[Dict[str, Any]]) -> List[Tuple[str, Dict[str, Any]]]:
        raise NotImplementedError()
+60 −0
Original line number Diff line number Diff line
# 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.

from typing import Any, Dict, List, Tuple
from device.service.driver_api._Driver import (
    RESOURCE_ENDPOINTS,
    RESOURCE_INTERFACES,
    RESOURCE_NETWORK_INSTANCES,
)
from ._Handler import _Handler
from .InterfaceHandler import InterfaceHandler
from .NetworkInstanceHandler import NetworkInstanceHandler
from .Tools import get_schema

_interface_handler = InterfaceHandler()
_network_instance_handler = NetworkInstanceHandler()

ALL_RESOURCE_KEYS = [
    RESOURCE_ENDPOINTS,
    RESOURCE_INTERFACES,
    RESOURCE_NETWORK_INSTANCES,
]

RESOURCE_KEY_MAPPER = {
    RESOURCE_ENDPOINTS: _interface_handler.get_resource_key(),
    RESOURCE_INTERFACES: _interface_handler.get_resource_key(),
    RESOURCE_NETWORK_INSTANCES: _network_instance_handler.get_resource_key(),
}

RESOURCE_KEY_TO_HANDLER: Dict[str, _Handler] = {
    _interface_handler.get_resource_key(): _interface_handler,
    _network_instance_handler.get_resource_key(): _network_instance_handler,
}

def get_handler(resource_key: str) -> _Handler:
    resource_key_schema = get_schema(resource_key)
    resource_key_schema = RESOURCE_KEY_MAPPER.get(resource_key_schema, resource_key_schema)
    for handler_schema, handler in RESOURCE_KEY_TO_HANDLER.items():
        if resource_key_schema.startswith(handler_schema):
            return handler
    raise Exception(f'Handler not found: resource_key={resource_key} resource_key_schema={resource_key_schema}')

def compose(resource_key: str, resource_value: Dict[str, Any]) -> Tuple[str, Dict[str, Any]]:
    handler = get_handler(resource_key=resource_key)
    return handler.compose(resource_key, resource_value)

def parse(resource_key: str, raw_payload: List[Dict[str, Any]]) -> List[Tuple[str, Dict[str, Any]]]:
    handler = get_handler(resource_key=resource_key)
    return handler.parse(raw_payload)
Loading