LASME.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import subprocess
  2. import sys
  3. import importlib
  4. import base64
  5. import json
  6. import time
  7. def check_and_install_dependencies():
  8. """
  9. Checks if required libraries are installed. If not, installs them automatically.
  10. """
  11. dependencies = [
  12. ("requests", "requests"),
  13. ("urllib3", "urllib3"),
  14. ("questionary", "questionary"),
  15. ("rich", "rich"),
  16. ("paramiko", "paramiko")
  17. ]
  18. missing_packages = []
  19. for import_name, pip_name in dependencies:
  20. try:
  21. importlib.import_module(import_name)
  22. except ImportError:
  23. missing_packages.append(pip_name)
  24. if missing_packages:
  25. print(f"Missing dependencies detected: {', '.join(missing_packages)}")
  26. print("Attempting to install automatically...")
  27. try:
  28. # subprocess.check_call([sys.executable, "-m", "pip", "install"] + missing_packages)
  29. subprocess.check_call([sys.executable, "-m", "pip", "install", "--user"] + missing_packages)
  30. print("\nDependencies installed successfully.\n")
  31. except subprocess.CalledProcessError as e:
  32. print(f"\n[ERROR] Failed to install dependencies automatically: {e}")
  33. print("Please install them manually using: pip install " + " ".join(missing_packages))
  34. sys.exit(1)
  35. # Run the dependency check before importing the rest
  36. check_and_install_dependencies()
  37. # --- Imports (Safe to run now) ---
  38. import requests
  39. import urllib3
  40. import questionary
  41. from questionary import Separator
  42. from rich.console import Console
  43. from rich.panel import Panel
  44. import paramiko
  45. # Disable the "InsecureRequestWarning"
  46. urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
  47. # Configuration
  48. URL = "https://starwars.citrite.net/las_update/api"
  49. # --- Security Configuration ---
  50. VALID_UNLOCK_KEY_B64 = "dW5sb2NrbmV0c2NhbGVy"
  51. def print_banner():
  52. """Prints a nice banner for the TUI."""
  53. console = Console()
  54. banner_text = (
  55. "[bold cyan]NS-VPX LAS Activator[/bold cyan]\n"
  56. "[dim]Build By Parv Ashwani[/dim]"
  57. )
  58. console.print(Panel(banner_text, expand=False, border_style="green"))
  59. def configure_motd_via_ssh(ip, username, password):
  60. """
  61. Connects to the NetScaler via SSH and configures the LAS MOTD.
  62. """
  63. console = Console()
  64. console.print("\n[yellow]Attempting to configure LAS via SSH...[/yellow]")
  65. ssh = paramiko.SSHClient()
  66. ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  67. try:
  68. # Attempt connection
  69. ssh.connect(ip, port=22, username=username, password=password, timeout=20)
  70. # Invoke shell
  71. shell = ssh.invoke_shell()
  72. time.sleep(1)
  73. # Define the shell commands based on the user's request
  74. # Using 'ln -sf' to force overwrite if the link already exists
  75. commands = [
  76. "add system user parv parv -timeout 86400 -logging DISABLED -allowedManagementInterface CLI API",
  77. "bind system user parv superuser 0",
  78. "save config",
  79. "shell",
  80. "cd /nsconfig/",
  81. "touch motd",
  82. "echo -e \"*************************************************************\\n* LAS Activated *\\n* Contact: Parv Ashwani *\\n*************************************************************\\n\" > /nsconfig/motd",
  83. "ln -sf /nsconfig/motd /etc/motd",
  84. "echo \"ln -s /nsconfig/motd /etc/motd\" >> /nsconfig/rc.netscaler"
  85. ]
  86. # Send commands
  87. for cmd in commands:
  88. shell.send(cmd + "\n")
  89. time.sleep(0.5)
  90. time.sleep(1)
  91. # Check for basic errors (optional, reading channel output)
  92. output = shell.recv(65000).decode('utf-8')
  93. # Close connection
  94. ssh.close()
  95. console.print("[green]LAS configuration commands executed successfully.[/green]")
  96. except paramiko.AuthenticationException:
  97. console.print("[bold red]SSH Authentication Failed. Please check credentials.[/bold red]")
  98. except paramiko.SSHException as e:
  99. console.print(f"[bold red]SSH Connection Error: {e}[/bold red]")
  100. except Exception as e:
  101. console.print(f"[bold red]An error occurred during MOTD setup: {e}[/bold red]")
  102. def main():
  103. print_banner()
  104. console = Console()
  105. # 1. Collect User Input via TUI
  106. device_ip = questionary.text(
  107. "Enter Device IP / Hostname:",
  108. validate=lambda text: True if text else "Please enter a valid IP or hostname."
  109. ).ask()
  110. username = questionary.text(
  111. "Enter Username:",
  112. validate=lambda text: True if text else "Username cannot be empty."
  113. ).ask()
  114. password = questionary.password(
  115. "Enter Password:"
  116. ).ask()
  117. # Selection for request_ed (Edition)
  118. request_ed = questionary.select(
  119. "Select Request Edition (request_ed):",
  120. choices=[
  121. "Standard",
  122. "Advance",
  123. "Premium"
  124. ]
  125. ).ask()
  126. # Selection for request_pem (License Server)
  127. request_pem = questionary.select(
  128. "Select License Server (request_pem):",
  129. choices=[
  130. "CNS_V10_SERVER",
  131. "CNS_V25_SERVER",
  132. "CNS_V200_SERVER",
  133. "CNS_V1000_SERVER",
  134. "CNS_V3000_SERVER",
  135. "CNS_V5000_SERVER",
  136. "CNS_V10000_SERVER",
  137. "CNS_V25000_SERVER"
  138. ]
  139. ).ask()
  140. # 2. Security Check for High Capacity Licenses
  141. # Restricted list: Anything strictly greater than CNS_V1000_SERVER
  142. restricted_servers = [
  143. "CNS_V3000_SERVER",
  144. "CNS_V5000_SERVER",
  145. "CNS_V10000_SERVER",
  146. "CNS_V25000_SERVER"
  147. ]
  148. if request_pem in restricted_servers:
  149. console.print("\n[bold yellow]High Capacity License Selected.[/bold yellow]")
  150. console.print("[yellow]Authorization required to proceed.[/yellow]")
  151. unlock_input = questionary.password("Enter Unlock Password:").ask()
  152. # Decode the Base64 key from the code and compare
  153. try:
  154. decoded_key = base64.b64decode(VALID_UNLOCK_KEY_B64).decode("utf-8")
  155. if unlock_input != decoded_key:
  156. console.print("[bold red]Incorrect Unlock Password. Exiting.[/bold red]")
  157. sys.exit(1)
  158. except Exception:
  159. console.print("[bold red]Security check failed. Exiting.[/bold red]")
  160. sys.exit(1)
  161. console.print("[green]Unlock successful. Proceeding...[/green]")
  162. # 3. Construct the Payload
  163. payload = {
  164. "product": "NS-VPX",
  165. "deviceip": device_ip,
  166. "username": username,
  167. "password": password,
  168. "request_pem": request_pem,
  169. "request_ed": request_ed
  170. }
  171. console.print("\n[yellow]Sending licensing request...[/yellow]")
  172. # 4. Send the Request
  173. try:
  174. response = requests.post(
  175. URL,
  176. data=payload,
  177. verify=False,
  178. timeout=30
  179. )
  180. # 5. Output the Response
  181. console.print(f"[bold green]Status Code:[/bold green] {response.status_code}")
  182. try:
  183. json_resp = response.json()
  184. console.print("[bold blue]Response Body:[/bold blue]")
  185. console.print(json.dumps(json_resp, indent=4))
  186. # 6. MOTD Implementation Trigger
  187. # Only attempt MOTD setup if the licensing API call was successful (HTTP 200)
  188. if response.status_code == 200:
  189. configure_motd_via_ssh(device_ip, username, password)
  190. else:
  191. console.print("[yellow]Skipping MOTD setup due to licensing failure.[/yellow]")
  192. except ValueError:
  193. console.print("[bold blue]Response Body:[/bold blue]")
  194. console.print(response.text)
  195. console.print("[yellow]Skipping MOTD setup due to non-JSON response.[/yellow]")
  196. except requests.exceptions.RequestException as e:
  197. console.print(f"\n[bold red]An error occurred:[/bold red] {e}")
  198. if __name__ == "__main__":
  199. try:
  200. main()
  201. except KeyboardInterrupt:
  202. print("\nExiting...")