build v_1.0.1

This commit is contained in:
2026-07-21 15:05:52 +03:00
commit 6563d755d8
27 changed files with 1974 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
"""Argument parsing and top-level orchestration of STAGE 1-4.
Mirrors the bash original's literal top-to-bottom execution order: banner
and the "STAGE 1: Collecting information" header print before anything
else, then argument parsing, then mode dispatch. The `openstack` CLI
binary check that used to run here is gone -- OpenStack access is now
direct Neutron/Keystone REST calls (`keystone_auth.py`, `neutron_client.py`),
so no external binary is required at all.
"""
from __future__ import annotations
import argparse
import os
import sys
from . import audit, keystone_auth, sync
from .constants import BANNER, SPRUT_API_BASE, USAGE
from .sprut_client import SprutClient
def print_banner() -> None:
print(BANNER)
def print_usage() -> None:
print(USAGE)
def _rule() -> None:
print("*" * 86)
def parse_args(argv: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("--dry-run", action="store_true", dest="dry_run")
parser.add_argument("--audit-only", action="store_true", dest="audit_only")
parser.add_argument("--from-audit", dest="from_audit", default=None)
parser.add_argument("-h", "--help", action="store_true", dest="help")
parser.add_argument("positional", nargs="*")
args = parser.parse_args(argv)
if args.help:
print_usage()
sys.exit(0)
audit_only_seen = args.audit_only
from_audit_seen = args.from_audit is not None
if audit_only_seen and from_audit_seen:
print("Error: --audit-only and --from-audit are mutually exclusive.", file=sys.stderr)
sys.exit(1)
if from_audit_seen:
mode = "from-audit"
elif audit_only_seen:
mode = "audit-only"
else:
mode = "full"
input_csv = None
output_file = None
if mode == "from-audit":
if not args.from_audit:
print("Error: --from-audit requires a path to an audit JSON file.", file=sys.stderr)
print_usage()
sys.exit(1)
if not os.path.isfile(args.from_audit):
print(f"Error: audit file '{args.from_audit}' not found.", file=sys.stderr)
sys.exit(1)
else:
if not args.positional:
print("Error: No input file provided.")
print_usage()
sys.exit(1)
input_csv = args.positional[0]
if len(args.positional) > 1:
output_file = args.positional[1]
return argparse.Namespace(
mode=mode,
dry_run=args.dry_run,
from_audit=args.from_audit,
input_csv=input_csv,
output_file=output_file,
)
def _default_output_file() -> str:
from datetime import datetime
return f"vpnaas_audit_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
def main(argv: list[str] | None = None) -> int:
# Python buffers stdout in blocks (not line-by-line) whenever it isn't a
# TTY, while stderr stays unbuffered -- under redirection (e.g. `2>&1 |
# tee log`) that visibly reorders stdout/stderr lines relative to each
# other. Bash's `echo` has no such issue (each call is its own unbuffered
# write), so line-buffer stdout here to keep interleaving faithful.
sys.stdout.reconfigure(line_buffering=True)
print_banner()
print("{====================STAGE 1: Collecting information====================}")
print()
print()
args = parse_args(argv if argv is not None else sys.argv[1:])
if args.dry_run:
print("Running in --dry-run mode: no objects will be created in Sprut.")
if args.mode == "from-audit":
state = audit.load_from_audit_json(args.from_audit)
token, _catalog = keystone_auth.authenticate()
sprut_client = SprutClient(SPRUT_API_BASE, token, args.dry_run)
audit.recheck_advanced_routers_from_audit(state, sprut_client)
else:
state, sprut_client = audit.collect(
args.input_csv, SPRUT_API_BASE, args.dry_run, audit_only=(args.mode == "audit-only")
)
output_file = args.output_file or _default_output_file()
print(f"Executing STEP 10: Writing audit JSON to {output_file}")
audit_data = audit.to_audit_dict(state, args.input_csv)
audit.write_audit_json(audit_data, output_file)
print("STEP 10 complete: audit JSON exported")
_rule()
print("{====================STAGE 1: COMPLETE====================}")
print()
if args.mode == "audit-only":
print("Mode --audit-only: stopping after STAGE 1 (Sprut configuration stages skipped).")
return 0
print("{====================STAGE 2: Collecting Existing Sprut Objects====================}")
sprut_state = sync.collect_sprut_state(sprut_client)
print("STAGE 2 complete: Sprut objects collected")
_rule()
print("{====================STAGE 3: Comparing and Creating Missing Objects in Sprut====================}")
correspondence = sync.compare_and_create_all(state, sprut_client, sprut_state)
print("STAGE 3 complete: Missing Sprut objects created")
_rule()
print("{====================STAGE 4: Creating IPsec Site Connections in Sprut====================}")
sync.create_ipsec_site_connections(state, sprut_client, sprut_state, correspondence)
print("STAGE 4 complete: IPsec site connections created in Sprut")
_rule()
return 0