275 lines
8.4 KiB
Python
275 lines
8.4 KiB
Python
"""
|
|
|
|
This library contains the functions that allow stacktrain to produce
|
|
Windows batch files.
|
|
|
|
"""
|
|
|
|
# Force Python 2 to use float division even for ints
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
|
|
import logging
|
|
import io
|
|
import ntpath
|
|
import os
|
|
import re
|
|
|
|
from string import Template
|
|
from shutil import copyfile
|
|
|
|
import stacktrain.config.general as conf
|
|
import stacktrain.core.helpers as hf
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
WBATCH_OUT_DIR = os.path.join(conf.top_dir, "wbatch")
|
|
# wbatch template dir
|
|
TPLT_DIR = os.path.join(conf.top_dir, "stacktrain/batch_for_windows_templates")
|
|
|
|
OUT_FILE = None
|
|
|
|
|
|
def wbatch_reset():
|
|
"""Clean Windows batch directory"""
|
|
hf.clean_dir(WBATCH_OUT_DIR)
|
|
copyfile(os.path.join(TPLT_DIR, "config_bat"),
|
|
os.path.join(WBATCH_OUT_DIR, "config.bat"))
|
|
|
|
def init():
|
|
"""Initialize variables and directory for Windows batch script creation"""
|
|
if conf.wbatch:
|
|
if hasattr(conf, 'vm_access') and conf.vm_access == "all":
|
|
logging.info("Already configured for shared folder access.")
|
|
else:
|
|
logging.info("Setting vm_access method to shared folder.")
|
|
conf.vm_access = "shared_folder"
|
|
else:
|
|
logging.debug("Not building Windows batch files.")
|
|
|
|
wbatch_reset()
|
|
|
|
|
|
def wbatch_new_file(file_name):
|
|
"""Create new Windows batch file"""
|
|
global OUT_FILE
|
|
|
|
hf.create_dir(WBATCH_OUT_DIR)
|
|
OUT_FILE = os.path.join(WBATCH_OUT_DIR, file_name)
|
|
open(OUT_FILE, "a").close()
|
|
|
|
|
|
def wbatch_close_file():
|
|
global OUT_FILE
|
|
|
|
OUT_FILE = None
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
class WbatchTemplate(Template):
|
|
# Default delimiter "$" occurs directly after backslash (in Windows paths)
|
|
delimiter = '#'
|
|
idpattern = r'[A-Z][_A-Z0-9]*'
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
# Note: Windows batch scripts with LF may seem to work, but (for instance) jump
|
|
# labels don't work properly
|
|
|
|
|
|
def wbatch_write(*args):
|
|
if OUT_FILE:
|
|
with io.open(OUT_FILE, 'a', newline='\r\n') as out:
|
|
try:
|
|
string = unicode(*args).rstrip()
|
|
out.write(string + "\n")
|
|
except TypeError:
|
|
logging.error("wbatch can't print %s: %s", type(str(*args)),
|
|
str(*args))
|
|
logging.exception("Exception")
|
|
import sys
|
|
sys.exit(1)
|
|
|
|
|
|
def wbatch_write_template(template, replace=None):
|
|
if replace is None:
|
|
replace = {}
|
|
|
|
with open(os.path.join(TPLT_DIR, template)) as tf:
|
|
for line in tf:
|
|
te = WbatchTemplate(line)
|
|
wbatch_write(te.substitute(replace))
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Batch function calls
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
def wbatch_abort_if_vm_exists(vm_name):
|
|
te = WbatchTemplate(u"CALL :vm_exists #VM_NAME")
|
|
wbatch_write(te.substitute(VM_NAME=vm_name))
|
|
|
|
|
|
def wbatch_wait_poweroff(vm_name):
|
|
te = WbatchTemplate(u"""ECHO %time% Waiting for VM #VM_NAME to power off.
|
|
CALL :wait_poweroff #VM_NAME
|
|
ECHO %time% VM #VM_NAME powered off.
|
|
""")
|
|
wbatch_write(te.substitute(VM_NAME=vm_name))
|
|
|
|
|
|
def wbatch_wait_auto():
|
|
te = WbatchTemplate(u"""ECHO %time% Waiting for autostart files to execute.
|
|
CALL :wait_auto
|
|
ECHO %time% All autostart files executed.
|
|
""")
|
|
wbatch_write(te.substitute())
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Batch commands
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
def wbatch_delete_disk(disk_path):
|
|
disk_name = os.path.basename(disk_path)
|
|
te = WbatchTemplate(r"IF EXIST %IMGDIR%\#DISK DEL %IMGDIR%\#DISK")
|
|
wbatch_write(te.substitute(DISK=disk_name))
|
|
|
|
|
|
def wbatch_rename_disk(src_name, target_name):
|
|
te = WbatchTemplate(r"MOVE /y %IMGDIR%\#SRC %IMGDIR%\#TARGET")
|
|
wbatch_write(te.substitute(SRC=src_name, TARGET=target_name))
|
|
|
|
|
|
def wbatch_cp_auto(src_path, target_path):
|
|
src = wbatch_path_to_windows(src_path)
|
|
target = os.path.basename(target_path)
|
|
te = WbatchTemplate(r"COPY %TOPDIR%\#SRC %AUTODIR%\#TARGET")
|
|
wbatch_write(te.substitute(SRC=src, TARGET=target))
|
|
|
|
|
|
def wbatch_sleep(seconds):
|
|
te = WbatchTemplate(r"TIMEOUT /T #SECONDS /NOBREAK")
|
|
wbatch_write(te.substitute(SECONDS=seconds))
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Templated parts
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
def wbatch_file_header(product):
|
|
replace = {"PRODUCT": product}
|
|
wbatch_write_template("template-file_header_bat", replace)
|
|
|
|
|
|
def wbatch_end_file():
|
|
wbatch_write_template("template-end_file_bat")
|
|
wbatch_close_file()
|
|
|
|
|
|
def wbatch_elevate_privileges():
|
|
wbatch_write_template("template-elevate_privs_bat")
|
|
|
|
|
|
def wbatch_find_vbm():
|
|
wbatch_write_template("template-find_vbm_bat")
|
|
|
|
|
|
def wbatch_mkdirs():
|
|
autodir = wbatch_path_to_windows(conf.autostart_dir)
|
|
imgdir = wbatch_path_to_windows(conf.img_dir)
|
|
logdir = wbatch_path_to_windows(conf.log_dir)
|
|
statusdir = wbatch_path_to_windows(conf.status_dir)
|
|
replace = {"AUTODIR": autodir,
|
|
"IMGDIR": imgdir,
|
|
"LOGDIR": logdir,
|
|
"STATUSDIR": statusdir}
|
|
wbatch_write_template("template-mkdirs_bat", replace)
|
|
|
|
|
|
def wbatch_begin_hostnet():
|
|
wbatch_new_file("create_hostnet.bat")
|
|
wbatch_file_header("host-only networks")
|
|
wbatch_write_template("template-begin_hostnet_bat")
|
|
# Creating networks requires elevated privileges
|
|
wbatch_elevate_privileges()
|
|
wbatch_find_vbm()
|
|
|
|
|
|
def wbatch_create_hostnet(if_ip, adapter):
|
|
replace = {"IFNAME": adapter,
|
|
"IFIP": if_ip}
|
|
wbatch_write_template("template-create_hostnet_bat", replace)
|
|
|
|
|
|
def wbatch_begin_base():
|
|
# Disable E1101 (no-member) in pylint
|
|
iso_name = os.path.basename(conf.iso_image.name) # pylint: disable=E1101
|
|
if not iso_name:
|
|
logging.error("Windows batch file needs install ISO URL.")
|
|
raise ValueError
|
|
|
|
wbatch_new_file("create_base.bat")
|
|
wbatch_file_header("base disk")
|
|
wbatch_find_vbm()
|
|
wbatch_mkdirs()
|
|
replace = {"INSTALLFILE": conf.iso_image.name, # pylint: disable=no-member
|
|
"ISOURL": conf.iso_image.url} # pylint: disable=no-member
|
|
wbatch_write_template("template-begin_base_bat", replace)
|
|
|
|
|
|
def wbatch_begin_node(node_name):
|
|
wbatch_new_file("create_{}_node.bat".format(node_name))
|
|
wbatch_file_header("{} VM".format(node_name))
|
|
wbatch_find_vbm()
|
|
wbatch_mkdirs()
|
|
basedisk = "{}.vdi".format(conf.get_base_disk_name())
|
|
|
|
replace = {"BASEDISK": basedisk}
|
|
wbatch_write_template("template-begin_node_bat", replace)
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# VBoxManage call handling
|
|
# -----------------------------------------------------------------------------
|
|
|
|
def wbatch_log_vbm(*args):
|
|
argl = list(*args)
|
|
for index, arg in enumerate(argl):
|
|
if re.match("--hostonlyadapter", arg):
|
|
# The next arg is the host-only interface name -> change it.
|
|
# We use the vboxnet interface name as a variable name for the
|
|
# Windows batch scripts. This is a reference to the variable,
|
|
# therefore the string must be somethin like "%vboxnet0%".
|
|
argl[index+1] = '"' + "%{}%".format(argl[index+1]) + '"'
|
|
elif re.match("--hostpath", arg):
|
|
# The next arg is the shared dir -> change it
|
|
argl[index+1] = r'%SHAREDIR%'
|
|
elif re.search(r"\.(iso|vdi)$", arg):
|
|
# Fix path of ISO or VDI image
|
|
img_name = os.path.basename(arg)
|
|
argl[index] = ntpath.join("%IMGDIR%", img_name)
|
|
|
|
# Have Windows echo what we are about to do
|
|
wbatch_write("ECHO VBoxManage " + " ". join(argl))
|
|
|
|
wbatch_write('"%VBM%" ' + " ". join(argl))
|
|
|
|
# Abort if VBoxManage call raised errorlevel
|
|
wbatch_write("IF %errorlevel% NEQ 0 GOTO :vbm_error")
|
|
|
|
# Blank line for readability
|
|
wbatch_write()
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Windows path name helpers
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
def wbatch_path_to_windows(full_path):
|
|
rel_path = hf.strip_top_dir(conf.top_dir, full_path)
|
|
|
|
# Convert path to backslash-type as expected by Windows batch files
|
|
rel_path = ntpath.normpath(rel_path)
|
|
return rel_path
|