shell bypass 403
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))