import acrobind
import acrort
import os
import prettytable
import register_mms
import subprocess
import shutil
import xml.etree.ElementTree as etree
Log = None
CACHED_INSTANCE_ID = None
CACHED_MACHINE_ID = None
CACHED_TARGET_DIR = None
MMS_PORT = 43234
OVERRIDE_CONFIG_FN = 'user.config'
def registry_read_string(key_name, value_name, *, open_hive=None):
root_reg = acrort.registry.open_system_hive(hive=open_hive)
if key_name not in root_reg.subkeys:
acrort.common.make_logic_error("Key '{}' not found. May be agent not installed".format(key_name)).throw()
key = root_reg.subkeys.open(key_name=key_name)
if value_name not in key.values:
acrort.common.make_logic_error("Value '{}' not found. May be agent not installed".format(value_name)).throw()
value = key.values.open(value_name=value_name)
return value.get(acrort.registry.TYPE_SZ)
def current_instance_id():
global CACHED_INSTANCE_ID
if CACHED_INSTANCE_ID:
return CACHED_INSTANCE_ID
key_short_path = "\\BackupAndRecovery\\Settings\\MachineManager\\"
request = r'SOFTWARE\{}'.format(acrort.common.BRAND_NAME) + key_short_path
CACHED_INSTANCE_ID = registry_read_string(request, 'InstanceID')
return CACHED_INSTANCE_ID
def current_machine_id():
global CACHED_MACHINE_ID
if CACHED_MACHINE_ID:
return CACHED_MACHINE_ID
key_short_path = "\\BackupAndRecovery\\Settings\\MachineManager\\"
request = r'SOFTWARE\{}'.format(acrort.common.BRAND_NAME) + key_short_path
CACHED_MACHINE_ID = registry_read_string(request, 'MMSCurrentMachineID')
return CACHED_MACHINE_ID
def target_dir():
global CACHED_TARGET_DIR
if CACHED_TARGET_DIR:
pass
else:
if is_win():
key_short_path = "\\Installer\\"
request = r'SOFTWARE\{}'.format(acrort.common.BRAND_NAME) + key_short_path
CACHED_TARGET_DIR = registry_read_string(request, 'TargetDir')
else:
CACHED_TARGET_DIR = "/usr/lib/Acronis"
return CACHED_TARGET_DIR
def config(id, agent_dir, os_caps, is_inside_virtual, is_server_essential, port):
return """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<Software>
<Acronis>
<BackupAndRecovery>
<Settings>
<MMSHttpPort>
""" + str(port) + """
</MMSHttpPort>
<MmsDmlDbProtocol>
""" + os.path.join(agent_dir, 'DML', 'dml.db3') + """
</MmsDmlDbProtocol>
<MachineManager>
<InstanceID>
""" + current_instance_id()[0:-4] + id + """
</InstanceID>
<MMSCurrentMachineID>
""" + current_machine_id()[0:-4] + id + """
</MMSCurrentMachineID>
</MachineManager>
</Settings>
</BackupAndRecovery>
</Acronis>
</Software>
<ServiceIdentifier>
""" + id + """
</ServiceIdentifier>
<OSCaps>
""" + str(os_caps) + """
</OSCaps>
<IsInsideVirtual>
""" + str(is_inside_virtual) + """
</IsInsideVirtual>
<ServerEssentialEdition>
""" + str(is_server_essential) + """
</ServerEssentialEdition>
</config>
"""
def index_to_id(index):
return str(index).zfill(4)
def is_win():
return os.name == 'nt'
def silent_start(arguments):
dev_null = open(os.devnull, 'wb')
subprocess.Popen(arguments, stdout=dev_null, stderr=dev_null, shell=False)
dev_null.close()
class Agent:
def __init__(self, *, id, deployment_dir):
self.id = id
self.agent_dir = os.path.join(os.path.abspath(deployment_dir), id)
config = etree.parse(os.path.join(self.agent_dir, OVERRIDE_CONFIG_FN))
self._os_caps = int(config.findtext('OSCaps', '0').strip())
self._is_inside_virtual = int(config.findtext('IsInsideVirtual', '0').strip())
self._is_server_essential = int(config.findtext('ServerEssentialEdition', '0').strip())
self._port = int(config.findtext('Software/Acronis/BackupAndRecovery/Settings/MMSHttpPort', str(MMS_PORT)).strip())
def start(self):
Log.write('Configuration file ...', os.path.join(self.agent_dir, OVERRIDE_CONFIG_FN));
silent_start([os.path.join(target_dir(), 'BackupAndRecovery', 'mms'), '-mode', 'console', '-homedir', self.agent_dir])
def register(self, destination, host, port, user, password):
register_mms.register('onpremise' if destination == 'premise' else destination,
host, port, [user, password], self._port)
def type(self):
if self._is_inside_virtual:
return 'virtual'
if self._os_caps == 0:
return 'workst'
else:
return 'server'
class AgentList:
def __init__(self, deployment_dir, index_from, index_to):
self._list = []
self._deployment_dir = deployment_dir
self._index_from = int(index_from) if index_from else None
self._index_to = int(index_to) if index_to else None
for entry in os.scandir(deployment_dir):
if entry.is_dir() and entry.name.isdigit():
self._list.append(Agent(id=entry.name, deployment_dir=deployment_dir))
self._list.sort(key=lambda agent: agent.id)
def deploy(self, workstation_count, server_count, virtual_count, server_essential_count):
def _deploy_agent(id, dir_path, os_caps, is_inside_virtual, is_server_essential, port):
dir_path = os.path.abspath(dir_path)
id_path = os.path.join(dir_path, id)
dml_path = os.path.join(id_path, 'DML')
access_vault_dir = os.path.normpath(os.path.join(id_path, 'AccessVault/config'))
os.mkdir(id_path)
os.mkdir(dml_path)
os.makedirs(access_vault_dir, exist_ok=True)
access_pref_file = os.path.join(access_vault_dir, 'preferred')
source_pref_file = os.path.normpath(os.path.join(acrort.fs.APPDATA_COMMON, acrort.common.BRAND_NAME, 'BackupAndRecovery/MMS/AccessVault/config/preferred'))
shutil.copyfile(source_pref_file, access_pref_file)
file = open(os.path.join(id_path, OVERRIDE_CONFIG_FN), 'w')
file.write(config(id, id_path, os_caps, is_inside_virtual, is_server_essential, port))
file.close()
#Log.write('Successfully deployed agent \'{}\': os_caps \'{}\', is_inside_virtual \'{}\', is_server_essential \'{}\''.format(id, os_caps, is_inside_virtual, is_server_essential))
return Agent(id=id, deployment_dir=dir_path)
last_id = int(max(agent.id for agent in self._list)) if self._list else 0
first_id = int(min(agent.id for agent in self._list)) if self._list else 0
start_index = last_id + 1
if self._index_to:
Log.write('In deployment command argument --to will be skipped.')
if self._index_from:
if self._index_from <= last_id:
Log.write('Selected folder \'{}\' already contains agents with id range {}-{}. Specify start index more than {}'.format(self._deployment_dir, first_id, last_id, last_id))
return
else:
start_index = self._index_from
for index in range(start_index, start_index + workstation_count):
Log.write('Deploying workstation agent \'{}\'.'.format(index))
self._list.append(_deploy_agent(index_to_id(index), self._deployment_dir, 0, 0, 0, MMS_PORT + index))
start_index += + workstation_count
for index in range(start_index, start_index + server_count):
Log.write('Deploying server agent \'{}\'.'.format(index))
self._list.append(_deploy_agent(index_to_id(index), self._deployment_dir, 2, 0, 0, MMS_PORT + index))
start_index += + server_count
for index in range(start_index, start_index + virtual_count):
Log.write('Deploying virtual agent \'{}\'.'.format(index))
self._list.append(_deploy_agent(index_to_id(index), self._deployment_dir, 0, 1, 0, MMS_PORT + index))
start_index += + server_essential_count
for index in range(start_index, start_index + server_essential_count):
Log.write('Deploying server essential agent \'{}\'.'.format(index))
self._list.append(_deploy_agent(index_to_id(index), self._deployment_dir, 0, 0, 1, MMS_PORT + index))
def _apply_range(self, func, desc):
first_id = int(min(agent.id for agent in self._list)) if self._list else 0
last_id = int(max(agent.id for agent in self._list)) if self._list else 0
index_from = self._index_from or first_id
index_to = self._index_to or last_id
if (index_to < index_from) or not (first_id <= index_from <= last_id) or not (first_id <= index_to <= last_id):
Log.write('Selected folder \'{}\' contains agents with id range {}-{}. Current range {}-{} is not valid.'.format(self._deployment_dir, first_id, last_id, index_from, index_to))
start_index = max(first_id, index_from)
last_index = min(last_id, index_to)
for agent in self._list:
if not (start_index <= int(agent.id) <= last_index):
continue
Log.write('{}: \'{}\'.'.format(desc, agent.id))
func(agent)
def start(self):
def _run_agent(agent):
agent.start()
self._apply_range(_run_agent, 'Starting agent')
def register(self, destination, host, port, user, password):
def _run_agent(agent):
agent.register(destination, host, port, user, password)
self._apply_range(_run_agent, 'Registering agent')
def describe(self):
info_table = prettytable.PrettyTable(["#", "type", "homedir"])
for agent in self._list:
info_table.add_row([int(agent.id), agent.type(), agent.agent_dir])
Log.write(info_table)
def main():
parser = acrobind.CommandLineParser()
parser.add_argument('-d', '--deploy', nargs=4, metavar=('WORKSTATIONS', 'SERVERS', 'VIRTUAL', 'ESSENTIAL'), help='Deploy several agents into specified directory.\
Example: acropsh -m multi_mms -d --deployment-folder C:/test -i 0 20')
parser.add_argument('-s', '--start', action='store_true', help='start idle agents')
parser.add_argument('-r', '--register', nargs=5, metavar=('DESTINATION(cloud/premise)', 'HOST', 'PORT', 'USER', 'PASSWORD'), help='register running agents')
#parser.add_argument('-c', '--cleanup', action='store_true', help='cleanup agents, support interval')
parser.add_argument('-p', '--print', action='store_true', help='print info')
parser.add_argument('-f', '--deployment-folder', help='Deployment directory for multi-mms configurations', required=True)
parser.add_argument('--from', help='Select agents FROM this index')
parser.add_argument('--to', help='Select agents TO this index')
parser.append_processor(acrobind.OutputArgumentsProcessor())
config = None
try:
config = parser.parse_arguments()
except acrort.Exception as exception:
error = exception.to_error()
ret = error.to_exit_code()
if ret == acrort.common.EXCEPTION_AWARE_RETURN_CODE:
error.throw()
return ret
global Log
Log = acrobind.Output(config, end='\n')
args = config['args']
if not os.path.exists(args.deployment_folder):
os.makedirs(args.deployment_folder)
agents = AgentList(args.deployment_folder, vars(args)['from'], vars(args)['to'])
if args.deploy:
agents.deploy(int(args.deploy[0]), int(args.deploy[1]), int(args.deploy[2]), int(args.deploy[3]))
if args.start:
agents.start()
if args.register:
agents.register(args.register[0], args.register[1], args.register[2], args.register[3], args.register[4])
if args.print:
agents.describe()
if __name__ == '__main__':
exit(acrobind.interruptable_safe_execute(main))
|