Commit 7c15dc2a authored by Mohamad Rahhal's avatar Mohamad Rahhal
Browse files

Merge branch 'feat/365-cttc-new-mikrotik-routeros-sbi-driver-for-l2-evpns' of...

Merge branch 'feat/365-cttc-new-mikrotik-routeros-sbi-driver-for-l2-evpns' of https://labs.etsi.org/rep/tfs/controller into feat/390-cttc-integration-of-spine-leaf-fabric-management
parents e216938d 86024602
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -228,6 +228,16 @@ class MikrotikRouterOSDriver(_Driver):
                            if "routing-table" in resource_value:
                                payload["routing-table"] = str(resource_value["routing-table"])

                        # --- Simple Queue ---
                        elif resource_key.startswith("/qos/queue/simple"):
                            endpoint = "/queue/simple"
                            payload = {
                                "name": str(resource_value["name"]),
                                "target": str(resource_value["target"]),
                                "max-limit": str(resource_value["max-limit"]),
                                "comment": str(resource_value.get("comment", "")),
                            }

                        # --- VRF ---
                        elif resource_key.startswith("/ip/vrf"):
                            endpoint = "/ip/vrf"
@@ -451,6 +461,27 @@ class MikrotikRouterOSDriver(_Driver):
                                        patch_response.raise_for_status()
                                        results.append(True)
                                        continue
                                if endpoint == "/queue/simple" and payload.get("name"):
                                    queue = next(
                                        (candidate for candidate in candidates
                                         if str(candidate.get("name", "")) == str(payload["name"])),
                                        None,
                                    )
                                    candidate_id = (
                                        queue.get(".id") or queue.get("id") or queue.get("number")
                                    ) if isinstance(queue, dict) else None
                                    if candidate_id:
                                        patch_url = f"{url}/{candidate_id}"
                                        patch_response = requests.patch(
                                            patch_url,
                                            json=payload,
                                            auth=self.__auth,
                                            timeout=self.__timeout,
                                            verify=False,
                                        )
                                        patch_response.raise_for_status()
                                        results.append(True)
                                        continue
                        except Exception:
                            LOGGER.debug("Could not pre-check existing MikroTik resource for %s", url, exc_info=True)
        
@@ -499,6 +530,8 @@ class MikrotikRouterOSDriver(_Driver):
                    return 5
                if resource_key.startswith("/ip/route"):
                    return 5
                if resource_key.startswith("/qos/queue/simple"):
                    return 5
                if resource_key.startswith("/routing/filter/rule"):
                    return 6
                if resource_key.startswith("/routing/bgp/template"):
@@ -566,6 +599,13 @@ class MikrotikRouterOSDriver(_Driver):
                            if "routing-table" in resource_value:
                                payload["routing-table"] = str(resource_value["routing-table"])

                        # --- Simple Queue ---
                        elif resource_key.startswith("/qos/queue/simple"):
                            endpoint = "/queue/simple"
                            payload = {
                                "name": str(resource_value["name"]),
                            }

                        # --- VRF ---
                        elif resource_key.startswith("/ip/vrf"):
                            endpoint = "/ip/vrf"
+1 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ def update_service_endpoint(
    vlan_tag : int, ipv4_address : str, neighbor_ipv4_address : str, ipv4_prefix_length : int,
    capacity_gbps : Optional[float] = None, e2e_latency_ms : Optional[float] = None,
    availability : Optional[float] = None, mtu : Optional[int] = None,
    service_input_bandwidth : Optional[int] = None, service_output_bandwidth : Optional[int] = None,
    static_routing : Optional[Dict[Tuple[str, int, int], str]] = None,
    context_uuid : Optional[str] = DEFAULT_CONTEXT_NAME, 
) -> Optional[Exception]:
+185 −0
Original line number Diff line number Diff line
{
  "ietf-l3vpn-svc:l3vpn-svc": {
    "vpn-services": {
      "vpn-service": [
        {
          "vpn-id": "svc-mikrotik-leaf1-leaf4-jetson-a-jetson-b"
        }
      ]
    },
    "sites": {
      "site": [
        {
          "site-id": "site_MikroTik-5-Leaf1-JetsonA",
          "management": {
            "type": "ietf-l3vpn-svc:provider-managed"
          },
          "locations": {
            "location": [
              {
                "location-id": "MikroTik-5-Leaf1"
              }
            ]
          },
          "devices": {
            "device": [
              {
                "device-id": "6ac9614b-6893-549d-b39c-d4af4320678a",
                "location": "MikroTik-5-Leaf1"
              }
            ]
          },
          "routing-protocols": {
            "routing-protocol": [
              {
                "type": "ietf-l3vpn-svc:static",
                "static": {
                  "cascaded-lan-prefixes": {
                    "ipv4-lan-prefixes": [
                      {
                        "lan": "172.16.1.0/24",
                        "lan-tag": "vlan10",
                        "next-hop": "172.16.1.10"
                      }
                    ]
                  }
                }
              }
            ]
          },
          "site-network-accesses": {
            "site-network-access": [
              {
                "site-network-access-id": "sfp-sfpplus6",
                "site-network-access-type": "ietf-l3vpn-svc:multipoint",
                "device-reference": "6ac9614b-6893-549d-b39c-d4af4320678a",
                "vpn-attachment": {
                  "vpn-id": "svc-mikrotik-leaf1-leaf4-jetson-a-jetson-b",
                  "site-role": "ietf-l3vpn-svc:hub-role"
                },
                "ip-connection": {
                  "ipv4": {
                    "address-allocation-type": "ietf-l3vpn-svc:static-address",
                    "addresses": {
                      "provider-address": "172.16.1.254",
                      "customer-address": "172.16.1.10",
                      "prefix-length": 24
                    }
                  }
                },
                "service": {
                  "svc-mtu": 1500,
                  "svc-input-bandwidth": 100000000,
                  "svc-output-bandwidth": 100000000,
                  "qos": {
                    "qos-profile": {
                      "classes": {
                        "class": [
                          {
                            "class-id": "qos-realtime",
                            "direction": "ietf-l3vpn-svc:both",
                            "latency": {
                              "latency-boundary": 10
                            },
                            "bandwidth": {
                              "guaranteed-bw-percent": 100
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            ]
          }
        },
        {
          "site-id": "site_MikroTik-8-Leaf4-JetsonB",
          "management": {
            "type": "ietf-l3vpn-svc:provider-managed"
          },
          "locations": {
            "location": [
              {
                "location-id": "MikroTik-8-Leaf4"
              }
            ]
          },
          "devices": {
            "device": [
              {
                "device-id": "03d3c087-da28-539e-aae0-60da7b90bad6",
                "location": "MikroTik-8-Leaf4"
              }
            ]
          },
          "routing-protocols": {
            "routing-protocol": [
              {
                "type": "ietf-l3vpn-svc:static",
                "static": {
                  "cascaded-lan-prefixes": {
                    "ipv4-lan-prefixes": [
                      {
                        "lan": "172.16.4.0/24",
                        "lan-tag": "vlan10",
                        "next-hop": "172.16.4.10"
                      }
                    ]
                  }
                }
              }
            ]
          },
          "site-network-accesses": {
            "site-network-access": [
              {
                "site-network-access-id": "sfp-sfpplus6",
                "site-network-access-type": "ietf-l3vpn-svc:multipoint",
                "device-reference": "03d3c087-da28-539e-aae0-60da7b90bad6",
                "vpn-attachment": {
                  "vpn-id": "svc-mikrotik-leaf1-leaf4-jetson-a-jetson-b",
                  "site-role": "ietf-l3vpn-svc:spoke-role"
                },
                "ip-connection": {
                  "ipv4": {
                    "address-allocation-type": "ietf-l3vpn-svc:static-address",
                    "addresses": {
                      "provider-address": "172.16.4.254",
                      "customer-address": "172.16.4.10",
                      "prefix-length": 24
                    }
                  }
                },
                "service": {
                  "svc-mtu": 1500,
                  "svc-input-bandwidth": 100000000,
                  "svc-output-bandwidth": 100000000,
                  "qos": {
                    "qos-profile": {
                      "classes": {
                        "class": [
                          {
                            "class-id": "qos-realtime",
                            "direction": "ietf-l3vpn-svc:both",
                            "latency": {
                              "latency-boundary": 10
                            },
                            "bandwidth": {
                              "guaranteed-bw-percent": 100
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      ]
    }
  }
}
+56 −3
Original line number Diff line number Diff line
@@ -53,6 +53,38 @@ def _route_rule(service_uuid: str, dst_address: str, gateway: str) -> Dict[str,
    }


def _routeros_rate(bps: Any) -> str:
    value = int(bps or 0)
    if value <= 0:
        return ""
    units = (
        (1_000_000_000, "G"),
        (1_000_000, "M"),
        (1_000, "k"),
    )
    for divisor, suffix in units:
        if value >= divisor and value % divisor == 0:
            return f"{value // divisor}{suffix}"
    return str(value)


def _queue_rule(service_uuid: str, customer_ip: str, input_bps: Any, output_bps: Any) -> Dict[str, Any]:
    upload_rate = _routeros_rate(input_bps)
    download_rate = _routeros_rate(output_bps)
    if not upload_rate and not download_rate:
        return {}
    if not upload_rate:
        upload_rate = download_rate
    if not download_rate:
        download_rate = upload_rate
    return {
        "name": f"tfs-l3vpn-{service_uuid[:8]}-{customer_ip.replace('.', '-')}",
        "target": f"{customer_ip}/32",
        "max-limit": f"{upload_rate}/{download_rate}",
        "comment": f"tfs-l3vpn:{service_uuid}:bandwidth",
    }


def setup_config_rules(
    service_uuid: str,
    local_settings: Dict[str, Any],
@@ -65,6 +97,7 @@ def setup_config_rules(
    remote_transit = transit_b if local_index == 0 else transit_a

    local_provider = local_settings["neighbor_address"]
    local_customer = local_settings["ip_address"]
    remote_customer = remote_settings["ip_address"]
    local_prefix = int(local_settings["prefix_length"])
    remote_prefix = int(remote_settings["prefix_length"])
@@ -72,7 +105,7 @@ def setup_config_rules(
    transit_prefix = ipaddress.ip_network(DEFAULT_TRANSIT_CIDR, strict=False).prefixlen
    remote_lan = _network(remote_customer, remote_prefix)

    return [
    rules = [
        json_config_rule_set(
            f"/interfaces/ip/l3vpn[{service_uuid}]/gateway",
            _ip_address_rule(service_uuid, "gateway", local_provider, local_prefix, bridge),
@@ -86,6 +119,15 @@ def setup_config_rules(
            _route_rule(service_uuid, remote_lan, remote_transit),
        ),
    ]
    queue = _queue_rule(
        service_uuid,
        local_customer,
        local_settings.get("svc_input_bandwidth"),
        local_settings.get("svc_output_bandwidth"),
    )
    if queue:
        rules.append(json_config_rule_set(f"/qos/queue/simple/l3vpn[{service_uuid}]", queue))
    return rules


def teardown_config_rules(
@@ -100,6 +142,7 @@ def teardown_config_rules(
    remote_transit = transit_b if local_index == 0 else transit_a

    local_provider = local_settings["neighbor_address"]
    local_customer = local_settings["ip_address"]
    remote_customer = remote_settings["ip_address"]
    local_prefix = int(local_settings["prefix_length"])
    remote_prefix = int(remote_settings["prefix_length"])
@@ -107,7 +150,16 @@ def teardown_config_rules(
    transit_prefix = ipaddress.ip_network(DEFAULT_TRANSIT_CIDR, strict=False).prefixlen
    remote_lan = _network(remote_customer, remote_prefix)

    return [
    queue = _queue_rule(
        service_uuid,
        local_customer,
        local_settings.get("svc_input_bandwidth"),
        local_settings.get("svc_output_bandwidth"),
    )
    rules = []
    if queue:
        rules.append(json_config_rule_delete(f"/qos/queue/simple/l3vpn[{service_uuid}]", queue))
    rules.extend([
        json_config_rule_delete(
            f"/ip/route/l3vpn[{service_uuid}]/remote",
            _route_rule(service_uuid, remote_lan, remote_transit),
@@ -120,4 +172,5 @@ def teardown_config_rules(
            f"/interfaces/ip/l3vpn[{service_uuid}]/gateway",
            _ip_address_rule(service_uuid, "gateway", local_provider, local_prefix, bridge),
        ),
    ]
    ])
    return rules
+380 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading