[+] log by new_name
[-] edict [~] Separate _create to _create and _clone [~] Preparing for multi-UP [~] Change cache principe
This commit is contained in:
169
src/main.py
169
src/main.py
@@ -13,39 +13,37 @@ from getpass import getpass
|
||||
from pathlib import Path
|
||||
from typing import Callable
|
||||
|
||||
import netmiko
|
||||
from cryptography.fernet import Fernet, InvalidToken
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||
from easydict import EasyDict as edict
|
||||
from loguru import logger
|
||||
from netmiko import ConnectHandler
|
||||
from pyVim.connect import SmartConnect, Disconnect
|
||||
from pyVmomi import vim
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
try:
|
||||
from pyVmomi.vim import ServiceInstanceContent, ServiceInstance
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
chars = string.ascii_letters + string.digits + string.punctuation
|
||||
chars = string.ascii_letters + string.digits
|
||||
yaml = YAML()
|
||||
workdir = Path.home() / ".vmker"
|
||||
os.makedirs(workdir / "logs", exist_ok=True)
|
||||
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<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | {message}")
|
||||
logger.add(workdir / "logs" / f"debug-{datetime.now().strftime('%d.%m.%Y_%H-%M.%S')}.log", level=0)
|
||||
logger.add(logdir / f"debug-{datetime.now().strftime('%d.%m.%Y_%H-%M.%S')}.log", level=0)
|
||||
logger.debug("Logger initialized")
|
||||
|
||||
|
||||
class VMKer:
|
||||
|
||||
def __init__(self):
|
||||
self.si: ServiceInstance = None
|
||||
self.content: ServiceInstanceContent = None
|
||||
self.si = None
|
||||
self.content = None
|
||||
self.config = None
|
||||
self.vm = set()
|
||||
self.template = None
|
||||
|
||||
@staticmethod
|
||||
def generate_key_from_password(password: str, salt: bytes) -> bytes:
|
||||
@@ -104,12 +102,12 @@ class VMKer:
|
||||
|
||||
def _connect(self):
|
||||
try:
|
||||
cfg = edict(self.config)
|
||||
self.si = SmartConnect(host=cfg.server, user=cfg.user, pwd=self._decrypt_pass(),
|
||||
disableSslCertValidation=cfg.insecure)
|
||||
cfg = self.config
|
||||
self.si = SmartConnect(host=cfg['server'], user=cfg['user'], pwd=self._decrypt_pass(),
|
||||
disableSslCertValidation=cfg['insecure'])
|
||||
atexit.register(Disconnect, self.si)
|
||||
self.content = self.si.RetrieveContent()
|
||||
logger.success(f"Connected to {self.content.about.fullName}({cfg.server}) as {cfg.user}")
|
||||
logger.success(f"Connected to {self.content.about.fullName}({cfg['server']}) as {cfg['user']}")
|
||||
return True
|
||||
except vim.fault.VimFault as e:
|
||||
# noinspection PyUnresolvedReferences
|
||||
@@ -240,20 +238,6 @@ class VMKer:
|
||||
print("Unknown command.")
|
||||
logger.info("Exited from configuration mode.")
|
||||
|
||||
|
||||
def _search_vm(self, _template_name, _new_name, datacenter):
|
||||
logger.debug("Checking VMs.")
|
||||
vm = None
|
||||
new = None
|
||||
container_view = self.content.viewManager.CreateContainerView(datacenter.vmFolder, [vim.VirtualMachine], True)
|
||||
for _vm in container_view.view:
|
||||
if _vm.name == _template_name:
|
||||
vm = _vm
|
||||
if _vm.name == _new_name:
|
||||
new = _vm
|
||||
container_view.Destroy()
|
||||
return vm, new
|
||||
|
||||
@staticmethod
|
||||
def _get_vm_ip(vm):
|
||||
ipv4 = []
|
||||
@@ -267,11 +251,80 @@ class VMKer:
|
||||
ipv6.append(ip.ipAddress)
|
||||
return ipv4, ipv6
|
||||
|
||||
def _parse_yml(self):
|
||||
def _parse_yml(self, ssh):
|
||||
pass
|
||||
|
||||
def _clone(self, new_name, destfolder, clone_spec):
|
||||
logger.info(f"Cloning: {self.template.name!r} > {new_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
|
||||
|
||||
t = 0
|
||||
while True:
|
||||
ipv4, ipv6 = self._get_vm_ip(vm)
|
||||
if len(ipv4) > 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
t += 1
|
||||
if t > 120:
|
||||
return logger.error("No IPv4 address found.")
|
||||
|
||||
logger.info(f"IPv4: {', '.join(ipv4)}")
|
||||
logger.info(f"IPv6: {', '.join(ipv6)}")
|
||||
|
||||
try:
|
||||
# TODO: Default user
|
||||
# TODO: key file
|
||||
ssh = ConnectHandler(
|
||||
device_type='linux',
|
||||
host=ipv4[0],
|
||||
username="root",
|
||||
use_keys=True,
|
||||
key_file="rsa"
|
||||
)
|
||||
except netmiko.exceptions.NetmikoAuthenticationException as e:
|
||||
return logger.error(f"SSH Error: Authentication. ({e})")
|
||||
# TODO: length
|
||||
_pwd = ''.join(random.choice(chars) for _ in range(12))
|
||||
ssh.send_command(f'echo -e "{_pwd}\n{_pwd}" | passwd root')
|
||||
logger.info(f"Password for root: {_pwd}")
|
||||
ssh.send_command(f'echo {new_name} > /etc/hostname')
|
||||
|
||||
# TODO: yml
|
||||
logger.info("Executing %.yml")
|
||||
|
||||
ssh.disconnect()
|
||||
|
||||
def _cache_vms(self, template_name, datacenter):
|
||||
logger.debug("Checking VMs.")
|
||||
self.vm = set()
|
||||
self.template = None
|
||||
container_view = self.content.viewManager.CreateContainerView(datacenter.vmFolder, [vim.VirtualMachine], True)
|
||||
for _vm in container_view.view:
|
||||
if _vm.name == template_name:
|
||||
self.template = _vm
|
||||
self.vm.add(_vm.name)
|
||||
container_view.Destroy()
|
||||
|
||||
def _create(self):
|
||||
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"
|
||||
i = 0
|
||||
while True:
|
||||
i += 1
|
||||
if not _logfile.exists():
|
||||
break
|
||||
_logfile = logdir / "by-new_vm_name" / f"{new_name} ({i}).log"
|
||||
logger.add(_logfile, level=0)
|
||||
self._load_config()
|
||||
if not self._connect():
|
||||
return
|
||||
@@ -282,13 +335,13 @@ class VMKer:
|
||||
return logger.error(f"Datacenter not found: {self.config['datacenter']}")
|
||||
datacenter = datacenter[0]
|
||||
logger.debug("datacenter found")
|
||||
self._cache_vms(template_name, datacenter)
|
||||
|
||||
template_name = f"{sys.argv[2]}-template"
|
||||
new_name = sys.argv[3]
|
||||
template, _new = self._search_vm(template_name, new_name, datacenter)
|
||||
if _new:
|
||||
return logger.error(f"Error: {new_name!r} already exist.")
|
||||
if not template:
|
||||
new_names = [f'{new_name}-{i+1}' for i in range(1)]
|
||||
for n in new_names:
|
||||
if n in self.vm:
|
||||
return logger.error(f"Error: {n!r} already exist.")
|
||||
if not template_name not in self.vm:
|
||||
return logger.error(f"Template not found: {template_name}")
|
||||
logger.debug("template found")
|
||||
|
||||
@@ -310,7 +363,6 @@ class VMKer:
|
||||
datastore = datastore[0]
|
||||
logger.debug("datastore found")
|
||||
|
||||
logger.info(f"Cloning: {template_name!r} > {new_name!r}")
|
||||
destfolder = datacenter.vmFolder
|
||||
relocate_spec = vim.vm.RelocateSpec()
|
||||
relocate_spec.datastore = datastore
|
||||
@@ -318,47 +370,8 @@ class VMKer:
|
||||
clone_spec = vim.vm.CloneSpec()
|
||||
clone_spec.location = relocate_spec
|
||||
clone_spec.powerOn = True
|
||||
task = 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:
|
||||
return logger.error(f"Error while cloning: {task.info.error}")
|
||||
logger.success("Cloned")
|
||||
|
||||
vm = task.info.result
|
||||
logger.info("Waiting to PowerOn..")
|
||||
|
||||
t = 0
|
||||
while True:
|
||||
ipv4, ipv6 = self._get_vm_ip(vm)
|
||||
if len(ipv4) > 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
t += 1
|
||||
if t > 120:
|
||||
return logger.error("No IPv4 address found.")
|
||||
|
||||
logger.info(f"IPv4: {', '.join(ipv4)}")
|
||||
logger.info(f"IPv6: {', '.join(ipv6)}")
|
||||
|
||||
# TODO: length
|
||||
_pwd1 = ''.join(random.choice(chars) for _ in range(12))
|
||||
|
||||
# TODO: Default user
|
||||
ssh = ConnectHandler(
|
||||
device_type='linux',
|
||||
host=ipv4[0],
|
||||
username="root",
|
||||
password="toor",
|
||||
)
|
||||
ssh.send_command(f'echo -e "{_pwd1}\n{_pwd1}" | passwd root')
|
||||
logger.info(f"Password for root: {_pwd1}")
|
||||
|
||||
# TODO: yml
|
||||
logger.info("Executing %.yml")
|
||||
|
||||
ssh.disconnect()
|
||||
|
||||
for n in new_names:
|
||||
self._clone(n, destfolder, clone_spec)
|
||||
else:
|
||||
logger.info(
|
||||
f"Usage: {sys.argv[0]} create <template_name> <new_name> [commands.yml | commands1.yml commands2.yml | ...]")
|
||||
|
||||
Reference in New Issue
Block a user