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))
|