Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wait for pid to disappear and tidy #11

Merged
merged 2 commits into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ runIOC.sh
relPaths.sh
*.py[oc]
*.sq3
/.idea
**/.idea
.project
*.py.bak
2 changes: 1 addition & 1 deletion installation_and_upgrade/ibex_install_utils/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ class ErrorWithFile(ErrorInTask):
Exception if there is an error when doing something with a file
"""
def __init__(self, message):
super(ErrorWithFile, self).__init__(message)
super(ErrorWithFile, self).__init__(message)
10 changes: 2 additions & 8 deletions installation_and_upgrade/ibex_install_utils/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@

import os
import shutil
import stat
from time import sleep

from ibex_install_utils.exceptions import ErrorWithFile


class FileUtils(object):
Expand All @@ -24,7 +20,8 @@ def remove_tree(path):
"""
empty_dir = r"\\isis\inst$\Kits$\CompGroup\ICP\empty_dir_for_robocopy"
if os.path.isdir(path):
os.system("robocopy \"{}\" \"{}\" /PURGE /NJH /NJS /NP /NFL /NDL /NS /NC /R:1 /LOG:NUL".format(empty_dir, path))
os.system("robocopy \"{}\" \"{}\" /PURGE /NJH /NJS /NP /NFL /NDL /NS /NC /R:1 /LOG:NUL".
format(empty_dir, path))

def mkdir_recursive(self, path):
"""
Expand Down Expand Up @@ -52,6 +49,3 @@ def move_dir(src, dst):
"""
shutil.copytree(src, dst)
FileUtils.remove_tree(src)



144 changes: 36 additions & 108 deletions installation_and_upgrade/ibex_install_utils/install_tasks.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
"""
Tasks associated with install
"""
from time import sleep

import os
import shutil
import socket
import subprocess
import git
from datetime import date

import git

from ibex_install_utils.exceptions import UserStop, ErrorInRun
from ibex_install_utils.file_utils import FileUtils
from ibex_install_utils.run_process import RunProcess
from ibex_install_utils.task import Task
from ibex_install_utils.user_prompt import UserPrompt

INSTRUMENT_BASE_DIR = os.path.join("C:\\", "Instrument")
Expand All @@ -28,6 +32,7 @@
SOURCE_FOLDER = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources")
SOURCE_MACHINE_SETTINGS_CONFIG_PATH = os.path.join(SOURCE_FOLDER, SETTINGS_CONFIG_FOLDER, "NDXOTHER")
SOURCE_MACHINE_SETTINGS_COMMON_PATH = os.path.join(SOURCE_FOLDER, SETTINGS_CONFIG_FOLDER, "common")
MYSQL_FILES_DIR = os.path.join(INSTRUMENT_BASE_DIR, "var", "mysql")

LABVIEW_DAE_DIR = os.path.join("C:\\", "LabVIEW modules", "DAE")

Expand Down Expand Up @@ -404,21 +409,34 @@ def backup_database(self):
if task.do_step:
try:
mysql_bin_dir = self._get_mysql_dir()
mysql_path = os.path.join(mysql_bin_dir, "mysql.exe")
mysql_admin_path = os.path.join(mysql_bin_dir, "mysqladmin.exe")
if not all([os.path.exists(p) for p in [mysql_path, mysql_admin_path]]):
raise OSError
if subprocess.call([mysql_path, "-u", "root", "-p", "--execute",
"SET GLOBAL innodb_fast_shutdown=0"]) != 0 or \
subprocess.call([mysql_admin_path, "-u", "root", "-p", "shutdown"]) != 0:
self._prompt.prompt_and_raise_if_not_yes(
"Stopping the MySQL service failed. Please do it manually")
except OSError:
RunProcess(MYSQL_FILES_DIR, "mysql.exe", executable_directory=mysql_bin_dir,
prog_args=["-u", "root", "-p", "--execute", "SET GLOBAL innodb_fast_shutdown=0"],
capture_pipes=False).run()

RunProcess(MYSQL_FILES_DIR, "mysqladmin.exe", executable_directory=mysql_bin_dir,
prog_args=["-u", "root", "-p", "shutdown"], capture_pipes=False).run()

# Must wait for the database to properly stop otherwise when we copy it the copy fails because the
# file disappears
mysql_pid_file = os.path.join(MYSQL_FILES_DIR, "Data", "{}.pid".format(self._machine_name))
for i in range(1, 60):
if os.path.exists(mysql_pid_file):
print("Waiting for pid file to be removed.")
sleep(1)
else:
break
if os.path.exists(mysql_pid_file):
raise ErrorInRun("MySQL appears not to have stopped the pid file, '{}', is still there!".format(
mysql_pid_file))

except ErrorInRun as ex:
self._prompt.prompt_and_raise_if_not_yes(
"Unable to find mysql location. Please shut down the service manually")
finally:
self._backup_dir(os.path.join("C:\\", "Instrument", "var", "mysql"))
self._prompt.prompt_and_raise_if_not_yes("Data backup complete. Please restart the MYSQL service")
"Unable to run mysql. Please shut down the service manually. Error is {}".format(ex.message))
self._prompt.prompt_and_raise_if_not_yes(
"Stopping the MySQL service failed. Please do it manually")

self._backup_dir(MYSQL_FILES_DIR)
self._prompt.prompt_and_raise_if_not_yes("Data backup complete. Please restart the MYSQL service")

def update_release_notes(self):
"""
Expand Down Expand Up @@ -526,6 +544,9 @@ def inform_instrument_scientists(self):
"Inform the instrument scientists that the upgrade has been completed")

def create_journal_sql_schema(self):
"""
Create the journal schema if it doesn't exist.
"""
with Task("Create journal table SQL schema if it doesn't exist", self._prompt) as task:
if task.do_step:
sql_password = self._prompt.prompt("Enter the MySQL root password:", UserPrompt.ANY,
Expand Down Expand Up @@ -645,96 +666,3 @@ def run_instrument_deploy_pre_stop(self):
self._upgrade_tasks.user_confirm_upgrade_type_on_machine('Client/Server Machine')
self._upgrade_tasks.take_screenshots()
self._upgrade_tasks.stop_ibex_server()


class Task(object):
"""
Task to be performed for install.

Confirms a step is to be run (if needed) and places the answer in do_step.
Wraps the task in print statements so users can see when a task starts and ends.
"""

def __init__(self, task_name, user_prompt):
"""
Initialised.
Args:
task_name: the name of the task
user_prompt: object allowing the user to be prompted for an answer
"""
self._task = task_name
self._user_prompt = user_prompt
self.do_step = True

def __enter__(self):
self.do_step = self._user_prompt.confirm_step(self._task)
print("{task} ...".format(task=self._task))
return self

def __exit__(self, exc_type, exc_val, exc_tb):
if exc_val is None:
print("... Done".format(task=self._task))


class RunProcess(object):
"""
Create a process runner to run a process.
"""
def __init__(self, working_dir, executable_file, executable_directory=None, press_any_key=False, prog_args=None):
"""
Create a process that needs running

Args:
working_dir: working directory of the process
executable_file: file of the process to run, e.g. a bat file
executable_directory: the directory in which the executable file lives, if None, default, use working dir
press_any_key: if true then press a key to finish the run process
prog_args(list[string]): arguments to pass to the program
"""
self._working_dir = working_dir
self._bat_file = executable_file
self._press_any_key = press_any_key
self._prog_args = prog_args
if executable_directory is None:
self._full_path_to_process_file = os.path.join(working_dir, executable_file)
else:
self._full_path_to_process_file = os.path.join(executable_directory, executable_file)

def run(self):
"""
Run the process

Returns:
Raises ErrorInRun: if there is a known problem with the run
"""
try:
print(" Running {0} ...".format(self._bat_file))

command_line = [self._full_path_to_process_file]
if self._prog_args is not None:
command_line.extend(self._prog_args)
if self._press_any_key:
output = subprocess.Popen(command_line, cwd=self._working_dir,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
output_lines, err = output.communicate(" ")
else:
output_lines = subprocess.check_output(
command_line,
cwd=self._working_dir,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE)

for line in output_lines.splitlines():
print(" > {line}".format(line=line))
print(" ... finished")
except subprocess.CalledProcessError as ex:
raise ErrorInRun("Command failed with error: {0}".format(ex))
except WindowsError as ex:
if ex.errno == 2:
raise ErrorInRun("Command '{cmd}' not found in '{cwd}'".format(
cmd=self._bat_file, cwd=self._working_dir))
elif ex.errno == 22:
raise ErrorInRun("Directory not found to run command '{cmd}', command is in : '{cwd}'".format(
cmd=self._bat_file, cwd=self._working_dir))
raise ex
81 changes: 81 additions & 0 deletions installation_and_upgrade/ibex_install_utils/run_process.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
Running processes infrastructure.
"""

import os
import subprocess

from ibex_install_utils.exceptions import ErrorInRun


class RunProcess(object):
"""
Create a process runner to run a process.
"""
def __init__(self, working_dir, executable_file, executable_directory=None, press_any_key=False, prog_args=None,
capture_pipes=True):
"""
Create a process that needs running

Args:
working_dir: working directory of the process
executable_file: file of the process to run, e.g. a bat file
executable_directory: the directory in which the executable file lives, if None, default, use working dir
press_any_key: if true then press a key to finish the run process
prog_args(list[string]): arguments to pass to the program
capture_pipes: whether to capture pipes and manage in python or let the process access the console
"""
self._working_dir = working_dir
self._bat_file = executable_file
self._press_any_key = press_any_key
self._prog_args = prog_args
self._capture_pipes = capture_pipes
if executable_directory is None:
self._full_path_to_process_file = os.path.join(working_dir, executable_file)
else:
self._full_path_to_process_file = os.path.join(executable_directory, executable_file)

def run(self):
"""
Run the process

Returns:
Raises ErrorInRun: if there is a known problem with the run
"""
try:
print(" Running {0} ...".format(self._bat_file))

command_line = [self._full_path_to_process_file]
if self._prog_args is not None:
command_line.extend(self._prog_args)

if not self._capture_pipes:
error_code = subprocess.call(command_line, cwd=self._working_dir)
if error_code != 0:
raise ErrorInRun("Command failed with error code: {0}".format(error_code))
output_lines = ""
elif self._press_any_key:
output = subprocess.Popen(command_line, cwd=self._working_dir,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
output_lines, err = output.communicate(" ")
else:
output_lines = subprocess.check_output(
command_line,
cwd=self._working_dir,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE)

for line in output_lines.splitlines():
print(" > {line}".format(line=line))
print(" ... finished")
except subprocess.CalledProcessError as ex:
raise ErrorInRun("Command failed with error: {0}".format(ex))
except WindowsError as ex:
if ex.errno == 2:
raise ErrorInRun("Command '{cmd}' not found in '{cwd}'".format(
cmd=self._bat_file, cwd=self._working_dir))
elif ex.errno == 22:
raise ErrorInRun("Directory not found to run command '{cmd}', command is in : '{cwd}'".format(
cmd=self._bat_file, cwd=self._working_dir))
raise ex
32 changes: 32 additions & 0 deletions installation_and_upgrade/ibex_install_utils/task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Task infrastructure.
"""


class Task(object):
"""
Task to be performed for install.

Confirms a step is to be run (if needed) and places the answer in do_step.
Wraps the task in print statements so users can see when a task starts and ends.
"""

def __init__(self, task_name, user_prompt):
"""
Initialised.
Args:
task_name: the name of the task
user_prompt: object allowing the user to be prompted for an answer
"""
self._task = task_name
self._user_prompt = user_prompt
self.do_step = True

def __enter__(self):
self.do_step = self._user_prompt.confirm_step(self._task)
print("{task} ...".format(task=self._task))
return self

def __exit__(self, exc_type, exc_val, exc_tb):
if exc_val is None:
print("... Done".format(task=self._task))
Loading