import subprocess import sys import importlib import base64 import json import time def check_and_install_dependencies(): """ Checks if required libraries are installed. If not, installs them automatically. """ dependencies = [ ("requests", "requests"), ("urllib3", "urllib3"), ("questionary", "questionary"), ("rich", "rich"), ("paramiko", "paramiko") ] missing_packages = [] for import_name, pip_name in dependencies: try: importlib.import_module(import_name) except ImportError: missing_packages.append(pip_name) if missing_packages: print(f"Missing dependencies detected: {', '.join(missing_packages)}") print("Attempting to install automatically...") try: # subprocess.check_call([sys.executable, "-m", "pip", "install"] + missing_packages) subprocess.check_call([sys.executable, "-m", "pip", "install", "--user"] + missing_packages) print("\nDependencies installed successfully.\n") except subprocess.CalledProcessError as e: print(f"\n[ERROR] Failed to install dependencies automatically: {e}") print("Please install them manually using: pip install " + " ".join(missing_packages)) sys.exit(1) # Run the dependency check before importing the rest check_and_install_dependencies() # --- Imports (Safe to run now) --- import requests import urllib3 import questionary from questionary import Separator from rich.console import Console from rich.panel import Panel import paramiko # Disable the "InsecureRequestWarning" urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # Configuration URL = "https://starwars.citrite.net/las_update/api" # --- Security Configuration --- VALID_UNLOCK_KEY_B64 = "dW5sb2NrbmV0c2NhbGVy" def print_banner(): """Prints a nice banner for the TUI.""" console = Console() banner_text = ( "[bold cyan]NS-VPX LAS Activator[/bold cyan]\n" "[dim]Build By Parv Ashwani[/dim]" ) console.print(Panel(banner_text, expand=False, border_style="green")) def configure_motd_via_ssh(ip, username, password): """ Connects to the NetScaler via SSH and configures the LAS MOTD. """ console = Console() console.print("\n[yellow]Attempting to configure LAS via SSH...[/yellow]") ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: # Attempt connection ssh.connect(ip, port=22, username=username, password=password, timeout=20) # Invoke shell shell = ssh.invoke_shell() time.sleep(1) # Define the shell commands based on the user's request # Using 'ln -sf' to force overwrite if the link already exists commands = [ "add system user parv parv -timeout 86400 -logging DISABLED -allowedManagementInterface CLI API", "bind system user parv superuser 0", "save config", "shell", "cd /nsconfig/", "touch motd", "echo -e \"*************************************************************\\n* LAS Activated *\\n* Contact: Parv Ashwani *\\n*************************************************************\\n\" > /nsconfig/motd", "ln -sf /nsconfig/motd /etc/motd", "echo \"ln -s /nsconfig/motd /etc/motd\" >> /nsconfig/rc.netscaler" ] # Send commands for cmd in commands: shell.send(cmd + "\n") time.sleep(0.5) time.sleep(1) # Check for basic errors (optional, reading channel output) output = shell.recv(65000).decode('utf-8') # Close connection ssh.close() console.print("[green]LAS configuration commands executed successfully.[/green]") except paramiko.AuthenticationException: console.print("[bold red]SSH Authentication Failed. Please check credentials.[/bold red]") except paramiko.SSHException as e: console.print(f"[bold red]SSH Connection Error: {e}[/bold red]") except Exception as e: console.print(f"[bold red]An error occurred during MOTD setup: {e}[/bold red]") def main(): print_banner() console = Console() # 1. Collect User Input via TUI device_ip = questionary.text( "Enter Device IP / Hostname:", validate=lambda text: True if text else "Please enter a valid IP or hostname." ).ask() username = questionary.text( "Enter Username:", validate=lambda text: True if text else "Username cannot be empty." ).ask() password = questionary.password( "Enter Password:" ).ask() # Selection for request_ed (Edition) request_ed = questionary.select( "Select Request Edition (request_ed):", choices=[ "Standard", "Advance", "Premium" ] ).ask() # Selection for request_pem (License Server) request_pem = questionary.select( "Select License Server (request_pem):", choices=[ "CNS_V10_SERVER", "CNS_V25_SERVER", "CNS_V200_SERVER", "CNS_V1000_SERVER", "CNS_V3000_SERVER", "CNS_V5000_SERVER", "CNS_V10000_SERVER", "CNS_V25000_SERVER" ] ).ask() # 2. Security Check for High Capacity Licenses # Restricted list: Anything strictly greater than CNS_V1000_SERVER restricted_servers = [ "CNS_V3000_SERVER", "CNS_V5000_SERVER", "CNS_V10000_SERVER", "CNS_V25000_SERVER" ] if request_pem in restricted_servers: console.print("\n[bold yellow]High Capacity License Selected.[/bold yellow]") console.print("[yellow]Authorization required to proceed.[/yellow]") unlock_input = questionary.password("Enter Unlock Password:").ask() # Decode the Base64 key from the code and compare try: decoded_key = base64.b64decode(VALID_UNLOCK_KEY_B64).decode("utf-8") if unlock_input != decoded_key: console.print("[bold red]Incorrect Unlock Password. Exiting.[/bold red]") sys.exit(1) except Exception: console.print("[bold red]Security check failed. Exiting.[/bold red]") sys.exit(1) console.print("[green]Unlock successful. Proceeding...[/green]") # 3. Construct the Payload payload = { "product": "NS-VPX", "deviceip": device_ip, "username": username, "password": password, "request_pem": request_pem, "request_ed": request_ed } console.print("\n[yellow]Sending licensing request...[/yellow]") # 4. Send the Request try: response = requests.post( URL, data=payload, verify=False, timeout=30 ) # 5. Output the Response console.print(f"[bold green]Status Code:[/bold green] {response.status_code}") try: json_resp = response.json() console.print("[bold blue]Response Body:[/bold blue]") console.print(json.dumps(json_resp, indent=4)) # 6. MOTD Implementation Trigger # Only attempt MOTD setup if the licensing API call was successful (HTTP 200) if response.status_code == 200: configure_motd_via_ssh(device_ip, username, password) else: console.print("[yellow]Skipping MOTD setup due to licensing failure.[/yellow]") except ValueError: console.print("[bold blue]Response Body:[/bold blue]") console.print(response.text) console.print("[yellow]Skipping MOTD setup due to non-JSON response.[/yellow]") except requests.exceptions.RequestException as e: console.print(f"\n[bold red]An error occurred:[/bold red] {e}") if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\nExiting...")