Skip to content
Snippets Groups Projects
Commit 38a350bc authored by Neda Moeini's avatar Neda Moeini
Browse files

Added import CLI for importing lab data.

parent 3f47469e
No related branches found
No related tags found
1 merge request!121Feature/nat 354 import lab data to core db
[{
"customer": "GÉANT",
"router_site": "AMS",
"hostname": "rt1.ams.nl.lab.office",
"ts_port": 22111,
"router_vendor": "juniper",
"router_role": "p",
"router_lo_ipv4_address": "62.40.119.2",
"router_lo_ipv6_address": "2001:798:1ab::2",
"router_lo_iso_address": "49.51e5.0001.0620.4011.9002.00",
"is_ias_connected": false
},
{
"customer": "GÉANT",
"router_site": "AMS",
"hostname": "rt1.ath.gr.lab.office",
"ts_port": 22111,
"router_vendor": "juniper",
"router_role": "p",
"router_lo_ipv4_address": "62.40.119.1",
"router_lo_ipv6_address": "2001:798:1ab::1",
"router_lo_iso_address": "49.51e5.0001.0620.4011.9001.00",
"is_ias_connected": false
}
]
[
{
"site_name": "AMS2",
"site_city": "Amsterdam",
"site_country": "The Netherlands",
"site_country_code": "NL",
"site_latitude": 0,
"site_longitude": 0,
"site_bgp_community_id": 4,
"site_internal_id": 4,
"site_tier": "1",
"site_ts_address": "0.1.1.1",
"customer": "GÉANT"
}
]
//Missing: City, Country Code, Tier
\ No newline at end of file
- site_name: AMS2
site_city: Amsterdam
site_country: The Netherlands
site_country_code: NL
site_latitude: 0
site_longitude: 0
site_bgp_community_id: 4
site_internal_id: 4
site_tier: '1'
site_ts_address: 0.1.1.1
customer: GÉANT
\ No newline at end of file
[{
"customer": "GÉANT",
"geant_s_sid": "12",
"iptrunk_type": "Dark_fiber",
"iptrunk_description": "Description",
"iptrunk_speed": "100G",
"iptrunk_minimum_links": 1,
"side_a_node_id": "",
"side_a_ae_iface": "string",
"side_a_ae_geant_a_sid": "string",
"side_a_ae_members": [
{
"interface_name": "string",
"interface_description": "string"
}
],
"side_b_node_id": "string",
"side_b_ae_iface": "string",
"side_b_ae_geant_a_sid": "string",
"side_b_ae_members": [
{
"interface_name": "string",
"interface_description": "string"
}
],
"iptrunk_ipv4_network": "string",
"iptrunk_ipv6_network": "string" // calculated from ipv6
}]
- id: LGS-00001 # Comes from SM identifies the Trunk
config:
common:
link_speed: 100g
minimum_links: 1
isis_metric: 500
type: "Leased"
nodeA:
name: rt1.ath.gr.lab.office.geant.net
ae_name: ae0
port_sid: LGA-00001 # Comes from SM - identifies the ae
members:
- interface_name: et-0/0/2
interface_description: et-0/0/2
ipv4_address: 62.40.98.0/31
ipv6_address: 2001:798:cc::1/126
nodeB:
name: rt1.ams.nl.lab.office.geant.net
ae_name: ae0
port_sid: LGA-00002 # Comes from SM - identifies the ae
members:
- interface_name: et-9/0/2
interface_description: et-9/0/2
ipv4_address: 62.40.98.1/31
ipv6_address: 2001:798:cc::2/126
......@@ -2,13 +2,13 @@
import typer
from orchestrator import OrchestratorCore, app_settings
from orchestrator.cli.main import app as cli_app
# noinspection PyUnresolvedReferences
import gso.products
import gso.workflows # noqa: F401
from gso.api import router as api_router
from gso.cli import netbox
from gso.cli import imports
from orchestrator.cli.main import app as cli_app
def init_gso_app() -> OrchestratorCore:
......@@ -25,8 +25,8 @@ def init_worker_app() -> OrchestratorCore:
def init_cli_app() -> typer.Typer:
"""Initialise :term:`GSO` as a CLI application."""
from gso.cli import import_sites # noqa: PLC0415
from gso.cli import import_sites, netbox # noqa: PLC0415
cli_app.add_typer(import_sites.app, name="import_sites")
cli_app.add_typer(imports.app, name="import-cli")
cli_app.add_typer(netbox.app, name="netbox-cli")
return cli_app()
""":term:`CLI` command for importing sites."""
import typer
app: typer.Typer = typer.Typer()
@app.command()
def import_sites() -> None:
"""Import sites from a source."""
# TODO: Implement this CLI command to import sites from a source.
typer.echo("Importing sites...")
""":term:`CLI` command for importing data to coreDB."""
import ipaddress
import json
import os
from typing import TypeVar, Type, Any
import typer
import yaml
from pydantic import ValidationError
from gso.api.v1.imports import (
SiteImportModel,
import_site,
RouterImportModel,
import_router,
IptrunkImportModel,
import_iptrunk,
)
from gso.services.subscriptions import get_active_subscriptions_by_field_and_value
app: typer.Typer = typer.Typer()
T = TypeVar("T", SiteImportModel, RouterImportModel, IptrunkImportModel)
def read_data(filepath: str) -> dict | None:
"""Read data from a JSON or YAML file."""
typer.echo(f"Starting import from {filepath}")
_, file_extension = os.path.splitext(filepath)
with open(filepath, "r") as f:
if file_extension.lower() == ".json":
return json.load(f)
elif file_extension.lower() in (".yaml", ".yml"):
return yaml.safe_load(f)
else:
typer.echo(f"Unsupported file format: {file_extension}")
return
def generic_import_data(filepath: str, import_model: Type[T], import_function: callable, name_key: str):
"""Generic function to import data from a JSON or YAML file."""
successfully_imported_data = []
data = read_data(filepath)
for details in data:
details["customer"] = "GÉANT"
typer.echo(f"Importing {name_key}: {details[name_key]}")
try:
initial_data = import_model(**details)
import_function(initial_data)
successfully_imported_data.append(getattr(initial_data, name_key))
typer.echo(f"Successfully imported {name_key}: {getattr(initial_data, name_key)}")
except ValidationError as e:
typer.echo(f"Validation error: {e}")
if successfully_imported_data:
typer.echo(f"Successfully imported {name_key}s:")
for item in successfully_imported_data:
typer.echo(f"- {item}")
@app.command()
def import_sites(
filepath: str = typer.Option(help="Path to the file containing the sites to import.", default="gso/sites.json")
):
"""
Import sites into GSO.
"""
# Use the import_data function to handle common import logic
generic_import_data(filepath, SiteImportModel, import_site, "site_name")
@app.command()
def import_routers(
filepath: str = typer.Option(
help="Path to the file containing the routers to import.", default="gso/routers.json", show_default=True
)
):
"""
Import routers into GSO.
"""
# Use the import_data function to handle common import logic
generic_import_data(filepath, RouterImportModel, import_router, "hostname")
def get_router_subscription_id(node_name: str) -> str | None:
"""Get the subscription id for a router by its node name."""
subscriptions = get_active_subscriptions_by_field_and_value("router_fqdn", node_name)
if subscriptions:
return str(subscriptions[0].subscription_id)
else:
return None
@app.command()
def import_iptrunks(
filepath: str = typer.Option(
help="Path to the file containing the routers to import.", default="gso/trunks.json", show_default=True
)
):
"""
Import IP trunks into GSO.
"""
successfully_imported_data = []
data = read_data(filepath)
for trunk in data:
ipv4_network_a = ipaddress.ip_network(trunk["config"]["nodeA"]["ipv4_address"], strict=False)
ipv4_network_b = ipaddress.ip_network(trunk["config"]["nodeB"]["ipv4_address"], strict=False)
ipv6_network_a = ipaddress.ip_network(trunk["config"]["nodeA"]["ipv6_address"], strict=False)
ipv6_network_b = ipaddress.ip_network(trunk["config"]["nodeB"]["ipv6_address"], strict=False)
# Check if IPv4 networks are equal
if ipv4_network_a == ipv4_network_b:
iptrunk_ipv4_network = ipv4_network_a
else:
# Handle the case where IPv4 networks are different
typer.echo(f"Error: IPv4 networks are different for trunk {trunk['id']}.")
continue
# Check if IPv6 networks are the same
if ipv6_network_a == ipv6_network_b:
iptrunk_ipv6_network = ipv6_network_a
else:
# Handle the case where IPv6 networks are different
typer.echo(f"Error: IPv6 networks are different for trunk {trunk['id']}.")
continue
typer.echo(
f'Importing IP Trunk: {get_active_subscriptions_by_field_and_value("router_fqdn", trunk["config"]["nodeA"]["name"])}'
)
try:
initial_data = IptrunkImportModel(
customer="GÉANT",
geant_s_sid=trunk["id"],
iptrunk_type=trunk["config"]["common"]["type"],
iptrunk_description=" ",
iptrunk_speed=trunk["config"]["common"]["link_speed"],
iptrunk_minimum_links=trunk["config"]["common"]["minimum_links"],
side_a_node_id=get_router_subscription_id(trunk["config"]["nodeA"]["name"]),
side_a_ae_iface=trunk["config"]["nodeA"]["ae_name"],
side_a_ae_geant_a_sid=trunk["config"]["nodeA"]["port_sid"],
side_a_ae_members=trunk["config"]["nodeA"]["members"],
side_b_node_id=get_router_subscription_id(trunk["config"]["nodeB"]["name"]),
side_b_ae_iface=trunk["config"]["nodeB"]["ae_name"],
side_b_ae_geant_a_sid=trunk["config"]["nodeB"]["port_sid"],
side_b_ae_members=trunk["config"]["nodeB"]["members"],
iptrunk_ipv4_network=iptrunk_ipv4_network,
iptrunk_ipv6_network=iptrunk_ipv6_network,
)
import_iptrunk(initial_data)
successfully_imported_data.append(trunk["id"])
typer.echo(f"Successfully imported IP Trunk: {trunk['id']}")
except ValidationError as e:
typer.echo(f"Validation error: {e}")
if successfully_imported_data:
typer.echo(f"Successfully imported IP Trunks:")
for item in successfully_imported_data:
typer.echo(f"- {item}")
......@@ -25,12 +25,12 @@ def _get_site_by_name(site_name: str) -> Site:
:param site_name: The name of the site.
:type site_name: str
"""
subscription = subscriptions.get_active_subscriptions_by_field_and_value("site_name", site_name)[0]
subscription = subscriptions.get_active_subscriptions_by_field_and_value("site_name", site_name)
if not subscription:
msg = f"Site with name {site_name} not found."
raise ValueError(msg)
return Site.from_subscription(subscription.subscription_id)
return Site.from_subscription(subscription[0].subscription_id)
@step("Create subscription")
......
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