Skip to content

Commit 98b00a7

Browse files
authored
Vpngw import (#80)
* fix importer issues * fix importer issues
1 parent d4b467f commit 98b00a7

File tree

13 files changed

+1564
-383
lines changed

13 files changed

+1564
-383
lines changed

services/vpngw/README.md

Lines changed: 98 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -546,12 +546,19 @@ Safe to rerun.
546546
```bash
547547
nebius-vpngw create-from-peer-config my-vpn.config.yaml \
548548
--peer-config-file gcp-peer.txt \
549-
--peer-config-file aws-peer.xml
549+
--peer-config-file branch-office.csv
550550
```
551551
552552
If the generated output already matches the existing file, rerunning the command
553553
is a no-op and exits successfully.
554554
555+
Supported peer input formats:
556+
557+
- `.txt`: free-form text, exported vendor configs, and config snippets
558+
- `.csv`: one row per tunnel; rows with the same `connection_name` + `vendor` are grouped
559+
- `.json`
560+
- `.yaml` / `.yml`
561+
555562
### Deployment
556563
557564
**Deploy or update:**
@@ -954,26 +961,61 @@ nebius-vpngw list-routes-remote --local-config-file <file>
954961
955962
## Peer Integration
956963
957-
### Supported Vendors
964+
### Supported Peer Inputs
958965
959-
- **GCP HA VPN:** Cloud Router config exports
960-
- **AWS Site-to-Site VPN:** Downloadable config files
961-
- **Azure VPN Gateway:** Exported configurations
962-
- **Cisco IOS:** IOS config snippets
966+
- **Text (`.txt`)**: vendor exports, router snippets, and key/value documents
967+
- **CSV (`.csv`)**: one row per tunnel; rows with the same `connection_name` + `vendor` are grouped
968+
- **JSON**
969+
- **YAML (`.yaml` / `.yml`)**
970+
971+
Vendor detection is best-effort and currently recognizes **GCP**, **AWS**, **Azure**, and **Cisco**. If nothing matches, the importer falls back to `vendor: generic`.
963972
964973
### Import Workflow
965974
966975
```bash
967976
nebius-vpngw create-from-peer-config nebius-vpn.config.yaml \
968-
--peer-config-file gcp-peer.txt \
969-
--peer-config-file aws-peer.xml
977+
--peer-config-file gcp-peer.txt
970978
```
971979
972-
**Merge behavior:**
980+
`create-from-peer-config` now builds `connections:` from parsed peer specs instead of reusing the template's fixed sample topology. The generated file is validated against the schema before it is written.
981+
982+
### Keyword Matching
983+
984+
The importer normalizes input keys and matches them against a keyword list. These aliases work across CSV headers, JSON/YAML keys, and `key: value` / `key = value` text.
985+
986+
| Target field | Accepted keywords |
987+
| --- | --- |
988+
| `connection.name` | `connection_name`, `vpn_name`, `peer_name`, `gateway_name`, `router_name`, `name` |
989+
| `connection.vendor` | `vendor`, `provider`, `cloud`, `platform` |
990+
| `connection.routing_mode` | `routing_mode`, `route_mode`, `routing_protocol`, `mode`, `protocol` |
991+
| `connection.bgp.remote_asn` | `remote_asn`, `peer_asn`, `bgp_asn`, `neighbor_asn`, `cloud_router_asn`, `vgw_asn`, `asn` |
992+
| `connection.remote_prefixes` | `remote_prefixes`, `remote_networks`, `destination_prefixes`, `destination_cidrs`, `routes`, `networks`, `subnets` |
993+
| `tunnel.name` | `tunnel_name`, `vpn_tunnel_name`, `interface_name`, `interface`, `name` |
994+
| `tunnel.remote_public_ip` | `remote_public_ip`, `peer_public_ip`, `remote_gateway_ip`, `vpn_gateway_ip`, `peer_ip`, `endpoint_ip`, `remote_ip`, `outside_ip` |
995+
| `tunnel.psk` | `psk`, `pre_shared_key`, `shared_secret`, `shared_key`, `ipsec_shared_secret`, `secret` |
996+
| `tunnel.inner_cidr` | `inner_cidr`, `inside_cidr`, `tunnel_cidr`, `link_cidr`, `vti_cidr`, `apipa_cidr`, `inside_ip_addresses` |
997+
| `tunnel.inner_local_ip` | `inner_local_ip`, `local_inside_ip`, `customer_inside_ip`, `customer_gateway_inside_address`, `peer_ip_address`, `apipa_local` |
998+
| `tunnel.inner_remote_ip` | `inner_remote_ip`, `remote_inside_ip`, `vpn_gateway_inside_address`, `cloud_inside_ip`, `bgp_peer_ip`, `ip_address`, `apipa_remote` |
999+
| `tunnel.gateway_instance_index` | `gateway_instance_index`, `instance_index`, `vm_index` |
1000+
| `tunnel.local_public_ip_index` | `local_public_ip_index`, `public_ip_index`, `nic_index`, `interface_index` |
1001+
| `tunnel.ha_role` | `ha_role`, `role`, `state`, `active_standby_role` |
9731002

974-
- Peer values overwrite template defaults when present
975-
- Topology is taken from the generated config and should be reviewed
976-
- Validate before deployment
1003+
### Defaulting Rules
1004+
1005+
If a field cannot be matched, the importer keeps going and fills schema-safe defaults:
1006+
1007+
- `vendor`: `generic`
1008+
- `routing_mode`: `static` when remote prefixes exist, `bgp` when a remote ASN exists, otherwise `static` for `cisco/generic` and `bgp` for cloud vendors
1009+
- BGP `remote_asn`: `65014` when BGP is selected but no ASN is found
1010+
- Static `remote_prefixes`: `192.0.2.0/24`
1011+
- Tunnel defaults:
1012+
- `gateway_instance_index: 0`
1013+
- `local_public_ip_index: 0`
1014+
- first tunnel `ha_role: active`, later tunnels `ha_role: passive`
1015+
- generated APIPA `/30` ranges when inner addresses are missing
1016+
- generated PSK placeholders such as `${GCP_HA_VPN_TUNNEL_1_PSK}`
1017+
1018+
Review the generated YAML before deployment, especially placeholder PSKs, remote prefixes, public IPs, and any inferred routing mode.
9771019

9781020
### Example: GCP HA VPN
9791021

@@ -992,7 +1034,7 @@ nebius-vpngw create-from-peer-config gcp-ha-vpn.config.yaml \
9921034
--peer-config-file gcp-peer.txt
9931035
```
9941036

995-
**3. Review and fill in required values (tenant/project/region/PSKs/local prefixes):**
1037+
**3. Generated BGP target shape (review and adjust values before deploy):**
9961038

9971039
```yaml
9981040
connections:
@@ -1004,30 +1046,61 @@ connections:
10041046
remote_asn: 65014
10051047
advertise_local_prefixes: true
10061048
tunnels:
1007-
- name: tunnel-1
1049+
- name: nebius-204-12-170-147-tunnel-1
10081050
gateway_instance_index: 0
1009-
remote_public_ip: "203.0.113.1"
1010-
psk: ${GCP_TUNNEL_1_PSK}
1051+
local_public_ip_index: 0
1052+
ha_role: active
1053+
remote_public_ip: "34.157.15.187"
1054+
psk: "${GCP_HA_VPN_TUNNEL_1_PSK}"
10111055
inner_cidr: "169.254.10.0/30"
10121056
inner_local_ip: "169.254.10.1"
10131057
inner_remote_ip: "169.254.10.2"
1014-
- name: tunnel-2
1058+
- name: nebius-204-12-170-147-tunnel-2
10151059
gateway_instance_index: 0
1016-
remote_public_ip: "203.0.113.2"
1017-
psk: ${GCP_TUNNEL_2_PSK}
1060+
local_public_ip_index: 0
1061+
ha_role: passive
1062+
remote_public_ip: "34.157.140.153"
1063+
psk: "${GCP_HA_VPN_TUNNEL_2_PSK}"
10181064
inner_cidr: "169.254.11.0/30"
10191065
inner_local_ip: "169.254.11.1"
10201066
inner_remote_ip: "169.254.11.2"
10211067
```
10221068

1069+
### Example: Static Routing Import
1070+
1071+
If the input contains remote prefixes but no BGP ASN, the generator emits a static connection:
1072+
1073+
```yaml
1074+
connections:
1075+
- name: onprem-static
1076+
vendor: cisco
1077+
routing_mode: static
1078+
remote_prefixes:
1079+
- "192.168.0.0/16"
1080+
bgp:
1081+
enabled: false
1082+
remote_asn: null
1083+
advertise_local_prefixes: false
1084+
tunnels:
1085+
- name: onprem-static-tunnel-1
1086+
gateway_instance_index: 0
1087+
local_public_ip_index: 0
1088+
ha_role: active
1089+
remote_public_ip: "203.0.113.5"
1090+
psk: "${ONPREM_STATIC_TUNNEL_1_PSK}"
1091+
inner_cidr: "169.254.30.0/30"
1092+
inner_local_ip: "169.254.30.1"
1093+
inner_remote_ip: "169.254.30.2"
1094+
```
1095+
10231096
**4. Validate and deploy:**
10241097

10251098
```bash
10261099
nebius-vpngw validate-config gcp-ha-vpn.config.yaml
10271100
nebius-vpngw apply --local-config-file gcp-ha-vpn.config.yaml
10281101
```
10291102

1030-
Peer import only fills what it can from the vendor file; PSKs and public IPs may still need to be set manually.
1103+
Peer import only fills what it can from the input. Any placeholder or inferred value should be treated as a review item, not as final deployment intent.
10311104

10321105
## VM Management
10331106

@@ -1660,12 +1733,10 @@ Notes:
16601733
│ │ ├── vm_diff.py # VM change detection
16611734
│ │ ├── route_manager.py # VPC route management
16621735
│ │ └── ssh_push.py # SSH deployment
1663-
│ ├── peer_parsers/ # Vendor config parsers
1736+
│ ├── peer_parsers/ # Keyword-based peer config importer
16641737
│ │ ├── __init__.py
1665-
│ │ ├── gcp.py
1666-
│ │ ├── aws.py
1667-
│ │ ├── azure.py
1668-
│ │ └── cisco.py
1738+
│ │ ├── common.py
1739+
│ │ └── importer.py
16691740
│ └── systemd/ # Systemd units/scripts
16701741
│ ├── nebius-vpngw-agent.service # Agent service unit
16711742
│ ├── nebius-vpngw-health-monitor.service # Tunnel health monitor service unit
@@ -1707,7 +1778,8 @@ Notes:
17071778

17081779
**Peer Parsers:**
17091780

1710-
- `gcp.py`, `aws.py`, `azure.py`, `cisco.py`: Vendor-specific config normalization
1781+
- `importer.py`: Keyword-based peer config import for `.txt`, `.csv`, `.json`, `.yaml`, `.yml`
1782+
- `common.py`: Shared key normalization, vendor inference, and importer helpers
17111783

17121784
---
17131785

services/vpngw/pyrightconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
]
2525
}
2626
],
27+
"venvPath": ".",
28+
"venv": ".venv",
2729
"pythonVersion": "3.10",
2830
"typeCheckingMode": "standard",
2931
"reportMissingModuleSource": "warning",

services/vpngw/src/nebius_vpngw/cli.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
from .config_loader import (
1717
GatewayGroupSpec,
1818
ResolvedDeploymentPlan,
19+
build_config_from_peer_files,
1920
load_local_config,
20-
merge_peer_configs_into_local_config,
2121
merge_with_peer_configs,
2222
)
2323
from .config_template import DEFAULT_CONFIG_TEMPLATE
@@ -1354,14 +1354,18 @@ def create_from_peer_config(
13541354
..., help="Path for new configuration file (recommended: *.config.yaml)"
13551355
),
13561356
peer_config_file: list[Path] = typer.Option(
1357-
..., exists=True, readable=True, help="Vendor peer config file(s)"
1357+
...,
1358+
exists=True,
1359+
readable=True,
1360+
help="Peer config file(s). Supported formats: .txt, .csv, .json, .yaml, .yml",
13581361
),
13591362
force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing file if it exists"),
13601363
):
1361-
"""Create a new configuration file by merging peer configs into the template.
1364+
"""Create a schema-aligned configuration from keyword-imported peer inputs.
13621365
1363-
This generates a standalone YAML config file aligned with the schema for
1364-
review and validation before deployment.
1366+
This generates a standalone YAML config file from `.txt`, `.csv`, `.json`,
1367+
`.yaml`, or `.yml` peer inputs using the shared keyword-based importer.
1368+
The generated output is validated against the config schema before write.
13651369
13661370
Safe to rerun: if the target file already contains the exact generated
13671371
output for the same inputs, the command exits successfully without
@@ -1377,7 +1381,7 @@ def create_from_peer_config(
13771381
console.print(
13781382
Panel.fit(
13791383
"[bold red]✗ No peer config file provided[/bold red]\n\n"
1380-
"Use --peer-config-file to specify at least one vendor config.",
1384+
"Use --peer-config-file to specify at least one input file.",
13811385
title="[red]Error[/red]",
13821386
border_style="red",
13831387
)
@@ -1408,15 +1412,16 @@ def create_from_peer_config(
14081412

14091413
try:
14101414
base_cfg = yaml.safe_load(DEFAULT_CONFIG_TEMPLATE) or {}
1411-
merged_cfg = merge_peer_configs_into_local_config(
1412-
base_cfg, peer_config_file, prefer_peer=True
1413-
)
1415+
merged_cfg = build_config_from_peer_files(base_cfg, peer_config_file)
1416+
from .schema import validate_config
1417+
1418+
validate_config(merged_cfg)
14141419
desired_text = _normalize_file_text(yaml.safe_dump(merged_cfg, sort_keys=False))
14151420

14161421
if merged_cfg == base_cfg:
14171422
console.print(
1418-
"[yellow]⚠️ Peer config did not change the template. "
1419-
"Review the file and fill in any missing fields manually.[/yellow]"
1423+
"[yellow]⚠️ The importer did not detect meaningful peer fields. "
1424+
"Review the input file and fill in any missing values manually.[/yellow]"
14201425
)
14211426

14221427
if config_file.exists() and not force:

0 commit comments

Comments
 (0)