From d8ca44a2859fbec703a188e91611c6e83fb4abae Mon Sep 17 00:00:00 2001 From: santaspeen Date: Thu, 18 Jul 2024 16:36:25 +0300 Subject: [PATCH] [+] args [+] MultiClone --- src/main.py | 96 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 34 deletions(-) diff --git a/src/main.py b/src/main.py index d524b2a..53c428c 100644 --- a/src/main.py +++ b/src/main.py @@ -8,6 +8,7 @@ import string import sys import textwrap import time +from concurrent.futures import ThreadPoolExecutor from datetime import datetime from getpass import getpass from pathlib import Path @@ -30,9 +31,30 @@ workdir = Path.home() / ".vmker" logdir = workdir / "logs" os.makedirs(logdir / "by-new_name", exist_ok=True) logger.remove() -logger.add(sys.stdout, level="INFO", backtrace=False, diagnose=False, - format="\r{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {message}") logger.add(logdir / f"debug-{datetime.now().strftime('%d.%m.%Y_%H-%M.%S')}.log", level=0) + +_argv = sys.argv[1:] +args = {} +i = 0 +while i < len(_argv): + arg = _argv[i] + if arg.startswith('-'): + key = arg[1:] + if i + 1 < len(_argv) and not _argv[i + 1].startswith('-'): + if not args.get(key): + args[key] = [] + args[key].append(_argv[i + 1]) + i += 1 + else: + logger.warning(f"Ignored invalid parameter: {key}") + i += 1 + +if args.get("debug"): + logger.add(sys.stdout, level=0, backtrace=False, diagnose=False, + format="\r{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {message}") +else: + logger.add(sys.stdout, level="INFO", backtrace=False, diagnose=False, + format="\r{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {message}") logger.debug("Logger initialized") @@ -94,10 +116,13 @@ class VMKer: def _load_config(self): cf = workdir / "config.json" + if args.get('c'): + cf = Path().resolve() / args['c'][0] + logger.debug(f"using config file: {cf}") if not cf.exists(): logger.info(f"Init before using program: {sys.argv[0]} init") exit(0) - with open(workdir / "config.json", "r") as f: + with open(cf, "r") as f: self.config = json.load(f) def _connect(self): @@ -255,17 +280,22 @@ class VMKer: pass def _clone(self, new_name, destfolder, clone_spec): - logger.info(f"Cloning: {self.template.name!r} > {new_name!r}") + logger.info(f"[{new_name}] Cloning from {self.template.name!r}") task = self.template.Clone(name=new_name, folder=destfolder, spec=clone_spec) while task.info.state == vim.TaskInfo.State.running: time.sleep(1) if task.info.state != vim.TaskInfo.State.success: logger.error(f"Error while cloning") logger.exception(task.info.error) - logger.success("Cloned") - logger.info("Waiting IP VirtualMachine..") - vm = task.info.result + logger.success(f"[{new_name}] Cloned") + return task.info.result + def _configure(self, vm): + _name = vm.name + user = 'root' + key_file = 'rsa' + + logger.info("Waiting VirtualMachine..") t = 0 while True: ipv4, ipv6 = self._get_vm_ip(vm) @@ -276,8 +306,8 @@ class VMKer: if t > 120: return logger.error("No IPv4 address found.") - logger.info(f"IPv4: {', '.join(ipv4)}") - logger.info(f"IPv6: {', '.join(ipv6)}") + logger.info(f"[{_name}] IPv4: {', '.join(ipv4)}") + logger.info(f"[{_name}] IPv6: {', '.join(ipv6)}") try: # TODO: Default user @@ -285,20 +315,21 @@ class VMKer: ssh = ConnectHandler( device_type='linux', host=ipv4[0], - username="root", + username=user, use_keys=True, - key_file="rsa" + key_file=key_file ) except netmiko.exceptions.NetmikoAuthenticationException as e: return logger.error(f"SSH Error: Authentication. ({e})") + logger.success(f"[SSH] [{_name}] Connected to {ipv4[0]} as {user} ({key_file=})") # TODO: length _pwd = ''.join(random.choice(chars) for _ in range(12)) - ssh.send_command(f'echo -e "{_pwd}\n{_pwd}" | passwd root') + ssh.send_command(f"echo '{user}:{_pwd}' | chpasswd") logger.info(f"Password for root: {_pwd}") - ssh.send_command(f'echo {new_name} > /etc/hostname') + ssh.send_command(f'echo {_name} > /etc/hostname') # TODO: yml - logger.info("Executing %.yml") + logger.info(f"[{vm.name}] Executing %.yml") ssh.disconnect() @@ -319,13 +350,13 @@ class VMKer: if len(sys.argv) > 3: new_name = sys.argv[3] template_name = f"{sys.argv[2]}-template" - _logfile = logdir / "by-new_vm_name" / f"{new_name}.log" + _logfile = logdir / "by-new_name" / f"{new_name}.log" i = 0 while True: i += 1 if not _logfile.exists(): break - _logfile = logdir / "by-new_vm_name" / f"{new_name} ({i}).log" + _logfile = logdir / "by-new_name" / f"{new_name} ({i}).log" logger.add(_logfile, level=0) self._load_config() if not self._connect(): @@ -339,7 +370,10 @@ class VMKer: logger.debug("datacenter found") self._cache_vms(template_name, datacenter) - new_names = [f'{new_name}-{i+1}' for i in range(1)] + if args.get("n"): + new_names = [f'{new_name}-{i + 1}' for i in range(int(args['n'][0]))] + else: + new_names = [new_name] for n in new_names: if n in self.vm: return logger.error(f"Error: {n!r} already exist.") @@ -372,15 +406,23 @@ class VMKer: clone_spec = vim.vm.CloneSpec() clone_spec.location = relocate_spec clone_spec.powerOn = True - for n in new_names: - self._clone(n, destfolder, clone_spec) + + def _caf(nn): + vm = self._clone(nn, destfolder, clone_spec) + self._configure(vm) + + for new_name in new_names: + with ThreadPoolExecutor(max_workers=2) as executor: + executor.submit(_caf, new_name) + else: logger.info( - f"Usage: {sys.argv[0]} create [commands.yml | commands1.yml commands2.yml | ...]") + f"Usage: {sys.argv[0]} create template new [-n VMs count] [[-f ] ...]") logger.info("Mark: template search on target host with '-template' suffix (%template_name%-template)") def main(self): logger.debug(f"{sys.argv=}") + logger.debug(f"{args=}") try: if len(sys.argv) > 1: match sys.argv[1]: @@ -400,19 +442,5 @@ class VMKer: logger.info("Exited by KeyboardInterrupt") -def generate_password(length=12): - chars = string.ascii_letters + string.digits + string.punctuation - return ''.join(random.choice(chars) for _ in range(length)) - - -def get_vm_ip(vm): - for nic in vm.guest.net: - if nic.ipConfig: - for ip in nic.ipConfig.ipAddress: - if ":" not in ip.ipAddress: # Skip IPv6 addresses - return ip.ipAddress - return None - - if __name__ == "__main__": VMKer().main()