def _check_member_monitor_options(self, member): if self._check_monitor_options(member): msg = _('OVN Load Balancer does not support different member ' 'monitor address or port.') raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)
def _validate_pool_algorithm(self, pool): if pool.lb_algorithm not in AMPHORA_SUPPORTED_LB_ALGORITHMS: msg = ('Amphora provider does not support %s algorithm.' % pool.lb_algorithm) raise exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)
def _validate_listener_protocol(self, listener): if listener.protocol not in AMPHORA_SUPPORTED_PROTOCOLS: msg = ('Amphora provider does not support %s protocol. ' 'Supported: %s' % (listener.protocol, ", ".join(AMPHORA_SUPPORTED_PROTOCOLS))) raise exceptions.UnsupportedOptionError(user_fault_string=msg, operator_fault_string=msg)
def validate_flavor(self, flavor_dict): """Validates flavor profile data. This will validate a flavor profile dataset against the flavor settings the amphora driver supports. :param flavor_dict: The flavor dictionary to validate. :type flavor: dict :return: None :raises DriverError: An unexpected error occurred. :raises UnsupportedOptionError: If the driver does not support one of the flavor settings. """ try: validate(flavor_dict, flavor_schema.SUPPORTED_FLAVOR_SCHEMA) except js_exceptions.ValidationError as e: error_object = '' if e.relative_path: error_object = '{} '.format(e.relative_path[0]) raise exceptions.UnsupportedOptionError( user_fault_string='{0}{1}'.format(error_object, e.message), operator_fault_string=str(e)) except Exception as e: raise exceptions.DriverError( user_fault_string='Failed to validate the flavor metadata ' 'due to: {}'.format(str(e)), operator_fault_string='Failed to validate the flavor metadata ' 'due to: {}'.format(str(e))) compute_flavor = flavor_dict.get(consts.COMPUTE_FLAVOR, None) if compute_flavor: compute_driver = stevedore_driver.DriverManager( namespace='octavia.compute.drivers', name=CONF.controller_worker.compute_driver, invoke_on_load=True ).driver # TODO(johnsom) Fix this to raise a NotFound error # when the octavia-lib supports it. compute_driver.validate_flavor(compute_flavor) amp_image_tag = flavor_dict.get(consts.AMP_IMAGE_TAG, None) if amp_image_tag: image_driver = stevedore_driver.DriverManager( namespace='octavia.image.drivers', name=CONF.controller_worker.image_driver, invoke_on_load=True ).driver try: image_driver.get_image_id_by_tag( amp_image_tag, CONF.controller_worker.amp_image_owner_id) except Exception as e: raise exceptions.NotFound( user_fault_string='Failed to find an image with tag {} ' 'due to: {}'.format( amp_image_tag, str(e)), operator_fault_string='Failed to find an image with tag ' '{} due to: {}'.format( amp_image_tag, str(e)))
def _check_for_allowed_cidrs(self, allowed_cidrs): # TODO(haleyb): add support for this if isinstance(allowed_cidrs, o_datamodels.UnsetType): allowed_cidrs = [] if allowed_cidrs: msg = _('OVN provider does not support allowed_cidrs option') raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)
def test_UnsupportedOptionError(self): unsupported_option_error = exceptions.UnsupportedOptionError( user_fault_string=self.user_fault_string, operator_fault_string=self.operator_fault_string) self.assertEqual(self.user_fault_string, unsupported_option_error.user_fault_string) self.assertEqual(self.operator_fault_string, unsupported_option_error.operator_fault_string) self.assertIsInstance(unsupported_option_error, Exception)
def validate_availability_zone(self, availability_zone_dict): """Validates availability zone profile data. This will validate an availability zone profile dataset against the availability zone settings the amphora driver supports. :param availability_zone_dict: The availability zone dict to validate. :type availability_zone_dict: dict :return: None :raises DriverError: An unexpected error occurred. :raises UnsupportedOptionError: If the driver does not support one of the availability zone settings. """ try: validate( availability_zone_dict, availability_zone_schema.SUPPORTED_AVAILABILITY_ZONE_SCHEMA) except js_exceptions.ValidationError as e: error_object = '' if e.relative_path: error_object = '{} '.format(e.relative_path[0]) raise exceptions.UnsupportedOptionError( user_fault_string='{0}{1}'.format(error_object, e.message), operator_fault_string=str(e)) except Exception as e: raise exceptions.DriverError( user_fault_string='Failed to validate the availability zone ' 'metadata due to: {}'.format(str(e)), operator_fault_string='Failed to validate the availability ' 'zone metadata due to: {}'.format(str(e)) ) compute_zone = availability_zone_dict.get(consts.COMPUTE_ZONE, None) if compute_zone: compute_driver = stevedore_driver.DriverManager( namespace='octavia.compute.drivers', name=CONF.controller_worker.compute_driver, invoke_on_load=True ).driver # TODO(johnsom) Fix this to raise a NotFound error # when the octavia-lib supports it. compute_driver.validate_availability_zone(compute_zone) check_nets = availability_zone_dict.get( consts.VALID_VIP_NETWORKS, []) management_net = availability_zone_dict.get( consts.MANAGEMENT_NETWORK, None) if management_net: check_nets.append(management_net) for check_net in check_nets: network_driver = utils.get_network_driver() # TODO(johnsom) Fix this to raise a NotFound error # when the octavia-lib supports it. network_driver.get_network(check_net)
def _validate_alpn_protocols(self, listener): if not listener.alpn_protocols: return supported = consts.AMPHORA_SUPPORTED_ALPN_PROTOCOLS not_supported = set(listener.alpn_protocols) - set(supported) if not_supported: msg = ('Amphora provider does not support %s ALPN protocol(s). ' 'Supported: %s' % (", ".join(not_supported), ", ".join(supported))) raise exceptions.UnsupportedOptionError(user_fault_string=msg, operator_fault_string=msg)
def _validate_hm_support(self, hm, action='create'): if not self._is_health_check_supported(): msg = _('OVN Load Balancer supports Health Check provider ' 'from version 2.12. Upgrade OVN in order to use it.') raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg) # type is only required for create if action == 'create': if isinstance(hm.type, o_datamodels.UnsetType): msg = _('OVN provider health monitor type not specified.') # seems this should be other than "unsupported"? raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg) if hm.type not in ovn_const.SUPPORTED_HEALTH_MONITOR_TYPES: msg = (_('OVN provider does not support %s ' 'health monitor type. Supported types: %s') % (hm.type, ', '.join(ovn_const.SUPPORTED_HEALTH_MONITOR_TYPES))) raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)
def _validate_members(self, db_pool, members): if db_pool.protocol == consts.PROTOCOL_UDP: # For UDP LBs, check that we are not mixing IPv4 and IPv6 for member in members: member_is_ipv6 = utils.is_ipv6(member.address) for listener in db_pool.listeners: lb = listener.load_balancer vip_is_ipv6 = utils.is_ipv6(lb.vip.ip_address) if member_is_ipv6 != vip_is_ipv6: msg = ("This provider doesn't support mixing IPv4 and " "IPv6 addresses for its VIP and members in UDP " "load balancers.") raise exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)
def member_create(self, member): # Validate monitoring options if present self._check_member_monitor_options(member) if self._ip_version_differs(member): raise ovn_exc.IPVersionsMixingNotSupportedError() admin_state_up = member.admin_state_up subnet_id = member.subnet_id if (isinstance(subnet_id, o_datamodels.UnsetType) or not subnet_id): subnet_id = self._ovn_helper._get_subnet_from_pool(member.pool_id) if not subnet_id: msg = _('Subnet is required, or Loadbalancer associated with ' 'Pool must have a subnet, for Member creation ' 'with OVN Provider Driver') raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg) if isinstance(admin_state_up, o_datamodels.UnsetType): admin_state_up = True request_info = {'id': member.member_id, 'address': member.address, 'protocol_port': member.protocol_port, 'pool_id': member.pool_id, 'subnet_id': subnet_id, 'admin_state_up': admin_state_up} request = {'type': ovn_const.REQ_TYPE_MEMBER_CREATE, 'info': request_info} self._ovn_helper.add_request(request) # NOTE(mjozefcz): If LB has FIP on VIP # and member has FIP we need to centralize # traffic for member. request_info = {'id': member.member_id, 'address': member.address, 'pool_id': member.pool_id, 'subnet_id': subnet_id, 'action': ovn_const.REQ_INFO_MEMBER_ADDED} request = {'type': ovn_const.REQ_TYPE_HANDLE_MEMBER_DVR, 'info': request_info} self._ovn_helper.add_request(request)
def validate_flavor(self, flavor_dict): try: validate(flavor_dict, flavor_schema.SUPPORTED_FLAVOR_SCHEMA) # validate flavor for slb objects if 'virtual-server' in flavor_dict: flavor = flavor_dict['virtual-server'] if 'name' in flavor: raise Exception('axapi key \'name\' is not allowed') if 'ip-address' in flavor: raise Exception( 'axapi key \'ip-address\' is not supported yet') self._validate_flavor_name_expressions(flavor) if 'virtual-port' in flavor_dict: flavor = flavor_dict['virtual-port'] if 'name' in flavor: raise Exception('axapi key \'name\' is not allowed') if 'port-number' in flavor: raise Exception('axapi key \'port-number\' is not allowed') if 'protocol' in flavor: raise Exception('axapi key \'protocol\' is not allowed') self._validate_flavor_name_expressions(flavor) if 'service-group' in flavor_dict: flavor = flavor_dict['service-group'] if 'name' in flavor: raise Exception('axapi key \'name\' is not allowed') self._validate_flavor_name_expressions(flavor) if 'server' in flavor_dict: flavor = flavor_dict['server'] if 'name' in flavor: raise Exception('axapi key \'name\' is not allowed') self._validate_flavor_name_expressions(flavor) if 'health-monitor' in flavor_dict: flavor = flavor_dict['health-monitor'] if 'name' in flavor: raise Exception('axapi key \'name\' is not allowed') self._validate_flavor_name_expressions(flavor) # validate nat-pool and nat-pool-list keys if 'nat-pool' in flavor_dict: nat = flavor_dict['nat-pool'] if 'pool-name' not in nat: raise Exception( 'pool-name is required for nat-pool flavor') if 'start-address' not in nat: raise Exception( 'start-address is required for nat-pool flavor') if 'end-address' not in nat: raise Exception( 'end-address is required for nat-pool flavor') if 'netmask' not in nat: raise Exception('netmask is required for nat-pool flavor') if 'nat-pool-list' in flavor_dict: for nat in flavor_dict['nat-pool-list']: if 'pool-name' not in nat: raise Exception( 'pool-name is required for nat-pool-list flavor') if 'start-address' not in nat: raise Exception( 'start-address is required for nat-pool-list flavor' ) if 'end-address' not in nat: raise Exception( 'end-address is required for nat-pool-list flavor') if 'netmask' not in nat: raise Exception( 'netmask is required for nat-pool-list flavor') if 'deployment' in flavor_dict: deployment = flavor_dict['deployment'] if ('dsr_type' in deployment and deployment['dsr_type'] not in ['l2dsr_transparent']): raise Exception( 'l2dsr_transparent is required value for dsr_type') except js_exceptions.ValidationError as e: error_object = '' if e.relative_path: error_object = '{} '.format(e.relative_path[0]) raise exceptions.UnsupportedOptionError( user_fault_string='{0}{1}'.format(error_object, e.message), operator_fault_string=str(e)) except Exception as e: raise exceptions.DriverError( user_fault_string='Failed to validate the flavor metadata ' 'due to: {}'.format(str(e)), operator_fault_string='Failed to validate the flavor metadata ' 'due to: {}'.format(str(e)))
def _check_for_supported_algorithms(self, algorithm): if algorithm not in ovn_const.OVN_NATIVE_LB_ALGORITHMS: msg = _('OVN provider does not support %s algorithm') % algorithm raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)
def _check_for_supported_protocols(self, protocol): if protocol not in ovn_const.OVN_NATIVE_LB_PROTOCOLS: msg = _('OVN provider does not support %s protocol') % protocol raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)
def member_batch_update(self, pool_id, members): request_list = [] skipped_members = [] pool_key, ovn_lb = self._ovn_helper._find_ovn_lb_by_pool_id(pool_id) external_ids = copy.deepcopy(ovn_lb.external_ids) pool = external_ids[pool_key] existing_members = pool.split(',') if pool else [] members_to_delete = copy.copy(existing_members) pool_subnet_id = None for member in members: if (self._check_monitor_options(member) or member.address and self._ip_version_differs(member)): skipped_members.append(member.member_id) continue # NOTE(froyo): if subnet_id not provided, lets try to get it # from the member pool_id subnet_id = member.subnet_id if (isinstance(subnet_id, o_datamodels.UnsetType) or not subnet_id): pool_subnet_id = ( pool_subnet_id if pool_subnet_id else self._ovn_helper._get_subnet_from_pool(pool_id)) # NOTE(mjozefcz): We need to have subnet_id information. if not pool_subnet_id: msg = _('Subnet is required, or Loadbalancer associated ' 'with Pool must have a subnet, for Member ' 'creation with OVN Provider Driver') raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg) member.subnet_id = pool_subnet_id admin_state_up = member.admin_state_up if isinstance(admin_state_up, o_datamodels.UnsetType): admin_state_up = True member_info = self._ovn_helper._get_member_info(member) if member_info not in existing_members: req_type = ovn_const.REQ_TYPE_MEMBER_CREATE else: # If member exists in pool, then Update req_type = ovn_const.REQ_TYPE_MEMBER_UPDATE # Remove all updating members so only deleted ones are left members_to_delete.remove(member_info) request_info = {'id': member.member_id, 'address': member.address, 'protocol_port': member.protocol_port, 'pool_id': member.pool_id, 'subnet_id': member.subnet_id, 'admin_state_up': admin_state_up} request = {'type': req_type, 'info': request_info} request_list.append(request) for member in members_to_delete: member_info = member.split('_') request_info = {'id': member_info[1], 'address': member_info[2].split(':')[0], 'protocol_port': member_info[2].split(':')[1], 'pool_id': pool_id} if len(member_info) == 4: request_info['subnet_id'] = member_info[3] request = {'type': ovn_const.REQ_TYPE_MEMBER_DELETE, 'info': request_info} request_list.append(request) for request in request_list: self._ovn_helper.add_request(request) if skipped_members: msg = (_('OVN provider does not support monitor options, ' 'so following members skipped: %s') % skipped_members) raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)
def loadbalancer_failover(self, loadbalancer_id): msg = _('OVN provider does not support loadbalancer failover') raise driver_exceptions.UnsupportedOptionError( user_fault_string=msg, operator_fault_string=msg)