def handle_delete(self, request, user, *args, **kwargs):
        """
        Handles DELETE requests to create a relationship of Environment with EnvironmentVip.

        URL: environment/<environment_id>/environmentvip/<environment_vip_id>/
        """

        self.log.info("Remove a relationship of Environment with EnvironmentVip")

        try:

            # Commons Validations

            # User permission
            if not has_perm(user, AdminPermission.ENVIRONMENT_MANAGEMENT, AdminPermission.WRITE_OPERATION):
                self.log.error(u"User does not have permission to perform the operation.")
                raise UserNotAuthorizedError(None)

            # Valid Environment
            environment_id = kwargs.get("environment_id")
            if not is_valid_int_greater_zero_param(environment_id):
                self.log.error(u"The environment_id parameter is not a valid value: %s.", environment_id)
                raise InvalidValueError(None, "environment_id", environment_id)

            # Valid EnvironmentVip ID
            environment_vip_id = kwargs.get("environment_vip_id")
            if not is_valid_int_greater_zero_param(environment_vip_id):
                self.log.error(u"The id_environment_vip parameter is not a valid value: %s.", environment_vip_id)
                raise InvalidValueError(None, "environment_vip_id", environment_vip_id)

            # Business Validations

            # Existing Environment ID
            environment = Ambiente.get_by_pk(environment_id)
            # Existing EnvironmentVip ID
            environment_vip = EnvironmentVip.get_by_pk(environment_vip_id)
            # Business Rules
            environment_environment_vip = EnvironmentEnvironmentVip().get_by_environment_environment_vip(
                environment.id, environment_vip.id
            )
            server_pool_list = EnvironmentEnvironmentVip.get_server_pool_member_by_environment_environment_vip(
                environment_environment_vip
            )

            # Valid integraty between environment/environmentvip related with reals
            # if exists reals fot this environment then raise a exception
            if server_pool_list:
                raise EnvironmentEnvironmentServerPoolLinked({"environment": environment.name})

            # Delete
            environment_environment_vip.delete()

            # Return nothing
            return self.response(dumps_networkapi({}))

        except UserNotAuthorizedError:
            return self.not_authorized()
        except InvalidValueError, e:
            return self.response_error(269, e.param, e.value)
def _reals_can_associate_server_pool_by_environment_vip_on_request_vip(server_pool, server_pool_member_list, environment_vip):

    try:
        environment_list_related = EnvironmentEnvironmentVip.get_environment_list_by_environment_vip(environment_vip)

        ipv4_list, ipv6_list = [], []

        for server_pool_member in server_pool_member_list:
            if server_pool_member.ip:
                ipv4_list.append(server_pool_member.ip)
            else:
                ipv6_list.append(server_pool_member.ipv6)

        for ipv4 in ipv4_list:
            environment = Ambiente.objects.filter(vlan__networkipv4__ip=ipv4).uniqueResult()
            if environment not in environment_list_related:
                raise api_exceptions.EnvironmentEnvironmentVipNotBoundedException(
                    error_messages.get(396) % (environment.name, ipv4.ip_formated, environment_vip.name)
                )

        for ipv6 in ipv6_list:
            environment = Ambiente.objects.filter(vlan__networkipv6__ipv6=ipv6).uniqueResult()
            if environment not in environment_list_related:
                raise api_exceptions.EnvironmentEnvironmentVipNotBoundedException(
                    error_messages.get(396) % (server_pool.environment.name, ipv6.ip_formated, environment_vip.name)
                )

    except Exception, error:
        log.error(error)
        raise error
Exemple #3
0
def _reals_can_associate_server_pool_by_environment_vip_on_request_vip(server_pool, server_pool_member_list, environment_vip):

    try:
        environment_list_related = EnvironmentEnvironmentVip.get_environment_list_by_environment_vip(
            environment_vip)

        ipv4_list, ipv6_list = [], []

        for server_pool_member in server_pool_member_list:
            if server_pool_member.ip:
                ipv4_list.append(server_pool_member.ip)
            else:
                ipv6_list.append(server_pool_member.ipv6)

        for ipv4 in ipv4_list:
            environment = Ambiente.objects.filter(
                vlan__networkipv4__ip=ipv4).uniqueResult()
            if environment not in environment_list_related:
                raise api_exceptions.EnvironmentEnvironmentVipNotBoundedException(
                    error_messages.get(396) % (
                        environment.name, ipv4.ip_formated, environment_vip.name)
                )

        for ipv6 in ipv6_list:
            environment = Ambiente.objects.filter(
                vlan__networkipv6__ipv6=ipv6).uniqueResult()
            if environment not in environment_list_related:
                raise api_exceptions.EnvironmentEnvironmentVipNotBoundedException(
                    error_messages.get(396) % (
                        server_pool.environment.name, ipv6.ip_formated, environment_vip.name)
                )

    except Exception, error:
        log.error(error)
        raise error
def valid_to_save_reals_v2(pools):
    """
    Valid values of pool member

    """

    for pool in pools:

        ids = [p['id'] for p in pool['server_pool_members'] if p['id']]
        db_members = ServerPoolMember.objects.filter(id__in=ids)
        db_members_id = [str(s.id) for s in db_members]

        # verify if member is invalid
        for member in pool['server_pool_members']:
            if member['id']:
                if str(member['id']) not in db_members_id:
                    raise exceptions.InvalidRealPoolException()

        # verify if port is invalid
        invalid_ports_real = [member['port_real'] for member in pool['server_pool_members'] if int(member['port_real']) > 65535 or int(member['port_real']) < 1]

        # verify if priority is invalid
        invalid_priority = [member['priority'] for member in pool['server_pool_members'] if int(member['priority']) > 4294967295 or int(member['priority']) < 0]

        # verify if pool member is duplicate
        ips_ports = [(member['port_real'], member['ip']['id'] if member['ip'] else member['ipv6']['id']) for member in pool['server_pool_members']]

        environment_vip_list = EnvironmentVip.get_environment_vips_by_environment_id(pool['server_pool']['environment']['id'])

        environment_vip_list_name = ', '.join([envvip.name for envvip in environment_vip_list])

        environment_list_related = EnvironmentEnvironmentVip.get_environment_list_by_environment_vip_list(environment_vip_list)

        for members in pool['server_pool_members']:
            if members['ip']:
                environment = Ambiente.objects.filter(vlan__networkipv4__ip=members['ip']['id']).uniqueResult()
                if environment not in environment_list_related:
                    raise api_exceptions.EnvironmentEnvironmentVipNotBoundedException(
                        error_messages.get(396) % (environment.name, members['ip']['ip_formated'], environment_vip_list_name)
                    )

        for members in pool['server_pool_members']:
            if members['ipv6']:
                environment = Ambiente.objects.filter(vlan__networkipv6__ipv6=members['ipv6']['id']).uniqueResult()
                if environment not in environment_list_related:
                    raise api_exceptions.EnvironmentEnvironmentVipNotBoundedException(
                        error_messages.get(396) % (environment.name, members['ipv6']['ip_formated'], environment_vip_list_name)
                    )

    if invalid_ports_real:
        raise exceptions.InvalidRealPoolException(
            'O número da porta deve estar entre 1 e 65535.')

    if invalid_priority:
        raise exceptions.InvalidRealPoolException(
            'O valor da Prioridade deve estar entre 0 e 4294967295.')

    if len(ips_ports) != len(set(ips_ports)):
        raise exceptions.InvalidRealPoolException(
            'Ips com portas iguais.')

    # if len(id_equips) != len(id_pool_member):
    #     raise exceptions.InvalidRealPoolException(
    #         'Quantidade de portas e equipamento difere.')

    # load data in variables for compare db with json
    pls = ServerPool.objects.filter(id__in=[pool['server_pool']['id'] for pool in pools])
    ps = {}
    for p in pools:
        ps[str(p['server_pool']['id'])] = p
    sp = {}
    for p in pls:
        sp[str(p.id)] = p

        # q_filters = list()
        # for members in pool['server_pool_members']:
        #     if members['id']:
        #         q_filters.append({
        #             'port_real': members['port_real'],
        #             'id': members['id']
        #         })

        # if len(q_filters)>0:
        #     members_par = ServerPoolMember.objects.filter(
        #         reduce(lambda x, y: x | y, [Q(**q_filter) for q_filter in q_filters]))
        # else:
        #     members_par = list()

        # members_all = ServerPoolMember.objects.filter(server_pool__id=p.id)

        # if len(members_par) != len(members_all) and p.pool_created:
        #     raise exceptions.PoolMemberChange(p.identifier)

    # return error when change names in pool created
    change_name = [sp[p].identifier for idx, p in enumerate(ps) if sp[p].identifier != ps[str(p)]['server_pool']['identifier'] and sp[p].pool_created]
    if len(change_name) > 0:
        raise exceptions.PoolNameChange(','.join(change_name))

    # return error when change environments in pool created
    change_env = [sp[p].identifier for idx, p in enumerate(ps) if str(sp[p].environment.id) != str(ps[str(p)]['server_pool']['environment']['id']) and sp[p].pool_created]
    change_env_all = [sp[p].id for idx, p in enumerate(ps) if str(sp[p].environment.id) != str(ps[str(p)]['server_pool']['environment']['id'])]
    change_real = ServerPoolMember.objects.filter(server_pool_id__in=change_env_all)

    if len(change_env) > 0 or len(change_real) > 0:
        raise exceptions.PoolEnvironmentChange(','.join(change_env))

    return ps, sp
    def handle_get(self, request, user, *args, **kwargs):
        """Handles POST requests to find all Equipments by search parameters.

        URLs: /equipment/find/
        """

        self.log.info('Find all Equipments')

        try:

            # Commons Validations

            # User permission
            if not has_perm(user, AdminPermission.EQUIPMENT_MANAGEMENT, AdminPermission.READ_OPERATION):
                self.log.error(
                    u'User does not have permission to perform the operation.')
                return self.not_authorized()

            # Business Validations

            # Get data from URL GET parameters
            equip_name = kwargs.get('equip_name').strip()
            id_ambiente = kwargs.get('id_ambiente')

            # Business Rules

            # Start with alls
            ambiente = Ambiente.get_by_pk(id_ambiente)
             # Get Equipment
            equip = Equipamento.get_by_name(equip_name)

            lista_ips_equip = set()
            lista_ipsv6_equip = set()

            environment_vip_list = EnvironmentVip.get_environment_vips_by_environment_id(id_ambiente)
            environment_list_related = EnvironmentEnvironmentVip.get_environment_list_by_environment_vip_list(environment_vip_list)

            # # Get all IPV4's Equipment
            for environment in environment_list_related:
                for ipequip in equip.ipequipamento_set.select_related().all():
                    network_ipv4 = ipequip.ip.networkipv4
                    if network_ipv4.vlan.ambiente == environment:
                        lista_ips_equip.add(ipequip.ip)

            # # Get all IPV6's Equipment
            for environment in environment_list_related:
                for ipequip in equip.ipv6equipament_set.select_related().all():
                    network_ipv6 = ipequip.ip.networkipv6
                    if network_ipv6.vlan.ambiente == environment:
                        lista_ipsv6_equip.add(ipequip.ip)

            # lists and dicts for return
            lista_ip_entregue = list()
            lista_ip6_entregue = list()

            for ip in lista_ips_equip:
                dict_ips4 = dict()
                dict_network = dict()

                dict_ips4['id'] = ip.id
                dict_ips4['ip'] = "%s.%s.%s.%s" % (
                    ip.oct1, ip.oct2, ip.oct3, ip.oct4)

                dict_network['id'] = ip.networkipv4_id
                dict_network["network"] = "%s.%s.%s.%s" % (
                    ip.networkipv4.oct1, ip.networkipv4.oct2, ip.networkipv4.oct3, ip.networkipv4.oct4)
                dict_network["mask"] = "%s.%s.%s.%s" % (
                    ip.networkipv4.mask_oct1, ip.networkipv4.mask_oct2, ip.networkipv4.mask_oct3, ip.networkipv4.mask_oct4)

                dict_ips4['network'] = dict_network

                lista_ip_entregue.append(dict_ips4)

            for ip in lista_ipsv6_equip:
                dict_ips6 = dict()
                dict_network = dict()

                dict_ips6['id'] = ip.id
                dict_ips6['ip'] = "%s:%s:%s:%s:%s:%s:%s:%s" % (
                    ip.block1, ip.block2, ip.block3, ip.block4, ip.block5, ip.block6, ip.block7, ip.block8)

                dict_network['id'] = ip.networkipv6.id
                dict_network["network"] = "%s:%s:%s:%s:%s:%s:%s:%s" % (
                    ip.networkipv6.block1, ip.networkipv6.block2, ip.networkipv6.block3, ip.networkipv6.block4, ip.networkipv6.block5, ip.networkipv6.block6, ip.networkipv6.block7, ip.networkipv6.block8)
                dict_network["mask"] = "%s:%s:%s:%s:%s:%s:%s:%s" % (
                    ip.networkipv6.block1, ip.networkipv6.block2, ip.networkipv6.block3, ip.networkipv6.block4, ip.networkipv6.block5, ip.networkipv6.block6, ip.networkipv6.block7, ip.networkipv6.block8)

                dict_ips6['network'] = dict_network

                lista_ip6_entregue.append(dict_ips6)

            lista_ip_entregue = lista_ip_entregue if len(
                lista_ip_entregue) > 0 else None
            lista_ip6_entregue = lista_ip6_entregue if len(
                lista_ip6_entregue) > 0 else None


            return self.response(dumps_networkapi({'list_ipv4': lista_ip_entregue, 'list_ipv6': lista_ip6_entregue}))

        except InvalidValueError, e:
            self.log.error(
                u'Parameter %s is invalid. Value: %s.', e.param, e.value)
            return self.response_error(269, e.param, e.value)
    def handle_put(self, request, user, *args, **kwargs):
        """
        Handles PUT requests to create a relationship of Environment with EnvironmentVip.

        URL: environment/<environment_id>/environmentvip/<environment_vip_id>/
        """

        self.log.info("Create a relationship of Environment with EnvironmentVip")

        try:

            # Commons Validations

            # User permission
            if not has_perm(user, AdminPermission.ENVIRONMENT_MANAGEMENT, AdminPermission.WRITE_OPERATION):
                self.log.error(u"User does not have permission to perform the operation.")
                raise UserNotAuthorizedError(None)

            # Valid Environment
            environment_id = kwargs.get("environment_id")
            if not is_valid_int_greater_zero_param(environment_id):
                self.log.error(u"The environment_id parameter is not a valid value: %s.", environment_id)
                raise InvalidValueError(None, "environment_id", environment_id)

            # Valid EnvironmentVip ID
            environment_vip_id = kwargs.get("environment_vip_id")
            if not is_valid_int_greater_zero_param(environment_vip_id):
                self.log.error(u"The id_environment_vip parameter is not a valid value: %s.", environment_vip_id)
                raise InvalidValueError(None, "environment_vip_id", environment_vip_id)

            # Business Validations

            # Existing Environment ID
            environment = Ambiente.get_by_pk(environment_id)

            # Existing EnvironmentVip ID
            environment_vip = EnvironmentVip.get_by_pk(environment_vip_id)

            with distributedlock(LOCK_ENVIRONMENT_VIP % environment_vip_id):

                # Business Rules
                # Set new values
                environment_environment_vip = EnvironmentEnvironmentVip()
                environment_environment_vip.environment = environment
                environment_environment_vip.environment_vip = environment_vip

                # Existing EnvironmentEnvironmentVip
                environment_environment_vip.validate()

                # Persist
                environment_environment_vip.save()

                # Return XML
                environment_environment_vip_map = {}
                environment_environment_vip_map["environment_environment_vip"] = model_to_dict(
                    environment_environment_vip, fields=["id"]
                )

                return self.response(dumps_networkapi(environment_environment_vip_map))

        except UserNotAuthorizedError:
            return self.not_authorized()
        except InvalidValueError, e:
            return self.response_error(269, e.param, e.value)
Exemple #7
0
    def handle_put(self, request, user, *args, **kwargs):
        """
        Handles PUT requests to create a relationship of Environment with EnvironmentVip.

        URL: environment/<environment_id>/environmentvip/<environment_vip_id>/
        """

        self.log.info(
            'Create a relationship of Environment with EnvironmentVip')

        try:

            # Commons Validations

            # User permission
            if not has_perm(user, AdminPermission.ENVIRONMENT_MANAGEMENT,
                            AdminPermission.WRITE_OPERATION):
                self.log.error(
                    u'User does not have permission to perform the operation.')
                raise UserNotAuthorizedError(None)

            # Valid Environment
            environment_id = kwargs.get('environment_id')
            if not is_valid_int_greater_zero_param(environment_id):
                self.log.error(
                    u'The environment_id parameter is not a valid value: %s.',
                    environment_id)
                raise InvalidValueError(None, 'environment_id', environment_id)

            # Valid EnvironmentVip ID
            environment_vip_id = kwargs.get('environment_vip_id')
            if not is_valid_int_greater_zero_param(environment_vip_id):
                self.log.error(
                    u'The id_environment_vip parameter is not a valid value: %s.',
                    environment_vip_id)
                raise InvalidValueError(None, 'environment_vip_id',
                                        environment_vip_id)

            # Business Validations

            # Existing Environment ID
            environment = Ambiente.get_by_pk(environment_id)

            # Existing EnvironmentVip ID
            environment_vip = EnvironmentVip.get_by_pk(environment_vip_id)

            with distributedlock(LOCK_ENVIRONMENT_VIP % environment_vip_id):

                # Business Rules
                # Set new values
                environment_environment_vip = EnvironmentEnvironmentVip()
                environment_environment_vip.environment = environment
                environment_environment_vip.environment_vip = environment_vip

                # Existing EnvironmentEnvironmentVip
                environment_environment_vip.validate()

                # Persist
                environment_environment_vip.save()

                # Return XML
                environment_environment_vip_map = {}
                environment_environment_vip_map[
                    'environment_environment_vip'] = model_to_dict(
                        environment_environment_vip, fields=['id'])

                return self.response(
                    dumps_networkapi(environment_environment_vip_map))

        except UserNotAuthorizedError:
            return self.not_authorized()
        except InvalidValueError, e:
            return self.response_error(269, e.param, e.value)
Exemple #8
0
    def handle_delete(self, request, user, *args, **kwargs):
        """
        Handles DELETE requests to create a relationship of Environment with EnvironmentVip.

        URL: environment/<environment_id>/environmentvip/<environment_vip_id>/
        """

        self.log.info(
            'Remove a relationship of Environment with EnvironmentVip')

        try:

            # Commons Validations

            # User permission
            if not has_perm(user, AdminPermission.ENVIRONMENT_MANAGEMENT,
                            AdminPermission.WRITE_OPERATION):
                self.log.error(
                    u'User does not have permission to perform the operation.')
                raise UserNotAuthorizedError(None)

            # Valid Environment
            environment_id = kwargs.get('environment_id')
            if not is_valid_int_greater_zero_param(environment_id):
                self.log.error(
                    u'The environment_id parameter is not a valid value: %s.',
                    environment_id)
                raise InvalidValueError(None, 'environment_id', environment_id)

            # Valid EnvironmentVip ID
            environment_vip_id = kwargs.get('environment_vip_id')
            if not is_valid_int_greater_zero_param(environment_vip_id):
                self.log.error(
                    u'The id_environment_vip parameter is not a valid value: %s.',
                    environment_vip_id)
                raise InvalidValueError(None, 'environment_vip_id',
                                        environment_vip_id)

            # Business Validations

            # Existing Environment ID
            environment = Ambiente.get_by_pk(environment_id)
            # Existing EnvironmentVip ID
            environment_vip = EnvironmentVip.get_by_pk(environment_vip_id)
            # Business Rules
            environment_environment_vip = EnvironmentEnvironmentVip(
            ).get_by_environment_environment_vip(environment.id,
                                                 environment_vip.id)
            server_pool_list = EnvironmentEnvironmentVip.get_server_pool_by_environment_environment_vip(
                environment_environment_vip)

            # Check if there are any pool from this environment used in any vip
            # of this environment vip
            if server_pool_list:
                raise EnvironmentEnvironmentServerPoolLinked(
                    {'environment': environment.name})

            # Delete
            environment_environment_vip.delete()

            # Return nothing
            return self.response(dumps_networkapi({}))

        except UserNotAuthorizedError:
            return self.not_authorized()
        except InvalidValueError, e:
            return self.response_error(269, e.param, e.value)
    def handle_get(self, request, user, *args, **kwargs):
        """Handles POST requests to find all Equipments by search parameters.

        URLs: /equipment/find/
        """

        self.log.info('Find all Equipments')

        try:

            # Commons Validations

            # User permission
            if not has_perm(user, AdminPermission.EQUIPMENT_MANAGEMENT,
                            AdminPermission.READ_OPERATION):
                self.log.error(
                    u'User does not have permission to perform the operation.')
                return self.not_authorized()

            # Business Validations

            # Get data from URL GET parameters
            equip_name = kwargs.get('equip_name').strip()
            id_ambiente = kwargs.get('id_ambiente')

            # Business Rules

            # Start with alls
            ambiente = Ambiente.get_by_pk(id_ambiente)
            # Get Equipment
            equip = Equipamento.get_by_name(equip_name)

            lista_ips_equip = set()
            lista_ipsv6_equip = set()

            environment_vip_list = EnvironmentVip.get_environment_vips_by_environment_id(
                id_ambiente)
            environment_list_related = EnvironmentEnvironmentVip.get_environment_list_by_environment_vip_list(
                environment_vip_list)

            # # Get all IPV4's Equipment
            for environment in environment_list_related:
                for ipequip in equip.ipequipamento_set.select_related(
                        'ip', 'networkipv4', 'vlan').all():
                    network_ipv4 = ipequip.ip.networkipv4
                    if network_ipv4.vlan.ambiente == environment:
                        lista_ips_equip.add(ipequip.ip)

            # # Get all IPV6's Equipment
            for environment in environment_list_related:
                for ipequip in equip.ipv6equipament_set.select_related(
                        'ip', 'networkipv6', 'vlan').all():
                    network_ipv6 = ipequip.ip.networkipv6
                    if network_ipv6.vlan.ambiente == environment:
                        lista_ipsv6_equip.add(ipequip.ip)

            # lists and dicts for return
            lista_ip_entregue = list()
            lista_ip6_entregue = list()

            for ip in lista_ips_equip:
                dict_ips4 = dict()
                dict_network = dict()

                dict_ips4['id'] = ip.id
                dict_ips4['ip'] = '%s.%s.%s.%s' % (ip.oct1, ip.oct2, ip.oct3,
                                                   ip.oct4)

                dict_network['id'] = ip.networkipv4_id
                dict_network['network'] = '%s.%s.%s.%s' % (
                    ip.networkipv4.oct1, ip.networkipv4.oct2,
                    ip.networkipv4.oct3, ip.networkipv4.oct4)
                dict_network['mask'] = '%s.%s.%s.%s' % (
                    ip.networkipv4.mask_oct1, ip.networkipv4.mask_oct2,
                    ip.networkipv4.mask_oct3, ip.networkipv4.mask_oct4)

                dict_ips4['network'] = dict_network

                lista_ip_entregue.append(dict_ips4)

            for ip in lista_ipsv6_equip:
                dict_ips6 = dict()
                dict_network = dict()

                dict_ips6['id'] = ip.id
                dict_ips6['ip'] = '%s:%s:%s:%s:%s:%s:%s:%s' % (
                    ip.block1, ip.block2, ip.block3, ip.block4, ip.block5,
                    ip.block6, ip.block7, ip.block8)

                dict_network['id'] = ip.networkipv6.id
                dict_network['network'] = '%s:%s:%s:%s:%s:%s:%s:%s' % (
                    ip.networkipv6.block1, ip.networkipv6.block2,
                    ip.networkipv6.block3, ip.networkipv6.block4,
                    ip.networkipv6.block5, ip.networkipv6.block6,
                    ip.networkipv6.block7, ip.networkipv6.block8)
                dict_network['mask'] = '%s:%s:%s:%s:%s:%s:%s:%s' % (
                    ip.networkipv6.block1, ip.networkipv6.block2,
                    ip.networkipv6.block3, ip.networkipv6.block4,
                    ip.networkipv6.block5, ip.networkipv6.block6,
                    ip.networkipv6.block7, ip.networkipv6.block8)

                dict_ips6['network'] = dict_network

                lista_ip6_entregue.append(dict_ips6)

            lista_ip_entregue = lista_ip_entregue if len(
                lista_ip_entregue) > 0 else None
            lista_ip6_entregue = lista_ip6_entregue if len(
                lista_ip6_entregue) > 0 else None

            return self.response(
                dumps_networkapi({
                    'list_ipv4': lista_ip_entregue,
                    'list_ipv6': lista_ip6_entregue
                }))

        except InvalidValueError, e:
            self.log.error(u'Parameter %s is invalid. Value: %s.', e.param,
                           e.value)
            return self.response_error(269, e.param, e.value)