def get_esd_entities(servicetype, esd): """ Map F5 ESD (Enhanced Service Definition) to service components. :param servicetype: octavia listener type :param esd: parsed ESD repository :return: AS3 service flags according to ESD definition """ service_args = {} irules = esd.get('lbaas_irule', None) if irules: service_args['iRules'] = [ as3.BigIP('/Common/' + rule) for rule in irules ] # client / server tcp profiles if servicetype in [ const.SERVICE_HTTP, const.SERVICE_HTTPS, const.SERVICE_TCP ]: ctcp = esd.get('lbaas_ctcp', None) stcp = esd.get('lbaas_stcp', None) if stcp and ctcp: # Server and Clientside profile defined service_args['profileTCP'] = as3.Service_Generic_profileTCP( ingress=as3.BigIP('/Common/' + ctcp), egress=as3.BigIP('/Common/' + stcp)) elif ctcp: service_args['profileTCP'] = as3.BigIP('/Common/' + ctcp) else: service_args['profileTCP'] = 'normal' if servicetype in [const.SERVICE_HTTP, const.SERVICE_HTTPS]: # OneConnect (Multiplex) Profile oneconnect = esd.get('lbaas_one_connect', None) if oneconnect: service_args['profileMultiplex'] = as3.BigIP('/Common/' + oneconnect) # HTTP Compression Profile compression = esd.get('lbaas_http_compression', None) if compression: service_args['profileHTTPCompression'] = as3.BigIP('/Common/' + compression) return service_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_monitor(health_monitor, target_address=None, target_port=None): args = dict() # Standard Octavia monitor types if health_monitor.type == 'HTTP': args['monitorType'] = 'http' elif health_monitor.type == 'HTTPS': args['monitorType'] = 'https' elif health_monitor.type == 'PING': args['monitorType'] = 'icmp' elif health_monitor.type == 'TCP': args['monitorType'] = 'tcp' args['send'] = '' args['receive'] = '' elif health_monitor.type == 'TLS-HELLO': args['monitorType'] = 'external' args['script'] = TLS_HELLO_CHECK args['receive'] = 'UP' elif health_monitor.type == 'UDP-CONNECT': args['monitorType'] = 'external' args['script'] = UDP_CHECK args['receive'] = 'UP' # F5 specific monitory types elif health_monitor.type == 'SIP': args['monitorType'] = 'sip' elif health_monitor.type == 'SMTP': args['monitorType'] = 'smtp' elif health_monitor.type == 'TCP-HALF_OPEN': args['monitorType'] = 'tcp-half-open' elif health_monitor.type == 'LDAP': args['monitorType'] = 'ldap' elif health_monitor.type == 'DNS': args['monitorType'] = 'dns' args['queryName'] = health_monitor.domain_name # No Health monitor type available else: return {} if health_monitor.type == 'HTTP' or health_monitor.type == 'HTTPS': http_version = '1.0' if health_monitor.http_version: http_version = health_monitor.http_version send = "{} {} HTTP/{}\\r\\n".format(health_monitor.http_method, health_monitor.url_path, http_version) if health_monitor.domain_name: send += "Host: {}\\r\\n\\r\\n".format(health_monitor.domain_name) else: send += "\\r\\n" args['send'] = send args['receive'] = _get_recv_text(health_monitor) args["interval"] = health_monitor.delay timeout = int(health_monitor.fall_threshold) * int( health_monitor.delay) + 1 args["timeout"] = timeout if target_address: args["targetAddress"] = target_address if target_port: args["targetPort"] = target_port # TODO: supporting HTTPS if health_monitor.type == 'HTTPS': args["clientTLS"] = as3.BigIP(CONF.f5_agent.profile_healthmonitor_tls) args['label'] = as3types.f5label(health_monitor.name or health_monitor.id) return as3.Monitor(**args)
def test_bigip(self): # creation bigip = {'bigip': 'test_bigip'} bigip_obj = as3classes.BigIP(bigip['bigip']) self.assertEqual(bigip, bigip_obj.to_dict())
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