GIF89a=( �' 7IAXKgNgYvYx\%wh&h}t�h%�s%x�}9�R��&�0%� (�.��5�SD��&�a)�x5��;ͣ*ȡ&ղ)ׯ7׵<ѻ4�3�H֧KͯT��Y�aq��q��F� !� ' !� NETSCAPE2.0 , =( ��pH,�Ȥr�l:xШtJ�Z�جv��z��xL.:��z�n���|N�����~�������& !�0`9R�}��"�"a:S�~x��������g���E�������R���E����B�� ��ȸ��D���"�Ů� �H��L��D٫D�B�����D���T���H �G��A R�ڐ |�� ٭&��E8�S�kG�A�px�a��� R2XB��E8I���6X�:vT)�~��q�賥��"F~%x� � 4#Z�0O|-4Bs�X:= Q� Sal��yXJ`GȦ|s h��K3l7�B|�$'7Jީܪ0!��D�n=�P� ����0`�R�lj����v>���5 �.69�ϸd�����nlv�9��f{���Pbx �l5}�p� ��� �3a���I�O����!ܾ���i��9��#��)p�a ޽ �{�)vm��%D~ 6f��s}Œ�D�W E�`!� �&L8x� �ܝ{)x`X/>�}m��R�*|`D�=�_ ^�5 !_&'a�O�7�c��`DCx`�¥�9�Y�F���`?��"� �n@`�} lď��@4>�d S �v�xN��"@~d��=�g�s~G��� ���ud &p8Q�)ƫlXD����A~H�ySun�j���k*D�LH�] ��C"J��Xb~ʪwSt}6K,��q�S:9ت:���l�@�`�� �.۬�t9�S�[:��=`9N����{¿�A !R�:���6��x�0�_ �;������^���#����!����U���;0L1�����p% A��U̬ݵ��%�S��!���~`�G���� ���=4�np�3���������u�u�ٮ|%2�I��r�#0��J``8�@S@5� ���^`8E�]�.�S���7 � �0�j S�D� z���i�S�����!���l��w9*�D�I�nEX��� &A�Go�Qf��F��;���}�J����F5��Q|���X��T��y���]� o ��C=��:���PB@ D׽S�(>�C�x}`��xJЬ�۠��p+eE0`�}`A �/NE�� �9@��� H�7�!%B0`�l*��!8 2�%� �:�1�0E��ux%nP1�!�C)�P81l�ɸF#Ƭ{����B0>�� �b�`��O3��()yRpb��E.ZD8�H@% �Rx+%���c� ���f��b�d�`F�"8�XH"��-�|1�6iI, 2�$+](A*j� QT�o0.�U�`�R�}`�SN����yae�����b��o~ S)�y�@��3 �tT�0�&�+~L�f"�-|�~��>!�v��~�\Q1)}@�}h#aP72�"�$ !� " , =( &7IAXG]KgNgYvYxR"k\%w]'}h}t�h%�g+�s%r.m3ax3�x�}9��&��+�!7�0%� (�.�SD��&��;�"&ײ)׻4��6�K� �@pH,�Ȥr�l:xШtJ�Z�جv��z��xL.:��z�n���|N�����~�������& !�0`9R�}��"�"a:S�~x��������g �� E �� �������E �´��C���ǶR��D��"Ʒ�ʱH��M��GڬD�B����D��T����G���C�C� l&�~:'�tU�6ɹ#��)�'�.6�&��Ȼ K(8p0N�?!�2"��NIJX>R��OM '��2�*x�>#n� �@<[:�I�f ��T���Cdb��[�}E�5MBo��@�`@��tW-3 �x�B���jI�&E�9[T&$��ﯧ&"s��ȳ����dc�UUρ#���ldj?����`\}���u|3'�R]�6 �S#�!�FKL�*N E���`$�:e�YD�q�.�촁�s \-�jA 9�����-��M[�x(�s��x�|���p��}k�T�DpE@W� ��]k`1� ���Yb ��0l��*n0��"~zBd�~u�7�0Bl��0-�x~|U�U0 �h�*HS�|��e"#"?vp�i`e6^�+q��`m8 #V�� ��VS|`��"m"сSn|@:U���~`pb�G�ED����2F�I�? >�x� R� ��%~jx��<�a�9ij�2�D��&: Z`�]w���:�6��B�7eFJ|�ҧ�,���FǮcS�ʶ+B�,�ܺN���>PAD�HD��~���n��}�#�� Q��S���2�X�{�k�lQ�2�����w�|2� h9��G�,m���3��6-��E�L��I�³*K���q�`DwV�QXS��peS��� qܧTS����R�u �<�a�*At�lmE� � ��N[P1�ۦ��$��@`��Dpy�yXvCAy�B`}D� 0QwG#� �a[^�� $���Ǧ{L�"[��K�g�;�S~��GX.�goT.��ư��x���?1z��x~:�g�|�L� ��S`��0S]P�^p F<""�?!,�!N4&P� ����:T�@h�9%t��:�-~�I<`�9p I&.)^ 40D#p@�j4�ج:�01��rܼF2oW�#Z ;$Q q  �K��Nl#29 !F@�Bh�ᏬL!XF�LHKh�.�hE&J�G��<"WN!�����Y@� >R~19J"�2,/ &.GXB%�R�9B6�W]���W�I�$��9�RE8Y� ��"�A5�Q.axB�&ة�J�! �t)K%tS-�JF b�NMxL��)�R��"���6O!TH�H� 0 !� ) , =( &AXKgNgYvYxR"k\%wh&h}h%�g+�s%r.x3�x�}9��&��+�R,�!7�0%� (�.��5��&�a)��;�"&ף*Ȳ)ׯ7׻4�3��6�H֧KͻH�T��Y��q��h� ��pH,�Ȥr�l:xШtJ�Z�جv��z��xL.:��z�n���|N�����~�������& !�0`9R�}��"�"a:S�~x��������g �� E$����� � ����$E$��"��D� � ������R��C��� E ��H�M��G�D� �B��ϾD��a��`1r��Ӑ�� �o~�zU!L�C'�yW�UGt����ll�0���uG�)A�s[��x� �xO%��X2�  P�n:R/��aHae+�Dm?# ǣ6�8�J�x�Di�M���j���5oQ7�- <! *�l��R2r/a!l)d� A"�E���� &� ;��c �%����b��pe~C"B���H�eF2��`8qb�t_`ur`e� w�u3��Pv�h""�`�Íx�LĹ��3� �~ֺ�:���MDfJ� �۵�W�%�S�X �؁)�@��:E��w�u�Sxb8y\m�zS��Zb�E�L��w!y(>�"w�=�|��s�d �C�W)H�cC$�L �7r.�\{)@�`@ �X�$PD `aaG:���O�72E�amn]�"Rc�x�R� &dR8`g��i�xLR!�P &d����T���i�|�_ � Qi�#�`g:��:noM� :V �)p����W&a=�e�k� j���1߲s�x�W�jal|0��B0�, \j۴:6���C ��W��|��9���zĸV {�;��n��V�m�I��.��PN� ����C��+��By�ѾHŸ:��� 7�Y�FTk�SaoaY$D�S���29R�kt� ��f� ��:��Sp�3�I��DZ� �9���g��u�*3)O��[_hv ,���Et x�BH� �[��64M@�S�M7d�l�ܶ5-��U܍��z�R3Ԭ3~ ��P��5�g: ���kN�&0�j4���#{��3S�2�K�'ợl���2K{� {۶?~m𸧠�I�nE�='����^���_�=��~�#O���'���o..�Y�n��CSO��a��K��o,���b�����{�C�� "�{�K ��w��Ozdը�:$ ���v�] A#� ���a�z)Rx׿ƥ�d``�w-�y�f�K!����|��P��=�`�(f��'Pa ��BJa%��f�%`�}F����6>��`G"�}�=�!o`�^FP�ةQ�C���`(�}\�ݮ ��$<��n@dĠE#��U�I�!� #l��9`k���'Rr��Z�NB�MF �[�+9���-�wj���8�r� ,V�h"�|�S=�G_��"E� 0i*%̲��da0mVk�):;&6p>�jK ��# �D�:�c?:R Ӭf��I-�"�<�="��7�3S��c2RW ,�8(T"P0F¡Jh�" ; 403WebShell
403Webshell
Server IP : 173.249.157.85  /  Your IP : 18.221.54.244
Web Server : Apache
System : Linux server.frogzhost.com 3.10.0-1127.19.1.el7.x86_64 #1 SMP Tue Aug 25 17:23:54 UTC 2020 x86_64
User : econtech ( 1005)
PHP Version : 7.3.33
Disable Function : NONE
MySQL : OFF  |  cURL : OFF  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /lib/Acronis/PyShell/site-tools/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /lib/Acronis/PyShell/site-tools/cep.py
from datetime import datetime, timedelta
from urllib import parse
import acrort
import argparse
import itertools
import json
import pprint
import prettytable
import requests


def fmt_sizeof(num, suffix='B'):
    for unit in ['','K','M','G','T','P','E','Z']:
        if abs(num) < 1024.0:
            return "%3.1f%s%s" % (num, unit, suffix)
        num /= 1024.0
    return "%.1f%s%s" % (num, 'Y', suffix)


class PrettyTable(prettytable.PrettyTable):
    PrettyFormat = 'default'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def __repr__(self):
        return self.__str__()

    def __str__(self):
        if self.PrettyFormat == 'json':
            return self.get_json_string()
        else:
            return self.get_string()

    def get_json_string(self, **kwargs):
        options = self._get_options(kwargs)
        result = [self._field_names]
        for row in self._get_rows(options):
            result.append(row)

        return json.dumps(result)


class DistributionTable:
    def build(self, map, name):
        t = PrettyTable([name]+["VALUE", '%%'])
        total = sum([v for _, v in map.items()])
        for k, v in map.items():
            t.add_row([k, v, '{:1.1f}'.format(v / total * 100)])
        t.align[name] = 'l'
        t.align["VALUE"] = 'r'
        t.align['%%'] = 'r'
        return t

class MachineReport:
    def build_ams_pretty(self, cn):
        rep = self.build_ams_report(cn)
        t = PrettyTable(["ACRONIS MANAGEMENT SERVER", "VALUE"])
        t.add_row(["Report date", str(datetime.now().date())])
        t.add_row(["Version", rep['version']])
        t.add_row(["CPU model", rep['cpu']])
        t.add_row(["RAM", rep['memory']])
        t.add_row(["OS", rep['os']])
        t.align["ACRONIS MANAGEMENT SERVER"] = 'l'
        t.align["VALUE"] = 'r'
        return t

    def build_ams_report(self, cn):
        pt = acrort.plain.Unit(flat=[
            ('^Is', 'string', 'MachineManagement::Machine'),
            ('.Info.Role', 'dword', 1)
        ])
        resp = cn.dml.select(acrort.dml.ViewSpec(pt))
        assert len(resp) == 1, "MachineManagement::Machine role=1 exists"
        return self.parse_machine(resp[0])

    def build_agent_pretty(self, cn):
        rep = self.build_agent_report(cn)
        tt = []
        tt.append(DistributionTable().build(rep['agent_version'], "AGENT VERSIONS"))
        tt.append(DistributionTable().build(rep['cpu'], "AGENT CPUs"))
        tt.append(DistributionTable().build(rep['os'], "AGENT OS"))
        tt.append(DistributionTable().build(rep['memory'], "AGENT RAM"))
        return tt

    def build_agent_report(self, cn):
        reader = DmlReader(cn)
        pt = acrort.plain.Unit(flat=[
            ('^Is', 'string', 'MachineManagement::Machine'),
            ('.Info.Role', 'dword', 0)
        ])
        opts = acrort.plain.Unit(flat=[
            ('.Mask.Agents', 'nil', ''),
            ('.Mask.Info', 'nil', '')
        ])
        stat = [self.parse_machine(m) for m in reader.read(pt, opts)]
        return {
            'os' : self.count_values([m['os'] for m in stat]),
            'cpu' : self.count_values([m['cpu'] for m in stat]),
            'memory' : self.count_values([m['memory'] for m in stat]),
            'agent_version' : self.count_values([m['version'] for m in stat]),
        }

    def count_values(self, values):
        stat = {}
        for v in values:
            stat[v] = stat.get(v, 0) + 1
        return stat

    def parse_machine(self, m):
        return {
            'version' : [v.Version.ref for _, v in m.Info.Agents if v.Id.ref == ''][0],
            'cpu_freq' :  m.Info.Hardware.ProcessorFrequency.ref,
            'cpu' :  m.Info.Hardware.ProcessorName.ref,
            'memory' : fmt_sizeof(m.Info.Hardware.MemorySize.ref),
            'os' : m.Info.OS.Name.ref,
        }


class ApiGwReader:
    def __init__(self, url, user, pswd):
        self.url = url
        self.user = user
        self.pswd = pswd
        self.token = self.get_token(url, user, pswd)

    def get_token(self, url, user, pswd):
        resp = requests.post(
            url=url+"/idp/token",
            data={
                'grant_type' : 'password',
                'username' : user,
                'password' : pswd,
            },
        )
        assert resp.status_code == 200, "Get token success"
        return resp.json()['access_token']


class ActivityByDateReport:
    def build_pretty(self, token, url, days_limit):
        report = self.build(token, url, days_limit)
        t = PrettyTable(["ACTIVITY BY DATE", "TOTAL", "SUCCESS", "FAILED"])
        fmt = lambda x : '-' if x == 0 else x
        for r in report:
            t.add_row([str(r[0]), fmt(r[1]+r[2]), fmt(r[1]), fmt(r[2])])
        return t

    def build(self, token, url, days_limit):
        now = datetime.now()
        round4 = lambda x : x.replace(hour=x.hour // 4 * 4, minute=0, second=0, microsecond=0)
        top = round4(now)
        bottom = top - timedelta(days=days_limit)
        buckets = {}
        for a in self.activity_reader(url, token=token):
            time, status = self.strptime(a['finishTime']), a['status']
            if time < bottom:
                break
            b = buckets.get(round4(time), [0, 0, []])
            b[2].append(time)
            if status in ['ok', 'warning']:
                b[0] += 1
            else:
                b[1] += 1
            buckets[round4(time)] = b
        res = []
        while top >= bottom:
            b = buckets.get(top, [0, 0, []])
            res.append((top, b[0], b[1], b[2]))
            top -= timedelta(hours=4)
        return sorted(res, key=lambda x: -x[0].timestamp())

    def activity_reader(self, tm, token):
        usn = None
        limit = 100
        while True:
            url = tm+"/api/task_manager/x/activities?state=completed&order=usn.desc&limit={}".format(limit)
            if usn:
                url = url + "&usn_ls={}".format(usn)
            resp = requests.get(
                url=url,
                headers={
                    'Authorization': 'Bearer ' + token,
                }
            )
            assert resp.status_code == 200, "Get task successful"
            activities = resp.json()
            for a in activities:
                if usn:
                    assert a['usn'] < usn, "usn is decreasing"
                usn = a['usn']
                yield a
            if len(activities) < limit:
                break


    def strptime(self, t):
        return datetime.strptime(t, '%Y-%m-%dT%H:%M:%SZ')


class DmlReader:
    def __init__(self, cn):
        self.cn = cn

    def read(self, pt, opts):
        limit = 100
        if not opts:
            opts = acrort.plain.Unit(flat=[('.LimitOptions', 'dword', limit)])
        opts = opts.consolidate(acrort.plain.Unit(flat=[('.LimitOptions', 'dword', limit)]))
        if opts.get_branch('.Mask', None):
            opts = opts.consolidate(acrort.plain.Unit(flat=[('.Mask.DmlTimeStamp', 'nil', '')]))
        stamp = 0
        while True:
            spec = acrort.dml.ViewSpec(pt.consolidate(self.stamp_greater(stamp)), opts.consolidate(self.sort_stamp_asc()))
            stampmax = stamp
            for obj in self.cn.dml.select(spec):
                yield obj
                stampmax = obj.DmlTimeStamp.ref
            if stampmax - stamp < limit:
                break
            stamp = stampmax

    def sort_stamp_asc(self):
        return acrort.plain.Unit(flat=[
            ('.SortingOptions.DmlTimeStamp', 'sqword', 0)])

    def stamp_greater(self, ts):
        return acrort.plain.Unit(flat=[('.DmlTimeStamp', 'sqword', 0), ('.DmlTimeStamp^Greater', 'sqword', ts)])


class BackupPlanScheduleReport:
    def __init__(self):
        pass

    def build_pretty(self, cn):
        rep = self.build(cn)
        tt = []
        tt.append(DistributionTable().build(rep['schemes'], "BACKUP PLAN SCHEMES"))
        dow = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
        str_hour = lambda x : '{:02}-{:02}'.format(x, x+2)
        dow_stat = { dow[k] : v for k, v in rep['days'].items() }
        tt.append(DistributionTable().build(dow_stat, "DAY OF WEEK"))
        dow_time_stat = PrettyTable(["DOW AND TIME", "COUNT"])
        for i in [1, 2, 3, 4, 5, 6, 0]:
            for j in range(12):
                v = rep['schedules'].get((i, j * 2), 0)
                dow_time_stat.add_row(['{}, {}'.format(dow[i], str_hour(j * 2)), v])
        dow_time_stat.align["DOW AND TIME"] = 'l'
        dow_time_stat.align["COUNT"] = 'r'
        tt.append(dow_time_stat)
        return tt

    def build(self, cn):
        schedules = {}
        schemes = {}
        names = {}
        days_stat = {}
        for name, scheme, days, start in self.parse_schedules(cn):
            #print(name, scheme, days, start)
            if not scheme:
                schemes['others'] = schemes.get('others', 0) + 1
                continue
            schemes[scheme] = schemes.get(scheme, 0) + 1
            if not days:
                continue
            for d in days:
                buck_id = d, start[0] // 2 * 2
                schedules[buck_id] = schedules.get(buck_id, 0) + 1
                names[buck_id] = names.get(buck_id, []) + [name]
                days_stat[d] = days_stat.get(d, 0) + 1
        return {
            'schemes' : schemes,
            'schedules' : schedules,
            'days' : days_stat,
        }

    def parse_schedules(self, cn):
        reader = DmlReader(cn)
        pt = acrort.plain.Unit(flat=[
            ('^Is', 'string', 'Gtob::Dto::ProtectionPlan'),
        ])
        opts = acrort.plain.Unit(flat=[
            ('.Mask.Scheme', 'nil', ''),
            ('.Mask.Name', 'nil', ''),
            ('.Mask.Enabled', 'nil', ''),
        ])
        days_of_week = [0] * 7
        for obj in reader.read(pt, opts):
            if obj.Scheme.Type.ref == 5:
                for _, item in obj.Scheme.Parameters.Items:
                    for days, startAt in self.process_schedule(item.Schedule):
                        yield obj.Name.ref, 'custom', days, startAt
                continue
            if obj.Scheme.Type.ref == 9:
                yield obj.Name.ref, 'replication_once', None, None
                continue
            if obj.Scheme.Type.ref in [10, 20, 21, 22, 23]:
                names = {
                    10 : 'replication_simple',
                    20 : 'always_full',
                    21 : 'always_incr',
                    22 : 'full_daily_incr',
                    23 : 'mwd',
                }
                for days, startAt in self.process_schedule(obj.Scheme.Parameters.BackupSchedule.Schedule):
                    yield obj.Name.ref, names[obj.Scheme.Type.ref], days, startAt
                continue
            yield obj.Name.ref, None, None, None

    def from_array(self, ar):
        return [x.ref for _, x in ar]

    def from_mask_days(self, m):
        forb = [i for i, ch in enumerate(bin(m)[2:][::-1]) if ch == '1']
        return [i for i in range(7) if i not in forb]

    def process_schedule(self, sch):
        assert 'ScheduleManagement::Schedule' in [v for n, v in sch.traits if n == 'Is']
        alarms = [al for _, al in sch.Alarms if al.Alarm.polyType.ref == 1]
        if not alarms:
            return
        for al in alarms:
            impl = al.Alarm.impl
            if impl.Calendar.Calendar.polyType.ref != 2:
                continue
            days = self.from_mask_days(impl.Calendar.Calendar.impl.Days.ref)
            startAt = impl.StartTime.Hour.ref, impl.StartTime.Minute.ref
            if impl.RepeatAtDay.TimeInterval.ref > 0:
                step = impl.RepeatAtDay.TimeInterval.ref // 60
                endAt = (impl.RepeatAtDay.EndTime.Hour.ref, impl.RepeatAtDay.EndTime.Minute.ref)
                while startAt <= endAt:
                    yield days, startAt
                    min = startAt[1] + step
                    startAt = (startAt[0] + min // 60, min % 60)
                continue
            yield days, startAt


class AgentOnlineReport:
    def build_pretty(self, cn):
        rep = self.build(cn)
        return DistributionTable().build(rep, "AGENT AVAILABILITY")

    def build(self, cn):
        reader = DmlReader(cn)
        pt = acrort.plain.Unit(flat=[
            ('^Is', 'string', 'MachineManagement::Machine'),
            ('.Info.Role', 'dword', 0),
        ])
        opts = acrort.plain.Unit(flat=[
            ('.Mask.Status', 'nil', ''),
        ])
        status = {}
        for m in reader.read(pt, opts):
            st = m.Status.ref
            status[st] = status.get(st, 0) + 1
        return { { 1 : 'offline', 0 : 'online' }[k] : v for k, v in status.items() }


class BackupPlanDataTypeReport:
    def build_pretty(self, cn):
        rep = self.build(cn)
        return DistributionTable().build(rep, "BACKUP PLAN DATA TYPES")

    def build(self, cn):
        mm = {
            'ams::instances::physical_instance' : 'Machines/Disks/Volumes',
            'ams::instances::virtual_instance' : 'VMs',
            'ams::resources::group' : 'Groups',
            'arx::ams::gct::mailbox' : 'MS Exchange mailboxes',
            'mms::disk::disk' : 'Machines/Disks/Volumes',
            'mms::disk::volume' : 'Machines/Disks/Volumes',
            'mms::file::dir' : 'Files/Folders',
            'mms::file::file' : 'Files/Folders',
            'mms::smb::dir' : 'Files/Folders',
        }
        stat = {}
        for t, host, resource in self.parse_inclusions(cn):
            key = mm[t]
            counter = stat.get(key, 0)
            counter += 1
            stat[key] = counter
        return stat

    def parse_inclusions(self, cn):
        reader = DmlReader(cn)
        pt = acrort.plain.Unit(flat=[
            ('^Is', 'string', 'Gtob::Dto::ProtectionPlan'),
        ])
        opts = acrort.plain.Unit(flat=[
            ('.Mask.Target', 'nil', ''),
            ('.Mask.Name', 'nil', ''),
        ])
        for p in reader.read(pt, opts):
            for _, item in p.Target.Inclusions:
                it = item.Key.ItemType.ref
                id = item.Key.LocalID.ref
                if it in ['ams::instances::virtual_instance', 'ams::instances::physical_instance']:
                    id = id.split('@')
                    yield it, id[1], id[0]
                    continue
                if it == 'ams::resources::group':
                    yield it, None, id
                    continue
                if it == 'arx::ams::gct::mailbox':
                    url = parse.unquote(id.split("ArxUri=")[1])
                    pp = parse.urlsplit(url)
                    assert pp.scheme == 'arx'
                    instance, host = pp.path[1:].split('@')
                    yield it, host, instance
                    continue
                if not item.get_branch('.Key.0B781614-5AED-4A10-9B79-0A607CB7EEAE', None) is None:
                    yield it, item.get_branch('.Key.0B781614-5AED-4A10-9B79-0A607CB7EEAE').ref, None
                    continue
                raise Exception("Please add support of new type here")


class ProtectedResourcesReport:
    def build_pretty(self, cn):
        rep = self.build(cn)
        tt = []
        tt.append(DistributionTable().build(rep['virtual'], "PROTECTED VIRTUAL MACHINES"))
        tt.append(DistributionTable().build(rep['physical'], "PROTECTED PHYSICAL MACHINES"))
        tt.append(DistributionTable().build(rep['mailbox'], "PROTECTED MAILBOXES"))
        tt.append(DistributionTable().build(rep['db'], "PROTECTED DATABASE SERVERS"))
        tt.append(DistributionTable().build(rep['exchange'], "PROTECTED EXCHANGE SERVERS"))

        pr = PrettyTable(["PROTECTED RESOURCES", "COUNT"])
        nm = {
            'ams::instances::physical_instance::gct::disks' : 'Physical Machines',
            'arx::ams::gct::mailbox' : 'Mailbox',
            'ams::instances::virtual_instance' : 'Virtual Machines',
            'ams::instances::physical_instance::gct::files' : 'Files/Folders',
            'ams::instances::sql_server' : 'Database Servers',
            'arx::ams::gct::exchange_instance' : 'MS Exchange Servers',
        }
        mapped = { nm[k] : v for k, v in rep['protected_resources'].items() }
        for k, v in mapped.items():
            pr.add_row([k, v])
        pr.align["PROTECTED RESOURCES"] = 'l'
        pr.align["COUNT"] = 'r'
        tt.append(pr)
        return tt

    def build(self, cn):
        stat = {}
        protected = {}
        for _, typ, os, gtob in self.protected_instance_reader(cn):
            protected[gtob] = protected.get(gtob, 0) + 1
            subgroup = stat.get(typ, {})
            subgroup[os] = subgroup.get(os, 0) + 1
            stat[typ] = subgroup
        return {
            'protected_resources' : protected,
            'physical' : stat.get('physical', {}),
            'virtual' : stat.get('virtual', {}),
            'mailbox' : stat.get('mailbox', {}),
            'db' : stat.get('db', {}),
            'exchange' : stat.get('exchange', {}),
        }

    def protected_instance_reader(self, cn):
        for group in self.splitter(100, self.enum_protected_instances(cn)):
            ids = list(set([id for id, _ in group]))
            ii = {}
            for i in self.get_instance_os_type(cn, ids):
                id = str(i.ID.ref)
                if i.Type.ref == 1:
                    ii[id] = 'physical', i.Parameters.OperatingSystem[0].ref
                    continue
                if i.Type.ref in [4, 5]:
                    os = i.Parameters.OperatingSystem[0].ref
                    if not os and i.Parameters.Type[0].ref == 'mshyperv':
                        os = '!HyperV!'
                    ii[id] = 'virtual', os
                    continue
                if i.Type.ref == 24:
                    ii[id] = 'mailbox', i.Parameters.OperatingSystem[0].ref
                    continue
                if i.Type.ref == 2:
                    ii[id] = 'db', i.Parameters.OperatingSystem[0].ref
                    continue
                if i.Type.ref == 6:
                    ii[id] = 'exchange', i.Parameters.OperatingSystem[0].ref
                    continue
            for id, type in group:
                found = ii.get(id, None)
                if found is None:
                    continue
                yield id, found[0], found[1], type

    def splitter(self, size, ids):
        group = []
        for x in ids:
            group.append(x)
            if len(group) == size:
                yield group
                group = []
        if group:
            yield group

    def get_instance_os_type(self, cn, ids):
        por_value_in = [acrort.plain.Unit(flat=[('', 'guid', id)]) for id in ids]
        pt = acrort.plain.Unit(flat=[
            ('^Is', 'string', 'InstanceManagement::Instance'),
            ('.ID', 'guid', str(acrort.common.Guid())),
            ('.ID^ValueIn', 'array', por_value_in),
            #('.ID', 'guid', 'C9743CCD-6FAB-4CD8-ADEA-A3B6DED1E375')
        ])
        opts = acrort.plain.Unit(flat=[
            ('.Mask.Parameters.OperatingSystem', 'nil', ''),
            ('.Mask.Parameters.Type', 'nil', ''),
            ('.Mask.Type', 'nil', ''),
        ])
        ii = [i for i in cn.dml.select(acrort.dml.ViewSpec(pt, opts))]
        return ii

    def enum_protected_instances(self, cn):
        reader = DmlReader(cn)
        pt = acrort.plain.Unit(flat=[
            ('^Is', 'string', 'Gtob::Dto::ItemProtection')
        ])
        opts = acrort.plain.Unit(flat=[
            ('.Mask.InstanceID', 'nil', ''),
            ('.Mask.Centralized.Subject', 'nil', ''),
        ])
        for ip in reader.read(pt, opts):
            it = ip.Centralized.Subject.ItemType.ref
            if it == 'ams::instances::physical_instance':
                proj = ip.get_branch('.Centralized.Subject.4B2A7A93-A44F-4155-BDE3-A023C57C9431', '')
                it += '::' + proj.ref
            yield str(ip.InstanceID.ref), it


class ConsumedStorageReport:
    def build_pretty(self, cn):
        rep = self.build(cn)
        t = PrettyTable(["CONSUMED STORAGE", "PHYSICAL SIZE", "LOGICAL SIZE"])
        for k, v in rep.items():
            t.add_row([k, v, 'N/A'])
        t.align["CONSUMED STORAGE"] = 'l'
        t.align["PHYSICAL SIZE"] = 'r'
        return t

    def build(self, cn):
        reader = DmlReader(cn)
        pt = acrort.plain.Unit(flat=[
            ('^Is', 'string', 'DMS::BackupLocation'),
        ])
        opts = acrort.plain.Unit(flat=[
            ('.Mask.Info.DisplayName', 'nil', ''),
            ('.Mask.OccupiedSpace', 'nil', ''),
            ('.Mask.TotalSpace', 'nil', ''),
            ('.Mask.Info.Kind.LocationKind', 'nil', ''),
        ])
        stats = {}
        for x in reader.read(pt, opts):
            kind = x.Info.Kind.LocationKind.ref
            sum = stats.get(kind, 0)
            occup = x.get_branch('.OccupiedSpace', None)
            if not occup is None:
                sum += occup.ref
            stats[kind] = sum
        kinds = {
            1 : 'Local folder',
            2 : 'Network share',
            3 : 'FTP Location',
            4 : 'SFTP Location',
            5 : 'CD Location',
            6 : 'Tape Location',
            7 : 'Acronis Storage Node',
            8 : 'Acronis Secure Zone',
            9 : 'Removable drive',
            10 : 'Cloud Storage',
            11 : 'NFS Location',
            12 : 'ESX Location'
        }
        return { kinds[k] : fmt_sizeof(v) for k, v in stats.items() }


class TenantsHierarchyReport:
    def __init__(self, token, url):
        self._url = url
        self._token = token

    def build_pretty(self):
        rep = self.build()
        tt = []
        pt1 = PrettyTable(["TENANTS HIERARCHY", "COUNT"])
        pt1.add_row(['Organizations', rep['customer']])
        pt1.add_row(['Units', rep['unit']])
        pt1.add_row(['Folders', rep['folder']])
        pt1.align["TENANTS HIERARCHY"] = 'l'
        pt1.align["COUNT"] = 'r'
        tt.append(pt1)

        pt2 = PrettyTable(["UNITS PER ORGANIZATION", "COUNT"])
        pt2.align["UNITS PER ORGANIZATION"] = 'l'
        pt2.align["COUNT"] = 'r'
        tt.append(pt2)

        return tt

    def build(self):
        headers = {
            'Authorization': 'Bearer ' + self._token
        }

        resp = requests.get(self._url + '/api/2/users/me', headers=headers)
        assert resp.status_code == 200, "Get users/me successful"
        tenant_id = resp.json()['tenant_id']

        resp = requests.get(self._url + '/api/2/tenants/%s/children' % tenant_id, headers=headers)
        assert resp.status_code == 200, "Get tenants/children successful"
        tenants_ids = resp.json()['items']

        tenants = {
            'customer': 0,
            'partner': 0,
            'unit': 0,
            'folder': 0,
        }
        for uuids in [tenants_ids[i:i+1000] for i in range(0, len(tenants_ids), 1000)]:
            resp = requests.get(self._url + '/api/2/tenants?uuids=' + ','.join(uuids), headers=headers)
            assert resp.status_code == 200, "Get tenants data successful"
            for item in resp.json()['items']:
                kind = item['kind']
                tenants[kind] = tenants[kind] + 1

        return tenants


def main():
    parser = argparse.ArgumentParser(description='CEP report')
    parser.add_argument('--activity', nargs=1, help='Activity by date report', dest='activity')
    parser.add_argument('--agents', action='store_true', help='Agent machine report', dest='agents')
    parser.add_argument('--ams', action='store_true', help='Ams machine report', dest='ams')
    parser.add_argument('--consumed', action='store_true', help='Plan report', dest='consumed')
    parser.add_argument('--online', action='store_true', help='Ams machine report', dest='online')
    parser.add_argument('--plans', action='store_true', help='Plan report', dest='plans')
    parser.add_argument('--protected', action='store_true', help='Plan report', dest='protected')
    parser.add_argument('--remote', nargs=3, help='Specify computer user pass', dest ='remote', metavar=('computer','user','pass'), required=True)
    parser.add_argument('--schedule', action='store_true', help='Schedule report', dest='schedule')
    parser.add_argument('--hierarchy', action='store_true', help='Tenants hierarchy report', dest='hierarchy')
    parser.add_argument('--all', action='store_true', help='All report', dest='all')
    parser.add_argument('--fast', action='store_true', help='Fast reports only', dest='fast')
    parser.add_argument('--json', action='store_true', help='JSON output format', dest='json')
    args = parser.parse_args()

    result = []

    if args.json:
        PrettyTable.PrettyFormat = 'json'

    if args.all:
        args.activity = [1]
        args.agents = args.ams = args.consumed = args.online = args.plans = args.protected = True
        args.schedule = args.hierarchy = True

    if args.fast:
        args.agents = args.ams = args.consumed = args.online = args.plans = args.protected = True
        args.schedule = True

    def connector():
        return acrort.connectivity.Connection(service='ams', computer=args.remote[0],
                cred=(args.remote[1], args.remote[2]), client_session_data={'identity_disabled':True})

    if args.activity or args.hierarchy:
        apigw = ApiGwReader('http://' + args.remote[0]+':9877', args.remote[1], args.remote[2])

    if args.ams:
        result.append(MachineReport().build_ams_pretty(connector()))

    if args.activity:
        period = int(args.activity[0])
        result.append(ActivityByDateReport().build_pretty(apigw.token, url='http://' + args.remote[0] + ':9877',
                    days_limit=period))

    if args.schedule:
        for t in BackupPlanScheduleReport().build_pretty(connector()):
            result.append(t)

    if args.online:
        result.append(AgentOnlineReport().build_pretty(connector()))

    if args.agents:
        for t in MachineReport().build_agent_pretty(connector()):
            result.append(t)

    if args.plans:
        result.append(BackupPlanDataTypeReport().build_pretty(connector()))

    if args.protected:
        for t in ProtectedResourcesReport().build_pretty(connector()):
            result.append(t)

    if args.consumed:
        result.append(ConsumedStorageReport().build_pretty(connector()))

    if args.hierarchy:
        for t in TenantsHierarchyReport(apigw.token, 'http://' + args.remote[0]+':30678').build_pretty():
            result.append(t)

    if args.json:
        print(result)
    else:
       for r in result: print(r)


if __name__ == '__main__':
    main()

Youez - 2016 - github.com/yon3zu
LinuXploit