build v_1.0.1
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user