import getpass
import json
import os
import shutil
import sys
import time
import importlib.util
import signal
import subprocess
import traceback
import pwd
import grp
CUR_PATH = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, CUR_PATH)
from config import get_config, get_value
from resolve_pwd import resolve_kmc_pwd
sys.path.append(os.path.join(CUR_PATH, ".."))
from om_log import LOGGER as LOG
_cfg = get_config()
_paths = _cfg.paths
OPT_CONFIG_PATH = _paths.config_dir
SCRIPT_PATH = os.path.join(CUR_PATH, "..")
CONFIG_PATH = os.path.join(SCRIPT_PATH, "../config")
DORADO_CONF_PATH = _paths.dorado_conf_path()
DM_USER = "DMUser"
DM_PWD = "DMPwd"
def init_get_info_fun():
try:
get_info_path = os.path.join(CUR_PATH, "../compat/get_config_info.py")
spec = importlib.util.spec_from_file_location("get_info_from_config", get_info_path)
get_info_from_config = importlib.util.module_from_spec(spec)
spec.loader.exec_module(get_info_from_config)
get_info = get_info_from_config.get_value
return get_info
except Exception as e:
LOG.error(f"init get_info fun failed {e}, traceback: {traceback.format_exc(limit=-1)}")
return None
get_info = init_get_info_fun()
if get_info is None:
LOG.error("init get_info fun failed")
def copy_file(source, dest):
if os.path.exists(dest):
os.remove(dest)
shutil.copy(source, dest)
def get_file_content(file_path):
with open(file_path, "r") as f:
return f.read()
def get_file_json(file_path):
with open(file_path, "r") as f:
return json.load(f)
def chown_path(file_path, u_id, g_id):
os.chown(file_path, u_id, g_id)
if os.path.isdir(file_path):
for root, dirs, files in os.walk(file_path):
for dir in dirs:
chown_path(os.path.join(root, dir), u_id, g_id)
for file in files:
os.chown(os.path.join(root, file), u_id, g_id)
def close_child_process(proc):
try:
os.killpg(proc.pid, signal.SIGKILL)
except ProcessLookupError as err:
_ = err
return 'success'
except Exception as err:
return str(err)
return 'success'
def exec_popen(cmd, timeout=None):
"""
subprocess.Popen in python3.
param cmd: commands need to execute
return: status code, standard output, error output
"""
bash_cmd = ["bash"]
pobj = subprocess.Popen(bash_cmd, shell=False, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid)
pobj.stdin.write(cmd.encode())
pobj.stdin.write(os.linesep.encode())
if not timeout:
stdout, stderr = pobj.communicate()
return_code = pobj.returncode
else:
try:
stdout, stderr = pobj.communicate(timeout=timeout)
except Exception as err:
return pobj.returncode, "", str(err)
finally:
return_code = pobj.returncode
close_child_process(pobj)
stdout, stderr = stdout.decode(), stderr.decode()
if stdout[-1:] == os.linesep:
stdout = stdout[:-1]
if stderr[-1:] == os.linesep:
stderr = stderr[:-1]
return return_code, stdout, stderr
def execute_command(command, raise_flag=False, timeout=None):
code, out, err = exec_popen(command, timeout=timeout)
if code:
if "echo" in command:
command = command.split("|", 1)[1]
LOG.error(f"execute cmd[{command}] failed, err[{err}], out[{out}].")
if raise_flag:
raise Exception(f"execute cmd[{command}] failed, err[{err}], out[{out}.")
print(out)
return code, out, err
def get_dm_password():
password_file = os.path.join(DORADO_CONF_PATH, DM_PWD)
if not os.path.exists(password_file):
LOG.error("DM password file not found.")
raise Exception("get dm password file not found.")
encode_dm_password = get_file_content(password_file)
decode_dm_password = resolve_kmc_pwd(encode_dm_password)
return decode_dm_password
def get_dr_status(dm_password=None):
if not dm_password:
dm_password = get_dm_password()
if dm_password == "":
LOG.error("DM Password is empty.")
cmd = (f"echo -e '{dm_password}' | sh {SCRIPT_PATH}/appctl.sh dr_operate progress_query "
f"--action=check --display=table 2>&1 | grep -E '^\\-|^\\|'")
execute_command(cmd, raise_flag=True, timeout=30)
data_json = get_file_json(os.path.join(CONFIG_PATH, "dr_status.json"))
return data_json.get("dr_status")
def check_dr_deploy_process_completed(role):
max_iterations = 180
if role == "standby":
max_iterations *= 10
count = 1
last_status = ""
dr_process_file = os.path.join(CONFIG_PATH, "dr_process_record.json")
step = 0
while count <= max_iterations:
time.sleep(5)
process_data = get_file_json(dr_process_file)
dr_status = process_data.get("data").get("dr_deploy")
if dr_status == "success":
LOG.info("executing dr_deploy success.")
return True
elif dr_status == "failed":
LOG.info("executing dr_deploy failed.")
return False
elif dr_status == "running":
for index, key in enumerate(list(process_data.get("data").keys())[:-1]):
status = process_data.get("data").get(key)
if last_status:
if index < step:
continue
if status != "default":
if status == "failed":
LOG.error(f"dr_deploy {key}: {status}.")
return False
if last_status != status:
LOG.info(f"dr_deploy {key}: {status}")
last_status = status
step = index
continue
elif step != index:
LOG.info(f"dr_deploy {key}: {status}")
step = index
continue
break
else:
LOG.info("Unexpected status for dr_deploy.")
return False
count += 1
LOG.info("Timeout reached without success.")
return False
def dr_deploy(role=None, dm_password=None, delete_flag=False):
if not dm_password:
dm_password = get_dm_password()
if dm_password == "":
LOG.error("DM Password is empty.")
raise Exception("get dm_password failed.")
if not role:
role = get_value("dr_deploy.role")
cmd = (f"echo -e '{dm_password}' | sh {SCRIPT_PATH}/appctl.sh dr_operate pre_check {role} "
f"--conf={_paths.deploy_param_json}")
execute_command(cmd, raise_flag=True, timeout=300)
LOG.info("dr_operate pre_check success.")
dr_process_file = os.path.join(CONFIG_PATH, "dr_process_record.json")
if os.path.exists(dr_process_file):
os.remove(dr_process_file)
cmd = (f"echo -e '{dm_password}' | sh {SCRIPT_PATH}/appctl.sh dr_operate deploy {role} ")
execute_command(cmd, raise_flag=True)
if check_dr_deploy_process_completed(role):
LOG.info("dr_deploy succeeded.")
if delete_flag:
deploy_user = get_value("deploy_user")
storage_share_fs = get_value("storage_share_fs")
cmd = (f"su -s /bin/bash - {deploy_user} -c 'dbstor --delete-file "
f"--fs-name={storage_share_fs} --file-name=onlyStart.file'")
execute_command(cmd, timeout=180)
sys.exit(0)
sys.exit(1)
def copy_version_yaml(deploy_user, deploy_group):
deploy_user_info = pwd.getpwnam(deploy_user)
deploy_group_info = grp.getgrnam(deploy_group)
u_id = deploy_user_info.pw_uid
g_id = deploy_group_info.gr_gid
version_file = os.path.join(SCRIPT_PATH, "../versions.yml")
chown_path(version_file, u_id, g_id)
version_dir = os.path.dirname(version_file)
storage_share_fs = get_value("storage_share_fs")
cmd = (f'su -s /bin/bash - "{deploy_user}" -c "dbstor --copy-file --import '
f'--fs-name={storage_share_fs} --source-dir={version_dir} --target-dir=/ --file-name=versions.yml"')
execute_command(cmd, timeout=180)
def dr_start_deploy():
role = get_value("dr_deploy.role")
dm_password = get_dm_password()
deploy_mode = get_value("deploy_mode")
deploy_user = get_value("deploy_user")
deploy_group = get_info("deploy_group")
count = 0
if deploy_mode == "dbstor":
storage_fs = get_value("storage_share_fs")
cmd = (f"su -s /bin/bash - {deploy_user} -c 'dbstor --query-file "
f"--fs-name={storage_fs} --file-dir=/' | grep 'dr_deploy_param.json' | wc -l")
code, count, err = execute_command(cmd, timeout=180)
else:
storage_fs = get_value("storage_metadata_fs")
metadata_path = _paths.metadata_fs_path(storage_fs)
if os.path.exists(f"{metadata_path}/dr_deploy_param.json"):
count = 1
if not count.isdigit():
LOG.error("get file count failed.")
sys.exit(1)
if int(count) > 0:
deploy_user_info = pwd.getpwnam(deploy_user)
deploy_group_info = grp.getgrnam(deploy_group)
u_id = deploy_user_info.pw_uid
g_id = deploy_group_info.gr_gid
os.chown(OPT_CONFIG_PATH, u_id, g_id)
if deploy_mode == "dbstor":
cmd = (f"su -s /bin/bash - {deploy_user} -c 'dbstor --copy-file --export --fs-name={storage_fs} "
f"--source-dir=/ --target-dir={OPT_CONFIG_PATH} --file-name=dr_deploy_param.json'")
execute_command(cmd, timeout=180)
else:
metadata_path = _paths.metadata_fs_path(storage_fs)
copy_file(f"{metadata_path}/dr_deploy_param.json",
f"{OPT_CONFIG_PATH}/dr_deploy_param.json")
copy_file(f"{OPT_CONFIG_PATH}/dr_deploy_param.json", f"{CONFIG_PATH}/dr_deploy_param.json")
if get_dr_status(dm_password) != "Normal":
msg = ("DR status is Abnormal. If you need, "
"please enter the container and manually execute the Dr_deploy process.")
LOG.error(msg)
cmd = f"sh {SCRIPT_PATH}/appctl.sh start"
execute_command(cmd, raise_flag=True, timeout=3600)
else:
if role == "active":
cmd = f"sh {SCRIPT_PATH}/appctl.sh start"
execute_command(cmd, raise_flag=True, timeout=3600)
copy_version_yaml(deploy_user, deploy_group)
LOG.info("dr_setup is True, executing dr_deploy tasks.")
dr_deploy(role=role, dm_password=dm_password)
LOG.info("DR_deploy Already executed.")
sys.exit(0)
def main():
split_env = os.environ['LD_LIBRARY_PATH'].split(":")
if _paths.dbstor_lib not in split_env:
LOG.error(f"ograc-dbstor-lib not found, current envpath[{os.environ['LD_LIBRARY_PATH']}]")
raise Exception("ograc-dbstor-lib not found")
action_dict = {
"get_dm_password": get_dm_password,
"get_dr_status": get_dr_status,
"start": dr_start_deploy
}
delete_config = False
if len(sys.argv) > 1:
if sys.argv[1] in action_dict:
return action_dict[sys.argv[1]]()
dr_deploy(delete_flag=delete_config)
if __name__ == "__main__":
try:
main()
except Exception as e:
LOG.error(f"execute failed, err[{str(e)}], traceback: [{traceback.format_exc(limit=-1)}]")
sys.exit(1)