|
|
@@ -1,178 +1,244 @@
|
|
|
-import subprocess
|
|
|
-import sys
|
|
|
-import importlib
|
|
|
-import base64
|
|
|
-import json
|
|
|
-
|
|
|
-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")
|
|
|
- ]
|
|
|
-
|
|
|
- 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
|
|
|
-
|
|
|
-# 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 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 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))
|
|
|
- except ValueError:
|
|
|
- console.print("[bold blue]Response Body:[/bold blue]")
|
|
|
- console.print(response.text)
|
|
|
-
|
|
|
- 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:
|
|
|
+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...")
|