def workaround_autotool_1469(network_id, loadbalancer_id, pool, bigips): """ This is a workaround for F5 TMSH / AS3 Bug tracked as 527004 / AUTOTOOL-1469. -> Custom Monitor noted as in-use and cannot be removed Workaround tries to unassociate monitor manually and without transactions via iControl REST API. :param loadbalancers: loadbalancers :param bigips: bigips """ if pool.health_monitor and driver_utils.pending_delete( pool.health_monitor): LOG.info("Disassociating health-monitor '%s'", pool.health_monitor.id) for bigip in bigips: try: pool_resource_path = '{pool_path}/~{net_id}~{lb_id}~{pool_id}'.format( pool_path=F5_POOL_PATH, net_id=m_tenant.get_name(network_id), lb_id=m_app.get_name(loadbalancer_id), pool_id=m_pool.get_name(pool.id)) pool_json = bigip.get(pool_resource_path) if pool_json.ok: pool_dict = pool_json.json() if 'monitor' in pool_dict: pool_dict['monitor'] = None bigip.put(pool_resource_path, json=pool_dict) else: LOG.warning( "Disassociating health-monitor '%s' failed: %s", pool.health_monitor.id, pool_json.text) except exceptions.AS3Exception as e: LOG.warning("Disassociating health-monitor '%s' failed: %s", pool.health_monitor.id, e)
def member_create(bigip, member): """Patches new member into existing pool :param bigip: bigip instance :param member: octavia member object """ path = '{}/{}/{}/members/-'.format( m_part.get_name(member.pool.load_balancer.vip.network_id), m_app.get_name(member.pool.load_balancer.id), m_pool.get_name(member.pool.id)) return bigip.patch(operation='add', path=path, value=m_member.get_member(member).to_dict())
def _get_action(l7policy): if l7policy.action not in SUPPORTED_ACTION_TYPE: raise PolicyActionNotSupported() args = dict() if l7policy.action == const.L7POLICY_ACTION_REDIRECT_TO_POOL: args['type'] = 'forward' pool_name = pool.get_name(l7policy.redirect_pool_id) args['select'] = {'pool': {'use': pool_name}} args['event'] = 'request' elif l7policy.action == const.L7POLICY_ACTION_REDIRECT_TO_URL: args['type'] = 'httpRedirect' args['location'] = l7policy.redirect_url args['event'] = 'request' elif l7policy.action == const.L7POLICY_ACTION_REDIRECT_PREFIX: args['type'] = 'httpRedirect' args['location'] = 'tcl:{}[HTTP::uri]'.format(l7policy.redirect_prefix) args['event'] = 'request' elif l7policy.action == const.L7POLICY_ACTION_REJECT: args['type'] = 'drop' args['event'] = 'request' return Policy_Action(**args)
def get_service(listener, cert_manager, esd_repository): """ Map Octavia listener -> AS3 Service :param listener: Octavia listener :param cert_manager: cert_manager wrapper instance :return: AS3 Service + additional AS3 application objects """ # Entities is a list of tuples, which each describe AS3 objects # which may reference each other but do not form a hierarchy. entities = [] vip = listener.load_balancer.vip project_id = listener.load_balancer.project_id label = as3types.f5label(listener.name or listener.description) virtual_address = '{}/32'.format(vip.ip_address) service_args = { 'virtualPort': listener.protocol_port, 'persistenceMethods': [], 'iRules': [], 'policyEndpoint': [], 'label': label } # Custom virtual address settings if CONF.f5_agent.service_address_icmp_echo: service_address = as3.ServiceAddress( virtualAddress=virtual_address, icmpEcho=CONF.f5_agent.service_address_icmp_echo) entities.append( (m_app.get_name(listener.load_balancer.id), service_address)) service_args['virtualAddresses'] = [[ as3.Pointer(m_app.get_name(listener.load_balancer.id)), virtual_address ]] else: service_args['virtualAddresses'] = [virtual_address] # Determine service type if listener.protocol == const.PROTOCOL_TCP: service_args['_servicetype'] = CONF.f5_agent.tcp_service_type # UDP elif listener.protocol == const.PROTOCOL_UDP: service_args['_servicetype'] = const.SERVICE_UDP # HTTP elif listener.protocol == const.PROTOCOL_HTTP: service_args['_servicetype'] = const.SERVICE_HTTP # HTTPS (non-terminated, forward TCP traffic) elif listener.protocol == const.PROTOCOL_HTTPS: service_args['_servicetype'] = CONF.f5_agent.tcp_service_type # Proxy elif listener.protocol == const.PROTOCOL_PROXY: service_args['_servicetype'] = const.SERVICE_TCP name, irule = m_irule.get_proxy_irule() service_args['iRules'].append(name) entities.append((name, irule)) # Terminated HTTPS elif listener.protocol == const.PROTOCOL_TERMINATED_HTTPS: service_args['_servicetype'] = const.SERVICE_HTTPS service_args['serverTLS'] = m_tls.get_listener_name(listener.id) service_args['redirect80'] = False # Certificate Handling auth_name = None certificates = cert_manager.get_certificates(listener) if listener.client_ca_tls_certificate_id and listener.client_authentication != 'NONE': # Client Side Certificates try: auth_name, secret = cert_manager.load_secret( project_id, listener.client_ca_tls_certificate_id) entities.append((auth_name, m_cert.get_ca_bundle(secret, auth_name, auth_name))) except exceptions.CertificateRetrievalException as e: LOG.error("Error fetching certificate: %s", e) entities.append( (m_tls.get_listener_name(listener.id), m_tls.get_tls_server([cert['id'] for cert in certificates], auth_name, listener.client_authentication))) entities.extend([(cert['id'], cert['as3']) for cert in certificates]) if listener.connection_limit > 0: service_args['maxConnections'] = listener.connection_limit # Add default pool if listener.default_pool_id: pool = listener.default_pool if pool.provisioning_status != lib_consts.PENDING_DELETE: default_pool = m_pool.get_name(listener.default_pool_id) service_args['pool'] = default_pool # only consider Proxy pool, everything else is determined by listener type if pool.protocol == const.PROTOCOL_PROXY: name, irule = m_irule.get_proxy_irule() service_args['iRules'].append(name) entities.append((name, irule)) # Pool member certificate handling (TLS backends) if pool.tls_enabled and listener.protocol in \ [ const.PROTOCOL_PROXY, const.PROTOCOL_HTTP, const.PROTOCOL_TERMINATED_HTTPS ]: client_cert = None trust_ca = None crl_file = None service_args['clientTLS'] = m_tls.get_pool_name(pool.id) certificates = cert_manager.get_certificates(pool) if len(certificates) == 1: cert = certificates.pop() entities.append((cert['id'], cert['as3'])) client_cert = cert['id'] if pool.ca_tls_certificate_id: trust_ca, secret = cert_manager.load_secret( project_id, pool.ca_tls_certificate_id) entities.append( (trust_ca, m_cert.get_ca_bundle(secret, trust_ca, trust_ca))) if pool.crl_container_id: # TODO: CRL currently not supported pass entities.append((m_tls.get_pool_name(pool.id), m_tls.get_tls_client(trust_ca=trust_ca, client_cert=client_cert, crl_file=crl_file))) # Insert header irules if service_args['_servicetype'] in const.SERVICE_HTTP_TYPES: # HTTP profiles only for name, irule in m_irule.get_header_irules(listener.insert_headers): service_args['iRules'].append(name) entities.append((name, irule)) # session persistence if listener.default_pool_id and listener.default_pool.session_persistence: persistence = listener.default_pool.session_persistence lb_algorithm = listener.default_pool.lb_algorithm if service_args['_servicetype'] in const.SERVICE_HTTP_TYPES: # Add APP_COOKIE / HTTP_COOKIE persistance only in HTTP profiles if persistence.type == 'APP_COOKIE' and persistence.cookie_name: # generate iRule for cookie_name escaped_cookie = persistence.cookie_name escaped_cookie.replace("\"", "") irule_name, irule = m_irule.get_app_cookie_irule( escaped_cookie) entities.append((irule_name, irule)) # add iRule to universal persistance profile name, obj_persist = m_persist.get_app_cookie(escaped_cookie) service_args['persistenceMethods'] = [as3.Pointer(name)] entities.append((name, obj_persist)) if lb_algorithm == 'SOURCE_IP': service_args[ 'fallbackPersistenceMethod'] = 'source-address' elif persistence.type == 'HTTP_COOKIE': service_args['persistenceMethods'] = ['cookie'] if lb_algorithm == 'SOURCE_IP': service_args[ 'fallbackPersistenceMethod'] = 'source-address' if persistence.type == 'SOURCE_IP': if not persistence.persistence_timeout and not persistence.persistence_granularity: service_args['persistenceMethods'] = ['source-address'] else: name, obj_persist = m_persist.get_source_ip( persistence.persistence_timeout, persistence.persistence_granularity) service_args['persistenceMethods'] = [as3.Pointer(name)] entities.append((name, obj_persist)) # Map listener tags to ESDs for tag in listener.tags: # get ESD of same name esd = esd_repository.get_esd(tag) if esd is None: continue # enrich service with iRules and other things defined in ESD esd_entities = get_esd_entities(service_args['_servicetype'], esd) for entity_name in esd_entities: if entity_name == 'iRules': service_args['iRules'].extend(esd_entities['iRules']) else: service_args[entity_name] = esd_entities[entity_name] endpoint_policies = [] # Map special L7policies to ESDs # TODO: Remove this as soon as all customers have migrated their scripts. # Triggering ESDs via L7policies is considered deprecated. Tags should be used instead. See the code above. for policy in listener.l7policies: # get ESD of same name esd = esd_repository.get_esd(policy.name) # Add ESD or regular endpoint policy if esd: # enrich service with iRules and other things defined in ESD esd_entities = get_esd_entities(service_args['_servicetype'], esd) for entity_name in esd_entities: if entity_name == 'iRules': service_args['iRules'].extend(esd_entities['iRules']) else: service_args[entity_name] = esd_entities[entity_name] elif policy.provisioning_status != lib_consts.PENDING_DELETE: endpoint_policies.append(policy) # UDP listener won't support policies if endpoint_policies and not service_args[ '_servicetype'] == const.SERVICE_UDP: # add a regular endpoint policy policy_name = m_policy.get_wrapper_name(listener.id) # make endpoint policy object endpoint_policy = (policy_name, m_policy.get_endpoint_policy(endpoint_policies)) entities.append(endpoint_policy) # reference endpoint policy object in service service_args['policyEndpoint'].append(policy_name) # Ensure no duplicate iRules service_args['iRules'] = list(set(service_args['iRules'])) # fastL4 profile doesn't support iRules, fallback to TCP Profile when iRules detected if service_args['_servicetype'] == const.SERVICE_L4 and len( service_args['iRules']) > 0: service_args['_servicetype'] = const.SERVICE_TCP # add default profiles to supported listeners if CONF.f5_agent.profile_http and service_args[ '_servicetype'] in const.SERVICE_HTTP_TYPES: if 'profileHTTP' not in service_args: service_args['profileHTTP'] = as3.BigIP(CONF.f5_agent.profile_http) if CONF.f5_agent.profile_l4 and service_args[ '_servicetype'] == const.SERVICE_L4: if 'profileL4' not in service_args: service_args['profileL4'] = as3.BigIP(CONF.f5_agent.profile_l4) if CONF.f5_agent.profile_tcp and service_args[ '_servicetype'] in const.SERVICE_TCP_TYPES: if 'profileTCP' not in service_args: service_args['profileTCP'] = as3.BigIP(CONF.f5_agent.profile_tcp) if CONF.f5_agent.profile_udp and service_args[ '_servicetype'] == const.SERVICE_UDP: if 'profileUDP' not in service_args: service_args['profileUDP'] = as3.BigIP(CONF.f5_agent.profile_udp) # Use the virtual-server address as SNAT address if CONF.f5_agent.snat_virtual: service_args['snat'] = 'self' # create service object and fill in additional fields service = as3.Service(**service_args) # add service to entities and return entities.append((get_name(listener.id), service)) return entities
def get_service(listener, cert_manager, esd_repository): """ Map Octavia listener -> AS3 Service :param listener: Octavia listener :param cert_manager: cert_manager wrapper instance :return: AS3 Service + additional AS3 application objects """ # Entities is a list of tuples, which each describe AS3 objects # which may reference each other but do not form a hierarchy. entities = [] vip = listener.load_balancer.vip project_id = listener.load_balancer.project_id label = as3types.f5label(listener.description or listener.id) service_args = { 'virtualPort': listener.protocol_port, 'virtualAddresses': [vip.ip_address], 'persistenceMethods': [], 'iRules': [], 'policyEndpoint': [], 'label': label } # Determine service type if listener.protocol == const.PROTOCOL_TCP: service_args['_servicetype'] = CONF.f5_agent.tcp_service_type # UDP elif listener.protocol == const.PROTOCOL_UDP: service_args['_servicetype'] = const.SERVICE_UDP # HTTP elif listener.protocol == const.PROTOCOL_HTTP: service_args['_servicetype'] = const.SERVICE_HTTP # HTTPS (non-terminated) elif listener.protocol == const.PROTOCOL_HTTPS: service_args['_servicetype'] = const.SERVICE_GENERIC # Proxy elif listener.protocol == const.PROTOCOL_PROXY: service_args['_servicetype'] = const.SERVICE_HTTP name, irule = m_irule.get_proxy_irule() service_args['iRules'].append(name) entities.append((name, irule)) # Terminated HTTPS elif listener.protocol == const.PROTOCOL_TERMINATED_HTTPS: service_args['_servicetype'] = const.SERVICE_HTTPS service_args['serverTLS'] = m_tls.get_listener_name(listener.id) service_args['redirect80'] = False # Certificate Handling auth_name = None certificates = cert_manager.get_certificates(listener) if listener.client_ca_tls_certificate_id and listener.client_authentication != 'NONE': # Client Side Certificates try: auth_name, secret = cert_manager.load_secret( project_id, listener.client_ca_tls_certificate_id) entities.append((auth_name, m_cert.get_ca_bundle(secret, auth_name, auth_name))) except exceptions.CertificateRetrievalException as e: LOG.error("Error fetching certificate: %s", e) entities.append( (m_tls.get_listener_name(listener.id), m_tls.get_tls_server([cert['id'] for cert in certificates], auth_name, listener.client_authentication))) entities.extend([(cert['id'], cert['as3']) for cert in certificates]) # add profile if CONF.f5_agent.profile_l4: service_args['profileL4'] = as3.BigIP(CONF.f5_agent.profile_l4) if CONF.f5_agent.profile_multiplex: service_args['profileMultiplex'] = as3.BigIP( CONF.f5_agent.profile_multiplex) if listener.connection_limit > 0: service_args['maxConnections'] = listener.connection_limit # Add default pool if listener.default_pool_id: pool = listener.default_pool if pool.provisioning_status != lib_consts.PENDING_DELETE: default_pool = m_pool.get_name(listener.default_pool_id) service_args['pool'] = default_pool # only consider Proxy pool, everything else is determined by listener type if pool.protocol == const.PROTOCOL_PROXY: name, irule = m_irule.get_proxy_irule() service_args['iRules'].append(name) entities.append((name, irule)) # Support of backup members (realized as fallback host via http_profile), pick the first one backup_members = [ member for member in pool.members if member.backup ] if backup_members: http_profile_name = m_member.get_name(backup_members[0].id) http_profile = as3.HTTP_Profile( fallbackRedirect=backup_members[0].ip_address) service_args['profileHTTP'] = as3.Pointer(http_profile_name) entities.append((http_profile_name, http_profile)) # Pool member certificate handling (TLS backends) if pool.tls_enabled: client_cert = None trust_ca = None crl_file = None service_args['clientTLS'] = m_tls.get_pool_name(pool.id) certificates = cert_manager.get_certificates(pool) if len(certificates) == 1: cert = certificates.pop() entities.append((cert['id'], cert['as3'])) client_cert = cert['id'] if pool.ca_tls_certificate_id: trust_ca, secret = cert_manager.load_secret( project_id, pool.ca_tls_certificate_id) entities.append( (trust_ca, m_cert.get_ca_bundle(secret, trust_ca, trust_ca))) if pool.crl_container_id: # TODO: CRL currently not supported pass entities.append((m_tls.get_pool_name(pool.id), m_tls.get_tls_client(trust_ca=trust_ca, client_cert=client_cert, crl_file=crl_file))) # Insert header irules for name, irule in m_irule.get_header_irules(listener.insert_headers): service_args['iRules'].append(name) entities.append((name, irule)) # session persistence if listener.default_pool_id and listener.default_pool.session_persistence: persistence = listener.default_pool.session_persistence lb_algorithm = listener.default_pool.lb_algorithm if persistence.type == 'APP_COOKIE': name, obj_persist = m_persist.get_app_cookie( persistence.cookie_name) service_args['persistenceMethods'] = [as3.Pointer(name)] entities.append((name, obj_persist)) if lb_algorithm == 'SOURCE_IP': service_args['fallbackPersistenceMethod'] = 'source-address' elif persistence.type == 'SOURCE_IP': if not persistence.persistence_timeout and not persistence.persistence_granularity: service_args['persistenceMethods'] = ['source-address'] else: name, obj_persist = m_persist.get_source_ip( persistence.persistence_timeout, persistence.persistence_granularity) service_args['persistenceMethods'] = [as3.Pointer(name)] entities.append((name, obj_persist)) elif persistence.type == 'HTTP_COOKIE': service_args['persistenceMethods'] = ['cookie'] if lb_algorithm == 'SOURCE_IP': service_args['fallbackPersistenceMethod'] = 'source-address' # Map listener tags to ESDs for tag in listener.tags: # get ESD of same name esd = esd_repository.get_esd(tag) if esd is None: continue # enrich service with iRules and other things defined in ESD esd_entities = get_esd_entities(service_args['_servicetype'], esd) for entity_name in esd_entities: if entity_name == 'iRules': service_args['iRules'].extend(esd_entities['iRules']) else: service_args[entity_name] = esd_entities[entity_name] # Map special L7policies to ESDs # TODO: Remove this as soon as all customers have migrated their scripts. # Triggering ESDs via L7policies is considered deprecated. Tags should be used instead. See the code above. for policy in listener.l7policies: # get ESD of same name esd = esd_repository.get_esd(policy.name) # Add ESD or regular endpoint policy if esd: # enrich service with iRules and other things defined in ESD esd_entities = get_esd_entities(service_args['_servicetype'], esd) for entity_name in esd_entities: if entity_name == 'iRules': service_args['iRules'].extend(esd_entities['iRules']) else: service_args[entity_name] = esd_entities[entity_name] elif policy.provisioning_status != lib_consts.PENDING_DELETE: # add a regular endpoint policy policy_name = m_policy.get_name(policy.id) # make endpoint policy object endpoint_policy = (policy_name, m_policy.get_endpoint_policy(policy)) entities.append(endpoint_policy) # reference endpoint policy object in service service_args['policyEndpoint'].append(policy_name) # Ensure no duplicate iRules service_args['iRules'] = list(set(service_args['iRules'])) # create service object and fill in additional fields service = as3.Service(**service_args) # add service to entities and return entities.append((get_name(listener.id), service)) return entities