shell bypass 403
UnknownSec Shell
:
/
proc
/
self
/
root
/
proc
/
self
/
root
/
proc
/
thread-self
/
root
/
lib
/
Acronis
/
PyShell
/
site-tools
/ [
drwxr-xr-x
]
upload
mass deface
mass delete
console
info server
name :
amsctl.py
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))
© 2024 UnknownSec