# -*- coding: utf-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from django.conf import settings
from django.core.exceptions import MultipleObjectsReturned
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from networkapi.ambiente.models import Ambiente
from networkapi.api_pools import exceptions
from networkapi.api_pools.models import OptionPool
from networkapi.api_pools.models import OptionPoolEnvironment
from networkapi.healthcheckexpect.models import Healthcheck
from networkapi.infrastructure.script_utils import exec_script
from networkapi.infrastructure.script_utils import ScriptError
from networkapi.ip.models import Ip
from networkapi.ip.models import Ipv6
from networkapi.requisicaovips.models import ServerPool
from networkapi.requisicaovips.models import ServerPoolMember
from networkapi.util import is_valid_int_greater_zero_param
from networkapi.util import is_valid_list_int_greater_zero_param
log = logging.getLogger(__name__)
# Todo
# Not to be used alone like this
# User has to specifically choose an existing healthcheck in order to use the same healthcheck
# between pools
[docs]def get_or_create_healthcheck(user, healthcheck_expect, healthcheck_type, healthcheck_request, healthcheck_destination, identifier=''):
try:
# Query HealthCheck table for one equal this
if identifier == '':
hc = Healthcheck.objects.get(healthcheck_expect=healthcheck_expect, healthcheck_type=healthcheck_type,
healthcheck_request=healthcheck_request, destination=healthcheck_destination)
else:
hc = Healthcheck.objects.get(identifier=identifier, healthcheck_expect=healthcheck_expect, healthcheck_type=healthcheck_type,
healthcheck_request=healthcheck_request, destination=healthcheck_destination)
# Else, add a new one
except ObjectDoesNotExist:
hc = Healthcheck(identifier=identifier, healthcheck_type=healthcheck_type, healthcheck_request=healthcheck_request,
healthcheck_expect=healthcheck_expect, destination=healthcheck_destination)
hc.save()
# Get the fisrt occureny and warn if redundant HCs are present
except MultipleObjectsReturned:
log.warning(
'Multiple healthcheck entries found for the given parameters')
if identifier == '':
hc = Healthcheck.objects.filter(healthcheck_expect=healthcheck_expect, healthcheck_type=healthcheck_type,
healthcheck_request=healthcheck_request, destination=healthcheck_destination).order_by('id')[0]
else:
hc = Healthcheck.objects.filter(identifier=identifier, healthcheck_expect=healthcheck_expect, healthcheck_type=healthcheck_type,
healthcheck_request=healthcheck_request, destination=healthcheck_destination).order_by('id')[0]
return hc
[docs]def save_server_pool(user, id, identifier, default_port, hc, env, balancing, maxconn, id_pool_member, servicedownaction):
# Save Server pool
old_healthcheck = None
if id:
sp = ServerPool.objects.get(id=id)
# store old healthcheck,lb method and service-down-action
old_servicedownaction = sp.servicedownaction
old_identifier = sp.identifier
old_healthcheck = Healthcheck.objects.get(id=sp.healthcheck.id)
old_lb_method = sp.lb_method
old_maxconn = sp.default_limit
# validate change of environment
if sp.environment and sp.environment.id != env.id:
validate_change_of_environment(id_pool_member, sp)
# Pool already created, it is not possible to change Pool Identifier
if(old_identifier != identifier and sp.pool_created):
raise exceptions.CreatedPoolIdentifierException()
update_pool_fields(default_port, env, identifier,
old_healthcheck, old_lb_method, old_maxconn, sp, user)
update_pool_maxconn(maxconn, old_maxconn, sp, user)
apply_health_check(hc, old_healthcheck, sp, user)
update_load_balancing_method(balancing, old_lb_method, sp, user)
apply_service_down_action(
old_servicedownaction, servicedownaction, sp, user)
else:
sp = ServerPool(identifier=identifier, default_port=default_port, healthcheck=hc,
environment=env, pool_created=False, lb_method=balancing, default_limit=maxconn, servicedownaction=servicedownaction)
sp.save()
return sp, (old_healthcheck.id if old_healthcheck else None)
[docs]def update_pool_fields(default_port, env, identifier, old_healthcheck, old_lb_method, old_maxconn, sp, user):
sp.default_port = default_port
sp.environment = env
sp.default_limit = old_maxconn
sp.healthcheck = old_healthcheck
sp.lb_method = old_lb_method
sp.identifier = identifier
sp.save()
[docs]def validate_change_of_environment(id_pool_member, sp):
if sp.pool_created:
raise exceptions.UpdateEnvironmentPoolCreatedException()
del_smp = sp.serverpoolmember_set.exclude(id__in=id_pool_member)
vip = sp.vipporttopool_set.count()
if vip > 0:
raise exceptions.UpdateEnvironmentVIPException()
if len(del_smp) > 0:
raise exceptions.UpdateEnvironmentServerPoolMemberException()
[docs]def update_pool_maxconn(maxconn, old_maxconn, sp, user):
sp.default_limit = maxconn
sp.save()
# If pool member exists, checks if all of them have the same maxconn
# before changing its default maxconn
if(len(sp.serverpoolmember_set.all()) > 0):
if(old_maxconn != sp.default_limit and sp.pool_created):
for serverpoolmember in sp.serverpoolmember_set.all():
if serverpoolmember.limit != old_maxconn:
raise exceptions.ScriptAlterLimitPoolDiffMembersException()
else:
serverpoolmember.limit = maxconn
serverpoolmember.save()
transaction.commit()
command = settings.POOL_MANAGEMENT_LIMITS % (sp.id)
code, _, _ = exec_script(command)
if code != 0:
sp.default_limit = old_maxconn
for serverpoolmember in sp.serverpoolmember_set.all():
serverpoolmember.limit = old_maxconn
serverpoolmember.save()
sp.save()
transaction.commit()
raise exceptions.ScriptAlterLimitPoolException()
[docs]def apply_health_check(hc, old_healthcheck, sp, user):
# Applies new healthcheck in pool
sp.healthcheck = hc
sp.save()
if (old_healthcheck.id != hc.id and sp.pool_created):
transaction.commit()
command = settings.POOL_HEALTHCHECK % (sp.id)
code, _, _ = exec_script(command)
if code != 0:
sp.healthcheck = old_healthcheck
sp.save()
transaction.commit()
raise exceptions.ScriptCreatePoolException()
[docs]def apply_service_down_action(old_servicedownaction, servicedownaction, sp, user):
# Applies new service-down-action in pool
sp.servicedownaction = servicedownaction
sp.save()
if (old_servicedownaction != sp.servicedownaction and sp.pool_created):
transaction.commit()
command = settings.POOL_SERVICEDOWNACTION % (sp.id)
code, _, _ = exec_script(command)
if code != 0:
sp.servicedownaction = old_servicedownaction
sp.save()
transaction.commit()
raise exceptions.ScriptAlterServiceDownActionException()
[docs]def update_load_balancing_method(balancing, old_lb_method, sp, user):
sp.lb_method = balancing
sp.save()
if (old_lb_method != sp.lb_method and sp.pool_created):
transaction.commit()
command = settings.POOL_MANAGEMENT_LB_METHOD % (sp.id)
code, _, _ = exec_script(command)
if code != 0:
sp.lb_method = old_lb_method
sp.save()
transaction.commit()
raise exceptions.ScriptCreatePoolException()
[docs]def prepare_to_save_reals(ip_list_full, ports_reals, nome_equips, priorities, weight, id_pool_member, id_equips):
list_server_pool_member = list()
if id_pool_member:
invalid_ports_real = [
i for i in ports_reals if int(i) > 65535 or int(i) < 1]
invalid_priority = [
i for i in priorities if int(i) > 4294967295 or int(i) < 0]
if invalid_priority:
raise exceptions.InvalidRealPoolException(
'O valor da Prioridade deve estar entre 0 e 4294967295.')
if invalid_ports_real:
raise exceptions.InvalidRealPoolException(
'O número da porta deve estar entre 1 e 65535.')
if len(id_equips) != len(id_pool_member):
raise exceptions.InvalidRealPoolException(
'Quantidade de portas e equipamento difere.')
for i in range(0, len(ip_list_full)):
for j in range(0, len(ip_list_full)):
if i == j:
pass
elif ports_reals[i] == ports_reals[j] and ip_list_full[i].get('id', '') == ip_list_full[j].get('id', ''):
raise exceptions.InvalidRealPoolException(
'Ips com portas iguais.')
for i in range(0, len(id_pool_member)):
list_server_pool_member.append({'id': ip_list_full[i].get('id', '') if ip_list_full[i] else '',
'ip': ip_list_full[i].get('ip', '') if ip_list_full[i] else '',
'port_real': ports_reals[i],
'nome_equips': nome_equips[i],
'priority': priorities[i],
'weight': weight[i],
'id_pool_member': id_pool_member[i],
})
return list_server_pool_member
[docs]def save_server_pool_member(user, pool, list_server_pool_member):
list_pool_member = list()
old_priorities_list = list()
pool_members_to_be_removed = get_pool_members_to_be_removed(
list_server_pool_member)
remove_pool_members(pool_members_to_be_removed, pool, user)
if list_server_pool_member:
apply_new_priorities = False
for dic in list_server_pool_member:
ip_object, ipv6_object = get_ip_objects(dic)
pool_member_id = dic['id_pool_member']
if pool_member_id:
pool_member = ServerPoolMember.objects.get(id=pool_member_id)
old_member_priority = pool_member.priority
old_priorities_list.append(old_member_priority)
update_pool_member(
pool, pool_member, dic, ip_object, ipv6_object, user)
if(old_member_priority != pool_member.priority and pool.pool_created):
apply_new_priorities = True
else:
pool_member = ServerPoolMember()
update_pool_member(
pool, pool_member, dic, ip_object, ipv6_object, user)
pool_member.save()
old_priorities_list.append(dic['priority'])
# execute script to create real if pool already created
# commits transaction. Rolls back if script returns error
if pool.pool_created:
ip_id = ip_object and ip_object.id or ipv6_object and ipv6_object.id
deploy_pool_member_config(
ip_id, pool.id, dic['port_real'], pool_member, user)
list_pool_member.append(pool_member)
# Applies new priority in pool - only 1 script run for all members
if(apply_new_priorities):
apply_priorities(list_pool_member, old_priorities_list, pool, user)
return list_pool_member
[docs]def update_pool_member(pool, pool_member, dic, ip_object, ipv6_object, user):
pool_member.server_pool = pool
pool_member.limit = pool.default_limit
pool_member.ip = ip_object
pool_member.ipv6 = ipv6_object
pool_member.identifier = dic['nome_equips']
pool_member.weight = dic['weight']
pool_member.priority = dic['priority']
pool_member.port_real = dic['port_real']
pool_member.save()
[docs]def get_ip_objects(dic):
ip_object = None
ipv6_object = None
if len(dic['ip']) <= 15:
ip_object = Ip.get_by_pk(dic['id'])
else:
ipv6_object = Ipv6.get_by_pk(dic['id'])
return ip_object, ipv6_object
[docs]def get_pool_members_to_be_removed(list_server_pool_member):
# Remove empty values from list
return [x['id_pool_member'] for x in list_server_pool_member if x['id_pool_member'] != '']
[docs]def remove_pool_members(id_pool_member_noempty, sp, user):
# exclue server pool member
del_smp = sp.serverpoolmember_set.exclude(id__in=id_pool_member_noempty)
if del_smp:
for obj in del_smp:
obj.delete()
# execute script remove real if pool already created
# commit transaction after each successful script call
if sp.pool_created:
command = settings.POOL_REAL_REMOVE % (
obj.server_pool_id, obj.ip_id if obj.ip else obj.ipv6_id, obj.port_real)
code, _, _ = exec_script(command)
if code != 0:
raise exceptions.ScriptCreatePoolException()
transaction.commit()
[docs]def deploy_pool_member_config(id_ip, id_pool, port_ip, spm, user):
transaction.commit()
# def prepare_and_save(self, server_pool, ip, ip_type, priority, weight, port_real, user, commit=False):
# spm.prepare_and_save(sp, ip_object, IP_VERSION.IPv4[1], dic['priority'], dic['weight'], dic['port_real'], user, True)
command = settings.POOL_REAL_CREATE % (id_pool, id_ip, port_ip)
code, _, _ = exec_script(command)
if code != 0:
spm.delete()
transaction.commit()
raise exceptions.ScriptCreatePoolException()
# if sp.healthcheck_id:
# spm.healthcheck = sp.healthcheck
[docs]def apply_priorities(list_pool_member, old_priorities_list, sp, user):
transaction.commit()
command = settings.POOL_MEMBER_PRIORITIES % (sp.id)
code, _, _ = exec_script(command)
if code != 0:
for i in range(0, len(old_priorities_list)):
list_pool_member[i].priority = old_priorities_list[i]
list_pool_member[i].save()
transaction.commit()
raise exceptions.ScriptAlterPriorityPoolMembersException()
[docs]def exec_script_check_poolmember_by_pool(pool_id):
# execute script check status real
command = settings.POOL_REAL_CHECK_BY_POOL % (pool_id)
status_code, stdout, stderr = exec_script(command)
if status_code != 0:
raise exceptions.ScriptCheckStatusPoolMemberException()
return stdout
[docs]def manager_pools(request):
"""
Manager Status Pool Members Enable/Disabled By Pool
:param request: HttpRequest
"""
try:
pool_id = request.DATA.get('server_pool_id')
pool_members = request.DATA.get('server_pool_members', [])
# List to validate pool member status
valid_status = [0, 1, False, True]
pool_members_id = [member.get('id') for member in pool_members]
if not is_valid_int_greater_zero_param(pool_id):
raise exceptions.InvalidIdPoolException()
# Validate pool members id
is_valid_list_int_greater_zero_param(pool_members_id)
pool_obj = ServerPool.objects.get(id=pool_id)
related_pool_members = pool_obj.serverpoolmember_set.order_by('id')
received_pool_members = ServerPoolMember.objects.filter(
id__in=pool_members_id).order_by('id')
relates = list(related_pool_members)
receives = list(received_pool_members)
if relates != receives:
raise exceptions.InvalidIdPoolMemberException(
u'Required All Pool Members By Pool')
for member in pool_members:
member_id = member.get('id')
member_status = member.get('status')
if member_status not in valid_status:
raise exceptions.InvalidStatusPoolMemberException()
server_pool_member = ServerPoolMember.objects.get(id=member_id)
server_pool_member.status = member_status
server_pool_member.save(request.user, commit=True)
# Execute Script To Set Status
command = settings.POOL_MANAGEMENT_MEMBERS_STATUS % pool_id
code, _, _ = exec_script(command)
if code != 0:
raise exceptions.ScriptManagementPoolException()
except (exceptions.ScriptManagementPoolException, ScriptError), exception:
# Rollback
for old_member in related_pool_members:
old_member.save(request.user, commit=True)
raise exception
[docs]def save_option_pool(user, type, description):
"""if id:
sp = OptionPool.objects.get(id=id)
sp.type = type
sp.description = description
sp.save()
else:"""
sp = OptionPool(type=type, name=description)
sp.save()
return sp
[docs]def update_option_pool(user, option_id, type, description):
sp = OptionPool.objects.get(id=option_id)
sp.type = type
sp.name = description
sp.save()
return sp
[docs]def delete_option_pool(user, option_id):
pool_option = OptionPool.objects.get(id=option_id)
environment_options = OptionPoolEnvironment.objects.all()
environment_options = environment_options.filter(option=option_id)
for eop in environment_options:
eop.delete()
pool_option.delete()
return option_id
[docs]def save_environment_option_pool(user, option_id, environment_id):
op = OptionPool.objects.get(id=option_id)
env = Ambiente.objects.get(id=environment_id)
# log.warning("objetos buscados %s %s", serializer_options.data, serializer_env.data)
ope = OptionPoolEnvironment()
ope.environment = env
ope.option = op
ope.save()
return ope
[docs]def update_environment_option_pool(user, environment_option_id, option_id, environment_id):
ope = OptionPoolEnvironment.objects.get(id=environment_option_id)
op = OptionPool.objects.get(id=option_id)
env = Ambiente.objects.get(id=environment_id)
ope.option = op
ope.environment = env
ope.save()
return ope
[docs]def delete_environment_option_pool(user, environment_option_id):
ope = OptionPoolEnvironment.objects.get(id=environment_option_id)
ope.delete()
return environment_option_id