def __init__(self, *args, **kwargs):
     """
     Initializes an AlbaNode, setting up its additional helpers
     """
     DataObject.__init__(self, *args, **kwargs)
     self._frozen = False
     self.client = ASDManagerClient(self)
     self._frozen = True
class AlbaNode(DataObject):
    """
    The AlbaNode contains information about nodes (containing OSDs)
    """
    __properties = [Property('ip', str, doc='IP Address'),
                    Property('port', int, doc='Port'),
                    Property('node_id', str, doc='Alba node_id identifier'),
                    Property('username', str, doc='Username of the AlbaNode'),
                    Property('password', str, doc='Password of the AlbaNode'),
                    Property('type', ['ASD', 'SUPERMICRO'], default='ASD', doc='The type of the AlbaNode')]
    __relations = [Relation('storagerouter', StorageRouter, 'alba_nodes', mandatory=False, doc='StorageRouter hosting the AlbaNode')]
    __dynamics = [Dynamic('ips', list, 3600),
                  Dynamic('all_disks', list, 5)]

    def __init__(self, *args, **kwargs):
        """
        Initializes an AlbaNode, setting up its additional helpers
        """
        DataObject.__init__(self, *args, **kwargs)
        self._frozen = False
        self.client = ASDManagerClient(self)
        self._frozen = True

    def _ips(self):
        """
        Returns the IPs of the node
        """
        return EtcdConfiguration.get('/ovs/alba/asdnodes/{0}/config/network|ips'.format(self.node_id))

    def _all_disks(self):
        """
        Returns a live list of all disks on this node
        """
        try:
            disks = self.client.get_disks(reraise=True)
        except (requests.ConnectionError, requests.Timeout):
            from ovs.dal.lists.albabackendlist import AlbaBackendList
            disks = []
            asd_ids = []
            for backend in AlbaBackendList.get_albabackends():
                # All backends of this node
                config = 'etcd://127.0.0.1:2379/ovs/arakoon/{0}-abm/config'.format(backend.name)
                osds = AlbaCLI.run('list-all-osds', config=config, as_json=True)
                for osd in osds:
                    node_id = osd.get('node_id')
                    asd_id = osd.get('long_id')
                    decommissioned = osd.get('decommissioned')
                    if node_id == self.node_id and asd_id not in asd_ids and decommissioned is False:
                        asd_ids.append(asd_id)
                        disks.append({'asd_id': asd_id,
                                      'node_id': osd.get('node_id'),
                                      'port': osd.get('port'),
                                      'ips': osd.get('ips'),
                                      'available': False,
                                      'state': {'state': 'error', 'detail': 'nodedown'},
                                      'log_level': 'info',
                                      'device': asd_id,
                                      'home': asd_id,
                                      'mountpoint': asd_id,
                                      'name': asd_id,
                                      'usage': {'available': 0, 'size': 0, 'used': 0}})
        return disks
class AlbaNode(DataObject):
    """
    The AlbaNode contains information about nodes (containing OSDs)
    """
    __properties = [Property('ip', str, unique=True, doc='IP Address'),
                    Property('port', int, doc='Port'),
                    Property('node_id', str, unique=True, doc='Alba node_id identifier'),
                    Property('username', str, doc='Username of the AlbaNode'),
                    Property('password', str, doc='Password of the AlbaNode'),
                    Property('type', ['ASD'], default='ASD', doc='The type of the AlbaNode'),
                    Property('package_information', dict, mandatory=False, default={}, doc='Information about installed packages and potential available new versions')]
    __relations = [Relation('storagerouter', StorageRouter, 'alba_node', onetoone=True, mandatory=False, doc='StorageRouter hosting the AlbaNode')]
    __dynamics = [Dynamic('storage_stack', dict, 5),
                  Dynamic('ips', list, 3600)]

    def __init__(self, *args, **kwargs):
        """
        Initializes an AlbaNode, setting up its additional helpers
        """
        DataObject.__init__(self, *args, **kwargs)
        self._frozen = False
        self.client = ASDManagerClient(self)
        self._frozen = True

    def _storage_stack(self):
        """
        Returns a live list of all disks known to this AlbaNode
        """
        storage_stack = {'status': 'ok',
                         'stack': {}}
        stack = storage_stack['stack']

        try:
            disk_data = self.client.get_disks()
        except (requests.ConnectionError, requests.Timeout, InvalidCredentialsError):
            storage_stack['status'] = 'nodedown'
            disk_data = {}
        partition_device_map = {}
        for disk_id, disk_info in disk_data.iteritems():
            entry = {'name': disk_id,
                     'asds': {}}
            entry.update(disk_info)
            if disk_info['state'] == 'ok':
                entry['status'] = 'uninitialized' if disk_info['available'] is True else 'initialized'
                entry['status_detail'] = ''
            else:
                entry['status'] = disk_info['state']
                entry['status_detail'] = disk_info.get('state_detail', '')
            stack[disk_id] = entry
            if 'partition_aliases' in disk_info:
                for partition_alias in disk_info['partition_aliases']:
                    partition_device_map[partition_alias] = disk_id
            else:
                partition_device_map[disk_id] = disk_id

        # Model Disk information
        for disk in self.disks:
            found = False
            for disk_id, disk_info in stack.iteritems():
                if any(alias in disk.aliases for alias in disk_info['aliases']):
                    found = True
            if found is False and len(disk.aliases) > 0:
                disk_id = disk.aliases[0].split('/')[-1]
                stack[disk_id] = {'available': False,
                                  'name': disk_id,
                                  'asds': {},
                                  'status': 'error',
                                  'status_detail': 'missing',
                                  'aliases': disk.aliases,
                                  'device': disk.aliases[0],
                                  'partition_aliases': [],
                                  'node_id': self.node_id}

        # Live ASD information
        try:
            asd_data = self.client.get_asds()
        except (requests.ConnectionError, requests.Timeout, InvalidCredentialsError):
            storage_stack['status'] = 'nodedown'
            asd_data = {}
        for partition_id, asds in asd_data.iteritems():
            if partition_id not in partition_device_map:
                continue
            disk_id = partition_device_map.get(partition_id)
            if disk_id is not None and disk_id in stack:
                for asd_id, asd_info in asds.iteritems():
                    stack[disk_id]['asds'][asd_id] = {'asd_id': asd_id,
                                                      'status': 'error' if asd_info['state'] == 'error' else 'initialized',
                                                      'status_detail': asd_info.get('state_detail', ''),
                                                      'state': asd_info['state'],
                                                      'state_detail': asd_info.get('state_detail', '')}
        return storage_stack

    def _ips(self):
        """
        Returns the IPs of the node
        """
        return Configuration.get('/ovs/alba/asdnodes/{0}/config/network|ips'.format(self.node_id))
    def test_asd_statistics(self):
        """
        Validates whether the ASD statistics work as expected.
        * Add keys that were not passed in
        * Collapse certain keys
        * Calculate correct per-second, average, total, min and max values
        """
        expected_0 = {'statistics': {'max': 0, 'n_ps': 0, 'min': 0, 'avg': 0, 'n': 0},
                      'range': {'max': 0, 'n_ps': 0, 'min': 0, 'avg': 0, 'n': 0},
                      'range_entries': {'max': 0, 'n_ps': 0, 'min': 0, 'avg': 0, 'n': 0},
                      'multi_get': {'max': 10, 'n_ps': 0, 'min': 1, 'avg': 13, 'n': 5},
                      'apply': {'max': 5, 'n_ps': 0, 'min': 5, 'avg': 5, 'n': 1},
                      'timestamp': None}
        expected_1 = {'statistics': {'max': 0, 'n_ps': 0, 'min': 0, 'avg': 0, 'n': 0},
                      'range': {'max': 0, 'n_ps': 0, 'min': 0, 'avg': 0, 'n': 0},
                      'range_entries': {'max': 0, 'n_ps': 0, 'min': 0, 'avg': 0, 'n': 0},
                      'multi_get': {'max': 10, 'n_ps': 1, 'min': 1, 'avg': 12.5, 'n': 10},
                      'apply': {'max': 5, 'n_ps': 0, 'min': 5, 'avg': 5, 'n': 1},
                      'timestamp': None}
        base_time = time.time()
        backend_type = BackendType()
        backend_type.code = 'alba'
        backend_type.name = 'ALBA'
        backend_type.save()
        backend = Backend()
        backend.name = 'foobar'
        backend.backend_type = backend_type
        backend.save()
        alba_backend = AlbaBackend()
        alba_backend.backend = backend
        alba_backend.save()
        alba_node = AlbaNode()
        alba_node.ip = '127.0.0.1'
        alba_node.port = 8500
        alba_node.username = '******'
        alba_node.password = '******'
        alba_node.node_id = 'foobar'
        alba_node.save()
        alba_disk = AlbaDisk()
        alba_disk.name = 'foo'
        alba_disk.alba_node = alba_node
        alba_disk.save()
        asd = AlbaASD()
        asd.asd_id = 'foo'
        asd.alba_backend = alba_backend
        asd.alba_disk = alba_disk
        asd.save()
        service_type = ServiceType()
        service_type.name = 'AlbaManager'
        service_type.save()
        service = Service()
        service.name = 'foobar'
        service.type = service_type
        service.ports = []
        service.save()
        abm_service = ABMService()
        abm_service.service = service
        abm_service.alba_backend = alba_backend
        abm_service.save()

        asdmanager_client = ASDManagerClient('')
        asdmanager_client._results['get_disks'] = []
        AlbaCLI._run_results['asd-multistatistics'] = {'foo': {'success': True,
                                                               'result': {'Apply': {'n': 1, 'avg': 5, 'min': 5, 'max': 5},
                                                                          'MultiGet': {'n': 2, 'avg': 10, 'min': 5, 'max': 10},
                                                                          'MultiGet2': {'n': 3, 'avg': 15, 'min': 1, 'max': 5}}}}
        statistics = asd._statistics(AlbaASD._dynamics[0])
        expected_0['timestamp'] = base_time
        self.assertDictEqual(statistics, expected_0, 'The first statistics should be as expected: {0} vs {1}'.format(statistics, expected_0))
        time.sleep(5)
        asdmanager_client._results['get_disks'] = []
        AlbaCLI._run_results['asd-multistatistics'] = {'foo': {'success': True,
                                                               'result': {'Apply': {'n': 1, 'avg': 5, 'min': 5, 'max': 5},
                                                                          'MultiGet': {'n': 5, 'avg': 10, 'min': 5, 'max': 10},
                                                                          'MultiGet2': {'n': 5, 'avg': 15, 'min': 1, 'max': 5}}}}
        statistics = asd._statistics(AlbaASD._dynamics[0])
        expected_1['timestamp'] = base_time + 5
        self.assertDictEqual(statistics, expected_1, 'The second statistics should be as expected: {0} vs {1}'.format(statistics, expected_1))