Sindbad~EG File Manager
import acrobind
import acrort
import collections
import csv
import datetime
import json
import operator
import os.path
import prettytable
import re
import subprocess
import sys
import time
import traceback
INSTANCE_TYPES = {
1: 'TYPE_MACHINE',
2: 'TYPE_DB',
3: 'TYPE_SHAREPOINT',
4: 'TYPE_VIRTUAL_MACHINE',
5: 'TYPE_VIRTUAL_SERVER',
6: 'TYPE_EXCHANGE',
7: 'TYPE_VIRTUAL_CLUSTER',
8: 'TYPE_VIRTUAL_APPLIANCE',
9: 'TYPE_VIRTUAL_APPLICATION',
10: 'TYPE_VIRTUAL_RESOURCE_POOL',
11: 'TYPE_VIRTUAL_CENTER',
12: 'TYPE_DATASTORE',
13: 'TYPE_DATASTORE_CLUSTER',
14: 'TYPE_MSSQL',
15: 'TYPE_VIRTUAL_NETWORK',
16: 'TYPE_VIRTUAL_FOLDER',
17: 'TYPE_VIRTUAL_DATACENTER',
18: 'TYPE_SMB_SHARED_FOLDER',
19: 'TYPE_MSSQL_INSTANCE',
20: 'TYPE_MSSQL_DATABASE',
21: 'TYPE_MSSQL_DATABASE_FOLDER',
22: 'TYPE_MSEXCHANGE_DATABASE',
23: 'TYPE_MSEXCHANGE_STORAGE_GROUP',
24: 'TYPE_MSEXCHANGE_MAILBOX',
}
BACKUP_STATUSES = {
0: 'STATUS_BACKUP_UNKNOWN',
1: 'STATUS_BACKUP_NONE',
2: 'STATUS_BACKUP_SUCCEEDED',
3: 'STATUS_BACKUP_WARNING',
4: 'STATUS_BACKUP_FAILED'
}
BACKUP_STATES = {
0: 'BACKUP_STATE_IDLE',
1: 'BACKUP_STATE_RUNNING',
}
INSTANCE_STATUSES = {
0: 'INSTANCE_STATUS_UNKNOWN',
1: 'INSTANCE_STATUS_NONE',
2: 'INSTANCE_STATUS_SUCCEEDED',
3: 'INSTANCE_STATUS_WARNING',
4: 'INSTANCE_STATUS_FAILED'
}
INSTANCE_STATES = {
0: 'IDLE',
0x01: 'INTERACTION_REQUIRED',
0x02: 'CANCELLING',
0x04: 'RUNNING_BACKUP',
0x08: 'RUNNING_RECOVER',
0x10: 'RUNNING_INSTALL',
0x20: 'RUNNING_REBOOT',
0x40: 'RUNNING_FAILBACK',
0x80: 'RUNNING_TEST',
0x100: 'RUNNING_FROM_IMAGE',
0x200: 'RUNNING_FINALIZE',
0x400: 'RUNNING_FAILOVER',
0x800: 'RUNNING_REPLICATION',
}
Log = None
args = None
def format_backtrace(exception):
exc_type, exc_value, exc_traceback = sys.exc_info()
info = traceback.format_exception(exc_type, exc_value, exc_traceback)
return ''.join(line for line in info)
def error_log(message):
if Log:
Log.write(message)
else:
print(message)
def print_bool_flag(flag):
if flag is None:
return '-'
if not flag:
return '-'
return '+'
def get_optional(object, prop_name):
val = None
if prop_name in object:
val = object[prop_name].ref
return val
def describe_tenant(tenant, full_format=True):
if tenant is None:
return 'MISSING'
if full_format:
return '\'{0}\'(Name:\'{1}\', Locator: \'{2}\', Kind: \'{3}\')'.format(tenant.ID.ref if 'ID' in tenant else 'MISSING',\
tenant.Name.ref if 'Name' in tenant else 'MISSING', tenant.Locator.ref if 'Locator' in tenant else 'MISSING',\
tenant.Kind.ref if 'Kind' in tenant else 'MISSING')
return '{}'.format(tenant.ID.ref if 'ID' in tenant else 'MISSING')
def get_tenant_string(object, full_format=True):
if object is None or 'Tenant' not in object:
return 'MISSING'
return describe_tenant(object.Tenant, full_format)
def to_instance_state(state):
if state == 0:
return 'IDLE(0)'
if state == 1:
return 'RUNNING(1)'
return 'UNKNOWN({})'.format(state)
def to_machine_status(status):
if status == 0:
return 'ONLINE'
if status == 1:
return 'OFFLINE'
if status == 2:
return 'FOREIGN'
if status == 3:
return 'EXPIRED'
return 'UNKNOWN'
def drop_agent_connection(connection, host_id):
try:
argument = acrort.plain.Unit(flat=[('.ID', 'string', '{}'.format(host_id).upper())])
activity_id = connection.tol.launch_command(command='651F1568-B113-479F-B578-A4167F9CA61B', argument=argument)
Log.write('Waiting for command activity {} completion...'.format(activity_id), end='')
result = connection.tol.get_result(activity_id)
Log.write('done')
except Exception as error:
Log.write('Error: {}'.format(error))
def fix_item_protection_tenant(connection, ip_id, instance_id, host_id):
print('Processing Gtob::Dto::ItemProtection with ID: {0}'.format(ip_id))
print('Getting corresponding InstanceManagement::Instance with ID: {0}'.format(instance_id))
(selected, instance) = acrobind.safe_select_object(connection, acrobind.create_viewspec_instance_by_id(instance_id))
if selected and instance is None:
print("Can't find instance with ID {0}, deleting wrong ItemProtection".format(instance_id))
return
print('Getting Msp::AMS::Dto::Machine for host ID: {0}'.format(host_id))
msp_machine = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('Msp::AMS::Dto::Machine', '.AgentID', '{}'.format(host_id)))
#print('msp_machine: {0}'.format(msp_machine))
if msp_machine is None:
print("Can't find Msp::AMS::Dto::Machine for host {0}, skipping".format(host_id))
return
tenant_id = msp_machine['OwnerID'].ref
print('Getting Tenants::HierarchyNode with ID: {0}'.format(tenant_id))
tenant = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('Tenants::HierarchyNode', '.ID', tenant_id))
if tenant is None:
print("Can't find Tenants::HierarchyNode with ID {0}, skipping".format(tenant_id))
return
print(tenant)
#connection.dml.update(pattern=acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ItemProtection', '.ID', ip_id), diff={'Tenant': tenant})
print('Gtob::Dto::ItemProtection with ID {0} is fixed.'.format(ip_id))
def fix_instance_tenant(connection, instance_id, host_id):
print('Processing InstanceManagement::Instance with ID: {0}'.format(instance_id))
print('Getting Msp::AMS::Dto::Machine for host ID: {0}'.format(host_id))
msp_machine = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('Msp::AMS::Dto::Machine', '.AgentID', '{}'.format(host_id)))
#print('msp_machine: {0}'.format(msp_machine))
if msp_machine is None:
print("Can't find Msp::AMS::Dto::Machine for host {0}, skipping".format(host_id))
return
tenant_id = msp_machine['OwnerID'].ref
print('Getting Tenants::HierarchyNode with ID: {0}'.format(tenant_id))
tenant = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('Tenants::HierarchyNode', '.ID', tenant_id))
if tenant is None:
print("Can't find Tenants::HierarchyNode with ID {0}, skipping".format(tenant_id))
return
print(tenant)
#connection.dml.update(pattern=instance_spec(instance_id), diff={'Tenant': tenant})
print('InstanceManagement::Instance with ID {0} is fixed.'.format(instance_id))
def fix_machine_tenant(connection, machine_id):
print('Processing MachineManagement::Machine with ID: {0}'.format(machine_id))
print('Getting Msp::AMS::Dto::Machine for host ID: {0}'.format(machine_id))
msp_machine = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('Msp::AMS::Dto::Machine', '.AgentID', '{}'.format(host_id)))
#print('msp_machine: {0}'.format(msp_machine))
if msp_machine is None:
print("Can't find Msp::AMS::Dto::Machine for host {0}, skipping".format(machine_id))
return False
tenant_id = msp_machine['OwnerID'].ref
print('Getting Tenants::HierarchyNode with ID: {0}'.format(tenant_id))
tenant = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('Tenants::HierarchyNode', '.ID', tenant_id))
if tenant is None:
print("Can't find Tenants::HierarchyNode with ID {0}, skipping".format(tenant_id))
return False
print(tenant)
#connection.dml.update(pattern=acrobind.create_viewspec_by_is_and_guid_property('MachineManagement::Machine', '.ID', machine_id), diff={'Tenant': tenant})
print('MachineManagement::Machine with ID {0} is fixed.'.format(machine_id))
return True
def analyze_tenant(object):
if 'Tenant' in object:
locator = '-'
if 'Locator' in object['Tenant']:
locator = object.Tenant.Locator.ref
return '\'{0}\'({1})'.format(object.Tenant.Name.ref, locator)
return None
def wrap_safe_exec_result(result):
if not result[0]:
return 'UNKNOWN'
return 'OK' if result[1] else 'MISSING'
def get_objects_count(connection, machine_id):
spec = acrobind.create_viewspec_by_is_and_guid_property('Dml::Sync::Caching::Registration', '.__source_machine', str(machine_id))
return acrobind.count_objects_by_spec(connection, spec)
def get_objects_count_2(connection, machine_id):
return acrobind.select_objects(connection, acrobind.create_viewspec_by_is_and_guid_property('Dml::Sync::Caching::Registration', '.__source_machine', str(machine_id)))
def delete_objects_count(dml, machine_id):
spec = acrobind.create_viewspec_by_is_and_guid_property('Dml::Sync::Caching::Registration', '.__source_machine', str(machine_id))
dml.delete(pattern=spec.pattern)
def check_caching_registration(connection):
print('Checking Dml::Sync::Caching::Registration')
if args.machine_id is not None and args.machine_status is not None and args.machine_status == 0:
print('Count: {0}'.format(get_objects_count(connection, args.machine_id)))
objects = get_objects_count_2(connection, args.machine_id)
print('Count2: {0}'.format(len(objects)))
def caching_registration_spec(registration_id, machine_id):
return acrobind.viewspec_apply_remote_host(acrobind.create_viewspec_by_is_and_guid_property('Dml::Sync::Caching::Registration', '.ID', registration_id), machine_id)
object = acrobind.safe_select_object(connection, caching_registration_spec('CE030FCE-9241-400D-97C0-601610EDD186', args.machine_id))
print('\tActivities: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('D0F82464-DD2F-4017-9745-DDEE4F44610A', args.machine_id))
print('\tProtect command activities: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('8B94773D-6748-443B-8170-91426FD0EA98', args.machine_id))
print('\tGtob::Protection::App::Machine: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('CC7554D7-62EA-4D57-8483-E6BFA12CDA72', args.machine_id))
print('\tClusterManager::Cluster: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('D236945B-755A-4A79-A614-67BB674E011A', args.machine_id))
print('\tGtob::Dto::Protection: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('54EB2BDC-1465-4C34-9C94-A08C843E6ED6', args.machine_id))
print('\tGtob::Dto::ProtectionPlan: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('BFBFEE9D-551C-4737-BF43-06CB97B7FACA', args.machine_id))
print('\tRunVmFromImage::VMResurrection: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('DE5FCF73-3A7E-4989-A869-CF61F509B0EB', args.machine_id))
print('\tStatistics::Counters: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('B9D53478-4CB1-45C6-9EAF-91AB6DDD38CC', args.machine_id))
print('\tReplication::Continuous::ProcessInfo: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('F47035AE-2057-4862-889A-40D6DADB7F9C', args.machine_id))
print('\tLocal Gtob::Dto::ItemProtection: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('055F55CC-2F09-4FB8-A5E0-63EC1F186E0F', args.machine_id))
print('\tCentralized Gtob::Dto::ItemProtection: {0}'.format(wrap_safe_exec_result(object)))
object = acrobind.safe_select_object(connection, caching_registration_spec('323976BC-3CB2-4DD2-8736-9343A7B4C3DB', args.machine_id))
print('\tLegacy Gtob::Dto::ItemProtection: {0}'.format(wrap_safe_exec_result(object)))
objects = []
#objects = acrobind.select_objects(connection, acrobind.viewspec_apply_remote_host(acrobind.create_viewspec_by_is('Gtob::Dto::ItemProtection'), args.machine_id))
#objects = acrobind.select_objects(connection, acrobind.viewspec_apply_remote_host(acrobind.create_viewspec_by_is('InstanceManagement::Instance'), args.machine_id))
#objects = acrobind.select_objects(connection, acrobind.viewspec_apply_remote_host(acrobind.create_viewspec_by_is('Gtob::Dto::Protection'), args.machine_id))
#objects = acrobind.select_objects(connection, acrobind.viewspec_apply_remote_host(acrobind.create_viewspec_by_is('Gtob::Dto::ProtectionPlan'), args.machine_id))
#objects = acrobind.select_objects(connection, acrobind.viewspec_apply_remote_host(acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.Environment.ProtectionPlanID', 'C3A38821-AF2D-05A0-21E2-23B2F5673916'), args.machine_id))
for o in objects:
print('--------------')
print(o)
elif args.machine_id is not None:
print('Machine is OFFLINE.')
else:
print('No machine ID.')
def check_running_activities(connection, spec, machine_id=None):
Log.write('Checking activities by spec: {0}'.format(spec.pattern))
pattern1 = [
('^Is', 'string', 'Tol::History::Plain::Activity'),
('.Tenant.ID', 'string', '{}'.format(args.tenant_id)),
('.State', 'dword', 0),
('.State^Less', 'dword', 5)]
spec1 = acrort.dml.ViewSpec(acrort.plain.Unit(flat=pattern1))
local_count = acrobind.count_objects_by_spec(connection, spec)
Log.write('AMS has \'{}\' activities.'.format(local_count))
if local_count > 0:
activities = acrobind.select_objects(connection, spec)
list_activities(activities)
if args.fix:
print('Do you want to change state of these activities to completed?(y/n)?')
if ask_user():
for a in activities:
id_str = '{0}'.format(a.ID.ref)
pattern = [
('', 'dword', 5),
]
diff_unit={'State': acrort.plain.Unit(flat=pattern)}
connection.dml.update(pattern=acrobind.create_viewspec_by_is_and_guid_property('Tol::History::Plain::Activity', '.ID', id_str).pattern, diff=diff_unit)
print('Activity {} is fixed.'.format(id_str))
else:
print('skipped.')
if machine_id:
Log.write('Checking remote activities on agent \'{0}\'.'.format(machine_id))
local_pattern = [
('^Is', 'string', 'Tol::History::Plain::Activity'),
('.State', 'dword', 0),
('.State^Less', 'dword', 5)]
local_spec = acrort.dml.ViewSpec(acrort.plain.Unit(flat=local_pattern))
remote_spec = acrobind.viewspec_apply_remote_host(local_spec, machine_id)
count = acrobind.count_objects_by_spec(connection, remote_spec)
Log.write('Agent \'{}\' has \'{}\' running activities.'.format(machine_id, count))
if count > 0:
activities = acrobind.select_objects(connection, remote_spec)
list_activities(activities)
def list_activities(activities):
table = prettytable.PrettyTable(["Name", "State", "ID", "Specific", "CommandID", "HostID"])
table.align["Name"] = "l"
table.padding_width = 1
for a in activities:
if args.extra:
Log.write(a)
name_str = '\'{0}\''.format(a.Name.ref)
state_str = '{0}'.format(a.State.ref)
id_str = '{0}'.format(a.ID.ref)
command_id_str = '{0}'.format(a.Details.CommandID.ref)
specific_id_str = '{0}'.format(a.Details.Specific.ref)
host_id_str = acrobind.get_trait_value('Source', a)
table.add_row([name_str, state_str, id_str, specific_id_str, command_id_str, host_id_str])
Log.write(table.get_string(sortby="Name"))
Log.write('')
def delete_plan_artifacts(connection, host_id, cplan_id):
print('Do you want to cleanup synced Gtob::Dto::ProtectionPlan(y/n)?')
if ask_user():
connection.dml.delete(pattern=acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.ID', cplan_id).pattern)
connection.dml.delete(pattern=acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::CentralizedProtection', '.ID', cplan_id).pattern)
connection.dml.delete(pattern=acrobind.viewspec_apply_source(acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.ID', cplan_id), host_id).pattern)
print('deleted.')
else:
print('skipped.')
print('Do you want to cleanup legacy Gtob::Dto::ItemProtection(y/n)?')
if ask_user() and host_id:
remote_ips_spec = acrobind.viewspec_apply_remote_host(acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ItemProtection', '.Plan', cplan_id), host_id)
objects = acrobind.select_objects(connection, remote_ips_spec)
for o in objects:
print('--------------')
print(o)
connection.dml.delete(pattern=remote_ips_spec.pattern)
print('deleted.')
else:
print('skipped.')
def origin_to_str(origin):
if origin == 1:
return 'L'
if origin == 2:
return 'C'
if origin ==3:
return 'D'
return 'U'
def redeploy_plan(connection, plan_id):
selection_state_spec = acrobind.create_viewspec_by_is_and_guid_property('Gtob::Gct::SelectionState', '.ID', plan_id)
selection_state = acrobind.select_object(connection, selection_state_spec)
print("Deleting Gtob::Gct::SelectionState: {}".format(selection_state))
connection.dml.delete(pattern=selection_state_spec.pattern)
digest_spec = acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ResourceDigest', '.ID', plan_id)
digest = acrobind.select_object(connection, digest_spec)
Log.write("Deleting Gtob::Dto::ResourceDigest: {}".format(digest))
connection.dml.delete(pattern=digest_spec.pattern)
deployment_request = [
('^Is', 'string', 'Gtob::Dto::AutomaticDeploymentRequest'),
('.ID', 'guid', plan_id),
('.ID^PrimaryKey', 'nil', None),
('.ID^AutomaticDeploymentRequest', 'nil', None),
('.InitiatedBy', 'string', 'infraview'),
('.Fails', 'array', [])
]
#deployment_request_unit = acrort.plain.Unit(flat=deployment_request)
#Log.write('Creating deployment request: {0}'.format(deployment_request_unit))
#connection.dml.create(deployment_request_unit, mode=acrort.dml.CREATE_OR_REPLACE)
Log.write('Running protect command for plan: {0}'.format(plan_id))
plan = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.ID', plan_id))
tenant_connection = acrort.connectivity.Connection('ams', client_session_data = {'tenant_id': '{}'.format(plan.Tenant.ID.ref)})
activity_id = tenant_connection.tol.launch_command(command='41830509-FCA4-4B3A-9978-3D00462DE006', argument=plan)
result = None
try:
result = tenant_connection.tol.get_result(activity_id)
tenant_connection.close()
except acrort.Exception as ex:
if acrort.common.interrupt_sentinel:
tenant_connection.tol.cancel_activity(activity_id)
Log.write('Protect canceled.')
tenant_connection.close()
raise
def fix_centralized_protection(connection, plan_id):
Log.write('Creating missing Gtob::Dto::Centralized::ItemProtection object for plan \'{}\'...'.format(plan_id))
spec = acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::Centralized::ItemProtection', '.Centralized.PlanID', plan_id)
protections = acrobind.select_objects(connection, spec)
if not protections:
Log.write('There are no Gtob::Dto::Centralized::ItemProtection objects for this plan')
return
affected_machines = []
protected_instances = []
for protection in protections:
affected_machines.append([('', 'guid', protection.HostID.ref)])
protected_instances.append(('.ProtectedInstances.{}'.format(protection.InstanceID.ref), 'dword', 1))
centralized_protection = acrort.plain.Unit(flat=[
('.ActivationStatus', 'dword', 1),
('.AffectedMachines', 'array', affected_machines),
('.AffectedMachines^IsContainer', 'string', 'vector'),
('.AffectedMachinesCount', 'dword', 1),
('.CurrentFrame.BackupNumber', 'dword', 0),
('.CurrentFrame.LastStartTime', 'sqword', 0),
('.CurrentFrame.SchemeDeploymentID', 'string', ''),
('.CurrentFrame^Is', 'string', 'Gtob::Dto::BackupFrameData'),
('.Host', 'guid', '00000000-0000-0000-0000-000000000000'),
('.ID', 'guid', plan_id),
('.ID^PrimaryKey', 'nil', None),
('.ID^Protection', 'nil', None),
('.LastResult', 'dword', 0),
('.ManualStartNumber', 'dword', 0),
('.Origin', 'dword', 2),
('.Owner', 'string', 'root'),
('.PlanDeploymentState', 'dword', 0),
('.PlanID', 'guid', plan_id),
('.PlanID^ForeignKey', 'nil', None),
*protected_instances,
('.Status', 'dword', 0),
('^Is', 'string', 'Gtob::Dto::CentralizedProtection'),
('^Is', 'string', 'Gtob::Dto::Protection'),
])
connection.dml.create(centralized_protection)
Log.write('Creating object:\n', centralized_protection)
def describe_plans(connection):
Log.write('[---Checking plans---]')
plans = None
if args.plan_name:
plans = acrobind.select_objects(connection, acrobind.create_viewspec_by_is_and_like('Gtob::Dto::ProtectionPlan', '.Name', args.plan_name))
if args.plan_id:
plans = acrobind.select_objects(connection, acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.ID', args.plan_id))
check_plan_list_internal(connection, plans)
if args.plan_id and args.change_owner:
new_tenant = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('Tenants::HierarchyNode', '.ID', args.change_owner))
if new_tenant:
diff = [
('^PackedDiff', 'nil', None),
('.DmlTimeStamp', 'sqword', 0),
('.DmlTimeStamp^Removed', 'nil', None),
]
tenant_patch = new_tenant.merge(acrort.plain.Unit(flat=diff))
new_owner_id = acrort.common.Guid(acrort.common.eval_md5('security-tenant-{}'.format(new_tenant.Locator.ref)))
new_tenant_str = describe_tenant(new_tenant, full_format=True)
plan = plans[0]
p_tenant = get_tenant_string(plan, full_format=True)
p_owner = get_optional(plan, "OwnerID")
Log.write('Do you want to change owner of Gtob::Dto::ProtectionPlan from \'{0}({1})\' to \'{2}({3})\'?(y/n)'.format(p_tenant, p_owner, new_tenant_str, new_owner_id))
if ask_user():
owner_patch = acrort.plain.Unit({'OwnerID' : new_owner_id, 'Tenant' : tenant_patch})
plan = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.ID', args.plan_id))
Log.write('Applying patch: {0}'.format(owner_patch))
new_plan = plan.consolidate(owner_patch)
connection.dml.create(new_plan, mode=acrort.dml.CREATE_OR_REPLACE)
if args.extra:
Log.write(plan)
Log.write('done')
else:
Log.write('Can\'t find tenant with ID \'{0}\''.format(args.change_owner))
if args.plan_id and args.redeploy:
Log.write('Do you want to redeploy Gtob::Dto::ProtectionPlan?(y/n)')
if ask_user():
result = redeploy_plan(connection, args.plan_id)
Log.write(result)
Log.write('done')
if args.plan_id and args.fix:
Log.write('Do you want to undeploy Gtob::Dto::ProtectionPlan?(y/n)')
if ask_user():
Log.write('Input host ID(for centralized plan leave it empty):')
host_id = sys.stdin.readline()
host_id = host_id.rstrip()
cplan_id = None
if not args.plan_id:
Log.write('Input plan ID(empty ID will skip action):')
cplan_id = sys.stdin.readline()
cplan_id = cplan_id.rstrip()
else:
cplan_id = args.plan_id
if cplan_id:
check_plan = None
if host_id:
check_plan = acrobind.select_object(connection, acrobind.viewspec_apply_source(acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.ID', cplan_id), host_id))
else:
check_plan = acrobind.select_object(connection, acrobind.create_viewspec_deployed_protection_plan_by_id(cplan_id))
force_undeploy = False
if not check_plan:
Log.write('Do you want to force undeploy?(y/n)')
if ask_user():
force_undeploy = True
if check_plan or force_undeploy:
arg = acrort.common.Guid(cplan_id)
if args.extra:
Log.write(check_plan)
Log.write('Running unprotect')
if host_id:
activity_id = connection.tol.launch_command(command='C006D24E-E6ED-494a-9789-237CD3A814E7', argument=arg, target_machine=host_id)
else:
activity_id = connection.tol.launch_command(command='C006D24E-E6ED-494a-9789-237CD3A814E7', argument=arg)
try:
if host_id:
result = connection.tol.get_result(activity_id, target_machine=host_id)
else:
result = connection.tol.get_result(activity_id)
except acrort.Exception as ex:
if acrort.common.interrupt_sentinel:
if host_id:
connection.tol.cancel_activity(activity_id, target_machine=host_id)
else:
connection.tol.cancel_activity(activity_id)
Log.write('Unprotect canceled.')
delete_plan_artifacts(connection, host_id, cplan_id)
raise
Log.write(result)
Log.write('done')
delete_plan_artifacts(connection, host_id, cplan_id)
else:
Log.write('Plan not found. Skipped.')
delete_plan_artifacts(connection, host_id, cplan_id)
else:
Log.write('skipped')
else:
Log.write('skipped')
if args.plan_id and args.fix_centralized_protection:
plan = plans[0]
is_centralized = (plan.Origin.ref == 2)
cprotection = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::CentralizedProtection', '.ID', args.plan_id))
if cprotection:
Log.write('Gtob::Dto::CentralizedProtection already exist')
else:
if not is_centralized:
Log.write('Gtob::Dto::CentralizedProtection can be created for centralized plans only')
else:
Log.write('Do you want to create missing Gtob::Dto::CentralizedProtection?(y/n)')
if ask_user():
fix_centralized_protection(connection, args.plan_id)
Log.write('done')
def list_tenants(connection):
Log.write('[---List of tenants that matches \'{}\'---]'.format(args.tenant_name))
objects = acrobind.select_objects(connection, acrobind.create_viewspec_by_is_and_like('Tenants::HierarchyNode', '.Name', args.tenant_name))
table = prettytable.PrettyTable(["Name", "ID", "Locator", "Kind"])
table.align["Name"] = "l"
table.padding_width = 1
for o in objects:
if args.extra:
Log.write(o)
o_name = '\'{0}\''.format(o.Name.ref)
o_id_str = '{0}'.format(o.ID.ref)
o_locator_str = '{0}'.format(o.Locator.ref)
o_kind_str = '{0}'.format(o.Kind.ref)
table.add_row([o_name, o_id_str, o_locator_str, o_kind_str])
Log.write(table.get_string(sortby="Name"))
Log.write('')
def handle_remove_object_request(connection, spec):
if args.delete:
print('Do you want to delete object by spec(y/n):\n\'{0}\'?'.format(spec.pattern))
if ask_user():
connection.dml.delete(pattern=spec.pattern)
print('done')
else:
print('skipped')
def fix_centralized_protections(connection, plans):
centralized_plan_found = False
for plan in plans:
try:
# Process centralized plans onnly
if plan.Origin.ref != 2:
continue
centralized_plan_found = True
cprotection_spec = acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::CentralizedProtection', '.ID', plan.ID.ref)
cprotection = acrobind.select_object(connection, cprotection_spec)
if cprotection:
Log.write('Gtob::Dto::CentralizedProtection for plan \'{}\' already exists, skipping'.format(plan.ID.ref))
else:
fix_centralized_protection(connection, plan.ID.ref)
except Exception as e:
error_log(format_backtrace(e))
if not centralized_plan_found:
Log.write('There are no centralized plans for current account, nothing to fix')
Log.write('done')
def check_plan_list(connection):
Log.write('[---Checking plans---]')
plans = []
ids = [
'9248C82C-3848-430B-A74E-A2B3490112B9',
'DC5BB2CF-F645-43A1-9F6D-F77EAC8DABCA',
'099E59F7-856B-47D6-B6C6-87F61B25E907',
'F1EFC07B-B6F7-45A6-8B0B-439ADBBCD73E',
'74C6AFDB-682D-454F-BCCA-F36656EBF991',
'0FD9477E-69ED-42C8-B7BE-B7F25C76814A',
'29FE3C53-C0BB-47B4-87E7-269030FBF754',
'5B01139F-97CF-4957-969D-AFFBA5FB2A13',
'D7DF4A24-00BB-407F-9AA1-A54476953466',
'8434793F-9745-4855-A38F-53F8F42B6674',
'7D0F79F7-0551-4154-A463-D689514D14F6',
'C382E30D-66B2-41D9-A9EA-7AA4C08405F7',
'7D5EA15A-F928-441E-960F-CAE5EBE96603',
'C47E4AB4-9D37-41AF-9C1D-4681A96892CC',
'127F8D2C-A4B2-4FED-9A52-0EBBA608D25B',
'F77882CF-4E45-4A95-B3E8-6061E7BE5A98',
'60B724D9-B0A2-4916-91E7-C31C72D31DDE',
'03E1BBA6-1F6C-4446-926D-E5D997E412A6',
'4F8F9FCE-299D-4493-8E97-4F98299D15BD',
'25921214-FF3C-4A4B-BA0E-D50D2E994E9E',
'26FE004C-4773-4D9E-9F55-257D3F8E10FA',
'807BDBD3-DDD1-4EEB-BC99-5C6B63453BBD',
'99A1B1C0-089F-490F-8865-DE38A82CF5BC',
'88C98A57-0537-4F55-A5E9-E21E3999516D',
'19E90A70-1BAB-4929-8BB2-2038AF58F718',
'998EE36E-4973-496D-B8A2-B14AA3ECD526',
'1584C104-F6B1-42A4-A62B-D1B1DDB23330',
'34742B08-07EE-488D-BB8C-67CE062EDC67',
'EB333349-4FA2-4822-ADBC-59E129AD02A3',
'3D382519-9E62-42A4-A302-EB42AB493856',
'34D86CC4-76B8-43C2-B87B-F29D660A3F1F',
'CF9B8B40-9AA5-450A-8A50-6C8987733558',
'49791B80-D074-488D-9578-A8B4FC81F483',
'E65223EC-BDAB-4EE0-BA8A-55EDF94AC0D7',
'952B2C76-B4AF-4636-B845-EDA9E7322940',
'CF7BFC0C-F2D0-424D-A4EA-2E36EC5604B5',
'7547E6FF-AE84-4EB4-A57A-E9BCB51AE8E6',
'1E150441-5993-45CF-85DA-100738092AEC',
'05FAB142-3952-4DB0-83A4-8140FEFEF498',
'DC7A5FE6-0E4D-4906-A990-0E3EED7FEF0C',
'BDDF59BE-4CB7-46F4-9B28-29EC5F735394',
'29DBFE77-7B92-4AEE-9633-F7A4417AF560',
'7518FB93-56F0-418D-AC70-645D0A005302',
'F9FF7DA1-D21D-459F-8ABA-D1C51C93ED04',
'13215CC3-10C2-43A6-B773-CABEE0E9BA0C',
'730B3DA3-E118-4C1A-8C12-2F4F9D246C83',
'781AF1F6-0EC4-48CD-A717-B07CEF53CF1A',
'1E48C701-548C-4D7D-9B1D-3E06D99AA86E'
]
for id in ids:
pattern = [
('^Is', 'string', 'Gtob::Dto::ProtectionPlan'),
('.ID', 'guid', id)
]
object = acrobind.select_object(connection, acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=pattern)))
if object is not None:
plans.append(object)
check_plan_list_internal(connection, plans)
def check_plan_list_internal(connection, plans):
plan_table = prettytable.PrettyTable(["Index", "Name/Instance", "ID", "Agents", "Status", "Tenant"])
plan_table.align["Name/Instance"] = "l"
plan_table.align["Status"] = "l"
plan_table.padding_width = 1
is_centralized = False
p_owner = None
p_tenant = None
p_id = None
affected_machines_str = '-'
affected_machines_ids_str = None
centralized_plan_id_str = '-'
cprotection = None
index = 0
for p in plans:
if args.extra:
Log.write(p)
p_id = p.ID.ref
p_id_str = '{0}'.format(p_id)
p_origin = origin_to_str(p.Origin.ref)
p_name = '\'{0}\'({1})'.format(p.Name.ref, p_origin)
p_owner = get_optional(p, "OwnerID")
if p.Origin.ref == 2: #'CENTRALIZED'
is_centralized = True
affected_machine_count = None
cprotection = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::CentralizedProtection', '.ID', p_id))
if not cprotection:
Log.write('Missing centralized protection for plan \'{0}\'!'.format(p_id))
continue
if args.extra:
print(cprotection)
affected_machines_str = 'MISSING'
if 'AffectedMachines' in cprotection:
affected_machines_str = '{0}'.format(len(cprotection.AffectedMachines))
for (number, affected_machine) in cprotection.AffectedMachines:
if affected_machines_ids_str:
affected_machines_ids_str = '{0}, {1}'.format(affected_machines_ids_str, affected_machine.ref)
else:
affected_machines_ids_str = '{0}'.format(affected_machine.ref)
else:
centralized_plan_id_str = '{0}'.format(p.CentralizedProtectionPlan.ref)
p_source = ''
for tname, tunit in p.traits:
if tname == 'Source':
p_source = '{0}'.format(tunit.ref)
break
p_tenant = get_tenant_string(p, full_format=True)
t = get_tenant_string(p, full_format=False)
items = []
if is_centralized:
items = check_cplan_related_item_protections(connection, p_id)
else:
items = check_plan_related_item_protections(connection, p_id)
plan_table.add_row([index, p_name, p_id_str, affected_machines_str, '', t])
index += 1
for item in items:
#item['lfi_status'] = None
#item['lfi_result'] = None
#item['lfi_time'] = None
#item['ns_time'] = None
#item['lsi_time'] = None
#item['cpi'] = '-'
#item['lpi'] = '-'
#item['legacy_pi'] = '-'
errors_string = ''
errors_count = len(item['lfi_errors'])
warnings_string = ''
warnings_count = len(item['lfi_warnings'])
is_ok = True
for error in item['lfi_errors']:
if errors_string != '':
errors_string += '\n{}'.format(errors_string)
else:
errors_string = error
for warning in item['lfi_warnings']:
if warnings_string != '':
warnings_string += '\n{}'.format(warnings_string)
else:
warnings_string = warning
last_result = ''
if errors_count > 0:
last_result = 'E({}): {}'.format(errors_count, errors_string)
if warnings_count > 0:
if errors_count > 0:
last_result += '\n'
last_result += 'W({}): {}'.format(warnings_count, warnings_string)
if errors_count == 0 and warnings_count == 0:
last_result = 'OK'
elif args.parameter1 == 'id':
last_result = 'E:{},W:{}'.format(errors_count, warnings_count)
status = '{}\nLFT: {}\nNST: {}'.format(last_result, item['lfi_time'], item['ns_time'])
#item_tenant_str = '{}({})'.format(item['tenant_id'], item['tenant_name'])
item_tenant_str = '{}'.format(item['tenant_id'])
plan_table.add_row([index, ' |- {}'.format(item['instance_id']), item['id'], item['host_id'], status, item_tenant_str])
index += 1
#Log.write(plan_table.get_string(sortby="Index", fields=["Name/Instance", "ID" "Agents", "Status", "Tenant"]))
if args.parameter1 == 'id':
Log.write(plan_table.get_string(sortby="Index", fields=["Name/Instance", "ID", "Agents", "Status", "Tenant"]))
else:
Log.write(plan_table.get_string(sortby="Index", fields=["Name/Instance", "Status", "Tenant"]))
#print(plan_table)
def check_tenant(connection):
Log.write('[---Checking tenant \'{}\'---]'.format(args.tenant_id))
tenant_spec = acrobind.create_viewspec_by_is_and_string_property('Tenants::HierarchyNode', '.ID', args.tenant_id)
t = acrobind.select_object(connection, tenant_spec)
if args.extra:
Log.write(t)
if not t:
Log.write('No such tenant.')
return
t_name = '\'{0}\'(ID:\'{1}\', Locator: \'{2}\', Kind: \'{3}\')'.format(t.Name.ref, t.ID.ref, t.Locator.ref, t.Kind.ref)
Log.write('Tenant match: {0}'.format(t_name))
handle_remove_object_request(connection, tenant_spec)
if args.update:
is_arr = [None, 'InstanceManagement::Instance', 'MachineManagement::Machine', 'Tol::History::Plain::Activity']
base_pattern = [
('.Tenant.ID', 'string', t.ID.ref)
]
raw_diff = acrort.plain.UnitDiff()
raw_diff.add_patch('.Tenant.Name', t.Name.ref)
for is_str in is_arr:
pattern = base_pattern.copy()
if is_str:
pattern.append(('^Is', 'string', is_str))
connection.dml.update(pattern=acrort.plain.Unit(flat=pattern), raw_diff=raw_diff)
Log.write('All tenant sections is successfully updated.')
if args.running_activities:
pattern = [
('^Is', 'string', 'Tol::History::Plain::Activity'),
('.Tenant.ID', 'string', '{}'.format(args.tenant_id)),
('.State', 'dword', 0),
('.State^Less', 'dword', 5)]
spec = acrort.dml.ViewSpec(acrort.plain.Unit(flat=pattern))
check_running_activities(connection, spec)
if args.machine_statistics:
stat = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('MachineManagement::Statistics', '.Tenant.ID', args.tenant_id))
Log.write(stat)
msp_machine_table = prettytable.PrettyTable(["AgentID", "OwnerID", "IsEnabled", "PublicKey"])
msp_machine_table.align["AgentID"] = "l"
msp_machine_table.padding_width = 1
machine_table = prettytable.PrettyTable(["Name", "ID", "Status", "InsideVirtual", "Tenant"])
machine_table.align["Name"] = "l"
machine_table.padding_width = 1
plan_table = prettytable.PrettyTable(["Name", "ID", "Origin", "Source", "Tenant"])
plan_table.align["Name"] = "l"
plan_table.padding_width = 1
tenants = []
if t.Kind.ref != -1:
Log.write('Checking subtenants of group...')
pattern = [
('^Is', 'string', 'Tenants::HierarchyNode'),
('.Locator', 'string', ''),
('.Locator^DirectRelative', 'string', t.Locator.ref)
]
tenants.extend(acrobind.select_objects(connection, acrort.dml.ViewSpec(acrort.plain.Unit(flat=pattern))))
else:
tenants.append(t)
all_plans = []
for t in tenants:
if t.ID.ref != args.tenant_id:
t_name = '\'{0}\'(ID:\'{1}\', Locator: \'{2}\', Kind: \'{3}\')'.format(t.Name.ref, t.ID.ref, t.Locator.ref, t.Kind.ref)
Log.write('Subtenant: {0}'.format(t_name))
msp_machines = acrobind.select_objects(connection, acrobind.create_viewspec_by_is_and_string_property('Msp::AMS::Dto::Machine', '.OwnerID', t.ID.ref))
machines = acrobind.select_objects(connection, acrobind.create_viewspec_by_is_and_string_property('MachineManagement::Machine', '.Tenant.ID', t.ID.ref))
plans = acrobind.select_objects(connection, acrobind.create_viewspec_by_is_and_string_property('Gtob::Dto::ProtectionPlan', '.Tenant.ID', t.ID.ref))
all_plans.extend(plans)
add_tenant_description(connection, t, msp_machine_table, msp_machines, machine_table, machines, plan_table, plans)
Log.write(msp_machine_table)
Log.write('')
Log.write(machine_table)
Log.write('')
Log.write(plan_table.get_string(sortby="Name"))
Log.write('')
if args.fix_centralized_protection:
Log.write('Do you want to fix Gtob::Dto::CentralizedProtection objects for account and its children?(y/n)')
if not ask_user():
return
fix_centralized_protections(connection, all_plans)
if args.check_multitenancy:
Log.write('Check virtual instances with mixed ownership...')
instances = acrobind.select_objects(connection, acrobind.create_viewspec_by_is_and_string_property('InstanceManagement::Instance', '.Tenant.ID', t.ID.ref))
if not instances:
Log.write('Tenant doesn\'t have virtual instances')
return
all_vcenters = set()
all_hosts = set()
hosts_to_select = set()
vcenters_to_select = set()
already_selected = {instance.ID.ref for instance in instances}
# Collect IDs of hosts and vcenters referened in virtual machines and servers
for instance in instances:
instance_id = instance.ID.ref
instance_type = INSTANCE_TYPES.get(instance.Type.ref)
if instance_type == 'TYPE_VIRTUAL_MACHINE':
if instance.get_branch('.Parameters.Server', None) is not None:
host_id = instance.Parameters.Server[0].ref
if host_id not in already_selected:
hosts_to_select.add(host_id)
all_hosts.add(host_id)
if instance_type == 'TYPE_VIRTUAL_SERVER':
all_hosts.add(instance.ID.ref)
if instance.get_branch('.Parameters.VirtualCenterUUID', None) is not None:
vcenter_id = instance.Parameters.VirtualCenterUUID[0].ref
if vcenter_id not in already_selected:
vcenters_to_select.add(vcenter_id)
all_vcenters.add(vcenter_id)
if instance_type == 'TYPE_VIRTUAL_CENTER':
all_vcenters.add(instance.ID.ref)
def values_pattern(property, values):
return [
('^Is', 'string', 'InstanceManagement::Instance'),
(property, 'guid', '00000000-0000-0000-0000-000000000000'),
(property + '^ValueIn', 'complex_trait', [
('', 'array', [[('', 'guid', value)] for value in values])
]),
]
def searchable_values_pattern(property, values):
return [
('^Is', 'string', 'InstanceManagement::Instance'),
(property, 'array', []),
(property + '^HasIntersection', 'complex_trait', [
('', 'array', [[('', 'string', str(value))] for value in values]),
])
]
instances = []
# Select all vcenters referenced by tenant hosts
if vcenters_to_select:
instances.extend(acrobind.select_objects(connection, acrobind.create_viewspec_by_pattern(values_pattern('.ID', vcenters_to_select))))
# Select all hosts that have reference to all found vcenters
if all_vcenters:
pattern = searchable_values_pattern('.Parameters.VirtualCenterUUID', all_vcenters)
hosts_by_vcenters = acrobind.select_objects(connection, acrobind.create_viewspec_by_pattern(pattern))
for host in hosts_by_vcenters:
host_id = host.ID.ref
all_hosts.add(host_id)
if host_id in hosts_to_select:
hosts_to_select.remove(host_id)
instances.extend(hosts_by_vcenters)
# Select all hosts referenced by tenant vms
if hosts_to_select:
instances.extend(acrobind.select_objects(connection, acrobind.create_viewspec_by_pattern(values_pattern('.ID', hosts_to_select))))
# Select all vms that have reference to all found hosts
if all_hosts:
pattern = searchable_values_pattern('.Parameters.Server', all_hosts)
vms = acrobind.select_objects(connection, acrobind.create_viewspec_by_pattern(pattern))
instances.extend(vms)
found = False
for instance in instances:
if instance.Tenant.ID.ref != args.tenant_id:
if not found:
Log.write('List of virtual instances with mixed ownership:\n')
found = True
Log.write('ID: %s' % instance.ID.ref)
Log.write('Name: %s' % instance.FullPath.ref)
Log.write('Type: %s' % INSTANCE_TYPES.get(instance.Type.ref))
Log.write('Owner ID: %s' % instance.Tenant.ID.ref)
Log.write('Owner Name: %s\n' % instance.Tenant.Name.ref)
if not found:
Log.write('All virtual instances belong to the same tenant')
def add_tenant_description(connection, t, msp_machine_table, msp_machines, machine_table, machines, plan_table, plans):
for msp in msp_machines:
if args.extra:
print(msp)
public_key = '{}'.format(msp.PublicKey.ref) if 'PublicKey' in msp else 'Missing'
msp_machine_table.add_row(['{}'.format(msp.AgentID.ref), '{}'.format(msp.OwnerID.ref), '{}'.format(msp.IsEnabled.ref), public_key])
for m in machines:
if args.extra:
Log.write(m)
tenant_info = get_tenant_string(m, full_format=False)
is_inside_virtual = ''
if 'IsInsideVirtual' in m.Info:
is_inside_virtual = '{}'.format(m.Info.IsInsideVirtual.ref)
machine_table.add_row([m.Info.Name.ref, '{}'.format(m.ID.ref), to_machine_status(m.Status.ref), is_inside_virtual, tenant_info])
for p in plans:
if args.extra:
Log.write(p)
p_name = '\'{0}\''.format(p.Name.ref)
p_id = p.ID.ref
p_id_str = '{0}'.format(p_id)
p_origin = origin_to_str(p.Origin.ref)
p_source = ''
for tname, tunit in p.traits:
if tname == 'Source':
p_source = '{0}'.format(tunit.ref)
break
t_name = get_tenant_string(p, full_format=False)
plan_table.add_row([p_name, p_id_str, p_origin, p_source, t_name])
def quotas_reconcile(connection):
def tail(file):
while True:
line = file.readline().strip()
if not line:
time.sleep(0.1)
continue
yield line
def create_request():
Log.write('Create request for quotas reconsiling.')
request = acrort.plain.Unit(flat=[
('.Tenant.ID', 'string', args.tenant_id),
('.Tenant.ID^PrimaryKey', 'nil', None),
('.Tenant.ID^Type', 'string', 'Gtob::Protection::ResourceUsage::ForceAcquireRequest'),
('^Is', 'string', 'Gtob::Protection::ResourceUsage::ForceAcquireRequest')
])
connection.dml.create(request)
log_file = open('/var/lib/Acronis/AMS/logs/resource-usage.0.log')
log_file.seek(0, 2)
create_request()
error_count = 0
for line in tail(log_file):
col = line.split(' ')
if len(col) < 5:
continue
col[4] = ' '.join(col[4:])
col = col[:5]
reconcile_prefix = '[Reconcile'
start_line = '[Reconcile] account: \'{}\'. Start.'.format(args.tenant_id)
finish_line = '[Reconcile] account: \'{}\'. Finish.'.format(args.tenant_id)
if col[4] == start_line:
Log.write(' '.join([col[0], col[1], col[4]]))
if reconcile_prefix in col[4] and col[3][0] == 'E':
Log.write(' '.join([col[0], col[1], col[4]]))
error_count += 1
if col[4] == finish_line:
Log.write(' '.join([col[0], col[1], col[4]]))
break
Log.write('\nScript finished. Error count: \'{}\''.format(error_count))
def list_machines(connection):
print('[---List of machines that match name \'{}\'---]'.format(args.machine_name))
machine_spec = acrobind.create_viewspec_by_is_and_like('MachineManagement::Machine', '.Info.Name', args.machine_name)
machines = acrobind.select_objects(connection, machine_spec)
table = prettytable.PrettyTable(["Name", "ID", "Tenant"])
table.align["Name"] = "l"
table.padding_width = 1
for m in machines:
if args.extra:
Log.write(m)
m_name = '\'{0}\''.format(m.Info.Name.ref)
m_id = m.ID.ref
m_id_str = '{0}'.format(m_id)
t = get_tenant_string(m)
table.add_row([m_name, m_id_str, t])
Log.write(table.get_string(sortby="Name"))
Log.write('')
def list_instances(connection):
Log.write('[---List of instances that match name \'{}\'---]'.format(args.instance_name))
spec = acrobind.create_viewspec_by_is_and_like('InstanceManagement::Instance', '.FullPath', args.instance_name)
objects = acrobind.select_objects(connection, spec)
list_instances_internal(connection, objects)
def list_instances_internal(connection, objects):
for o in objects:
table = prettytable.PrettyTable(["Name", "Value", ""])
table.align["Name"] = "l"
table.align["Value"] = "l"
table.padding_width = 1
if args.extra:
Log.write(o)
o_name = '\'{0}\''.format(str(get_optional(o, 'FullPath')))
o_id_str = '{0}'.format(o.ID.ref)
o_host_id_str = '{0}'.format(o.HostID.ref)
o_state_str = to_instance_state(get_optional(o, 'State'))
o_backup_state_str = to_instance_state(o.BackupState.ref)
o_status = '{0}'.format(o.Status.ref)
o_backup_status_str = '{0}'.format(o.BackupStatus.ref)
o_inside_virtual_str = 'MISSING'
if 'HostInfo' in o:
o_inside_virtual_str = '{0}'.format(get_optional(o.HostInfo, "IsInsideVirtual"))
availability_str = 'MISSING'
if 'Availability' in o:
availability_str = '{0}'.format(to_machine_status(o.Availability.ref))
last_backup_str = '-'
if 'LastBackup' in o:
last_backup_str = describe_time(o.LastBackup.ref)
last_backup_try_str = '-'
if 'LastBackupTry' in o:
last_backup_try_str = describe_time(o.LastBackupTry.ref)
next_backup_time_str = '-'
if 'NextBackupTime' in o and 'Time' in o.NextBackupTime:
next_backup_time_str = describe_time(o.LastBackup.ref)
t = get_tenant_string(o, full_format=False)
table.add_row(['Name', o_name, ''])
table.add_row(['ID', o_id_str, ''])
table.add_row(['HostID', o_host_id_str, ''])
table.add_row(['State', o_state_str, ''])
table.add_row(['Status', o_status, ''])
table.add_row(['State(Backup)', o_backup_state_str, ''])
table.add_row(['Status(Backup)', o_backup_status_str, ''])
table.add_row(['LastBackup', last_backup_str, ''])
table.add_row(['LastBackupTry', last_backup_try_str, ''])
table.add_row(['NextBackupTime', next_backup_time_str, ''])
table.add_row(['Availability', availability_str, ''])
table.add_row(['InsideVirtual', o_inside_virtual_str, ''])
table.add_row(['Tenant', t, ''])
Log.write(table)
Log.write('')
def check_instance(connection):
Log.write('Checking instance \'{}\'...'.format(args.instance_id))
local_spec = acrobind.create_viewspec_by_is_and_guid_property('InstanceManagement::Instance', '.ID', args.instance_id)
Log.write('AMS instance:')
object = acrobind.select_object(connection, local_spec)
if object is not None:
list_instances_internal(connection, [object])
else:
Log.write('Not found')
return
spec = acrobind.viewspec_apply_remote_host(local_spec, object.HostID.ref)
Log.write('Agent instance:')
try:
objects = acrobind.select_objects(connection, spec)
list_instances_internal(connection, objects)
except Exception as e:
if args.extra:
Log.write('Failed to get instance from agent: {}'.format(e))
else:
Log.write('Failed to get instance from agent')
items = check_instance_related_item_protections(connection, args.instance_id)
#TODO:
print(items)
def check_machine(connection):
if not args.delete and args.break_connection and args.machine_id:
drop_agent_connection(connection, args.machine_id)
Log.write('[---Check machine with id \'{}\'---]'.format(args.machine_id))
machine_spec = acrobind.create_viewspec_by_is_and_guid_property('MachineManagement::Machine', '.ID', args.machine_id)
machine = acrobind.select_object(connection, machine_spec)
if not machine:
Log.write('Not found.')
#return
if args.extra:
Log.write(machine)
msp_machine = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property('Msp::AMS::Dto::Machine', '.AgentID', '{}'.format(args.machine_id)))
public_key = 'MISSING'
is_enabled = 'MISSING'
if msp_machine and 'PublicKey' in msp_machine:
public_key = '{0}'.format(msp_machine.PublicKey.ref)
if msp_machine and 'IsEnabled' in msp_machine:
is_enabled = '{0}'.format(msp_machine.IsEnabled.ref)
if args.extra:
Log.write(msp_machine)
machine_table = prettytable.PrettyTable(["Name", "Value", ""])
machine_table.align["Name"] = "l"
machine_table.align["Value"] = "l"
machine_table.padding_width = 1
name_str = '-'
if machine and 'Info' in machine and 'Name' in machine.Info:
name_str = '{}'.format(machine.Info.Name.ref)
machine_table.add_row(['Name', name_str, ''])
id_str = '-'
if machine and 'ID' in machine:
id_str = '{}'.format(machine.ID.ref)
machine_table.add_row(['ID', id_str, ''])
status_str = '-'
if machine and 'Status' in machine:
machine_statuses = {0: 'ONLINE (0)', 1: 'OFFLINE (1)'}
if machine.Status.ref in machine_statuses:
status_str = machine_statuses[machine.Status.ref]
else:
status_str = str(machine.Status.ref)
machine_table.add_row(['Status', status_str, ''])
machine_table.add_row(['PublicKey', public_key, ''])
machine_table.add_row(['IsEnabled', is_enabled, ''])
is_inside_virtual_str = '-'
if machine and 'Info' in machine and 'IsInsideVirtual' in machine.Info:
is_inside_virtual_str = '{}'.format(machine.Info.IsInsideVirtual.ref)
machine_table.add_row(['IsInsideVirtual', is_inside_virtual_str, ''])
machine_table.add_row(['Tenant', get_tenant_string(machine), ''])
current_version = 'Unknown'
try:
if 'Info' in machine:
current_version = machine.Info.Agents[0].Version.ref
except:
pass
installed_agents = 'unknown'
if machine and 'Info' in machine:
if is_mobile_agent(machine.Info.Agents):
installed_agents = 'mobile {}'.format(installed_agents)
if is_ad_agent(machine.Info.Agents):
installed_agents = 'ad {}'.format(installed_agents)
if is_ati_agent(machine.Info.Agents):
installed_agents = 'ati {}'.format(installed_agents)
if is_win_agent(machine.Info.Agents):
installed_agents = 'win {}'.format(installed_agents)
if is_sql_agent(machine.Info.Agents):
installed_agents = 'sql {}'.format(installed_agents)
if is_esx_agent(machine.Info.Agents):
installed_agents = 'esx {}'.format(installed_agents)
if is_hyperv_agent(machine.Info.Agents):
installed_agents = 'hyperv {}'.format(installed_agents)
if is_exchange_agent(machine.Info.Agents):
installed_agents = 'exchange {}'.format(installed_agents)
if not installed_agents:
Log.write(machine.Info.Agents)
machine_table.add_row(['Current version', '{} | {}'.format(current_version, installed_agents), ''])
update_desc = '-'
if machine and 'UpdateState' in machine and 'UpdateIsAvailable' in machine.UpdateState:
if machine.UpdateState.UpdateIsAvailable.ref:
update_desc = '{}'.format(machine.UpdateState.UpdateVersion.ref)
update_url = ''
if machine and 'UpdateState' in machine and 'UpdateUrl' in machine.UpdateState:
update_url = '{}'.format(machine.UpdateState.UpdateUrl.ref)
machine_table.add_row(['Available update', '{}'.format(update_desc), update_url])
Log.write(machine_table)
if machine and 'Tenant' not in machine and args.fix:
Log.write('Do you want to fix Tenant info for machine object with ID \'{0}\'(y/n)?'.format(args.machine_id))
if ask_user():
fix_machine_tenant(connection, args.machine_id)
Log.write('done')
else:
Log.write('skipped')
Log.write('Instances:')
instances_spec = acrobind.create_viewspec_by_is_and_guid_property('InstanceManagement::Instance', '.HostID', args.machine_id)
objects = acrobind.select_objects(connection, instances_spec)
list_instances_internal(connection, objects)
#check_caching_registration(connection)
if args.running_activities:
pattern = [
('^Is', 'string', 'Tol::History::Plain::Activity'),
('^Source', 'string', '{}'.format(args.machine_id)),
('.State', 'dword', 0),
('.State^Less', 'dword', 5)]
spec = acrort.dml.ViewSpec(acrort.plain.Unit(flat=pattern))
check_running_activities(connection, spec, args.machine_id)
if args.reset_update:
Log.write('Reseting update status for MachineManagement::Machine with ID {0}.'.format(args.machine_id))
pattern = [
('.MachineIsProcessed', 'bool', False),
]
diff_unit={'UpdateState': acrort.plain.Unit(flat=pattern)}
#diff_unit={'Status': 1}
connection.dml.update(pattern=acrobind.create_viewspec_by_is_and_guid_property('MachineManagement::Machine', '.ID', args.machine_id).pattern, diff=diff_unit)
if args.delete:
do_deletion(connection)
def ask_user():
answer = sys.stdin.readline()
if answer.startswith('y') or answer.startswith('Y'):
return True
else:
return False
return False
def wrap_error(error):
if error == acrort.common.SUCCESS:
return 'OK'
return 'E'
def describe_time(time):
return datetime.datetime.fromtimestamp(time).strftime('%Y-%m-%d %H:%M:%S')
def check_instance_related_item_protections(connection, instance_id):
spec = acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ItemProtection', '.InstanceID', instance_id)
return check_item_protection_objects_internal(connection, spec)
def check_plan_related_item_protections(connection, plan_id):
spec = acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ItemProtection', '.Local.PlanID', plan_id)
return check_item_protection_objects_internal(connection, spec)
def check_cplan_related_item_protections(connection, cplan_id):
spec = acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ItemProtection', '.Centralized.PlanID', cplan_id)
return check_item_protection_objects_internal(connection, spec)
def check_item_protection_objects_internal(connection, spec):
ips = acrobind.select_objects(connection, spec)
items = []
if not (len(ips) > 0):
return items
for ip in ips:
if args.extra:
print(ip)
item = {}
item['tenant_id'] = get_tenant_string(ip, full_format=False)
item['tenant'] = get_tenant_string(ip, full_format=True)
item['tenant_name'] = ip.Tenant.Name.ref
item['id'] = '{0}'.format(ip.ID.ref)
item['instance_id'] = '{0}'.format(ip.InstanceID.ref)
item['host_id'] = '-'
if 'HostID' in ip:
item['host_id'] = '{0}'.format(ip.HostID.ref)
instance_spec = acrobind.create_viewspec_by_is_and_guid_property('InstanceManagement::Instance', '.ID', ip.InstanceID.ref)
instance_spec = acrobind.viewspec_apply_mask(instance_spec, acrobind.create_mask3('.ID', '.FullPath', '.Type'))
instance = acrobind.select_object(connection, instance_spec)
if instance:
#print(instance)
item['instance_id'] = '{}({})'.format(instance.FullPath.ref, instance.Type.ref)
item['lfi_status'] = None
item['lfi_result'] = None
item['lfi_result_full'] = None
item['lfi_errors'] = []
item['lfi_warnings'] = []
item['lfi_time'] = None
item['ns_time'] = None
item['lsi_time'] = None
item['cpi'] = '-'
item['lpi'] = '-'
item['legacy_pi'] = '-'
if 'LastFinishInfo' in ip:
item['lfi_status'] = '{0}'.format(ip.LastFinishInfo.Status.ref)
item['lfi_result'] = '-'
if 'CompletionResult' in ip.LastFinishInfo:
item['lfi_result'] = '{0}'.format(wrap_error(ip.LastFinishInfo.CompletionResult))
item['lfi_result_full'] = '{0}'.format(ip.LastFinishInfo.CompletionResult.ref)
item['lfi_time'] = '{0}'.format(describe_time(ip.LastFinishInfo.Time.ref))
str_limit = 70
if 'Errors' in ip.LastFinishInfo:
for index, error in ip.LastFinishInfo.Errors:
full_error_str = '{}'.format(error.Error.ref)
last_error_pos = full_error_str.rfind('| error')
last_error_pos_2 = full_error_str.find('\n', last_error_pos)
error_str = full_error_str[last_error_pos:last_error_pos_2 if last_error_pos_2 - last_error_pos < str_limit else last_error_pos + str_limit]
last_function_pos = full_error_str.rfind('| $module:')
last_function_pos_2 = full_error_str.find('\n', last_function_pos)
function_str = full_error_str[last_function_pos:last_function_pos_2 if last_function_pos_2 - last_function_pos < str_limit else last_function_pos + str_limit]
#print(error_str)
#item['lfi_errors'].append('{}({})'.format(error_str, function_str))
item['lfi_errors'].append('{}'.format(error_str))
if 'Warnings' in ip.LastFinishInfo:
for index, warning in ip.LastFinishInfo.Warnings:
full_error_str = '{}'.format(warning.Error.ref)
last_error_pos = full_error_str.rfind('| error')
last_error_pos_2 = full_error_str.find('\n', last_error_pos)
error_str = full_error_str[last_error_pos:last_error_pos_2 if last_error_pos_2 - last_error_pos < str_limit else last_error_pos + str_limit]
last_function_pos = full_error_str.rfind('| $module:')
last_function_pos_2 = full_error_str.find('\n', last_function_pos)
function_str = full_error_str[last_function_pos:last_function_pos_2 if last_function_pos_2 - last_function_pos < str_limit else last_function_pos + str_limit]
#item['lfi_warnings'].append('{}({})'.format(error_str, function_str))
item['lfi_warnings'].append('{}'.format(error_str))
if 'LastStartInfo' in ip and 'Time' in ip.LastStartInfo:
item['lsi_time'] = '{0}'.format(describe_time(ip.LastStartInfo.Time.ref))
if 'NextBackupTime' in ip and 'Time' in ip.NextBackupTime:
item['ns_time'] = '{0}'.format(describe_time(ip.NextBackupTime.Time.ref))
if 'Centralized' in ip:
item['cpi'] = '{0}'.format(ip.Centralized.PlanID.ref)
if 'Local' in ip:
item['lpi'] = '{0}'.format(ip.Local.PlanID.ref)
item['legacy_pi'] = '{0}'.format(ip.Plan.ref) if 'Plan' in ip else '-'
items.append(item)
return items
def do_deletion(connection):
print('Do you want to delete MachineManagement::Machine related to this machine? (y/n)')
if ask_user():
connection.dml.delete(pattern=acrobind.create_viewspec_by_is_and_guid_property('MachineManagement::Machine', '.ID', args.machine_id).pattern)
print('deleted.')
else:
print('skipped.')
if args.instance_id:
print('Do you want to delete all Gtob::Dto::ItemProtection related to this machine? (y/n)')
if ask_user():
connection.dml.delete(pattern=acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ItemProtection', '.InstanceID', args.instance_id).pattern)
print('deleted.')
else:
print('skipped.')
print('Do you want to delete Msp::AMS::Dto::Machine related to this machine? (y/n)')
if ask_user():
connection.dml.delete(pattern=acrobind.create_viewspec_by_is_and_string_property('Msp::AMS::Dto::Machine', '.AgentID', '{}'.format(args.machine_id)).pattern)
print('deleted.')
else:
print('skipped.')
print('Do you want to delete all InstanceManagement::Instance objects related to this machine? (y/n)')
if ask_user():
connection.dml.delete(pattern=acrobind.create_viewspec_by_is_and_guid_property('InstanceManagement::InstanceAspect', '.Key.HostID', args.machine_id).pattern)
connection.dml.delete(pattern=acrobind.create_viewspec_by_is_and_guid_property('InstanceManagement::Instance', '.HostID', args.machine_id).pattern)
print('deleted.')
else:
print('skipped.')
def undeploy_local_plan(connection, pp_info, plan_id):
try:
host_id = pp_info[plan_id]['source']
if not host_id:
Log.write('Can\'t find host_id for plan {0}'.format(plan_id))
return
if 'status' in pp_info[plan_id] and pp_info[plan_id]['status'] != 0:
Log.write('Can\'t undeploy plan {0} because agent ({1}) is OFFLINE'.format(plan_id, host_id))
return
Log.write('Trying to undeploy Gtob::Dto::ProtectionPlan ({0}) from host ({1})...'.format(plan_id, host_id), end='')
arg = acrort.common.Guid(plan_id)
if host_id:
activity_id = connection.tol.launch_command(command='C006D24E-E6ED-494a-9789-237CD3A814E7', argument=arg, target_machine=host_id)
else:
activity_id = connection.tol.launch_command(command='C006D24E-E6ED-494a-9789-237CD3A814E7', argument=arg)
try:
if host_id:
result = connection.tol.get_result(activity_id, target_machine=host_id)
else:
result = connection.tol.get_result(activity_id)
except acrort.Exception as ex:
if acrort.common.interrupt_sentinel:
if host_id:
connection.tol.cancel_activity(activity_id, target_machine=host_id)
else:
connection.tol.cancel_activity(activity_id)
Log.write('canceled')
Log.write('done')
Log.write('Removing synced Gtob::Dto::ProtectionPlan ({0})...'.format(plan_id), end='')
connection.dml.delete(pattern=acrobind.viewspec_apply_source(acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.ID', plan_id), host_id).pattern)
Log.write('done')
Log.write('Removing Gtob::Dto::ItemProtection from host({0})...'.format(host_id), end='')
bytes_arg = None
with open("a_injection.py", "rb") as tol_command:
bytes_arg = acrort.common.Blob(bytes=tol_command.read())
run_request = [
('.Script.Location', 'string', "attachment:a_injection.py?delete_legacy_item_protections"),
('.Script.Argument', 'string', plan_id),
('.Script.Body', 'blob', bytes_arg)
]
request_unit = acrort.plain.Unit(flat=run_request)
activity_id = connection.tol.launch_command(command=acrort.remoting.RUN_SCRIPT_COMMAND_ID_BUSINESS, argument=request_unit, target_machine=host_id)
result = connection.tol.get_result(activity_id, target_machine=host_id)
Log.write('done')
except Exception as e:
Log.write('Error: {0}'.format(e))
def _init_counters(connection, data_cache, entry_id, spec):
entry_id_internal = '_{}'.format(entry_id)
data_cache[entry_id_internal] = {}
data_cache[entry_id_internal]['wrong'] = 0
data_cache[entry_id_internal]['ok'] = 0
data_cache[entry_id_internal]['total'] = acrobind.count_objects_by_spec(connection, spec)
data_cache[entry_id_internal]['counter_reset'] = data_cache[entry_id_internal]['total'] / 10
data_cache[entry_id_internal]['counter'] = data_cache[entry_id_internal]['counter_reset']
def _update_counters_and_file(data_cache, entry_id, filename, wrong_item_id, error_string):
entry_id_internal = '_{}'.format(entry_id)
if error_string:
data_cache[entry_id_internal]['wrong'] = data_cache[entry_id_internal]['wrong'] + 1
with open(filename, "a") as myfile:
myfile.write('{0}: {1}\n'.format(wrong_item_id, error_string))
else:
data_cache[entry_id_internal]['ok'] = data_cache[entry_id_internal]['ok'] + 1
data_cache[entry_id_internal]['counter'] = data_cache[entry_id_internal]['counter'] - 1
if not data_cache['specific_tenant'] and data_cache[entry_id_internal]['counter'] <= 0:
Log.write('-', end='')
data_cache[entry_id_internal]['counter'] = data_cache[entry_id_internal]['counter_reset']
def _do_check(connection, data_cache, entry_id, filename, spec, callback):
entry_id_internal = '_{}'.format(entry_id)
with open(filename, "w") as myfile:
myfile.truncate()
if not data_cache['specific_tenant']:
Log.write('[----------][{}]'.format(data_cache[entry_id_internal]['total']))
Log.write('[', end='')
else:
Log.write('Objects count: {}'.format(data_cache[entry_id_internal]['total']))
start = time.time()
acrobind.enumerate_objects(connection, spec, callback, error_log)
if not data_cache['specific_tenant']:
Log.write('-]')
Log.write('OK: {0} WRONG: {1}. Elapsed: {2:.2f} s'.format(data_cache[entry_id_internal]['ok'], data_cache[entry_id_internal]['wrong'], time.time() - start))
def check_tenants_consistency(connection, data_cache):
data_cache['tenants'] = {}
spec = acrobind.create_viewspec_by_is('Tenants::HierarchyNode')
filename = 'amsctl_wrong_tenants.txt'
def apply_limits(filename, spec):
if args.parameter1 is not None:
filename = '{0}_{1}'.format(args.parameter1, filename)
limit_pattern = [
('.ID', 'string', '{}'.format(args.parameter1))
]
return filename, acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=limit_pattern)), spec.options)
return filename, spec
filename, spec = apply_limits(filename, spec)
_init_counters(connection, data_cache, 'tenants', spec)
def tenants_callback(connection, cache, file, object):
tenant_id = '{0}'.format(object.ID.ref)
parent_exists = False
error_string = ''
if 'ParentID' in object:
parent_exists = True
else:
error_string = '{0} {1}'.format(error_string, 'missing_parent_id_field')
locator_exists = False
if 'Locator' in object:
locator_exists = True
else:
error_string = '{0} {1}'.format(error_string, 'missing_locator')
kind_exists = False
if 'Kind' in object:
kind_exists = True
else:
error_string = '{0} {1}'.format(error_string, 'missing_kind')
data_cache['tenants'][tenant_id] = {}
data_cache['tenants'][tenant_id]['parent_exists'] = parent_exists
data_cache['tenants'][tenant_id]['locator_exists'] = locator_exists
data_cache['tenants'][tenant_id]['kind_exists'] = kind_exists
data_cache['tenants'][tenant_id]['used'] = False
_update_counters_and_file(data_cache, 'tenants', file, tenant_id, error_string)
callback = lambda x: tenants_callback(connection, data_cache, filename, x)
Log.write('Checking Tenants::HierarchyNode consistency. Logging wrong item ids to \'{}\'.'.format(filename))
_do_check(connection, data_cache, 'tenants', filename, spec, callback)
def check_orphan_msp_machines(connection, data_cache):
data_cache['msp_machines'] = {}
data_cache['to_delete']['msp_machines'] = []
spec = acrobind.create_viewspec_by_is('Msp::AMS::Dto::Machine')
filename = 'amsctl_wrong_msp_machines.txt'
def apply_limits(filename, spec):
if args.parameter1 is not None:
filename = '{0}_{1}'.format(args.parameter1, filename)
limit_pattern = [
('.Owner', 'string', '{}'.format(args.parameter1))
]
return filename, acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=limit_pattern)), spec.options)
return filename, spec
filename, spec = apply_limits(filename, spec)
_init_counters(connection, data_cache, 'msp_machines', spec)
spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask4('.AgentID', '.IsEnabled', '.OwnerID', '.PublicKey'))
def msp_machine_callback(connection, cache, file, object):
host_id_str = '{}'.format(object.AgentID.ref)
cache['msp_machines'][host_id_str] = {}
cache['msp_machines'][host_id_str]['used'] = False
cache['msp_machines'][host_id_str]['public_key_exists'] = False
cache['msp_machines'][host_id_str]['is_enabled'] = False
cache['msp_machines'][host_id_str]['is_enabled_field_exists'] = False
cache['msp_machines'][host_id_str]['owner_id'] = ''
cache['msp_machines'][host_id_str]['owner_id_field_exists'] = False
cache['msp_machines'][host_id_str]['owner_exists'] = False
error_string = ''
if 'PublicKey' in object:
cache['msp_machines'][host_id_str]['public_key_exists'] = True
else:
error_string = '{0} {1}'.format(error_string, 'missing_public_key')
if 'IsEnabled' in object:
cache['msp_machines'][host_id_str]['is_enabled_field_exists'] = True
cache['msp_machines'][host_id_str]['is_enabled'] = object.IsEnabled.ref
else:
error_string = '{0} {1}'.format(error_string, 'missing_is_enabled')
if 'OwnerID' in object:
cache['msp_machines'][host_id_str]['owner_id_field_exists'] = True
cache['msp_machines'][host_id_str]['owner_id'] = object['OwnerID'].ref
else:
error_string = '{0} {1}'.format(error_string, 'missing_owner_id')
if cache['msp_machines'][host_id_str]['owner_id'] in data_cache['tenants']:
data_cache['tenants'][cache['msp_machines'][host_id_str]['owner_id']]['used'] = True
cache['msp_machines'][host_id_str]['owner_exists'] = True
else:
error_string = '{0} {1}'.format(error_string, 'no_owner(d)')
data_cache['to_delete']['msp_machines'].append(host_id_str)
_update_counters_and_file(data_cache, 'msp_machines', file, host_id_str, error_string)
callback = lambda x: msp_machine_callback(connection, data_cache, filename, x)
Log.write('Checking orphan Msp::Agent::Dto::Machine. Logging wrong item ids to \'{}\'.'.format(filename))
_do_check(connection, data_cache, 'msp_machines', filename, spec, callback)
def is_mobile_agent(agent_info):
for index, a in agent_info:
if a.Id.ref == '{AC127296-8E50-41DE-8EFE-853C4B5CD8A8}':
return True
return False
def is_ad_agent(agent_info):
for index, a in agent_info:
if a.Id.ref == '{9F148D12-3653-478B-A039-239BE36950AB}':
return True
return False
def is_ati_agent(agent_info):
for index, a in agent_info:
agent_type_id = a.Id.ref
if agent_type_id == '{8397FC51-C78E-4E26-9860-6A4A8622DF7C}':
return True
#Home agent have no Version AgentInfo and have empty Id in AgentInfo
if len(agent_info) == 1 and agent_type_id == '':
return True
else:
return False
return False
def is_win_agent(agent_info):
for index, a in agent_info:
if a.Id.ref == '{BA262882-C484-479A-8D07-B0EAC55CE27B}':
return True
return False
def is_sql_agent(agent_info):
for index, a in agent_info:
if a.Id.ref == '{C293F7DD-9531-4916-9346-12B0F3BFCCAA}':
return True
return False
def is_esx_agent(agent_info):
for index, a in agent_info:
if a.Id.ref == '{EC3CF038-C08A-4341-82DF-F75133705F63}':
return True
return False
def is_hyperv_agent(agent_info):
for index, a in agent_info:
if a.Id.ref == '{87671A98-2B47-4D4C-98FB-490CA111E7A7}':
return True
return False
def is_exchange_agent(agent_info):
for index, a in agent_info:
if a.Id.ref == '{30BAF589-C02A-463F-AB37-5A9193375700}':
return True
return False
def check_if_tenant_is_valid(object, data_cache, item_type, item_id):
tenant_id = '-'
data_cache[item_type][item_id]['has_tenant'] = False
error_string = ''
if 'Tenant' in object:
tenant_id = object.Tenant.ID.ref
data_cache[item_type][item_id]['has_tenant'] = True
else:
error_string = '{0} {1}'.format(error_string, 'missing_tenant_field')
data_cache[item_type][item_id]['tenant'] = tenant_id
if data_cache['resolve_links']:
data_cache[item_type][item_id]['has_existing_tenant'] = False
if data_cache[item_type][item_id]['has_tenant']:
if tenant_id in data_cache['tenants']:
data_cache[item_type][item_id]['has_existing_tenant'] = True
else:
error_string = '{0} {1}'.format(error_string, 'no_tenant(d)')
data_cache['to_delete'][item_type].append(item_id)
else:
data_cache[item_type][item_id]['has_existing_tenant'] = True
return error_string
def check_orphan_agents(connection, data_cache):
data_cache['agents'] = {}
data_cache['to_delete']['agents'] = []
spec = acrobind.create_viewspec_machines_by_role(0)
filename = 'amsctl_wrong_agents.txt'
def apply_limits(filename, spec):
if args.parameter1 is not None:
filename = '{0}_{1}'.format(args.parameter1, filename)
limit_pattern = [
('.Tenant.ID', 'string', '{}'.format(args.parameter1))
]
return filename, acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=limit_pattern)), spec.options)
return filename, spec
filename, spec = apply_limits(filename, spec)
_init_counters(connection, data_cache, 'agents', spec)
spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask4('.ID', '.Role', '.Tenant', '.Info.Agents'))
def agents_callback(connection, cache, file, object):
id_str = '{}'.format(object.ID.ref)
data_cache['agents'][id_str] = {}
data_cache['agents'][id_str]['used'] = False
data_cache['agents'][id_str]['has_existing_msp_machine'] = False
error_string = ''
#Mobile agents don't have Msp::Agent::Dto::Configuration
if not is_mobile_agent(object.Info.Agents):
if id_str in data_cache['msp_machines']:
data_cache['agents'][id_str]['has_existing_msp_machine'] = True
data_cache['msp_machines'][id_str]['used'] = True
else:
error_string = '{0} {1}'.format(error_string, 'no_msp_machine')
tenant_error = check_if_tenant_is_valid(object, data_cache, 'agents', id_str)
if tenant_error:
error_string = '{0} {1}'.format(error_string, tenant_error)
_update_counters_and_file(data_cache, 'agents', file, id_str, error_string)
callback = lambda x: agents_callback(connection, data_cache, filename, x)
Log.write('Checking orphan MachineManagement::Machine. Logging wrong item ids to \'{}\'.'.format(filename))
_do_check(connection, data_cache, 'agents', filename, spec, callback)
def check_orphan_instances(connection, data_cache):
data_cache['instances'] = {}
data_cache['to_delete']['instances'] = []
spec = acrobind.create_viewspec_by_is('InstanceManagement::Instance')
filename = 'amsctl_wrong_instances.txt'
def apply_limits(filename, spec):
if args.parameter1 is not None:
filename = '{0}_{1}'.format(args.parameter1, filename)
limit_pattern = [
('.Tenant.ID', 'string', '{}'.format(args.parameter1))
]
return filename, acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=limit_pattern)), spec.options)
return filename, spec
filename, spec = apply_limits(filename, spec)
_init_counters(connection, data_cache, 'instances', spec)
spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask3('.ID', '.HostID', '.Tenant'))
def instance_callback(connection, cache, file, object):
id_str = '{}'.format(object.ID.ref)
data_cache['instances'][id_str] = {}
data_cache['instances'][id_str]['has_host_id'] = False
data_cache['instances'][id_str]['host_id'] = '-'
data_cache['instances'][id_str]['has_existing_host'] = False
data_cache['instances'][id_str]['has_existing_tenant'] = False
data_cache['instances'][id_str]['has_tenant'] = False
data_cache['instances'][id_str]['is_deprecated'] = False
error_string = ''
if is_deprecated_vm_instance(id_str):
error_string = '{0} {1}'.format(error_string, 'deprecated_vm_instance(d)')
data_cache['instances'][id_str]['is_deprecated'] = True
data_cache['to_delete']['instances'].append(id_str)
else:
if 'HostID' in object:
data_cache['instances'][id_str]['has_host_id'] = False
data_cache['instances'][id_str]['host_id'] = '{}'.format(object.HostID.ref)
else:
error_string = '{0} {1}'.format(error_string, 'missing_host_id_field')
host_id_str = data_cache['instances'][id_str]['host_id']
if host_id_str in data_cache['agents']:
data_cache['instances'][id_str]['has_existing_host'] = True
data_cache['agents'][host_id_str]['used'] = True
else:
error_string = '{0} {1}'.format(error_string, 'no_host')
tenant_error = check_if_tenant_is_valid(object, data_cache, 'instances', id_str)
if tenant_error:
error_string = '{0} {1}'.format(error_string, tenant_error)
_update_counters_and_file(data_cache, 'instances', file, id_str, error_string)
callback = lambda x: instance_callback(connection, data_cache, filename, x)
Log.write('Checking orphan InstanceManagement::Instance. Logging wrong item ids to \'{}\'.'.format(filename))
_do_check(connection, data_cache, 'instances', filename, spec, callback)
def check_orphan_plans(connection, data_cache):
data_cache['plans'] = {}
data_cache['to_delete']['plans'] = []
spec = acrobind.create_viewspec_by_is('Gtob::Dto::ProtectionPlan')
filename = 'amsctl_wrong_plans.txt'
def apply_limits(filename, spec):
if args.parameter1 is not None:
filename = '{0}_{1}'.format(args.parameter1, filename)
limit_pattern = [
('.Tenant.ID', 'string', '{}'.format(args.parameter1))
]
return filename, acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=limit_pattern)), spec.options)
return filename, spec
filename, spec = apply_limits(filename, spec)
_init_counters(connection, data_cache, 'plans', spec)
spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask3('.ID', '.Origin', '.Tenant'))
def plan_callback(connection, cache, file, object):
id_str = '{}'.format(object.ID.ref)
data_cache['plans'][id_str] = {}
data_cache['plans'][id_str]['origin'] = '-'
error_string = ''
if 'Origin' in object:
data_cache['plans'][id_str]['status'] = object.Origin.ref
else:
error_string = '{0} {1}'.format(error_string, 'missing_origin_field')
tenant_error = check_if_tenant_is_valid(object, data_cache, 'plans', id_str)
if tenant_error:
error_string = '{0} {1}'.format(error_string, tenant_error)
_update_counters_and_file(data_cache, 'plans', file, id_str, error_string)
callback = lambda x: plan_callback(connection, data_cache, filename, x)
Log.write('Checking orphan Gtob::Dto::ProtectionPlan. Logging wrong item ids to \'{}\'.'.format(filename))
_do_check(connection, data_cache, 'plans', filename, spec, callback)
def check_deployment_fact_consistency(connection, data_cache):
data_cache['deployment_facts'] = {}
data_cache['to_delete']['deployment_facts'] = []
spec = acrobind.create_viewspec_by_is('Gtob::Protection::PlanDeploymentFact')
filename = 'amsctl_wrong_deployment_facts.txt'
def apply_limits(filename, spec):
if args.parameter1 is not None:
filename = '{0}_{1}'.format(args.parameter1, filename)
limit_pattern = [
('.PlanObject.Tenant.ID', 'string', '{}'.format(args.parameter1))
]
return filename, acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=limit_pattern)), spec.options)
return filename, spec
filename, spec = apply_limits(filename, spec)
_init_counters(connection, data_cache, 'deployment_facts', spec)
spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask3('.ID', '.PlanObject.CentralizedProtectionPlan', '.PlanObject.Tenant'))
def fact_callback(connection, cache, file, object):
host_id_str = '{}'.format(object.ID.Host.ref)
plan_id_str = '{}'.format(object.ID.Plan.ref)
id_str = '{0}+{1}'.format(host_id_str, plan_id_str)
data_cache['deployment_facts'][id_str] = {}
data_cache['deployment_facts'][id_str]['plan'] = plan_id_str
data_cache['deployment_facts'][id_str]['host'] = host_id_str
data_cache['deployment_facts'][id_str]['need_redeploy'] = False
data_cache['deployment_facts'][id_str]['exists'] = True
source_id = acrobind.get_trait_value('Source', object)
error_string = ''
if source_id is None:
error_string = '{0} {1}'.format(error_string, 'missing_source_trait')
if source_id is not None and str(source_id) != host_id_str :
error_string = '{0} {1}'.format(error_string, 'source_differes_from_host')
if plan_id_str not in data_cache['plans']:
error_string = '{0} {1}'.format(error_string, 'missing_plan(d)')
data_cache['to_delete']['deployment_facts'].append(plan_id_str)
if 'PlanObject' in object:
tenant_error = check_if_tenant_is_valid(object.PlanObject, data_cache, 'deployment_facts', id_str)
if tenant_error:
error_string = '{0} {1}'.format(error_string, tenant_error)
else:
error_string = '{0} {1}'.format(error_string, 'missing_plan_object_field')
_update_counters_and_file(data_cache, 'deployment_facts', file, id_str, error_string)
callback = lambda x: fact_callback(connection, data_cache, filename, x)
Log.write('Checking orphan Gtob::Protection::PlanDeploymentFact. Logging wrong item ids to \'{}\'.'.format(filename))
_do_check(connection, data_cache, 'deployment_facts', filename, spec, callback)
def check_protections_consistency(connection, data_cache):
if args.parameter1 is not None:
Log.write('Can\'t calculate protection for specific tenant.')
#return
data_cache['protections'] = {}
data_cache['to_delete']['protections'] = []
spec = acrobind.create_viewspec_by_is('Gtob::Dto::CentralizedProtection')
filename = 'amsctl_wrong_protections.txt'
_init_counters(connection, data_cache, 'protections', spec)
spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask2('.ID', '.AffectedMachines'))
def protection_callback(connection, cache, file, object):
id_str = '{}'.format(object.ID.ref)
plan_id_str = id_str
data_cache['protections'][id_str] = {}
error_string = ''
if plan_id_str not in data_cache['plans']:
error_string = '{0} {1}'.format(error_string, 'missing_plan(d)')
data_cache['to_delete']['protections'].append(id_str)
else:
for (number, affected_machine) in object.AffectedMachines:
fact_id = '{0}+{1}'.format(affected_machine.ref, plan_id_str)
no_fact = fact_id not in data_cache['deployment_facts']
if no_fact:
error_string = '{0} {1}'.format(error_string, 'missing_deployment_fact')
data_cache['deployment_facts'][fact_id] = {}
data_cache['deployment_facts'][fact_id]['plan'] = plan_id_str
data_cache['deployment_facts'][fact_id]['host'] = affected_machine.ref
data_cache['deployment_facts'][fact_id]['exists'] = False
remote_spec = acrobind.viewspec_apply_remote_host(acrobind.create_viewspec_by_is_and_guid_property('Gtob::Dto::ProtectionPlan', '.CentralizedProtectionPlan', plan_id_str), affected_machine.ref)
remote_spec = acrobind.viewspec_apply_mask(remote_spec, acrobind.create_mask2('.ID', '.CentralizedProtectionPlan'))
(mms_plan_selected, mms_plan) = acrobind.safe_select_object(connection, remote_spec)
if mms_plan_selected:
if mms_plan is None:
error_string = '{0} {1}'.format(error_string, 'plan_missing_on_agent(r)')
data_cache['deployment_facts'][fact_id]['need_redeploy'] = True
data_cache['to_redeploy'].append(plan_id_str)
else:
if mms_plan is None:
error_string = '{0} {1}'.format(error_string, 'unknown_plan_state_on_agent')
data_cache['deployment_facts'][fact_id]['need_redeploy'] = True
data_cache['unknown_to_redeploy'].append(plan_id_str)
_update_counters_and_file(data_cache, 'protections', file, id_str, error_string)
callback = lambda x: protection_callback(connection, data_cache, filename, x)
Log.write('Checking orphan Gtob::Dto::CentralizedProtection. Logging wrong item ids to \'{}\'.'.format(filename))
_do_check(connection, data_cache, 'protections', filename, spec, callback)
def check_orphan_item_protections(connection, data_cache):
data_cache['item_protections'] = {}
data_cache['to_delete']['item_protections'] = []
data_cache['item_protections']['stat'] = {}
data_cache['item_protections']['stat']['is_legacy'] = 0
data_cache['item_protections']['stat']['missing_tenant_field'] = 0
data_cache['item_protections']['stat']['total'] = 0
data_cache['item_protections']['stat']['error_item_type'] = 0
data_cache['item_protections']['stat']['error_item_id'] = 0
data_cache['item_protections']['stat']['local_item_protection'] = 0
data_cache['item_protections']['stat']['missing_centralized'] = 0
data_cache['item_protections']['stat']['missing_local'] = 0
data_cache['item_protections']['stat']['missing_item_type'] = 0
data_cache['item_protections']['stat']['missing_item_id'] = 0
spec = acrobind.create_viewspec_by_is('Gtob::Dto::ItemProtection')
filename = 'amsctl_wrong_item_protections.txt'
def apply_limits(filename, spec):
if args.parameter1 is not None:
filename = '{0}_{1}'.format(args.parameter1, filename)
limit_pattern = [
('.Tenant.ID', 'string', '{}'.format(args.parameter1))
]
return filename, acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=limit_pattern)), spec.options)
return filename, spec
filename, spec = apply_limits(filename, spec)
_init_counters(connection, data_cache, 'item_protections', spec)
def ip_callback(connection, cache, file, object):
id_str = '{}'.format(object.ID.ref)
is_centralized = [unit.ref for name, unit in object.traits if unit.ref == "Gtob::Dto::Centralized::ItemProtection"]
stat = data_cache['item_protections']['stat']
data_cache['item_protections'][id_str] = {}
data_cache['item_protections'][id_str]['cplan'] = '-'
data_cache['item_protections'][id_str]['lplan'] = '-'
error_string = ''
stat['total'] = stat['total'] + 1
is_legacy = False
if 'Instance' in object:
is_legacy = True
stat['is_legacy'] = stat['is_legacy'] + 1
tenant_id = ''
tenant_error = check_if_tenant_is_valid(object, data_cache, 'item_protections', id_str)
if tenant_error:
error_string = '{0} {1}'.format(error_string, tenant_error)
has_centralized = False
if 'Centralized' in object:
has_centralized = True
has_local = False
if 'Local' in object:
has_local = True
local_item_protection = False
if 'LastStartInfo' in object and 'BackupFrame' in object.LastStartInfo:
start_frame = object.LastStartInfo.BackupFrame.ref
if start_frame.startswith('LOCAL'):
local_item_protection = not is_centralized
if local_item_protection:
stat['local_item_protection'] = stat['local_item_protection'] + 1
item_type = get_optional(object, 'ItemType')
wrong_item_type = False
if 'ItemType' in object and item_type == 0:
stat['error_item_type'] = stat['error_item_type'] + 1
error_string = '{0} {1}'.format(error_string, 'error_item_type')
item_id = get_optional(object, 'ItemID')
if 'ItemID' in object and str(item_id) == '00000000-0000-0000-0000-000000000000':
stat['error_item_id'] = stat['error_item_id'] + 1
error_string = '{0} {1}'.format(error_string, 'error_item_id')
data_cache['item_protections'][id_str]['wrong_item_id'] = True
if cache['resolve_links']:
if object is not None and 'Centralized' in object and 'PlanID' in object.Centralized:
cplan_id = '{0}'.format(object.Centralized.PlanID.ref)
data_cache['item_protections'][id_str]['cplan'] = cplan_id
if cplan_id not in data_cache['plans']:
error_string = '{0} {1}'.format(error_string, 'link_to_missing_centralized_plan')
if object is not None and 'Local' in object and 'PlanID' in object.Local:
lplan_id = '{0}'.format(object.Local.PlanID.ref)
data_cache['item_protections'][id_str]['lplan'] = lplan_id
if lplan_id not in data_cache['plans']:
error_string = '{0} {1}'.format(error_string, 'link_to_missing_local_plan')
if not is_legacy:
if not has_local:
any_problem_exists = True
stat['missing_local'] = stat['missing_local'] + 1
error_string = '{0} {1}'.format(error_string, 'missing_local')
if not has_centralized and is_centralized:
any_problem_exists = True
stat['missing_centralized'] = stat['missing_centralized'] + 1
error_string = '{0} {1}'.format(error_string, 'missing_centralized')
if not local_item_protection and (item_type or item_id):
if item_type is None:
stat['missing_item_type'] = stat['missing_item_type'] + 1
error_string = '{0} {1}'.format(error_string, 'missing_item_type')
any_problem_exists = True
if item_id is None:
stat['missing_item_id'] = stat['missing_item_id'] + 1
error_string = '{0} {1}'.format(error_string, 'missing_item_id')
any_problem_exists = True
#log_str = '{0}: {1}'.format(id_str, error_string)
#if not cache['resolve_links']:
# log_str = '{0}\n{1}'.format(error_string, object)
log_str = '{0}\n{1}'.format(error_string, object)
_update_counters_and_file(data_cache, 'item_protections', file, id_str, error_string)
callback = lambda x: ip_callback(connection, data_cache, filename, x)
Log.write('Checking orphan Gtob::Dto::ItemProtection. Logging wrong item ids to \'{}\'.'.format(filename))
_do_check(connection, data_cache, 'item_protections', filename, spec, callback)
stat = data_cache['item_protections']['stat']
for key in sorted(stat):
Log.write('{0}: {1}'.format(key, stat[key]))
def _log_for_unused_objects(connection, cache, entry_id, filename):
with open(filename, "w") as myfile:
myfile.truncate()
counter = 0
for id, value in cache[entry_id].items():
if not value['used']:
counter = counter + 1
with open(filename, "a") as myfile:
myfile.write('{}\n'.format(id))
Log.write('Unused {}: {}'.format(entry_id, counter))
def check_for_unused_objects(connection, cache):
_log_for_unused_objects(connection, cache, 'tenants', 'amsctl_unused_tenants.txt')
_log_for_unused_objects(connection, cache, 'msp_machines', 'amsctl_unused_msp_machines.txt')
_log_for_unused_objects(connection, cache, 'agents', 'amsctl_unused_agents.txt')
def consistency_redeploy_plans(connection, ids):
creation_time = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
log_file_name = 'amsctl_redeploy_{}.txt'.format(creation_time)
Log.write('Plans to redeploy: {0}'.format(len(ids)))
start = time.time()
for plan_id in ids:
Log.write('Redeploying \'{0}\''.format(plan_id))
try:
redeploy_plan(connection, plan_id)
except Exception as error:
Log.write('Skipping because of error: {0}'.format(error))
Log.write('elapsed: {0:.2f} s.'.format(time.time() - start))
def delete_obsolete_data(connection, cache):
creation_time = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
log_file_name = 'amsctl_deleted_objects_{}.txt'.format(creation_time)
Log.write('Removing wrong objects...')
start = time.time()
def handle_ids(connection, spec, filename):
def store_object(object, filename):
with open(filename, "a") as myfile:
myfile.write('{}\n'.format(object))
callback = lambda x: store_object(x, filename)
acrobind.enumerate_objects(connection, spec, callback, error_log)
connection.dml.delete(pattern=spec.pattern)
for type, ids in cache['to_delete'].items():
Log.write('To delete \'{0}\': {1}'.format(type, len(ids)))
if len(ids) <= 0:
continue
if type == 'agents':
spec = acrobind.create_viewspec_by_is('MachineManagement::Machine')
spec = acrobind.viewspec_apply_ids(spec, ids)
handle_ids(connection, spec, log_file_name)
if type == 'instances':
spec = acrobind.create_viewspec_by_is('InstanceManagement::Instance')
spec = acrobind.viewspec_apply_ids(spec, ids)
handle_ids(connection, spec, log_file_name)
if type == 'deployment_facts':
spec = acrobind.create_viewspec_by_is('Gtob::Protection::PlanDeploymentFact')
ids_pattern = []
for id in ids:
ids_pattern.append([('', 'guid', id)])
pattern = [
('.ID.Plan', 'guid', '00000000-0000-0000-0000-000000000000'),
('.ID.Plan^ValueIn', 'complex_trait', [('', 'array', ids_pattern)]),
]
spec = acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=pattern)), spec.options)
handle_ids(connection, spec, log_file_name)
if type == 'plans':
spec = acrobind.create_viewspec_by_is('Gtob::Dto::ProtectionPlan')
spec = acrobind.viewspec_apply_ids(spec, ids)
handle_ids(connection, spec, log_file_name)
if type == 'item_protections':
spec = acrobind.create_viewspec_by_is('Gtob::Dto::ItemProtection')
spec = acrobind.viewspec_apply_ids(spec, ids)
handle_ids(connection, spec, log_file_name)
if type == 'protections':
spec = acrobind.create_viewspec_by_is('Gtob::Dto::CentralizedProtection')
spec = acrobind.viewspec_apply_ids(spec, ids)
handle_ids(connection, spec, log_file_name)
if type == 'msp_machines':
spec = acrobind.create_viewspec_by_is('Msp::AMS::Dto::Machine')
ids_pattern = []
for id in ids:
ids_pattern.append([('', 'string', id)])
pattern = [
('.AgentID', 'string', '00000000-0000-0000-0000-000000000000'),
('.AgentID^ValueIn', 'complex_trait', [('', 'array', ids_pattern)]),
]
spec = acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=pattern)), spec.options)
handle_ids(connection, spec, log_file_name)
Log.write('elapsed: {0:.2f} s.'.format(time.time() - start))
def obsolete_data(connection):
cache = {}
cache['resolve_links'] = True
cache['specific_tenant'] = False
cache['to_delete'] = {}
cache['to_redeploy'] = []
cache['unknown_to_redeploy'] = []
action = 'all'
if args.parameter2 is not None and args.parameter2.startswith('ip'):
action = 'item_protection_only'
cache['resolve_links'] = False
elif args.parameter2 is not None and args.parameter2.startswith('protection'):
action = 'protection_plan_only'
cache['resolve_links'] = False
if args.parameter1 is not None:
cache['specific_tenant'] = True
Log.write('Action: {}'.format(action))
Log.write('Resolve links between objects: {}'.format(cache['resolve_links']))
if action in ['all']:
check_tenants_consistency(connection, cache)
check_orphan_msp_machines(connection, cache)
check_orphan_agents(connection, cache)
check_orphan_instances(connection, cache)
if action in ['protection_plan_only', 'all']:
check_orphan_plans(connection, cache)
check_deployment_fact_consistency(connection, cache)
check_protections_consistency(connection, cache)
if action in ['item_protection_only', 'all']:
check_orphan_item_protections(connection, cache)
if action in ['all']:
check_for_unused_objects(connection, cache)
for type, ids in cache['to_delete'].items():
Log.write('To delete \'{0}\': {1}'.format(type, len(ids)))
Log.write('To redeploy: {0}'.format(len(cache['to_redeploy'])))
Log.write('Unknown deployment status: {0}'.format(len(cache['unknown_to_redeploy'])))
if args.fix:
print('Do you want to redeploy wrong protection plans {0}? (y/n)'.format(len(cache['to_redeploy'])))
if ask_user():
consistency_redeploy_plans(connection, cache['to_redeploy'])
print('Do you want to redeploy unknown protection plans {0}? (y/n)'.format(len(cache['unknown_to_redeploy'])))
if ask_user():
consistency_redeploy_plans(connection, cache['unknown_to_redeploy'])
if args.delete:
print('Do you want to delete obsolete data? (y/n)')
if ask_user():
delete_obsolete_data(connection, cache)
def init_agent_stat(stat):
stat['by_version'] = {}
stat['by_type'] = {}
stat['by_type']['mac'] = {}
stat['by_type']['lin'] = {}
stat['by_type']['win'] = {}
stat['by_type']['exc'] = {}
stat['by_type']['vmw'] = {}
stat['by_type']['mob'] = {}
stat['by_type']['sql'] = {}
stat['by_type']['oth'] = {}
def map_agent_type(id):
if id == '':
return 'Version'
if id == '{BA262882-C484-479A-8D07-B0EAC55CE27B}':
return 'Agent for Windows'
if id == '{C293F7DD-9531-4916-9346-12B0F3BFCCAA}':
return 'Agent for SQL'
if id == '{AC127296-8E50-41DE-8EFE-853C4B5CD8A8}':
return 'Agent for Mobile'
if id == '{EC3CF038-C08A-4341-82DF-F75133705F63}':
return 'Agent for VMware'
if id == '{87671A98-2B47-4D4C-98FB-490CA111E7A7}':
return 'Agent for Hyper-V'
if id == '{30BAF589-C02A-463F-AB37-5A9193375700}':
return 'Agent for Exchange'
if id == '{CDC40814-3769-4D13-B222-629463D3F5CA}':
return 'Agent for ESX (Appliance)'
if id == '{09726067-5E56-4775-8D3B-FD9110BCAAD1}':
return 'Agent for ARX single pass'
if id == '{383CD411-7A5D-4658-B184-3B651A6E12BB}':
return 'Agent for Online exchange agent'
if id == '{9F148D12-3653-478B-A039-239BE36950AB}':
return 'Agent for AD'
if id == '{8397FC51-C78E-4E26-9860-6A4A8622DF7C}':
return 'Agent for Home'
return 'oth'
def increment_counter(stat, counter_name):
if counter_name not in stat:
stat[counter_name] = 1
return
stat[counter_name] += 1
def init_agents_info(data):
data[map_agent_type('')] = ''
data[map_agent_type('{BA262882-C484-479A-8D07-B0EAC55CE27B}')] = ''
data[map_agent_type('{AC127296-8E50-41DE-8EFE-853C4B5CD8A8}')] = ''
data[map_agent_type('{EC3CF038-C08A-4341-82DF-F75133705F63}')] = ''
data[map_agent_type('{87671A98-2B47-4D4C-98FB-490CA111E7A7}')] = ''
data[map_agent_type('{C293F7DD-9531-4916-9346-12B0F3BFCCAA}')] = ''
data[map_agent_type('{30BAF589-C02A-463F-AB37-5A9193375700}')] = ''
data[map_agent_type('{CDC40814-3769-4D13-B222-629463D3F5CA}')] = ''
data[map_agent_type('{09726067-5E56-4775-8D3B-FD9110BCAAD1}')] = ''
data[map_agent_type('{383CD411-7A5D-4658-B184-3B651A6E12BB}')] = ''
data[map_agent_type('{9F148D12-3653-478B-A039-239BE36950AB}')] = ''
#home
data[map_agent_type('{8397FC51-C78E-4E26-9860-6A4A8622DF7C}')] = ''
def map_os_type(type):
if type == 1:
return 'Unknown'
if type == 2:
return 'Windows9x'
if type == 3:
return 'Windows'
if type == 4:
return 'Linux'
if type == 5:
return 'MacOs'
return '{0}'.format(value)
def map_os_caps(value):
result = []
if value & 1:
result.append('BOOTMEDIA')
if value & 2:
result.append('SERVER')
if value & 4:
result.append('APPLIANCE')
if value & 8:
result.append('LINUX_RAMDISK')
if value & 16:
result.append('HYPERV')
if value & 32:
result.append('DR_APPLIANCE')
return '|'.join(result)
def map_architecture_edition(value):
if value == 0:
return 'UNKNOWN'
if value == 1:
return 'X86'
if value == 2:
return 'X64'
return '{0}'.format(value)
def get_app_stat(info, host_id):
if host_id not in info['apps']:
info['apps'][host_id] = {}
info['apps'][host_id]['exchange'] = {}
info['apps'][host_id]['exchange']['2003'] = 0
info['apps'][host_id]['exchange']['2007'] = 0
info['apps'][host_id]['exchange']['2010'] = 0
info['apps'][host_id]['exchange']['2013'] = 0
info['apps'][host_id]['exchange']['-'] = 0
info['apps'][host_id]['mssql'] = {}
info['apps'][host_id]['mssql']['SQL Server 2005'] = 0
info['apps'][host_id]['mssql']['SQL Server 2008'] = 0
info['apps'][host_id]['mssql']['SQL Server 2008R2'] = 0
info['apps'][host_id]['mssql']['SQL Server 2012'] = 0
info['apps'][host_id]['mssql']['SQL Server 2014'] = 0
info['apps'][host_id]['mssql']['SQL Server Unknown'] = 0
return info['apps'][host_id]
def map_sql_server_version(version):
if version.startswith('9.0'):
return 'SQL Server 2005'
if version.startswith('10.0'):
return 'SQL Server 2008'
if version.startswith('10.50'):
return 'SQL Server 2008R2'
if version.startswith('11.0'):
return 'SQL Server 2012'
if version.startswith('12.0'):
return 'SQL Server 2014'
return 'SQL Server Unknown'
def map_exchange_server_version(version):
if version == '-':
return 'Exchange Server Unknown'
return 'Exchange Server {0}'.format(version)
def collect_summary_info(connection, info, object):
is_online = (object.Status.ref == 0)
is_mobile = is_mobile_agent(object.Info.Agents)
is_ati = is_ati_agent(object.Info.Agents)
info["total"]['total'] = info["total"]['total'] + 1
if is_online:
info["total"]['total_online'] = info["total"]['total_online'] + 1
if is_mobile:
info["total"]['mobile'] = info["total"]['mobile'] + 1
if is_online:
info["total"]['mobile_online'] = info["total"]['mobile_online'] + 1
elif is_ati:
info["total"]['home'] = info["total"]['home'] + 1
if is_online:
info["total"]['home_online'] = info["total"]['home_online'] + 1
else:
info['total']['abr'] = info['total']['abr'] + 1
if is_online:
info["total"]['abr_online'] = info["total"]['abr_online'] + 1
def collect_online_info(connection, info, object):
is_mobile = is_mobile_agent(object.Info.Agents)
is_ati = is_ati_agent(object.Info.Agents)
info["total"]['total'] = info["total"]['total'] + 1
if is_mobile:
info["total"]['mobile'] = info["total"]['mobile'] + 1
elif is_ati:
info["total"]['home'] = info["total"]['home'] + 1
else:
info['total']['abr'] = info['total']['abr'] + 1
def process_agents(connection, info, object):
agent_id = '{0}'.format(object.ID.ref)
info[agent_id] = {}
data = info[agent_id]
tenant = ''
if 'Tenant' in object:
tenant = object.Tenant.Name.ref
data['Tenant'] = tenant
init_agents_info(data)
is_online = (object.Status.ref == 0)
is_mobile = is_mobile_agent(object.Info.Agents)
is_ati = is_ati_agent(object.Info.Agents)
def get_version(agents_info):
for index, a in object.Info.Agents:
version = a.Version.ref
return version
if is_mobile:
agent_type = map_agent_type('{AC127296-8E50-41DE-8EFE-853C4B5CD8A8}')
data[agent_type] = get_version(object.Info.Agents)
elif is_ati:
agent_type = map_agent_type('{8397FC51-C78E-4E26-9860-6A4A8622DF7C}')
data[agent_type] = get_version(object.Info.Agents)
else:
for index, a in object.Info.Agents:
version = a.Version.ref
agent_type = map_agent_type(a.Id.ref)
data[agent_type] = version
if agent_type == 'oth':
Log.write('UnknownType: {0}, {1}'.format(a.Name.ref, a.Id.ref))
data['ProcessorArchitecture'] = object.Info.Architecture.ref
#if 'Hardware' in object.Info:
# data['MemorySize'] = object.Info.Hardware.MemorySize.ref
# data['ProcessorFrequency'] = object.Info.Hardware.ProcessorFrequency.ref
# data['ProcessorName'] = object.Info.Hardware.ProcessorName.ref
#else: #For Mobile
# data['MemorySize'] = '-'
# data['ProcessorFrequency'] = '-'
# data['ProcessorName'] = ''
#data['Name'] = object.Info.Name.ref
data['Status'] = object.Status.ref
data['OSArchitectureEdition'] = map_architecture_edition(object.Info.OS.ArchitectureEdition.ref)
data['OSName'] = object.Info.OS.Name.ref
data['OSCaps'] = map_os_caps(object.Info.OS.OSCaps.ref)
data['OSType'] = map_os_type(object.Info.OS.OSType.ref)
data['OSVersionMajor'] = object.Info.OS.VersionMajor.ref
data['OSVersionMinor'] = object.Info.OS.VersionMinor.ref
if 'TimezoneOffsetInMinutes' in object.Info:
data['TimezoneOffsetInMinutes'] = object.Info.TimezoneOffsetInMinutes.ref
else: #For Mobile
data['TimezoneOffsetInMinutes'] = '-'
data['LastConnectionTime'] = '-'
if 'LastConnectionTime' in object:
data['LastConnectionTime'] = object.LastConnectionTime.ref
if 'Tenant' in object:
data['TenantName'] = object.Tenant.Name.ref
data['TenantID'] = object.Tenant.ID.ref
data['TenantLocator'] = object.Tenant.Locator.ref
else:
data['TenantName'] = '-'
data['TenantID'] = '-'
data['TenantLocator'] = '-'
if 'UpdateState' in object:
data['UpdateIsAvailable'] = object.UpdateState.UpdateVersion.ref
else: #For Mobile
data['UpdateIsAvailable'] = '-'
app_stat = get_app_stat(info, agent_id)
for app_version, count in app_stat['mssql'].items():
data[app_version] = count
for app_vesion, count in app_stat['exchange'].items():
data[map_exchange_server_version(app_version)] = count
def print_agent_stat(stat):
Log.write('By version:')
sorted_items = sorted(stat['by_version'].items(), key=operator.itemgetter(1))
sorted_items.reverse()
for version, count in sorted_items:
Log.write('\t{0}: {1}'.format(version, count))
Log.write('By type:')
for type, data in stat['by_type'].items():
Log.write('\t{0}:'.format(type))
sorted_items = sorted(data.items(), key=operator.itemgetter(1))
sorted_items.reverse()
for version, count in sorted_items:
Log.write('\t\t{0}: {1}'.format(version, count))
def process_mssql_instances(connection, info, object):
host_id = '{0}'.format(object.HostID.ref)
stat = get_app_stat(info, host_id)
if 'SqlServerVersion' not in object.Parameters:
stat['mssql'][map_sql_server_version('-')] += 1
return
for index, value in object.Parameters.SqlServerVersion:
#Log.write('Found mssql version \'{0}\''.format(value.ref))
app_version = map_sql_server_version(value.ref)
if app_version not in stat['mssql']:
stat['mssql'][map_sql_server_version('-')] += 1
else:
stat['mssql'][app_version] += 1
def process_exchange_instances(connection, info, object):
host_id = '{0}'.format(object.HostID.ref)
stat = get_app_stat(info, host_id)
#print(object)
if 'ExchangeServerVersion' not in object.Parameters:
stat['exchange']['-'] += 1
return
for index, value in object.Parameters.ExchangeServerVersion:
#Log.write('Found exchange version \'{0}\''.format(value.ref))
if value.ref not in stat['exchange']:
stat['exchange']['-'] += 1
else:
stat['exchange'][value.ref] += 1
def collect_agents_statistics(connection):
info = {}
info['total'] = {}
info['total']['home'] = 0
info['total']['mobile'] = 0
info['total']['abr'] = 0
info['total']['total'] = 0
info['apps'] = {}
if args.parameter1 != 'summary' and args.parameter1 != 'online':
#prefetch application instance info
Log.write('Collecting information applications...')
start = time.time()
callback = lambda x: process_exchange_instances(connection, info, x)
#INSTANCE_TYPE_EXCHANGE = 6,
pattern = [
('^Is', 'string', 'InstanceManagement::Instance'),
('.Type', 'dword', 6),
]
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=pattern))
acrobind.enumerate_objects(connection, spec, callback, error_log)
callback = lambda x: process_mssql_instances(connection, info, x)
#INSTANCE_TYPE_MSSQL = 19,
pattern = [
('^Is', 'string', 'InstanceManagement::Instance'),
('.Type', 'dword', 19),
]
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=pattern))
acrobind.enumerate_objects(connection, spec, callback, error_log)
Log.write('done ({0:.2f} s)'.format(time.time() - start))
#determine all agents type
Log.write('Collecting information about agents...')
start = time.time()
spec = acrobind.create_viewspec_machines_by_role(0)
if args.parameter1 == 'summary':
info['total']['home_online'] = 0
info['total']['mobile_online'] = 0
info['total']['abr_online'] = 0
info['total']['total_online'] = 0
spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask3('.ID', '.Status', '.Info'))
agents_callback = lambda x: collect_summary_info(connection, info, x)
elif args.parameter1 == 'online':
pattern = [
('^Is', 'string', 'MachineManagement::Machine'),
('.Info.Role', 'dword', 0),
('.Status', 'dword', 0),
]
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=pattern))
spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask3('.ID', '.Status', '.Info'))
agents_callback = lambda x: collect_online_info(connection, info, x)
else:
info['total']['home_online'] = 0
info['total']['mobile_online'] = 0
info['total']['abr_online'] = 0
info['total']['total_online'] = 0
agents_callback = lambda x: process_agents(connection, info, x)
acrobind.enumerate_objects(connection, spec, agents_callback, error_log)
Log.write('done ({0:.2f} s)'.format(time.time() - start))
if args.parameter1 == 'summary' or args.parameter1 == 'online':
print(info['total'])
return
info.pop('apps', None)
info.pop('total', None)
print(json.dumps(info))
#print_agent_stat(info)
#for tenant, data in info['by_tenant'].items():
# Log.write("Tenant: {0}".format(tenant))
# print_agent_stat(data)
def map_usert_agent_type(id):
if id == '{BA262882-C484-479A-8D07-B0EAC55CE27B}':
return 'Workstation'
if id == '{C293F7DD-9531-4916-9346-12B0F3BFCCAA}':
return 'SQL'
if id == '{AC127296-8E50-41DE-8EFE-853C4B5CD8A8}':
return 'Mobile'
if id == '{EC3CF038-C08A-4341-82DF-F75133705F63}':
return 'VMware'
if id == '{87671A98-2B47-4D4C-98FB-490CA111E7A7}':
return 'Hyper-V'
if id == '{30BAF589-C02A-463F-AB37-5A9193375700}':
return 'Exchange'
if id == '{CDC40814-3769-4D13-B222-629463D3F5CA}':
return 'ESX (Appliance)'
if id == '{09726067-5E56-4775-8D3B-FD9110BCAAD1}':
return 'ARX single pass'
if id == '{383CD411-7A5D-4658-B184-3B651A6E12BB}':
return 'Online ARX'
if id == '{9F148D12-3653-478B-A039-239BE36950AB}':
return 'AD'
if id == '{8397FC51-C78E-4E26-9860-6A4A8622DF7C}':
return 'Home'
if id == 'home':
return 'Home'
return 'unknown'
def init_users_info(data):
data[map_usert_agent_type('{BA262882-C484-479A-8D07-B0EAC55CE27B}')] = {}
data[map_usert_agent_type('{AC127296-8E50-41DE-8EFE-853C4B5CD8A8}')] = {}
data[map_usert_agent_type('{EC3CF038-C08A-4341-82DF-F75133705F63}')] = {}
data[map_usert_agent_type('{87671A98-2B47-4D4C-98FB-490CA111E7A7}')] = {}
data[map_usert_agent_type('{C293F7DD-9531-4916-9346-12B0F3BFCCAA}')] = {}
data[map_usert_agent_type('{30BAF589-C02A-463F-AB37-5A9193375700}')] = {}
data[map_usert_agent_type('{CDC40814-3769-4D13-B222-629463D3F5CA}')] = {}
data[map_usert_agent_type('{09726067-5E56-4775-8D3B-FD9110BCAAD1}')] = {}
data[map_usert_agent_type('{383CD411-7A5D-4658-B184-3B651A6E12BB}')] = {}
data[map_usert_agent_type('{9F148D12-3653-478B-A039-239BE36950AB}')] = {}
data[map_usert_agent_type('home')] = {}
data[map_usert_agent_type('unknown')] = {}
def process_users(connection, info, object):
#print(object)
#print(len(object.Info.Agents))
if 'Tenant' not in object:
#Log.write("Object without tenant info:\n{0}".format(object))
return
tenant_id = object.Tenant.ID.ref
for index, a in object.Info.Agents:
data = None
agent_type = None
#Home agent have no Version AgentInfo and have empty Id in AgentInfo
if len(object.Info.Agents) == 1 and a.Id.ref == '':
agent_type = map_usert_agent_type('home')
#print('Home detected {0}'.format(object))
elif a.Id.ref:
agent_type = map_usert_agent_type(a.Id.ref)
if not agent_type:
continue
data = info[agent_type]
if data is None:
Log.write('UnknownType: {0}, {1}'.format(a.Name.ref, a.Id.ref))
print(agent_type)
print(info)
continue
if tenant_id in data:
#Log.write('Already calculated')
return
#Log.write(agent_type)
data[tenant_id] = {}
data[tenant_id]["ID"] = object.Tenant.ID.ref
data[tenant_id]["Name"] = object.Tenant.Name.ref
data[tenant_id]["Locator"] = object.Tenant.Locator.ref
data[tenant_id]["ParentID"] = object.Tenant.ParentID.ref
data[tenant_id]["Kind"] = object.Tenant.Kind.ref
def collect_users_info(connection):
info = {}
init_users_info(info)
#determine all agents type
Log.write('Collecting information about agents...')
start = time.time()
agents_callback = lambda x: process_users(connection, info, x)
spec = acrobind.create_viewspec_machines_by_role(0)
#spec = acrobind.viewspec_apply_mask(spec, acrobind.create_mask3('.ID', '.Tenant', '.Info.Agents', '.Info.OS'))
acrobind.enumerate_objects(connection, spec, agents_callback, error_log)
Log.write('done ({0:.2f} s)'.format(time.time() - start))
print(json.dumps(info))
def process_autoupdate(connection, object, table):
if args.extra:
Log.write(object)
if table:
table.add_row(['{}'.format(object.Version.ref), '{}'.format(object.OS.ref), '{}'.format(object.Arch.ref), '{}'.format(object.Locale.ref), '{}'.format(object.BuildUrl.ref)])
if args.fix:
print('Do you want to delete this object(y/n)?')
if ask_user():
object_pattern = [
('^Is', 'string', 'AutoUpdate::Dto::Update'),
('.BuildUrl', 'string', object.BuildUrl.ref),
('.ID.ID', 'guid', object.ID.ID.ref),
('.ID.SequencedID', 'qword', object.ID.SequencedID.ref),
]
connection.dml.delete(pattern=acrort.plain.Unit(flat=object_pattern))
print('deleted.')
else:
print('skipped.')
def analyze_updates(connection):
build_url = None
if args.build_url:
build_url = args.build_url
update_id = None
if args.update_id:
update_id = args.update_id
Log.write('Listing updates for build url {0} and update id {1}'.format(build_url if build_url else "All", update_id if update_id else "All"))
table = prettytable.PrettyTable(["Version", "OS", "Arch", "Locale", "BuildUrl"])
table.align["Version"] = "l"
table.padding_width = 1
callback = lambda x: process_autoupdate(connection, x, table)
pattern = [
('^Is', 'string', 'AutoUpdate::Dto::Update'),
]
if build_url:
pattern.append(('.BuildUrl', 'string', build_url))
pattern.append(('.BuildUrl^Like', 'string', '%{0}%'.format(build_url)))
if update_id:
pass
#pattern.append(('.ID.ID', 'guid', update_id))
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=pattern))
acrobind.enumerate_objects(connection, spec, callback, error_log)
print(table.get_string(sortby="Version"))
print('')
def counter_mode(connection):
statistics_views = {'machine-statistics': 'Statistics::Agents', 'backup-statistics': 'Statistics::Resources'}
if args.count in statistics_views:
stat = acrobind.select_object(connection, acrobind.create_viewspec_by_is_and_string_property(statistics_views[args.count], '.Tenant.ID', "all"))
if not stat:
Log.write("Failed to select statistics object.")
return
print(stat)
else:
Log.write("Trying to count objects: {}".format(args.count))
count = acrobind.count_objects(connection, args.count)
print("{0}: {1}".format(args.count, count))
#BEGIN --perf_stat analyze--
def get_perf_stat_folder():
return os.path.join(acrort.fs.APPDATA_COMMON, acrort.common.BRAND_NAME, 'AMS', 'perf_stats')
def init_dml_perf_stat(stat, time_range):
stat[time_range] = {}
stat[time_range]['report_count_{}'.format(time_range)] = {}
stat[time_range]['report_time_{}'.format(time_range)] = {}
stat[time_range]['subscribe_create_count_{}'.format(time_range)] = {}
stat[time_range]['subscribe_create_time_{}'.format(time_range)] = {}
stat[time_range]['subscribe_delete_count_{}'.format(time_range)] = {}
stat[time_range]['subscribe_delete_time_{}'.format(time_range)] = {}
stat[time_range]['subscribe_update_count_{}'.format(time_range)] = {}
stat[time_range]['subscribe_update_time_{}'.format(time_range)] = {}
def load_dml_stat(file_name, stat):
with open(file_name, 'r') as csvfile:
try:
reader = csv.reader(csvfile, 'ams')
for row in reader:
stat[row[0]] = int(row[1])
except Exception as error:
Log.write('Failed to process stat file \'{0}\', reason: {1}.'.format(file_name, error))
def perf_stat():
Log.write('DML perf stat')
dml_perf_stat()
def dml_perf_stat():
csv.register_dialect('ams', delimiter=';', skipinitialspace=True, quoting=csv.QUOTE_NONE)
perf_folder = get_perf_stat_folder()
stat = {}
desc = {}
init_dml_perf_stat(stat, 'total')
init_dml_perf_stat(stat, '1_minute')
for i in range(1, 10):
core_id = '0_{}'.format(i)
desc_file = '{0}/{1}_desc.log'.format(perf_folder, core_id)
try:
with open(desc_file, 'r') as csvfile:
try:
reader = csv.reader(csvfile, 'ams')
for row in reader:
desc[row[0]] = '{}:{}'.format(row[2], row[1])
except Exception as error:
Log.write('Failed to process file \'{0}\', reason: {1}.'.format(desc_file, error))
for key, value in stat.items():
for stat_id in value:
stat_file_name = '{0}/{1}_{2}.log'.format(perf_folder, stat_id, core_id)
load_dml_stat(stat_file_name, stat[key][stat_id])
except Exception as error:
Log.write('Failed to process file \'{0}\', reason: {1}. Skipping'.format(desc_file, error))
for key, stat_pack in stat.items():
table = prettytable.PrettyTable([key, "value", "file"])
table.align[key] = "l"
table.align["value"] = "l"
table.align["file"] = "l"
table.padding_width = 1
last_minute_filter = 100
total_minute_filter = 1000
if args.parameter1 is not None:
last_minute_filter = int(args.parameter1)
if args.parameter2 is not None:
total_minute_filter = int(args.parameter2)
filter = last_minute_filter if key == '1_minute' else total_minute_filter
for stat_id, state_value in stat_pack.items():
sorted_stat = reversed(sorted(state_value.items(), key=lambda x:x[1]))
top_str = ''
column_name = stat_id
for i, v in sorted_stat:
if v > filter:
table.add_row([column_name, v, desc[i]])
column_name = ''
Log.write(table)
#END --perf_stat analyze--
def dml_stat():
Log.write('DML perf stat')
csv.register_dialect('ams', delimiter=';', skipinitialspace=True, quoting=csv.QUOTE_NONE)
perf_folder = get_perf_stat_folder()
stat = {}
stat['recent'] = {}
stat['total'] = {}
stat['result'] = {}
stat['result']['recent'] = {}
stat['result']['total'] = {}
try:
with open('dml_perf_last_1_minute.log', 'r') as csvfile:
try:
reader = csv.reader(csvfile, 'ams')
for row in reader:
stat['recent'][int(row[0])] = int(row[1])
except Exception as error:
Log.write('Failed to process file \'{0}\', reason: {1}.'.format('dml_perf_last_1_minute.log', error))
with open('dml_perf_total.log', 'r') as csvfile:
try:
reader = csv.reader(csvfile, 'ams')
for row in reader:
stat['total'][int(row[0])] = int(row[1])
except Exception as error:
Log.write('Failed to process file \'{0}\', reason: {1}.'.format('dml_perf_total.log', error))
except Exception as error:
Log.write('Failed to process file, reason: {0}. Skipping'.format(error))
def load_stat(stat, type):
for key, value in stat[type].items():
temp = key
counter_id = temp % 100
temp = temp // 100
op_id = temp % 100
temp = temp // 100
core_id = temp % 100
temp = temp // 100
dispatcher_id = temp % 100
counter_class = temp // 100
if dispatcher_id not in stat['result'][type]:
stat['result'][type][dispatcher_id] = {}
dispatcher_stat = stat['result'][type][dispatcher_id]
if counter_class > 1:
core_id = core_id - 1
if core_id not in dispatcher_stat:
dispatcher_stat[core_id] = {}
core_stat = dispatcher_stat[core_id]
unified_key = key
if counter_class > 1:
unified_key = key % 10000000
unified_key = 100000000 + dispatcher_id * 1000000 + core_id * 10000 + op_id * 100 + counter_id
#continue
#if key == 100000001:
# print(unified_key)
if op_id not in core_stat:
core_stat[op_id] = {}
def init_op_stat(stat, op_id):
stat[op_id] = {}
stat[op_id]['key'] = unified_key
stat[op_id]['val'] = 0
init_op_stat(core_stat[op_id], 0)
init_op_stat(core_stat[op_id], 1)
init_op_stat(core_stat[op_id], 2)
init_op_stat(core_stat[op_id], 5)
init_op_stat(core_stat[op_id], 6)
op_stat = core_stat[op_id]
c_id = (counter_class - 1)* 100 + counter_id
if c_id not in op_stat:
op_stat[c_id] = {}
op_stat[c_id]['key'] = {}
op_stat[c_id]['val'] = {}
op_stat[c_id]['val'] = value
op_stat[c_id]['key'] = unified_key
#Log.write('{} - {}:{}:{}:{}'.format(key, dispatcher_id, core_id, op_id, counter_id))
load_stat(stat, 'recent')
load_stat(stat, 'total')
table = prettytable.PrettyTable(['id', 'core + op', "calls=sucess+failed (time)", "calls=sucess+failed (time)", "avg_rate/cur_rate", "pending", "batch+sql+completion", "batch(elems)/register"])
for dispatcher, dispatcher_stat in stat['result']['total'].items():
for core_id, core_stat in dispatcher_stat.items():
for op_id, op_stat in core_stat.items():
op_name = ''
if op_id == 0:
op_name = 'create'
elif op_id == 1:
op_name = 'update'
elif op_id == 2:
op_name = 'delete'
elif op_id == 3:
op_name = 'tr'
elif op_id == 4:
op_name = 'tu'
elif op_id == 5:
op_name = 'report'
elif op_id == 6:
op_name = 'subscribe'
time_val = 0
calls_val = 0
s_calls_val = 0
f_calls_val = 0
p_calls_val = 0
b_processing_time = '-'
sql_exec_time = '-'
completion_exec_time = '-'
batch_count = '-'
batch_elements = '-'
register_counts = '-'
key = 0
for c_id, c_stat in op_stat.items():
key = c_stat['key']
if c_id == 0:
time_val = c_stat['val']
elif c_id == 1:
calls_val = c_stat['val']
elif c_id == 2:
s_calls_val = c_stat['val']
elif c_id == 3:
f_calls_val = c_stat['val']
elif c_id == 4:
p_calls_val = c_stat['val']
elif c_id == 100: #batch processing time
b_processing_time = c_stat['val']
elif c_id == 101: #sql execution time
sql_exec_time = c_stat['val']
elif c_id == 102: #completion time
completion_exec_time = c_stat['val']
elif c_id == 103: #batch count
batch_count = c_stat['val']
elif c_id == 104: #batch elements
batch_elements = c_stat['val']
elif c_id == 105: #register count
register_counts = c_stat['val']
key = key // 100
#print(key)
#if key == 100000001:
# print(unified_key)
cur_time_val = 0
cur_calls_val = 0
cur_s_calls_val = 0
cur_f_calls_val = 0
cur_p_calls_val = 0
cur_b_processing_time = 0
cur_sql_exec_time = 0
cur_completion_exec_time = 0
cur_batch_count = 0
cur_batch_elements = 0
cur_register_counts = 0
if dispatcher in stat['result']['recent'] and core_id in stat['result']['recent'][dispatcher] and op_id in stat['result']['recent'][dispatcher][core_id]:
for c_id, c_stat in stat['result']['recent'][dispatcher][core_id][op_id].items():
if c_id == 0:
cur_time_val = c_stat['val']
elif c_id == 1:
cur_calls_val = c_stat['val']
elif c_id == 2:
cur_s_calls_val = c_stat['val']
elif c_id == 3:
cur_f_calls_val = c_stat['val']
elif c_id == 4:
cur_p_calls_val = c_stat['val']
elif c_id == 100: #batch processing time
cur_b_processing_time = c_stat['val']
elif c_id == 101: #sql execution time
cur_sql_exec_time = c_stat['val']
elif c_id == 102: #completion time
cur_completion_exec_time = c_stat['val']
elif c_id == 103: #batch count
cur_batch_count = c_stat['val']
elif c_id == 104: #batch elements
cur_batch_elements = c_stat['val']
elif c_id == 105: #register count
cur_register_counts = c_stat['val']
cur_process_rate_val = 0
cur_rate_val = 0
if cur_time_val != '-' and cur_calls_val != '-' and cur_p_calls_val != '-' and cur_time_val != 0:
cur_process_rate_val = cur_calls_val
cur_rate_val = cur_p_calls_val + cur_calls_val
rate = cur_rate_val - cur_process_rate_val
rate_sgn = ''
if rate > 0:
rate_sgn = '+'
if rate < 0:
rate_sgn = '-'
#Log.write('{}:{}:{} = {}'.format(dispatcher, core_id, op_name, val_str))
table.add_row([key, '{:<2}:{:<2}:{:>10}'.format(dispatcher, core_id, op_name),\
'{:10} = {:8} + {:<4} ({:<8})'.format(calls_val, s_calls_val, f_calls_val, time_val), \
'{:5} = {:3} + {:<3} ({:<6})'.format(cur_calls_val, cur_s_calls_val, cur_f_calls_val, cur_time_val), \
'{:8.2f} / {:<8.2f}'.format(0 if time_val <= 0 else calls_val/time_val, 0 if cur_time_val <= 0 else cur_calls_val/cur_time_val), \
'{} ({}{})'.format(p_calls_val, rate_sgn, cur_p_calls_val), \
'{:1} + {:4} + {:<2}'.format(cur_b_processing_time, cur_sql_exec_time, cur_completion_exec_time), \
'{:4} ({:4}) / {:<2}'.format(cur_batch_count, cur_batch_elements, cur_register_counts) \
])
table.align['core + op'] = "l"
table.align['time'] = "l"
table.align['calls'] = "l"
table.align['success'] = "l"
table.align['failed'] = "l"
table.align['rate'] = "l"
table.align['pending'] = "l"
table.padding_width = 1
Log.write(table.get_string(sortby='id', reversesort=False))
#print(table)
return
#continue
table = prettytable.PrettyTable([key, "value", "file"])
last_minute_filter = 100
total_minute_filter = 1000
if args.parameter1 is not None:
last_minute_filter = int(args.parameter1)
if args.parameter2 is not None:
total_minute_filter = int(args.parameter2)
filter = last_minute_filter if key == '1_minute' else total_minute_filter
for stat_id, state_value in stat_pack.items():
sorted_stat = reversed(sorted(state_value.items(), key=lambda x:x[1]))
top_str = ''
column_name = stat_id
for i, v in sorted_stat:
if v > filter:
table.add_row([column_name, v, desc[i]])
column_name = ''
#Log.write(table)
#BEGIN --fix instance status--
def getEpochTime(hours_ago):
ago = datetime.datetime.now() - datetime.timedelta(hours=hours_ago)
return int(time.mktime(ago.timetuple()))
def fix_instances(connection):
creation_time = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
Log.write('[FIX_INSTANCES]: {}'.format(creation_time))
days = 1
if args.parameter1:
days = int(args.parameter1)
hours = 24
if args.parameter2:
hours = int(args.parameter2)
hours_ago = hours * days
epochTime = getEpochTime(hours_ago)
activities_pattern = [
('^Is', 'string', 'Tol::History::Plain::Activity'),
('.State', 'dword', 5),
('.Details.CommandID', 'guid', '8F01AC13-F59E-4851-9204-DE1FD77E36B4'),
('.Period.FinishTime', 'sqword', epochTime),
('.Period.FinishTime^Greater', 'sqword', epochTime)
]
if args.fix_instances != 'all':
activities_pattern.append(('.Tenant.ID', 'string', args.fix_instances))
activities_options = [
('.Mask.ID', 'nil', None),
('.Mask.Details.CommandID', 'nil', None),
('.Mask.Environment.InstanceID', 'nil', None),
('.Mask.Environment.ProtectionPlanName', 'nil', None),
('.Mask.Tenant', 'nil', None)
]
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=activities_pattern), options=acrort.plain.Unit(flat=activities_options))
cache = {}
table = prettytable.PrettyTable(['id', 'plan', 'host', 'instance', 'tenant'])
table.padding_width = 1
def process_activity(connection, activity, cache, act_table):
source_id = acrobind.get_trait_value('Source', activity)
id = '{}'.format(activity.ID.ref)
instance_id = '{}'.format(activity.Environment.InstanceID.ref)
tenant_id = '{}'.format(activity.Tenant.ID.ref)
plan_name = ''
if 'ProtectionPlanName' in activity.Environment:
plan_name = '{}'.format(activity.Environment.ProtectionPlanName.ref)
cache[id] = {}
cache[id]['host_id'] = source_id
cache[id]['tenant_id'] = tenant_id
cache[id]['instance_id'] = instance_id
act_table.add_row([id, plan_name, source_id, instance_id, get_tenant_string(activity)])
callback = lambda x: process_activity(connection, x, cache, table)
acrobind.enumerate_objects(connection, spec, callback, error_log)
if args.extra:
Log.write(table)
update_instance_state(connection, cache)
def drop_orphaned_machines(connection):
locator = '/{}/'.format(args.tenant_id) if args.tenant_id else None
tenant_locator_to_machines = collections.defaultdict(list)
def collect_tenant_locators(object):
Log.write('-', end='')
tenant_locator_to_machines[object.Tenant.Locator.ref].append(object)
def tenant_exists(locator):
Log.write('-', end='')
spec = acrobind.create_viewspec_by_is_and_string_property('Tenants::HierarchyNode', '.Locator', locator)
return acrobind.count_objects_by_spec(connection, spec) > 0
spec_m = acrobind.create_viewspec_by_is('MachineManagement::Machine')
spec_m = acrobind.viewspec_apply_tenant_locator(spec_m, locator)
mask = acrobind.create_mask2('.Info.Name', '.Tenant')
Log.write('Collect all MachineManagement::Machine...')
acrobind.enumerate_objects(connection, acrobind.viewspec_apply_mask(spec_m, mask), collect_tenant_locators, error_log)
Log.write('\nFound {} tenants'.format(len(tenant_locator_to_machines)))
Log.write('Search missed Tenants::HierarchyNode...')
tenant_locator_to_machines = {k: v for k, v in tenant_locator_to_machines.items() if not tenant_exists(k)}
Log.write('\nFound {} missed Tenants::HierarchyNode.'.format(len(tenant_locator_to_machines)))
if not tenant_locator_to_machines:
return
table = prettytable.PrettyTable(['ID', 'Name', 'Tenant', 'Locator'])
table.align = 'l'
table.padding_width = 1
for key, values in tenant_locator_to_machines.items():
for v in values:
tenant = v.get_branch('.Tenant', None)
table.add_row([str(v.ID.ref), v.Info.Name.ref, tenant.ID.ref if 'ID' in tenant else '-', tenant.Locator.ref])
Log.write('Orphaned machines:\n{}\nDelete entries?'.format(table.get_string(sortby='Locator')))
if not ask_user():
return
for _, values in tenant_locator_to_machines.items():
for v in values:
Log.write('-', end='')
connection.dml.delete(key=acrort.dml.get_object_key(acrort.plain.Unit(v)))
Log.write('\nDone.')
if not args.drop_orphaned_instances:
Log.write('You deleted orphaned machines. Do you want to delete orphaned instances? (recommended)')
if ask_user():
drop_orphaned_instances(connection)
def drop_orphaned_instances(connection):
host_id_to_keys = collections.defaultdict(list)
locator = '/{}/'.format(args.tenant_id) if args.tenant_id else None
def is_aspect(object):
return acrobind.get_trait_value('Is', object) == 'InstanceManagement::InstanceAspect'
def collect_host_ids(object):
Log.write('-', end='')
host_id = object.get_branch('.Key.HostID' if is_aspect(object) else '.HostID').ref
host_id_to_keys[str(host_id)].append(object)
def machine_exists(host_id):
Log.write('-', end='')
spec = acrobind.create_viewspec_by_is_and_id('MachineManagement::Machine', host_id)
return acrobind.count_objects_by_spec(connection, spec) > 0
spec_i = acrobind.create_viewspec_by_is('InstanceManagement::Instance')
spec_i = acrobind.viewspec_apply_tenant_locator(spec_i, locator)
spec_ia = acrobind.create_viewspec_by_is('InstanceManagement::InstanceAspect')
spec_ia = acrobind.viewspec_apply_tenant_locator(spec_ia, locator)
mask = acrobind.create_mask4('.HostID', '.FullPath', '.Type', '.Tenant')
Log.write('Collect all InstanceManagement::Instance...')
acrobind.enumerate_objects(connection, acrobind.viewspec_apply_mask(spec_i, mask), collect_host_ids, error_log)
Log.write('\nCollect all InstanceManagement::InstanceAspect...')
acrobind.enumerate_objects(connection, acrobind.viewspec_apply_mask(spec_ia, mask), collect_host_ids, error_log)
Log.write('\nFound {} hosts'.format(len(host_id_to_keys)))
Log.write('Search missed MachineManagement::Machine...')
host_id_to_keys = {k: v for k, v in host_id_to_keys.items() if not machine_exists(k)}
Log.write('\nFound {} missed MachineManagement::Machine.'.format(len(host_id_to_keys)))
if not host_id_to_keys:
return
table = prettytable.PrettyTable(['HostID', 'Is', 'InstanceID', 'FullPath', 'Type', 'Tenant', 'Locator'])
table.align = 'l'
table.padding_width = 1
for key, values in host_id_to_keys.items():
for v in values:
is_trait = acrobind.get_trait_value('Is', v)
instance_id = str(v.get_branch('.Key.ID' if is_aspect(v) else '.ID').ref)
tenant = v.get_branch('.Tenant', None)
table.add_row([key, is_trait, instance_id, v.FullPath.ref, v.Type.ref, tenant.ID.ref if 'ID' in tenant else '-', tenant.Locator.ref])
Log.write('Orphaned instances:\n{}\nDelete entries?'.format(table.get_string(sort_key=operator.itemgetter(0, 1))))
if not ask_user():
return
for _, values in host_id_to_keys.items():
for v in values:
Log.write('-', end='')
connection.dml.delete(key=acrort.dml.get_object_key(acrort.plain.Unit(v)))
Log.write('\nDone.')
def update_instance_state(connection, data):
Log.write('Fixing instance statuses for tenant: \'{}\'.'.format(args.fix_instances))
mask = acrort.plain.Unit(flat=[
('.Mask.FullPath', 'nil', None),
('.Mask.Tenant', 'nil', None),
('.Mask.BackupState', 'nil', None),
('.Mask.BackupStatus', 'nil', None),
('.Mask.State', 'nil', None),
('.Mask.Status', 'nil', None),
('.Mask.Availability', 'nil', None),
('.Mask.LastBackup', 'nil', None),
('.Mask.LastBackupTry', 'nil', None),
('.Mask.NextBackupTime', 'nil', None)
])
def set_ams_instance_availability(connection, ams_instance_spec, availability):
diff = [
('', 'dword', availability),
]
diff_unit={'Availability': acrort.plain.Unit(flat=diff)}
connection.dml.update(pattern=ams_instance_spec.pattern, diff=diff_unit)
def update_ams_instance_state(connection, ams_instance_spec, mms_instance, ams_instance, diff_next_start_time):
status = mms_instance['Status']
if (ams_instance.Status.ref == 1 and mms_instance.Status.ref == 1) or (ams_instance.Status.ref == 0 and mms_instance.Status.ref == 0):
status = [
('', 'dword', 2)
]
status = acrort.plain.Unit(flat=status)
diff_unit={
'BackupState': mms_instance['BackupState'],
'BackupStatus': mms_instance['BackupStatus'],
'State': mms_instance['State'],
'Status': status,
'LastBackup': mms_instance['LastBackup'],
'LastBackupTry': mms_instance['LastBackupTry'],
'NextBackupTime': diff_next_start_time,
}
Log.write('Applying patch:')
for name, value in diff_unit.items():
Log.write('{}: {}'.format(name, str(value)))
connection.dml.update(pattern=ams_instance_spec.pattern, diff=diff_unit)
#creation_time = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
#fixed_instances_log = 'fixed_instances_log_{}.txt'.format(creation_time)
#with open(fixed_instances_log, "w") as myfile:
# myfile.truncate()
processed_instances = []
for activity_id, value in data.items():
if value['instance_id'] in processed_instances:
#Log.write('Skipping already processed instance \'{}\' on host \'{}\' (account \'{}\')'.format(value['instance_id'], value['host_id'], value['tenant_id']))
continue
else:
processed_instances.append(value['instance_id'])
Log.write('Checking instance \'{}\' on host \'{}\' (account \'{}\')'.format(value['instance_id'], value['host_id'], value['tenant_id']))
ams_instance_spec = acrobind.create_viewspec_by_is_and_guid_property('InstanceManagement::Instance', '.ID', value['instance_id'])
ams_instance_spec = acrobind.viewspec_apply_mask(ams_instance_spec, mask)
ams_instance = acrobind.select_object(connection, ams_instance_spec)
if ams_instance is None:
Log.write('AMS doesn\'t have such instance. Skipping.')
continue
availability = 13
if 'Availability' in ams_instance:
availability = ams_instance.Availability.ref
mms_instance_spec = acrobind.viewspec_apply_remote_host(ams_instance_spec, value['host_id'])
mms_instance = None
try:
mms_instance = acrobind.select_object(connection, mms_instance_spec)
except Exception as error:
#Log.write('Couldn\'t get instance from host {}'.format(value['host_id']))
if availability != 1:
Log.write('Reseting Availability property for instance {} to 1.'.format(value['instance_id']))
if args.fix:
set_ams_instance_availability(connection, ams_instance_spec, 1)
continue
#print(ams_instance)
if mms_instance is None:
Log.write('Instance {} is missing on host {}'.format(value['instance_id'], value['host_id']))
continue
if availability != 0:
Log.write('Reseting Availability property for instance {} to 0.'.format(value['instance_id']))
if args.fix:
set_ams_instance_availability(connection, ams_instance_spec, 0)
table = prettytable.PrettyTable(['Property', 'ams', 'mms', 'equal'])
table.padding_width = 1
def process_property(prop_name, ams_object, mms_object, table):
ams_prop_state = None
if prop_name in ams_object:
ams_prop_state = ams_object[prop_name]
mms_prop_state = None
if prop_name in mms_object:
mms_prop_state = mms_object[prop_name]
equal = (ams_prop_state == mms_prop_state)
ams_prop_state_str = '-'
if ams_prop_state is not None and ams_prop_state.is_composite():
ams_prop_state_str = '{}'.format(ams_prop_state)
else:
ams_prop_state_str = '{}'.format(ams_prop_state.ref)
mms_prop_state_str = '-'
if mms_prop_state is not None and mms_prop_state.is_composite():
mms_prop_state_str = '{}'.format(mms_prop_state)
else:
mms_prop_state_str = '{}'.format(mms_prop_state.ref)
table.add_row([prop_name, ams_prop_state_str, mms_prop_state_str, '+' if equal else '-'])
return equal
instance_name = ''
tenant_str = get_tenant_string(ams_instance)
if 'FullPath' in ams_instance:
instance_name = ams_instance.FullPath.ref
if 'FullPath' not in mms_instance or \
'BackupState' not in mms_instance or \
'BackupStatus' not in mms_instance or \
'State' not in mms_instance or \
'Status' not in mms_instance or \
'LastBackup' not in mms_instance or \
'LastBackupTry' not in mms_instance or \
'NextBackupTime' not in mms_instance:
Log.write('CORRUPTED MMS INSTANCE PROBLEM detected: {}, {}, {}'.format(value['instance_id'], instance_name, tenant_str))
#print(mms_instance)
continue
instance_ok = True
instance_ok = instance_ok and process_property('FullPath', ams_instance, mms_instance, table)
instance_ok = instance_ok and process_property('BackupState', ams_instance, mms_instance, table)
instance_ok = instance_ok and process_property('BackupStatus', ams_instance, mms_instance, table)
instance_ok = instance_ok and process_property('State', ams_instance, mms_instance, table)
instance_ok = instance_ok and process_property('Status', ams_instance, mms_instance, table)
instance_ok = instance_ok and process_property('LastBackup', ams_instance, mms_instance, table)
instance_ok = instance_ok and process_property('LastBackupTry', ams_instance, mms_instance, table)
diff_next_start_time_unit = None
if 'NextBackupTime' in mms_instance and 'Time' in mms_instance.NextBackupTime and mms_instance.NextBackupTime.Time.ref is not None:
mms_next_start_time = mms_instance.NextBackupTime.Time.ref
ams_next_start_time = 0
if 'NextBackupTime' in ams_instance and 'Time' in ams_instance.NextBackupTime and ams_instance.NextBackupTime.Time.ref is not None:
ams_next_start_time = ams_instance.NextBackupTime.Time.ref
time_equal = (mms_next_start_time == ams_next_start_time)
instance_ok = instance_ok and time_equal
#print('{} {}'.format(mms_next_start_time, ams_next_start_time))
else:
#print('FALSE')
time_equal = False
instance_ok = False
table.add_row(['NextStartTime', '{}'.format(ams_next_start_time), '{}'.format(mms_next_start_time), '+' if time_equal else '-'])
diff_next_start_time = [
('.Time', 'sqword', mms_next_start_time),
('^Is', 'string', 'Gtob::Dto::NextExecutionTime')
]
if 'Trigger' in mms_instance.NextBackupTime and mms_instance.NextBackupTime.Trigger.ref is not None:
diff_next_start_time.append(('.Trigger', 'dword', mms_instance.NextBackupTime.Trigger.ref))
diff_next_start_time_unit = acrort.plain.Unit(flat=diff_next_start_time)
else:
diff_next_start_time_unit = ams_instance.NextBackupTime
if not instance_ok:
Log.write('SYNC PROBLEM detected: {}, {}, {}'.format(value['instance_id'], instance_name, tenant_str))
Log.write(table)
if args.fix:
update_ams_instance_state(connection, ams_instance_spec, mms_instance, ams_instance, diff_next_start_time_unit)
#with open(fixed_instances_log, "a") as myfile:
# myfile.write('{}\n'.format(value['instance_id']))
if instance_ok:
if (ams_instance.Status.ref == 1 and mms_instance.Status.ref == 1) or (ams_instance.Status.ref == 0 and mms_instance.Status.ref == 0):
Log.write('STATUS PROBLEM detected: {}, {}, {}'.format(value['instance_id'], instance_name, tenant_str))
Log.write(table)
if args.fix:
update_ams_instance_state(connection, ams_instance_spec, mms_instance, ams_instance, diff_next_start_time_unit)
if 'LastBackup' in mms_instance and 'NextBackupTime' in mms_instance and 'Time' in mms_instance.NextBackupTime and mms_instance.NextBackupTime.Time.ref is not None:
try:
if mms_instance.LastBackup.ref > mms_instance.NextBackupTime.Time.ref:
Log.write('SCHEDULE PROBLEM detected: {}, {}, {}'.format(value['instance_id'], instance_name, tenant_str))
Log.write(table)
except Exception as error:
print(mms_instance.NextBackupTime)
print(error)
#END --fix instance status--
def check_sync(connection):
creation_time = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
Log.write('[FIX_INSTANCES]: {}'.format(creation_time))
agents_pattern = [
('^Is', 'string', 'MachineManagement::Machine'),
('.Info.Role', 'dword', 0),
('.Status', 'dword', 0)
]
if args.check_sync != 'all':
agents_pattern.append(('.Tenant.ID', 'string', args.check_sync))
options = [
('.Mask.ID', 'nil', None),
('.Mask.Info.Name', 'nil', None),
('.Mask.Tenant', 'nil', None)
]
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=agents_pattern), options=acrort.plain.Unit(flat=options))
processed_agents = []
flag = False
def process_agent(connection, cache, wait_flag, agent):
#print(agent)
host_id = agent.ID.ref
if host_id in cache:
Log.write('.', end='')
wait_flag = True
return
else:
if wait_flag:
Log.write('.')
wait_flag = False
cache.append(host_id)
name_str = '-'
try:
name_str = agent.Info.Name.ref
except:
pass
if 'Tenant' not in agent:
Log.write('MISSING tenant in {}, {}, {}'.format(name_str, host_id, get_tenant_string(agent)))
return
tenant_id = '{}'.format(agent.Tenant.ID.ref)
#print(host_id)
ams_activities_pattern = [
('^Is', 'string', 'Tol::History::Plain::Activity'),
('.State', 'dword', 5),
#('^Source', 'string', '{}'.format(host_id)),
('.Details.MachineName', 'string', name_str),
('.Tenant.ID', 'string', tenant_id),
('.Details.Specific', 'string', 'Business'),
]
agent_activities_pattern = [
('^Is', 'string', 'Tol::History::Plain::Activity'),
('.__source_machine', 'guid', agent.ID.ref),
('.State', 'dword', 5),
('.Details.Specific', 'string', 'Business'),
]
options = [
('.SortingOptions.Period.FinishTime', 'sqword', 0),
('.SortingOptions.Period.FinishTime^Descending', 'nil', None),
('.LimitOptions', 'dword', 1),
('.Mask.ID', 'nil', None),
('.Mask.Tenant', 'nil', None),
('.Mask.Details.MachineName', 'nil', None),
('.Mask.Period.FinishTime', 'nil', None)
]
ams_act_spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=ams_activities_pattern), options=acrort.plain.Unit(flat=options))
mms_act_spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=agent_activities_pattern), options=acrort.plain.Unit(flat=options))
ams_act = acrobind.select_object(connection, ams_act_spec)
#print(ams_act)
ams_act_time = 0
if ams_act is not None and 'Period' in ams_act and 'FinishTime' in ams_act.Period:
ams_act_time = ams_act.Period.FinishTime.ref
mms_act = None
try:
mms_act = acrobind.select_object(connection, mms_act_spec)
except Exception as error:
#Log.write('Couldn\'t get instance from host {}'.format(value['host_id']))
pass
mms_act_time = 0
if mms_act is not None and 'Period' in mms_act and 'FinishTime' in mms_act.Period:
mms_act_time = mms_act.Period.FinishTime.ref
if ams_act_time < mms_act_time:
Log.write('NEED RESYNC: {}, {}, {}, {}, {}'.format(ams_act_time, mms_act_time, name_str, host_id, get_tenant_string(agent)))
if args.fix:
if args.parameter1:
psql_command = []
psql_command.append('psql')
psql_command.append('acronis_cms')
psql_command.append('-c')
psql_command.append('update attachedmachines set lastoperationid=1 where machineid=\'{}\''.format(host_id))
ret = subprocess.call(psql_command)
Log.write('Update last operation id: {}'.format(ret))
drop_agent_connection(connection, host_id)
else:
Log.write('OK: {}, {}, {}, {}, {}'.format(ams_act_time, mms_act_time, name_str, host_id, get_tenant_string(agent)))
callback = lambda x: process_agent(connection, processed_agents, flag, x)
acrobind.enumerate_objects(connection, spec, callback, error_log)
def sync_monitor(connection):
sync_monitor_for = args.sync_monitor
names = {
'7': 'Instance',
'77': 'Aspect',
'9': 'Machine',
'15': 'Plan',
'17': 'CentralizedItemProtection',
'18': 'LocalItemProtection',
'29': 'Activity',
'101': 'Configuration',
'102': 'Applications',
'103': 'Cluster',
'104': 'VmRessurection',
'105': 'Archive',
'106': 'Slice',
'107': 'Vault',
'108': 'Location',
'109': 'Alert',
'110': 'Notification',
'111': 'Autoupdate',
'112': 'Counter',
'113': 'ProcessInfo',
'114': 'UpgradeEvent11',
'115': 'LocalPlan',
'116': 'LocalProtectionObject',
'117': 'Migration'
}
def format_epoch(x):
if x is None:
return 'Never'
if x == 0:
return 'Never'
t = time.localtime(x)
return time.strftime('%Y-%m-%d %H:%M:%S', t)
def print_process_report(data):
result = prettytable.PrettyTable(["ID", "Commits", "Reads", "RR", "Changed", "News", "Completions", "Objects", "Expired", "Fails"])
result.align = "r"
result.align["ID"] = "l"
for x in data:
is_summary = [unit.ref for name, unit in x.traits if unit.ref == "Sync::Replication::Monitoring::SummaryCounter"]
if not is_summary and 'Processes' in x:
for name, p in x.Processes:
if name in names:
session_id = '-'
if 'SessionID' in x:
session_id = str(x.SessionID.ref)
result.add_row([
"/{}/{}/{}".format(str(x.MachineID.ref), session_id, names[name]),
p.CommitCount.ref,
p.ReadCount.ref,
p.RemoteReadCount.ref,
format_epoch(p.SourceChanged.ref),
get_optional(p, 'ReadNewsCount'),
get_optional(p, 'CompletionCount'),
p.ObjectCount.ref,
get_optional(p, 'ExpiredObjectsCount'),
p.FailCount.ref
])
Log.write(result.get_string(sortby="ID", reversesort=False))
def print_session_report(data):
result = prettytable.PrettyTable(["ID", "StartTime", "InPackets", "InSize/K", "OutPackets", "OutSize/K",
"Commits", "CommitTime", "Reads", "ReadTime", "RR", "RRTime",
"Changed", "Recon", "Fails"])
result.float_format["InSize/K"] = ".2"
result.float_format["OutSize/K"] = ".2"
result.align = "r"
result.align["ID"] = "l"
for x in data:
is_summary = [unit.ref for name, unit in x.traits if unit.ref == "Sync::Replication::Monitoring::SummaryCounter"]
if is_summary:
result.add_row([
'Summary',
'-',
x.InCount.ref,
x.InSize.ref / 1024,
x.OutCount.ref,
x.OutSize.ref / 1024,
x.CommitCount.ref,
x.CommitTime.ref / 1000,
x.ReadCount.ref,
x.ReadTime.ref / 1000,
'-',
'-',
'-',
'-',
x.FailCount.ref
])
else:
result.add_row([
"/{}/{}".format(str(x.MachineID.ref), str(get_optional(x, 'SessionID'))),
format_epoch(get_optional(x, 'StartTime')),
x.InCount.ref,
x.InSize.ref / 1024,
x.OutCount.ref,
x.OutSize.ref / 1024,
x.CommitCount.ref,
x.CommitTime.ref / 1000,
x.ReadCount.ref,
x.ReadTime.ref / 1000,
get_optional(x, 'RemoteReadCount'),
'-' if get_optional(x, 'RemoteReadTime') is None else str(get_optional(x, 'RemoteReadTime') / 1000),
format_epoch(get_optional(x, 'Changed')),
get_optional(x, 'ReconcileTime'),
x.FailCount.ref
])
Log.write(result.get_string(sortby="ID", reversesort=False))
def apply_limits(spec):
if args.parameter1 is not None and args.parameter2 is not None:
limit_pattern = [
('.{0}'.format(args.parameter1), 'dword', int(args.parameter2))
]
return acrort.dml.ViewSpec(spec.pattern.consolidate(acrort.plain.Unit(flat=limit_pattern)), spec.options)
return spec
if sync_monitor_for == 'summary':
pattern = [
('^Is', 'string', 'Sync::Replication::Monitoring::Counter'),
('^Is', 'string', 'Sync::Replication::Monitoring::SummaryCounter')
]
data = acrobind.select_objects(connection, apply_limits(acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=pattern))))
print_session_report(data)
elif sync_monitor_for == 'all':
spec = apply_limits(acrobind.create_viewspec_by_is('Sync::Replication::Monitoring::Counter'))
print(spec.pattern)
data = acrobind.select_objects(connection, spec)
print_session_report(data)
print_process_report(data)
else:
data = acrobind.select_objects(connection, apply_limits(acrobind.create_viewspec_by_is_and_guid_property('Sync::Replication::Monitoring::Counter', '.MachineID', sync_monitor_for)))
print_session_report(data)
print_process_report(data)
def is_deprecated_vm_instance(id):
deprecated_ids = [
'98259016-909E-48dd-A240-EE97209F545C',
'E7F120F4-5479-4C91-AEA0-ACE049E8F4CC',
'1052D468-8EA9-6C59-A0DB-9E56FC6A23C6',
'ADFC498F-C6A4-AF0B-0476-277362346360',
'B7A68552-D940-4781-B4CD-95F178DA7B2C'
]
return id in deprecated_ids;
def check_deprecated_vms(connection):
deprecated_ids = [
'98259016-909E-48dd-A240-EE97209F545C',
'E7F120F4-5479-4C91-AEA0-ACE049E8F4CC',
'1052D468-8EA9-6C59-A0DB-9E56FC6A23C6',
'ADFC498F-C6A4-AF0B-0476-277362346360',
'B7A68552-D940-4781-B4CD-95F178DA7B2C'
]
instances_spec = acrobind.create_viewspec_by_is('InstanceManagement::Instance')
instances_spec = acrobind.viewspec_apply_ids(instances_spec, deprecated_ids)
instance_aspects_spec = acrobind.create_viewspec_by_is('InstanceManagement::InstanceAspect')
ids_pattern = []
for id in deprecated_ids:
ids_pattern.append([('', 'guid', id)])
pattern = [
('.Key.ID', 'guid', '00000000-0000-0000-0000-000000000000'),
('.Key.ID^ValueIn', 'complex_trait', [('', 'array', ids_pattern)]),
]
instance_aspects_spec = acrort.dml.ViewSpec(instance_aspects_spec.pattern.consolidate(acrort.plain.Unit(flat=pattern)), instance_aspects_spec.options)
objects = acrobind.select_objects(connection, instances_spec)
#dump instances
for i in objects:
Log.write(i)
aspects = acrobind.select_objects(connection, instance_aspects_spec)
for i in aspects:
Log.write(i)
if args.fix:
Log.write('Removing deprecated aspects and instances...')
start = time.time()
connection.dml.delete(pattern=instance_aspects_spec.pattern)
connection.dml.delete(pattern=instances_spec.pattern)
Log.write('Elapsed: {0:.2f} s'.format(time.time() - start))
def check_status(connection_args):
Log.write('')
start = time.time()
connection = acrort.connectivity.Connection(*connection_args)
Log.write('Connection time: {0:.2f} s'.format(time.time() - start))
table = prettytable.PrettyTable(["DB", "Connection Time (s)"])
table.align["DB"] = "l"
table.align["Connection Time (s)"] = "l"
table.padding_width = 1
def check_db_connection(connection, object, table):
options = [('.LimitOptions', 'dword', 1)]
start = time.time()
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=[('^Is', 'string', object)]), options=acrort.plain.Unit(flat=options))
objects = acrobind.select_objects(connection, spec)
table.add_row([object, '{0:.2f}'.format(time.time() - start)])
check_db_connection(connection, 'Tenants::HierarchyNode', table)
check_db_connection(connection, 'Gtob::Dto::ItemProtection', table)
check_db_connection(connection, 'Tol::History::Plain::Activity', table)
check_db_connection(connection, 'MachineManagement::Machine', table)
check_db_connection(connection, 'InstanceManagement::Instance', table)
check_db_connection(connection, 'Agent::Configuration', table)
check_db_connection(connection, 'Tenant::Configuration', table)
check_db_connection(connection, 'GroupManagement::Group', table)
check_db_connection(connection, 'ArchiveManagement::Archive', table)
check_db_connection(connection, 'Msp::AMS::Dto::Machine', table)
Log.write(table.get_string(sortby="Connection Time (s)", reversesort=True))
Log.write('')
if args.extra:
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=[('^Is', 'string', 'Msp::Agent::Dto::Configuration')]))
object = acrobind.select_object(connection, spec)
table = prettytable.PrettyTable(["Name", "Value"])
table.align["Name"] = "l"
table.padding_width = 1
table.add_row(["AgentID", str(get_optional(object, 'AgentID'))])
table.add_row(["ZmqPublicKey", str(get_optional(object.Zmq, 'ZmqPublicKey'))])
table.add_row(["Uplink", '{0}:{1}'.format(str(get_optional(object.Uplink.Address, 'Address')), str(get_optional(object.Uplink.Address, 'Port')))])
Log.write(table.get_string(sortby="Name"))
Log.write('')
def check_notifications():
'''
Print statistics about backup notifications
'''
def group(name, pattern):
return '(?P<{}>{})'.format(name, pattern)
def convert_date(date):
return datetime.datetime.strptime(date, '%Y-%m-%d %H:%M:%S:%f')
def calculate_average(avg, current, count):
if count == 1:
return avg + current
else:
return avg * (count - 1) / count + current / count
date_pattern = r'\d[\d-]+ \d[\d:]+'
thread_id_pattern = r'\d+'
level_pattern = r'[\w]\d+'
log_header_pattern = '^{} {} {}:'.format(group('date', date_pattern), thread_id_pattern, level_pattern)
guid_pattern = '[0-9A-F-]+'
activity_header_pattern = r'\[Activity\] ID: {}'.format(group('activity_id', guid_pattern))
start_string = 'Backup reports: processing completed activity:'
end_string = 'Backup reports: MarkProcessedAndFlushActivities'
activity_start_line = '{} {} {}'.format(log_header_pattern, start_string, activity_header_pattern)
activity_end_line = '{} {} {}'.format(log_header_pattern, end_string, activity_header_pattern)
log_file = os.path.join(acrort.fs.APPDATA_COMMON, acrort.common.BRAND_NAME, 'AMS', 'backup_notifications.0.log')
with open(log_file, encoding='latin-1') as log:
activities = {}
durations = []
avg_time = datetime.timedelta()
completed_count = 0
line_number = 0
for line in log:
line_number += 1
r = re.search(activity_start_line, line)
if r:
activity_id = r.group('activity_id')
activities[activity_id] = {'id': activity_id, 'start': convert_date(r.group('date'))}
r = re.search(activity_end_line, line)
if r:
activity_id = r.group('activity_id')
data = activities.get(activity_id)
if data:
completed_count += 1
data['end'] = convert_date(r.group('date'))
data['duration'] = data['end'] - data['start']
avg_time = calculate_average(avg_time, data['duration'], completed_count)
durations.append(data)
s = sorted(durations, key=lambda x: x['duration'])
Log.write('Slowest processed activities:')
for idx, record in enumerate(reversed(s[-5:])):
Log.write(idx + 1, 'time:', str(record['duration']), ' activity:', record['id'])
Log.write()
Log.write('Fastest processed activities:')
for idx, record in enumerate(s[:5]):
Log.write(idx + 1, 'time:', str(record['duration']), ' activity:', record['id'])
Log.write()
Log.write('Average activity processing time:', avg_time)
Log.write('Count of analysed activities: ', completed_count)
def delete_object(connection, args):
spec = acrort.plain.Unit(flat=[('.ID', 'guid', args.delete_object)])
connection.dml.delete(pattern=spec)
def fix_all_centralized_protections(connection):
Log.write('Do you want to fix Gtob::Dto::CentralizedProtection objects for all tenants?(y/n)')
if not ask_user():
return
plans_pattern = [
('^Is', 'string', 'Gtob::Dto::ProtectionPlan'),
('.Origin', 'dword', 2), # Centralized plans only
]
options = [
('.Mask.ID', 'nil', None),
('.Mask.Origin', 'nil', None),
]
spec = acrort.dml.ViewSpec(pattern=acrort.plain.Unit(flat=plans_pattern), options=acrort.plain.Unit(flat=options))
plans = []
acrobind.enumerate_objects(connection, spec, lambda x: plans.append(x), error_log)
Log.write('Going to check {} plans'.format(len(plans)))
fix_centralized_protections(connection, plans)
def report_all_instances(connection):
Log.write(';'.join(['ID', 'BackupState', 'BackupStatus', 'FullPath', 'HostID', 'Mobile', 'AD', 'ATIH', 'BackupAgent', 'SQL', 'Exchange', 'ESX', 'Hyper-V', 'LastBackup', 'NextBackup', 'State', 'Status', 'Type']))
pattern = acrort.plain.Unit(flat=[
('^Is', 'string', 'InstanceManagement::Instance'),
])
options = acrort.plain.Unit(flat=[
('.Mask.ID', 'nil', None),
('.Mask.BackupState', 'nil', None),
('.Mask.BackupStatus', 'nil', None),
('.Mask.FullPath', 'nil', None),
('.Mask.HostID', 'nil', None),
('.Mask.LastBackup', 'nil', None),
('.Mask.NextBackupTime.Time', 'nil', None),
('.Mask.State', 'nil', None),
('.Mask.Status', 'nil', None),
('.Mask.Type', 'nil', None),
('.Mask.HostInfo.Agents', 'nil', None),
])
spec = acrort.dml.ViewSpec(pattern, options)
def print_instance(instance):
is_esx = False
is_hyperv = False
is_mobile = False
is_ad = False
is_ati = False
is_win = False
is_sql = False
is_exchange = False
is_hyperv = False
if 'HostInfo' in instance and 'Agents' in instance.HostInfo:
is_mobile = is_mobile_agent(instance.HostInfo.Agents)
is_ad = is_ad_agent(instance.HostInfo.Agents)
is_ati = is_ati_agent(instance.HostInfo.Agents)
is_win = is_win_agent(instance.HostInfo.Agents)
is_sql = is_sql_agent(instance.HostInfo.Agents)
is_exchange = is_exchange_agent(instance.HostInfo.Agents)
is_esx = is_esx_agent(instance.HostInfo.Agents)
is_hyperv = is_hyperv_agent(instance.HostInfo.Agents)
last_backup_stamp = instance.LastBackup.ref if instance.LastBackup.ref else 0
next_backup_stamp = instance.NextBackupTime.Time.ref if instance.NextBackupTime.Time.ref else 0
record = [
instance.ID.ref,
BACKUP_STATES.get(instance.BackupState.ref),
BACKUP_STATUSES.get(instance.BackupStatus.ref),
instance.FullPath.ref,
instance.HostID.ref,
'+' if is_mobile else '-',
'+' if is_ad else '-',
'+' if is_ati else '-',
'+' if is_win else '-',
'+' if is_sql else '-',
'+' if is_exchange else '-',
'+' if is_esx else '-',
'+' if is_hyperv else '-',
datetime.datetime.utcfromtimestamp(last_backup_stamp),
datetime.datetime.utcfromtimestamp(next_backup_stamp),
INSTANCE_STATES.get(instance.State.ref) if 'State' in instance else None,
INSTANCE_STATUSES.get(instance.Status.ref),
INSTANCE_TYPES.get(instance.Type.ref),
]
print(';'.join([str(field) for field in record]).encode('utf-8').strip())
acrobind.enumerate_objects(connection, spec, print_instance, error_log)
def format_epoch(x):
t = time.localtime(x)
return time.strftime('%Y-%m-%d %H:%M:%S', t)
def collect_protections(connection, tenants):
pattern = acrort.plain.Unit(flat=[
('^Is', 'string', 'Gtob::Dto::Centralized::ItemProtection'),
])
options = acrort.plain.Unit(flat=[
('.Mask.ID', 'nil', None),
('.Mask.Centralized.PlanID', 'nil', None),
('.Mask.HostID', 'nil', None),
])
spec = acrort.dml.ViewSpec(pattern, options)
spec = acrobind.viewspec_apply_tenants(spec, tenants)
stat = {}
protections = [x for x in acrobind.dml_utils.enum_objects(connection, spec)]
for x in protections:
agent = ''
if 'HostID' in x:
agent = str(x.HostID.ref)
else:
for name, trait_value in x.traits:
if 'name' == 'Source':
agent = str(trait_value.ref)
if agent in stat:
stat[agent] += 1
else:
stat[agent] = 1
return stat
def collect_tenants(connection, tenants):
pattern = [
('^Is', 'string', 'Tenants::HierarchyNode'),
]
if tenants:
ids_pattern = []
for tenant_id in tenants:
ids_pattern.append([('', 'string', tenant_id)])
pattern.append(('.Tenant.ID', 'string', ''))
pattern.append(('.Tenant.ID^ValueIn', 'complex_trait', [('', 'array', ids_pattern)]))
options = acrort.plain.Unit(flat=[
('.Mask.ID', 'nil', None),
('.Mask.Name', 'nil', None),
('.Mask.Locator', 'nil', None),
('.Mask.Parent', 'nil', None),
])
spec = acrort.dml.ViewSpec(acrort.plain.Unit(flat=pattern), options)
tenants = { str(x.ID.ref): x for x in acrobind.dml_utils.enum_objects(connection, spec) }
return { id: tenant.Name.ref for id, tenant in tenants.items() }
def collect_public_keys(connection, tenants):
pattern = [
('^Is', 'string', 'Msp::AMS::Dto::Machine'),
]
if tenants:
ids_pattern = []
for tenant_id in tenants:
ids_pattern.append([('', 'string', tenant_id)])
pattern.append(('.OwnerID', 'string', ''))
pattern.append(('.OwnerID^ValueIn', 'complex_trait', [('', 'array', ids_pattern)]))
options = acrort.plain.Unit(flat=[
('.Mask.AgentID', 'nil', None),
('.Mask.IsEnabled', 'nil', None),
('.Mask.OwnerID', 'nil', None),
('.Mask.PublicKey', 'nil', None),
('.Mask.LastSeenTime', 'nil', None),
])
spec = acrort.dml.ViewSpec(acrort.plain.Unit(flat=pattern), options)
return { str(x.AgentID.ref): x for x in acrobind.dml_utils.enum_objects(connection, spec) }
def collect_offline_machines(connection, tenants):
pattern = acrort.plain.Unit(flat=[
('^Is', 'string', 'MachineManagement::Machine'),
('.Info.Role', 'dword', 0),
])
options = acrort.plain.Unit(flat=[
('.Mask.ID', 'nil', None),
('.Mask.Info.Agents', 'nil', None),
('.Mask.Info.Hardware.MemorySize', 'nil', None),
('.Mask.Info.Hardware.ProcessorName', 'nil', None),
('.Mask.Info.Hardware.ProcessorFrequency', 'nil', None),
('.Mask.Info.Name', 'nil', None),
('.Mask.Info.OS.ArchitectureEdition', 'nil', None),
('.Mask.Info.OS.Name', 'nil', None),
('.Mask.Info.OS.OSCaps', 'nil', None),
('.Mask.Info.OS.OSType', 'nil', None),
('.Mask.Info.OS.ProductType', 'nil', None),
('.Mask.Info.ResidentialAddresses', 'nil', None),
('.Mask.LastConnectionTime', 'nil', None),
('.Mask.MachineAddress', 'nil', None),
('.Mask.Status', 'nil', None),
('.Mask.Tenant', 'nil', None),
('.Mask.UpdateState.UpdateIsAvailable', 'nil', None)
])
spec = acrort.dml.ViewSpec(pattern, options)
spec = acrobind.viewspec_apply_tenants(spec, tenants)
return { str(x.ID.ref): x for x in acrobind.dml_utils.enum_objects(connection, spec) }
def compare_numbers(lhs, rhs):
if lhs > rhs:
return 1
elif lhs == rhs:
return 0
else:
return -1
def compare_versions(lhs, rhs):
major, minor, build = lhs.split('.')
major2, minor2, build2 = rhs.split('.')
if major == major2:
if minor == minor2:
return compare_numbers(build, build2)
else:
return compare_numbers(minor, minor2)
return compare_numbers(major, major2)
def get_product_type(build_number):
if len(build_number) <= 2:
return "mobile"
versions = build_number.split('.')
if int(versions[0]) < 11:
return "mobile"
elif int(versions[0]) == 1:
return "msp"
elif int(versions[2]) < 100:
return "home"
else:
return "msp"
def get_agent_version(machine):
version = machine.Info.Agents[0].Version.ref
for agent in machine.Info.Agents:
agent = agent[1]
agent_version = agent.Version.ref
if agent_version.count('.') != 2:
continue
if compare_versions(agent_version, version) > 0:
version = agent_version
return version
def get_os_caps(value):
if value & 4:
return 'APPLIANCE'
if value & 1:
return 'BOOTMEDIA'
if value & 8:
return 'LINUX_RAMDISK'
if value & 16:
return 'HYPERV'
if value & 32:
return 'DR_APPLIANCE'
if value & 2:
return 'SERVER'
return ''
def get_additional_info(value):
if value & 2:
return 'DOMAIN CONTROLLER'
if value & 3:
return 'SERVER'
if value & 1:
return 'WORKSTATION'
return ""
def report_all_machines(connection):
tenants = []
if args.tenant_id:
tenants.append(args.tenant_id)
Log.write('Checking subtenants of {0}'.format(args.tenant_id))
tenant_spec = acrobind.create_viewspec_by_is_and_string_property('Tenants::HierarchyNode', '.ID', args.tenant_id)
t = acrobind.select_object(connection, tenant_spec)
if t:
pattern = [
('^Is', 'string', 'Tenants::HierarchyNode'),
('.Locator', 'string', ''),
('.Locator^DirectRelative', 'string', t.Locator.ref)
]
tenants_objects = acrobind.select_objects(connection, acrort.dml.ViewSpec(acrort.plain.Unit(flat=pattern)))
for ten in tenants_objects:
tenants.append(ten.ID.ref)
protections = collect_protections(connection, tenants)
tenant_names = collect_tenants(connection, tenants)
public_keys = collect_public_keys(connection, tenants)
offline_machines = collect_offline_machines(connection, tenants)
print(';'.join(["Account", "EUC", "AccountName", "AgentID", "MachineName", "IsEnabled", "PublicKey", "DmlTimeStamp", "OSName", "OS", "Architecture", "OSCaps", "OSInfo", "Memory",
"ProcessorName", "ProcessorFrequency", "IP", "Build", "Product", "IsUpdateAvailable", "PlanCount", "LastConnectionTimeOld", "DaysOfflineOld", "LastConnectionTime", "DaysOffline", "Status"]))
report = []
for agent_id, machine in offline_machines.items():
agent_version = get_agent_version(machine)
product = get_product_type(agent_version)
machine_name = machine.Info.Name.ref
last_connection_time_old = 0
sec_offline_old = 0
if 'LastConnectionTime' in machine:
last_connection_time_old = format_epoch(machine.LastConnectionTime.ref)
sec_offline_old = int(time.time()) - machine.LastConnectionTime.ref
if agent_id not in public_keys:
continue
key = public_keys.get(agent_id, None)
if not key:
print('Cannot find key for {}({})'.format(machine_name, agent_id))
continue
if 'OwnerID' in key:
owner_id = str(key.OwnerID.ref)
if 'Tenant' not in machine:
print('Cannot find account for {}({}), owner={}, name={}'.format(machine_name, agent_id, owner_id, tenant_names.get(owner_id, 'Not found')))
continue
last_connection_time = 0
sec_offline = 0
if 'LastSeenTime' in key:
last_connection_time = format_epoch(key.LastSeenTime.ref)
sec_offline = int(time.time()) - key.LastSeenTime.ref
stamp = key.DmlTimeStamp.ref
if 'Status' in machine:
is_offline = machine.Status.ref == 1
else:
is_offline = True
is_enabled = key.IsEnabled.ref
os = {1: "Unknown", 2: "Windows", 3: "Windows", 4: "Linux", 5: "MacOS"}[machine.Info.OS.OSType.ref]
os_name = machine.Info.OS.Name.ref
architecture = {0: "Unknown", 1: "x86", 2: "x64"}[machine.Info.OS.ArchitectureEdition.ref]
os_caps = ""
if 'OSCaps' in machine.Info.OS:
os_caps = get_os_caps(machine.Info.OS.OSCaps.ref)
os_info = ""
if 'ProductType' in machine.Info.OS:
os_info = get_additional_info(machine.Info.OS.ProductType.ref)
memory = 0
processor = ""
if 'Hardware' in machine.Info:
memory = round(machine.Info.Hardware.MemorySize.ref / 1024 / 1024)
processor = machine.Info.Hardware.ProcessorName.ref
frequency = machine.Info.Hardware.ProcessorFrequency.ref
update_available = False
if 'UpdateState' in machine:
update_available = machine.UpdateState.UpdateIsAvailable.ref
address = ','.join([y.ref for i, y in machine.Info.ResidentialAddresses])
public_key = ''
plan_count = protections.get(agent_id, 0)
if 'PublicKey' in key:
public_key = str(key.PublicKey.ref)
account = machine.Tenant.Locator.ref
account_name = tenant_names.get(machine.Tenant.ID.ref, 'Not found')
euc_name = tenant_names.get(machine.Tenant.ParentID.ref, 'Not found')
report.append([account, euc_name, account_name, agent_id, machine_name, is_enabled, public_key, stamp, os_name, os, architecture, os_caps, os_info, memory, processor,
frequency, address, agent_version, product, update_available, plan_count, last_connection_time_old, round(sec_offline_old / 86400), last_connection_time, round(sec_offline / 86400), is_offline])
for r in report:
print(';'.join([str(c) for c in r]))
def main():
parser = acrobind.CommandLineParser()
parser.append_processor(acrobind.OutputArgumentsProcessor())
parser.add_argument('--connection', nargs=3, metavar=('HOST', 'USERNAME', 'PASSWORD'))
#resource specific options
parser.add_argument('-mi', '--check-machine-id', dest='machine_id', help='Show info about machine by its ID and related instances.\
Use \'-f\' option to enable fix mode. Use \'-ru\' for resetting \'MachineIsProcessed\' property. \
Use \'-ra\' to list running activities. Use \'-br\' for dropping connection. \
Example: \'acropsh -m amsctl -mi A8B415CD-3259-4E71-A38B-DE136FBCF6CE\'.', required=False)
parser.add_argument('-mn', '--check-machine-name', dest='machine_name', help='List all machines that match provided name.\
Example: \'acropsh -m amsctl -mn MyMachine\'.', required=False)
parser.add_argument('-ru', '--reset-update', dest='reset_update', action='store_true', help='Reset \'MachineIsProcessed\' property for MachineManagement::Machine.\
Can be used with \'-mi\' option only. Example: \'acropsh -m amsctl -mi A8B415CD-3259-4E71-A38B-DE136FBCF6CE -ru\'.', required=False)
parser.add_argument('-ra', '--running-activities', dest='running_activities', action='store_true', help='List all running activities for tenant or machine if it is ONLINE.\
Can be used with \'-mi\' option only. Example: \'acropsh -m amsctl -mi A8B415CD-3259-4E71-A38B-DE136FBCF6CE -ra\'.', required=False)
parser.add_argument('-br', '--break-connection', dest='break_connection', action='store_true', help='Drop connection with agent if it is connected.\
Can be used with \'-mi\' option only. Example: \'acropsh -m amsctl -mi A8B415CD-3259-4E71-A38B-DE136FBCF6CE -br\'.', required=False)
parser.add_argument('-in', '--check-instance-name', dest='instance_name', help='List all instances that match provided name.', required=False)
parser.add_argument('-ii', '--check-instance-id', dest='instance_id', help='Show info about instance by its ID ans applied protections.', required=False)
parser.add_argument('-dom', '--drop-orphaned-machines', dest='drop_orphaned_machines', help='Remove machines without tenant.\
Can be used with \'-ti\' option.', action='store_true', required=False)
parser.add_argument('-doi', '--drop-orphaned-instances', dest='drop_orphaned_instances', help='Remove instances without host.\
Can be used with \'-ti\' option.', action='store_true', required=False)
#tenant specific options
parser.add_argument('-ti', '--check-tenant-id', dest='tenant_id', help='Show info about specified tenant and its related objects.\
Use \'-qr\' for recalculating quotas usage. Use \'-ms\' for showing statistics object for this tenant. Use \'-ra\' for running activities. Example: \'acropsh -m amsctl -ti 13456\'.', required=False)
parser.add_argument('-tn', '--check-tenant-name', dest='tenant_name', help='List tenants that match specified name.', required=False)
parser.add_argument('-qr', '--quotas-reconcile', dest='quotas_reconcile', action='store_true', help='Recalculate quotas for account.\
Can be used with \'-ti\' only. Use \'all\' for reconcile quotas on all accounts. Example: \'acropsh -m amsctl -ti 13456 -qr\'.', required=False)
parser.add_argument('-ms', '--machine-statistics', dest='machine_statistics', action='store_true', help='Show MachineManagement::Statistics for account.\
Can be used with \'-ti\' only. Example: \'acropsh -m amsctl -ti 13456 -ms\'.', required=False)
parser.add_argument('--check-multitenancy', dest='check_multitenancy', action='store_true', help='Check if tenant has virtual instances that belong to hosts of different tenant')
parser.add_argument('-u', '--update', dest='update', action='store_true', help='Update section \'.Tenant\' in all dml objects by specified tenant')
#plans specific options
parser.add_argument('-pn', '--plan-name', dest='plan_name', help='List all protection plans that match specified name.', required=False)
parser.add_argument('-pi', '--plan-id', dest='plan_id', help='Show info about protection plan and related item protections.\
Use \'-r\' for protection plan redeployment.', required=False)
parser.add_argument('-r', '--redeploy', dest='redeploy', action='store_true', help='Force protection plan redeployment.\
Can be used only with \'-pi\' option. Example: \'acropsh -m amsctl -pi E9A35C00-388F-4522-AD07-981139D6F9A3 -r\'.', required=False)
parser.add_argument('--check-plan-list', dest='check_plan_list', help='List all protection plans that match specified name.', required=False)
# Option for both tenants and plans
parser.add_argument('-fcp', '--fix-centralized-protection', dest='fix_centralized_protection', action='store_true', help='Create missing Gtob::Dto::CentralizedProtection object.\
Can be used only with \'-pi\' and \'-ti\' options. Example: \'acropsh -m amsctl -pi E9A35C00-388F-4522-AD07-981139D6F9A3 -fcp\'.', required=False)
parser.add_argument('-o', '--change-owner', dest='change_owner', help='Change owner of Gtob::Dto::CentralizedProtection object.\
Can be used only with \'-pi\' option. Example: \'acropsh -m amsctl -pi E9A35C00-388F-4522-AD07-981139D6F9A3 -o 102665\'.', required=False)
#common options
parser.add_argument('-f', '--fix', dest='fix', action='store_true', help='Enable fix mode. Can be used with other options.', required=False)
parser.add_argument('-d', '--delete', dest='delete', action='store_true', help='Enable delete mode (interactive). Can be used with other options.', required=False)
parser.add_argument('-e', '--extra', dest='extra', action='store_true', help='Prints extra data. Can be used with other options.', required=False)
#auto update
parser.add_argument('-bu', '--build-url', dest='build_url', help='List auto update using build url', required=False)
parser.add_argument('-ui', '--update-id', dest='update_id', help='List auto update using id', required=False)
parser.add_argument('-lu', '--list-updates', dest='list_updates', action='store_true', help='List all auto updates', required=False)
#misc options
parser.add_argument('-p1', '--parameter1', dest='parameter1', help='Custom parameter that can be used with other options.', required=False)
parser.add_argument('-p2', '--parameter2', dest='parameter2', help='Custom parameter that can be used with other options.', required=False)
parser.add_argument('--fix-instances', dest='fix_instances', help='Check/Fix instances statuses for specified tenant or \'all\'. \
It checks all recent activities for last 24 hours or time period specifid using \'p1\' and \'p2\' options.\
Use \'--fix-instances all\' for all tenants.\
Use \'-f\' for fixing. Use\'p1\' for specifing the amount of days and \'p2\' for hours.\
Example: \'--fix-instances all -f -p1 2 -p2 13\'', required=False)
parser.add_argument('--check-sync', dest='check_sync', help='Check/Fix sync statuses for machines of specified tenant or \'all\'. \
It checks most recent activitiy on AMS side and most recent activity on MMS side. If theirs \'FinishTime\' isn\'t equal and \'-f\' specified \
connection to MMS will be dropped and it will cause re-sync. Use \'p1\' with any value to drop connection to old agent (<=6.0).', required=False)
parser.add_argument('--check-deprecated-vms', dest='check_deprecated_vms', action='store_true', help='Checks existence of deprecated virtual instances (aka Red hat KVM and etc).\
Use \'-f\' to force deletion of deprecated instances.', required=False)
parser.add_argument('-sm', '--sync-monitor', dest='sync_monitor', help='Show sync statistics. Show summary if \'summary\' is specified. Show all channels if \'all\' specified.\
Show specific machine channels if machine ID is specified. Example: \'watch acropsh -m amsctl -sm E9A35C00-388F-4522-AD07-981139D6F9A3\'.', required=False)
parser.add_argument('-ps', '--perf-stat', dest='perf_stat', action='store_true', help='Show aggregated DML performance statistics using data from \'SERVICE_HOME_DIR/perf_stat\' folder.', required=False)
parser.add_argument('-ds', '--dml-stat', dest='dml_stat', action='store_true', help='Show aggregated DML performance statistics using data from \'SERVICE_HOME_DIR/perf_stat\' folder.', required=False)
parser.add_argument('-od', '--obsolete-data', dest='obsolete_data', action='store_true', help='Analyze objects consistency. May take a lot of time if run for \'all\'.\
Use \'-p1\' for checking objects of specific tenant . Use \'p2\' for specifing action: \'ip\' - only check item protections, \'protection\' or \'all\'.\
Example: \'acropsh amsctl.py -od -p1 92271\'.', required=False)
parser.add_argument('-ai', '--agent-info', dest='agent_info', action='store_true', help='Collect agents statistics and outputs it as JSON.', required=False)
parser.add_argument('-usi', '--user-info', dest='user_info', action='store_true', help='Collect info about users and outputs it as JSON.', required=False)
parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', help='Disable output to stdout except result.', required=False)
parser.add_argument('-c', '--count', dest='count', help='Count objects. Specify \'machine-statistics\' for machine statistics or \'backup-statistics\' for backup statistics, \
otherwise tries to count objects assumes input as \'Is\' value.\
Example: \'acropsh amsctl.py -c Gtob::Dto::ItemProtection\'.', required=False)
parser.add_argument('-s', '--status', dest='check_status', action='store_true', help='Check AMS status.', required=False)
parser.add_argument('-bn', '--backup-notifications', dest='backup_notifications', action='store_true', help='Show information about backup notifications', required=False)
parser.add_argument('-do', '--delete-object', dest='delete_object', help='Delete object by ID', required=False)
parser.add_argument('--fix-all-centralized-protections', dest='fix_all_centralized_protections', action='store_true', help='Create missing Gtob::Dto::CentralizedProtection objects \
for all plans', required=False)
parser.add_argument('--report-all-instances', dest='report_all_instances', action='store_true', help='Output information about all instances')
parser.add_argument('--report-all-machines', dest='report_all_machines', action='store_true', help='Report offline machines')
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 args
args = config['args']
try:
global Log
if args.quiet:
Log = acrobind.NullOutput()
else:
Log = acrobind.Output(config, end='\n')
if args.perf_stat:
perf_stat()
return
if args.dml_stat:
dml_stat()
return
if args.backup_notifications:
check_notifications()
return
if args.connection:
hostname = args.connection[0]
username = args.connection[1]
password = args.connection[2]
Log.write('Connecting to AMS \'{0}@{1}\' ...'.format(username, hostname), end='')
connection_args = ('ams', hostname, (username, password))
else:
Log.write('Connecting to AMS locally...', end='')
connection_args = ('ams', )
if args.check_status:
check_status(connection_args)
return
connection = acrort.connectivity.Connection(*connection_args, client_session_data={"identity_disabled": True})
Log.write('done')
if args.count:
counter_mode(connection)
return
if args.list_updates or args.build_url or args.update_id:
analyze_updates(connection)
return
if args.user_info:
collect_users_info(connection)
return
if args.agent_info:
collect_agents_statistics(connection)
return
if args.obsolete_data:
obsolete_data(connection)
return
if args.report_all_machines:
report_all_machines(connection)
return
if args.quotas_reconcile:
quotas_reconcile(connection)
return
if args.drop_orphaned_machines:
drop_orphaned_machines(connection)
return
if args.drop_orphaned_instances:
drop_orphaned_instances(connection)
return
if args.tenant_id:
check_tenant(connection)
return
if args.tenant_name:
list_tenants(connection)
return
if args.check_plan_list:
check_plan_list(connection)
return
if args.plan_name or args.plan_id:
describe_plans(connection)
return
if args.machine_name:
list_machines(connection)
return
if args.instance_name:
list_instances(connection)
return
if args.fix_instances:
fix_instances(connection)
return
if args.check_sync:
check_sync(connection)
return
if args.sync_monitor:
sync_monitor(connection)
return
if args.check_deprecated_vms:
check_deprecated_vms(connection)
return
if args.delete_object:
delete_object(connection, args)
return
if args.fix_all_centralized_protections:
fix_all_centralized_protections(connection)
return
if args.report_all_instances:
report_all_instances(connection)
return
if args.report_all_machines:
report_all_machines(connection)
return
if args.instance_id:
check_instance(connection)
if args.machine_id:
check_machine(connection)
except Exception as e:
error_log(format_backtrace(e))
if __name__ == '__main__':
exit(acrobind.interruptable_safe_execute(main))
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists