diff --git a/specs/version1.1/enable-provider-driver.rst b/specs/version1.1/enable-provider-driver.rst new file mode 100644 index 0000000000..595e74dd7a --- /dev/null +++ b/specs/version1.1/enable-provider-driver.rst @@ -0,0 +1,1739 @@ +============================== +Enable Provider Driver Support +============================== +.. contents:: Specification Table of Contents + :depth: 4 + :backlinks: none + +https://storyboard.openstack.org/#!/story/1655768 + +Provider drivers are implementations that give Octavia operators a choice of +which load balancing systems to use in their Octavia deployment. Currently, the +default Octavia driver is the only one available. Operators may want to employ +other load balancing implementations, including hardware appliances, +in addition to the default Octavia driver. + +Problem description +=================== +Neutron LBaaS v2 supports a *provider* parameter, giving LBaaS users a way to +direct LBaaS requests to a specific backend driver. The Octavia API includes a +*provider* parameter as well, but currently supports one provider, the +Octavia driver. Adding support for other drivers is needed. With this in place, +operators can configure load balancers using multiple providers, either the +Octavia default or others. + +Proposed change +=============== +Available drivers will be enabled by entries in the Octavia configuration file. +Drivers will be loaded via stevedore and Octavia will communicate with drivers +through a standard class interface defined below. Most driver functions will be +asynchronous to Octavia, and Octavia will provide a library of functions +that give drivers a way to update status and statistics. Functions that are +synchronous are noted below. + +Octavia API functions not listed here will continue to be handled by the +Octavia API and will not call into the driver. Examples would be show, list, +and quota requests. + +Driver Entry Points +------------------- + +Provider drivers will be loaded via +`stevedore `_. Drivers will +have an entry point defined in their setup tools configuration using the +Octavia driver namespace "octavia.api.drivers". This entry point name will +be used to enable the driver in the Octavia configuration file and as the +"provider" parameter users specify when creating a load balancer. An example +for the octavia reference driver would be: + +.. code-block:: python + + octavia = octavia.api.drivers.octavia.driver:OctaviaDriver + +Octavia Provider Driver API +--------------------------- + +Provider drivers will be expected to support the full interface described +by the Octavia API, currently v2.0. If a driver does not implement an API +function, drivers should fail a request by raising a ``NotImplementedError`` +exception. If a driver implements a function but does not support a particular +option passed in by the caller, the driver should raise an +``UnsupportedOptionError``. + +It is recommended that drivers use the +`jsonschema `_ package or +`voluptuous `_ to validate the +request against the current driver capabilities. + +See the `Exception Model`_ below for more details. + +.. note:: Driver developers should refer to the official `Octavia API reference ` document for details of the fields and expected outcome of these calls. + +Load balancer +^^^^^^^^^^^^^ + +* **create** + + Creates a load balancer. + + Octavia will pass in the load balancer object with all requested settings. + + The load balancer will be in the ``PENDING_CREATE`` provisioning_status and + ``OFFLINE`` operating_status when it is passed to the driver. The driver + will be responsible for updating the provisioning status of the load + balancer to either ``ACTIVE`` if successfully created, or ``ERROR`` if not + created. + + The Octavia API will accept and do basic API validation of the create + request from the user. The load balancer python object representing the + request body will be passed to the driver create method as it was received + and validated with the following exceptions: + + 1. The provider will be removed as this is used for driver selection. + 2. The flavor will be expanded from the provided ID to be the full + dictionary representing the flavor metadata. + + **Load balancer object** + + As of the writing of this specification the create load balancer object may + contain the following: + + +-----------------+--------+-----------------------------------------------+ + | Name | Type | Description | + +=================+========+===============================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------+--------+-----------------------------------------------+ + | description | string | A human-readable description for the resource.| + +-----------------+--------+-----------------------------------------------+ + | flavor | dict | The flavor keys and values. | + +-----------------+--------+-----------------------------------------------+ + | listeners | list | A list of `Listener objects`_. | + +-----------------+--------+-----------------------------------------------+ + | loadbalancer_id | string | ID of load balancer to create. | + +-----------------+--------+-----------------------------------------------+ + | name | string | Human-readable name of the resource. | + +-----------------+--------+-----------------------------------------------+ + | project_id | string | ID of the project owning this resource. | + +-----------------+--------+-----------------------------------------------+ + | vip_address | string | The IP address of the Virtual IP (VIP). | + +-----------------+--------+-----------------------------------------------+ + | vip_network_id | string | The ID of the network for the VIP. | + +-----------------+--------+-----------------------------------------------+ + | vip_port_id | string | The ID of the VIP port. | + +-----------------+--------+-----------------------------------------------+ + | vip_subnet_id | string | The ID of the subnet for the VIP. | + +-----------------+--------+-----------------------------------------------+ + + The driver is expected to validate that the driver supports the request + and raise an exception if the request cannot be accepted. + + **VIP port creation** + + Some provider drivers will want to create the Neutron port for the VIP, and + others will want Octavia to create the port instead. In order to support both + use cases, the create_vip_port() method will ask provider drivers to create + a VIP port. If the driver expects Octavia to create the port, the driver + will raise a NotImplementedError exception. Octavia will call this function + before calling loadbalancer_create() in order to determine if it should + create the VIP port. Octavia will call create_vip_port() with a loadbalancer + ID and a partially defined VIP dictionary. Provider drivers that support + port creation will create the port and return a fully populated VIP + dictionary. + + **VIP dictionary** + + +-----------------+--------+-----------------------------------------------+ + | Name | Type | Description | + +=================+========+===============================================+ + | vip_address | string | The IP address of the Virtual IP (VIP). | + +-----------------+--------+-----------------------------------------------+ + | vip_network_id | string | The ID of the network for the VIP. | + +-----------------+--------+-----------------------------------------------+ + | vip_port_id | string | The ID of the VIP port. | + +-----------------+--------+-----------------------------------------------+ + | vip_subnet_id | string | The ID of the subnet for the VIP. | + +-----------------+--------+-----------------------------------------------+ + + *Creating a Fully Populated Load Balancer* + + If the "listener" option is specified, the provider driver will iterate + through the list and create all of the child objects in addition to + creating the load balancer instance. + +* **delete** + + Removes an existing load balancer. + + Octavia will pass in the load balancer ID and cascade bollean as parameters. + + The load balancer will be in the ``PENDING_DELETE`` provisioning_status when + it is passed to the driver. The driver will notify Octavia that the delete + was successful by setting the provisioning_status to ``DELETED``. If the + delete failed, the driver will update the provisioning_status to ``ERROR``. + + The API includes an option for cascade delete. When cascade is set to + True, the provider driver will delete all child objects of the load balancer. + +* **failover** + + Performs a failover of a load balancer. + + Octavia will pass in the load balancer ID as a parameter. + + The load balancer will be in the ``PENDING_UPDATE`` provisioning_status when + it is passed to the driver. The driver will update the provisioning_status + of the load balancer to either ``ACTIVE`` if successfully failed over, or + ``ERROR`` if not failed over. + + Failover can mean different things in the context of a provider driver. For + example, the Octavia driver replaces the current amphora(s) with another + amphora. For another provider driver, failover may mean failing over from + an active system to a standby system. + +* **update** + + Modifies an existing load balancer using the values supplied in the load + balancer object. + + Octavia will pass in a load balancer object with the fields to be updated. + + As of the writing of this specification the update load balancer object may + contain the following: + + +-----------------+--------+-----------------------------------------------+ + | Name | Type | Description | + +=================+========+===============================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------+--------+-----------------------------------------------+ + | description | string | A human-readable description for the resource.| + +-----------------+--------+-----------------------------------------------+ + | loadbalancer_id | string | ID of load balancer to update. | + +-----------------+--------+-----------------------------------------------+ + | name | string | Human-readable name of the resource. | + +-----------------+--------+-----------------------------------------------+ + + The load balancer will be in the ``PENDING_UPDATE`` provisioning_status when + it is passed to the driver. The driver will update the provisioning_status + of the load balancer to either ``ACTIVE`` if successfully updated, or + ``ERROR`` if the update was not successful. + + The driver is expected to validate that the driver supports the request. + The method will then return or raise an exception if the request cannot be + accepted. + +**Abstract class definition** + +.. code-block:: python + + class Driver(object): + + def create_vip_port(self, loadbalancer_id, vip_dictionary): + """Creates a port for a load balancer VIP. + + If the driver supports creating VIP ports, the driver will create a + VIP port and return the vip_dictionary populated with the vip_port_id. + If the driver does not support port creation, the driver will raise + a NotImplementedError. + + :param: loadbalancer_id (string): ID of loadbalancer. + :param: vip_dictionary (dict): The VIP dictionary. + :returns: VIP dictionary with vip_port_id. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: The driver does not support creating + VIP ports. + """ + raise NotImplementedError() + + def loadbalancer_create(self, loadbalancer): + """Creates a new load balancer. + + :param loadbalancer (object): The load balancer object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: The driver does not support create. + :raises UnsupportedOptionError: The driver does not + support one of the configuration options. + """ + raise NotImplementedError() + + def loadbalancer_delete(self, loadbalancer_id, cascade=False): + """Deletes a load balancer. + + :param loadbalancer_id (string): ID of the load balancer to delete. + :param cascade (bool): If True, deletes all child objects (listeners, + pools, etc.) in addition to the load balancer. + :return: Nothing if the delete request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + """ + raise NotImplementedError() + + def loadbalancer_failover(self, loadbalancer_id): + """Performs a fail over of a load balancer. + + :param loadbalancer_id (string): ID of the load balancer to failover. + :return: Nothing if the failover request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises: NotImplementedError if driver does not support request. + """ + raise NotImplementedError() + + def loadbalancer_update(self, loadbalancer): + """Updates a load balancer. + + :param loadbalancer (object): The load balancer object. + :return: Nothing if the update request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: The driver does not support request. + :raises UnsupportedOptionError: The driver does not + support one of the configuration options. + """ + raise NotImplementedError() + +Listener +^^^^^^^^ + +* **create** + + Creates a listener for a load balancer. + + Octavia will pass in the listener object with all requested settings. + + The listener will be in the ``PENDING_CREATE`` provisioning_status and + ``OFFLINE`` operating_status when it is passed to the driver. The driver + will be responsible for updating the provisioning status of the listener + to either ``ACTIVE`` if successfully created, or ``ERROR`` if not created. + + The Octavia API will accept and do basic API validation of the create + request from the user. The listener python object representing the + request body will be passed to the driver create method as it was received + and validated with the following exceptions: + + 1. The project_id will be removed, if present, as this field is now + deprecated. The listener will inherit the project_id from the parent + load balancer. + 2. The default_tls_container_ref will be expanded and provided to the driver + in pkcs12 format. + 3. The sni_container_refs will be expanded and provided to the driver in + pkcs12 format. + + .. _Listener objects: + + **Listener object** + + As of the writing of this specification the create listener object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | connection_limit | int | The max number of connections permitted | + | | | for this listener. Default is -1, which | + | | | is infinite connections. | + +-----------------------+--------+------------------------------------------+ + | default_pool | object | A `Pool object`_. | + +-----------------------+--------+------------------------------------------+ + | default_pool_id | string | The ID of the pool used by the listener | + | | | if no L7 policies match. | + +-----------------------+--------+------------------------------------------+ + | default_tls_container | object | A pkcs12 format certicate and key. | + +-----------------------+--------+------------------------------------------+ + | description | string | A human-readable description for the | + | | | listener. | + +-----------------------+--------+------------------------------------------+ + | insert_headers | dict | A dictionary of optional headers to | + | | | insert into the request before it is sent| + | | | to the backend member. See | + | | | `Supported HTTP Header Insertions`_. | + | | | Keys and values are specified as strings.| + +-----------------------+--------+------------------------------------------+ + | l7policies | list | A list of `L7policy objects`_. | + +-----------------------+--------+------------------------------------------+ + | listener_id | string | ID of listener to create. | + +-----------------------+--------+------------------------------------------+ + | loadbalancer_id | string | ID of load balancer. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the listener. | + +-----------------------+--------+------------------------------------------+ + | protocol | string | Protocol type: One of HTTP, HTTPS, TCP, | + | | | or TERMINATED_HTTPS. | + +-----------------------+--------+------------------------------------------+ + | protocol_port | int | Protocol port number. | + +-----------------------+--------+------------------------------------------+ + | sni_containers | object | A pkcs12 format set of certificates. | + +-----------------------+--------+------------------------------------------+ + + .. _Supported HTTP Header Insertions: + + As of the writing of this specification the Supported HTTP Header Insertions + are: + + +-------------------+------+------------------------------------------------+ + | Key | Type | Description | + +===================+======+================================================+ + | X-Forwarded-For | bool | When True a X-Forwarded-For header is inserted | + | | | into the request to the backend member that | + | | | specifies the client IP address. | + +-------------------+------+------------------------------------------------+ + | X-Forwarded-Port | int | A X-Forwarded-Port header is inserted into the | + | | | request to the backend member that specifies | + | | | the integer provided. Typically this is used to| + | | | indicate the port the client connected to on | + | | | the load balancer. | + +-------------------+------+------------------------------------------------+ + + *Creating a Fully Populated Listener* + + If the "default_pool" or "l7policies" option is specified, the provider + driver will create all of the child objects in addition to creating the + listener instance. + +* **delete** + + Deletes an existing listener. + + Octavia will pass the listener ID as a parameter. + + The listener will be in the ``PENDING_DELETE`` provisioning_status when + it is passed to the driver. The driver will notify Octavia that the delete + was successful by setting the provisioning_status to ``DELETED``. If the + delete failed, the driver will update the provisioning_status to ``ERROR``. + +* **update** + + Modifies an existing listener using the values supplied in the listener + object. + + Octavia will pass in a listener object with the fields to be updated. + + As of the writing of this specification the update listener object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+----------+-------------------------------+ + | connection_limit | int | The max number of connections permitted | + | | | for this listener. Default is -1, which | + | | | is infinite connections. | + +-----------------------+--------+------------------------------------------+ + | default_pool_id | string | The ID of the pool used by the listener | + | | | if no L7 policies match. | + +-----------------------+--------+------------------------------------------+ + | default_tls_container | object | A pkcs12 format certicate and key. | + +-----------------------+--------+------------------------------------------+ + | description | string | A human-readable description for the | + | | | listener. | + +-----------------------+--------+------------------------------------------+ + | insert_headers | dict | A dictionary of optional headers to | + | | | insert into the request before it is sent| + | | | to the backend member. See | + | | | `Supported HTTP Header Insertions`_. | + | | | Keys and values are specified as strings.| + +-----------------------+--------+------------------------------------------+ + | listener_id | string | ID of listener to update. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the listener. | + +-----------------------+--------+------------------------------------------+ + | sni_containers | object | A pkcs12 format set of certificates. | + +-----------------------+--------+------------------------------------------+ + + The listener will be in the ``PENDING_UPDATE`` provisioning_status when + it is passed to the driver. The driver will update the provisioning_status + of the listener to either ``ACTIVE`` if successfully updated, or ``ERROR`` + if the update was not successful. + + The driver is expected to validate that the driver supports the request. + The method will then return or raise an exception if the request cannot be + accepted. + +**Abstract class definition** + +.. code-block:: python + + class Driver(object): + def listener_create(self, listener): + """Creates a new listener. + + :param listener (object): The listener object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + + def listener_delete(self, listener_id): + """Deletes a listener. + + :param listener_id (string): ID of the listener to delete. + :return: Nothing if the delete request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + """ + raise NotImplementedError() + + def listener_update(self, listener): + """Updates a listener. + + :param listener (object): The listener object. + :return: Nothing if the update request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + +Pool +^^^^ + +* **create** + + Creates a pool for a load balancer. + + Octavia will pass in the pool object with all requested settings. + + The pool will be in the ``PENDING_CREATE`` provisioning_status and + ``OFFLINE`` operating_status when it is passed to the driver. The driver + will be responsible for updating the provisioning status of the pool + to either ``ACTIVE`` if successfully created, or ``ERROR`` if not created. + + The Octavia API will accept and do basic API validation of the create + request from the user. The pool python object representing the request + body will be passed to the driver create method as it was received and + validated with the following exceptions: + + 1. The project_id will be removed, if present, as this field is now + deprecated. The listener will inherit the project_id from the parent + load balancer. + + .. _Pool object: + + **Pool object** + + As of the writing of this specification the create pool object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | description | string | A human-readable description for the | + | | | pool. | + +-----------------------+--------+------------------------------------------+ + | healthmonitor | object | A `Healthmonitor object`_. | + +-----------------------+--------+------------------------------------------+ + | lb_algorithm | string | Load balancing algorithm: One of | + | | | ROUND_ROBIN, LEAST_CONNECTIONS, or | + | | | SOURCE_IP. | + +-----------------------+--------+------------------------------------------+ + | listener_id | string | ID of listener. Required if | + | | | loadbalancer_id not specified. | + +-----------------------+--------+------------------------------------------+ + | loadbalancer_id | string | ID of load balancer. Required if | + | | | listener_id not specified. | + +-----------------------+--------+------------------------------------------+ + | members | list | A list of `Member objects`_. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the pool. | + +-----------------------+--------+------------------------------------------+ + | pool_id | string | ID of pool to create. | + +-----------------------+--------+------------------------------------------+ + | protocol | string | Protocol type: One of HTTP, HTTPS, | + | | | PROXY, or TCP. | + +-----------------------+--------+------------------------------------------+ + | session_persistence | dict | Defines session persistence as one of | + | | | {'type': <'HTTP_COOKIE' | 'SOURCE_IP'>} | + | | | OR | + | | | {'type': 'APP_COOKIE', | + | | | 'cookie_name': } | + +-----------------------+--------+------------------------------------------+ + +* **delete** + + Removes an existing pool and all of its members. + + Octavia will pass the pool ID as a parameter. + + The pool will be in the ``PENDING_DELETE`` provisioning_status when + it is passed to the driver. The driver will notify Octavia that the delete + was successful by setting the provisioning_status to ``DELETED``. If the + delete failed, the driver will update the provisioning_status to ``ERROR``. + +* **update** + + Modifies an existing pool using the values supplied in the pool object. + + Octavia will pass in a pool object with the fields to be updated. + + As of the writing of this specification the update pool object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | description | string | A human-readable description for the | + | | | pool. | + +-----------------------+--------+------------------------------------------+ + | lb_algorithm | string | Load balancing algorithm: One of | + | | | ROUND_ROBIN, LEAST_CONNECTIONS, or | + | | | SOURCE_IP. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the pool. | + +-----------------------+--------+------------------------------------------+ + | pool_id | string | ID of pool to update. | + +-----------------------+--------+------------------------------------------+ + | session_persistence | dict | Defines session persistence as one of | + | | | {'type': <'HTTP_COOKIE' | 'SOURCE_IP'>} | + | | | OR | + | | | {'type': 'APP_COOKIE', | + | | | 'cookie_name': } | + +-----------------------+--------+------------------------------------------+ + + The pool will be in the ``PENDING_UPDATE`` provisioning_status when it is + passed to the driver. The driver will update the provisioning_status of the + pool to either ``ACTIVE`` if successfully updated, or ``ERROR`` if the + update was not successful. + + The driver is expected to validate that the driver supports the request. + The method will then return or raise an exception if the request cannot be + accepted. + +**Abstract class definition** + +.. code-block:: python + + class Driver(object): + def pool_create(self, pool): + """Creates a new pool. + + :param pool (object): The pool object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + + def pool_delete(self, pool_id): + """Deletes a pool and its members. + + :param pool_id (string): ID of the pool to delete. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + """ + raise NotImplementedError() + + def pool_update(self, pool): + """Updates a pool. + + :param pool (object): The pool object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + +Member +^^^^^^ + +* **create** + + Creates a member for a pool. + + Octavia will pass in the member object with all requested settings. + + The member will be in the ``PENDING_CREATE`` provisioning_status and + ``OFFLINE`` operating_status when it is passed to the driver. The driver + will be responsible for updating the provisioning status of the member + to either ``ACTIVE`` if successfully created, or ``ERROR`` if not created. + + The Octavia API will accept and do basic API validation of the create + request from the user. The member python object representing the + request body will be passed to the driver create method as it was received + and validated with the following exceptions: + + 1. The project_id will be removed, if present, as this field is now + deprecated. The member will inherit the project_id from the parent + load balancer. + + .. _Member objects: + + **Member object** + + As of the writing of this specification the create member object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | address | string | The IP address of the backend member to | + | | | receive traffic from the load balancer. | + +-----------------------+--------+------------------------------------------+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | member_id | string | ID of member to create. | + +-----------------------+--------+------------------------------------------+ + | monitor_address | string | An alternate IP address used for health | + | | | monitoring a backend member. | + +-----------------------+--------+------------------------------------------+ + | monitor_port | int | An alternate protocol port used for | + | | | health monitoring a backend member. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the member. | + +-----------------------+--------+------------------------------------------+ + | pool_id | string | ID of pool. | + +-----------------------+--------+------------------------------------------+ + | protocol_port | int | The port on which the backend member | + | | | listens for traffic. | + +-----------------------+--------+------------------------------------------+ + | subnet_id | string | Subnet ID. | + +-----------------------+--------+------------------------------------------+ + | weight | int | The weight of a member determines the | + | | | portion of requests or connections it | + | | | services compared to the other members of| + | | | the pool. For example, a member with a | + | | | weight of 10 receives five times as many | + | | | requests as a member with a weight of 2. | + | | | A value of 0 means the member does not | + | | | receive new connections but continues to | + | | | service existing connections. A valid | + | | | value is from 0 to 256. Default is 1. | + +-----------------------+--------+------------------------------------------+ + +* **delete** + + Removes a pool member. + + Octavia will pass the member ID as a parameter. + + The member will be in the ``PENDING_DELETE`` provisioning_status when + it is passed to the driver. The driver will notify Octavia that the delete + was successful by setting the provisioning_status to ``DELETED``. If the + delete failed, the driver will update the provisioning_status to ``ERROR``. + +* **update** + + Modifies an existing member using the values supplied in the listener object. + + Octavia will pass in a member object with the fields to be updated. + + As of the writing of this specification the update member object may contain + the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | member_id | string | ID of member to update. | + +-----------------------+--------+------------------------------------------+ + | monitor_address | string | An alternate IP address used for health | + | | | monitoring a backend member. | + +-----------------------+--------+------------------------------------------+ + | monitor_port | int | An alternate protocol port used for | + | | | health monitoring a backend member. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the member. | + +-----------------------+--------+------------------------------------------+ + | weight | int | The weight of a member determines the | + | | | portion of requests or connections it | + | | | services compared to the other members of| + | | | the pool. For example, a member with a | + | | | weight of 10 receives five times as many | + | | | requests as a member with a weight of 2. | + | | | A value of 0 means the member does not | + | | | receive new connections but continues to | + | | | service existing connections. A valid | + | | | value is from 0 to 256. Default is 1. | + +-----------------------+--------+------------------------------------------+ + + The member will be in the ``PENDING_UPDATE`` provisioning_status when + it is passed to the driver. The driver will update the provisioning_status + of the member to either ``ACTIVE`` if successfully updated, or ``ERROR`` + if the update was not successful. + + The driver is expected to validate that the driver supports the request. + The method will then return or raise an exception if the request cannot be + accepted. + +* **batch update** + + Set the state of members for a pool in one API call. This may include + creating new members, deleting old members, and updating existing members. + Existing members are matched based on address/port combination. + + For example, assume a pool currently has two members. These members have the + following address/port combinations: ‘192.0.2.15:80’ and ‘192.0.2.16:80’. + Now assume a PUT request is made that includes members with address/port + combinations: ‘192.0.2.16:80’ and ‘192.0.2.17:80’. The member ‘192.0.2.15:80’ + will be deleted because it was not in the request. The member ‘192.0.2.16:80’ + will be updated to match the request data for that member, because it was + matched. The member ‘192.0.2.17:80’ will be created, because no such member + existed. + + The members will be in the ``PENDING_CREATE``, ``PENDING_UPDATE``, or + ``PENDING_DELETE`` provisioning_status when it is passed to the driver. + The driver will update the provisioning_status of the members to either + ``ACTIVE`` or ``DELETED`` if successfully updated, or ``ERROR`` + if the update was not successful. + + The batch update method will supply a list of `Member objects`_. + Existing members not in this list should be deleted, + existing members in the list should be updated, + and members in the list that do not already exist should be created. + +**Abstract class definition** + +.. code-block:: python + + class Driver(object): + def member_create(self, member): + """Creates a new member for a pool. + + :param member (object): The member object. + + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + + def member_delete(self, member_id): + + """Deletes a pool member. + + :param member_id (string): ID of the member to delete. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + """ + raise NotImplementedError() + + def member_update(self, member): + + """Updates a pool member. + + :param member (object): The member object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + + def member_batch_update(self, members): + """Creates, updates, or deletes a set of pool members. + + :param members (list): List of member objects. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + +Health Monitor +^^^^^^^^^^^^^^ + +* **create** + + Creates a health monitor on a pool. + + Octavia will pass in the health monitor object with all requested settings. + + The health monitor will be in the ``PENDING_CREATE`` provisioning_status and + ``OFFLINE`` operating_status when it is passed to the driver. The driver + will be responsible for updating the provisioning status of the health + monitor to either ``ACTIVE`` if successfully created, or ``ERROR`` if not + created. + + The Octavia API will accept and do basic API validation of the create + request from the user. The healthmonitor python object representing the + request body will be passed to the driver create method as it was received + and validated with the following exceptions: + + 1. The project_id will be removed, if present, as this field is now + deprecated. The listener will inherit the project_id from the parent + load balancer. + + .. _Healthmonitor object: + + **Healthmonitor object** + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | delay | int | The interval, in seconds, between health | + | | | checks. | + +-----------------------+--------+------------------------------------------+ + | expected_codes | string | The expected HTTP status codes to get | + | | | from a successful health check. This may | + | | | be a single value, a list, or a range. | + +-----------------------+--------+------------------------------------------+ + | healthmonitor_id | string | ID of health monitor to create. | + +-----------------------+--------+------------------------------------------+ + | http_method | string | The HTTP method that the health monitor | + | | | uses for requests. One of CONNECT, | + | | | DELETE, GET, HEAD, OPTIONS, PATCH, POST, | + | | | PUT, or TRACE. | + +-----------------------+--------+------------------------------------------+ + | max_retries | int | The number of successful checks before | + | | | changing the operating status of the | + | | | member to ONLINE. | + +-----------------------+--------+------------------------------------------+ + | max_retries_down | int | The number of allowed check failures | + | | | before changing the operating status of | + | | | the member to ERROR. A valid value is | + | | | from 1 to 10. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the monitor. | + +-----------------------+--------+------------------------------------------+ + | pool_id | string | The pool to monitor. | + +-----------------------+--------+------------------------------------------+ + | timeout | int | The time, in seconds, after which a | + | | | health check times out. This value must | + | | | be less than the delay value. | + +-----------------------+--------+------------------------------------------+ + | type | string | The type of health monitor. One of HTTP, | + | | | HTTPS, PING, TCP, or TLS-HELLO. | + +-----------------------+--------+------------------------------------------+ + | url_path | string | The HTTP URL path of the request sent by | + | | | the monitor to test the health of a | + | | | backend member. Must be a string that | + | | | begins with a forward slash (/). | + +-----------------------+--------+------------------------------------------+ + +* **delete** + + Deletes an existing health monitor. + + Octavia will pass in the health monitor ID as a parameter. + + The health monitor will be in the ``PENDING_DELETE`` provisioning_status + when it is passed to the driver. The driver will notify Octavia that the + delete was successful by setting the provisioning_status to ``DELETED``. + If the delete failed, the driver will update the provisioning_status to + ``ERROR``. + +* **update** + + Modifies an existing health monitor using the values supplied in the + health monitor object. + + Octavia will pass in a health monitor object with the fields to be updated. + + As of the writing of this specification the update health monitor object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | delay | int | The interval, in seconds, between health | + | | | checks. | + +-----------------------+--------+------------------------------------------+ + | expected_codes | string | The expected HTTP status codes to get | + | | | from a successful health check. This may | + | | | be a single value, a list, or a range. | + +-----------------------+--------+------------------------------------------+ + | healthmonitor_id | string | ID of health monitor to create. | + +-----------------------+--------+------------------------------------------+ + | http_method | string | The HTTP method that the health monitor | + | | | uses for requests. One of CONNECT, | + | | | DELETE, GET, HEAD, OPTIONS, PATCH, POST, | + | | | PUT, or TRACE. | + +-----------------------+--------+------------------------------------------+ + | max_retries | int | The number of successful checks before | + | | | changing the operating status of the | + | | | member to ONLINE. | + +-----------------------+--------+------------------------------------------+ + | max_retries_down | int | The number of allowed check failures | + | | | before changing the operating status of | + | | | the member to ERROR. A valid value is | + | | | from 1 to 10. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the monitor. | + +-----------------------+--------+------------------------------------------+ + | timeout | int | The time, in seconds, after which a | + | | | health check times out. This value must | + | | | be less than the delay value. | + +-----------------------+--------+------------------------------------------+ + | url_path | string | The HTTP URL path of the request sent by | + | | | the monitor to test the health of a | + | | | backend member. Must be a string that | + | | | begins with a forward slash (/). | + +-----------------------+--------+------------------------------------------+ + + The health monitor will be in the ``PENDING_UPDATE`` provisioning_status + when it is passed to the driver. The driver will update the + provisioning_status of the health monitor to either ``ACTIVE`` if + successfully updated, or ``ERROR`` if the update was not successful. + + The driver is expected to validate that the driver supports the request. + The method will then return or raise an exception if the request cannot be + accepted. + +**Abstract class definition** + +.. code-block:: python + + class Driver(object): + def health_monitor_create(self, healthmonitor): + """Creates a new health monitor. + + :param healthmonitor (object): The health monitor object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + + def health_monitor_delete(self, healthmonitor_id): + """Deletes a healthmonitor_id. + + :param healthmonitor_id (string): ID of the monitor to delete. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + """ + raise NotImplementedError() + + def health_monitor_update(self, healthmonitor): + """Updates a health monitor. + + :param healthmonitor (object): The health monitor object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + +L7 Policy +^^^^^^^^^ + +* **create** + + Creates an L7 policy. + + Octavia will pass in the L7 policy object with all requested settings. + + The L7 policy will be in the ``PENDING_CREATE`` provisioning_status and + ``OFFLINE`` operating_status when it is passed to the driver. The driver + will be responsible for updating the provisioning status of the L7 policy + to either ``ACTIVE`` if successfully created, or ``ERROR`` if not created. + + The Octavia API will accept and do basic API validation of the create + request from the user. The l7policy python object representing the + request body will be passed to the driver create method as it was received + and validated with the following exceptions: + + 1. The project_id will be removed, if present, as this field is now + deprecated. The l7policy will inherit the project_id from the parent + load balancer. + + .. _L7policy objects: + + **L7policy object** + + As of the writing of this specification the create l7policy object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | action | string | The L7 policy action. One of | + | | | REDIRECT_TO_POOL, REDIRECT_TO_URL, or | + | | | REJECT. | + +-----------------------+--------+------------------------------------------+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | description | string | A human-readable description for the | + | | | L7 policy. | + +-----------------------+--------+------------------------------------------+ + | l7policy_id | string | The ID of the L7 policy. | + +-----------------------+--------+------------------------------------------+ + | listener_id | string | The ID of the listener. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the L7 policy. | + +-----------------------+--------+------------------------------------------+ + | position | int | The position of this policy on the | + | | | listener. Positions start at 1. | + +-----------------------+--------+------------------------------------------+ + | redirect_pool_id | string | Requests matching this policy will be | + | | | redirected to the pool with this ID. | + | | | Only valid if action is REDIRECT_TO_POOL.| + +-----------------------+--------+------------------------------------------+ + | redirect_url | string | Requests matching this policy will be | + | | | redirected to this URL. Only valid if | + | | | action is REDIRECT_TO_URL. | + +-----------------------+--------+------------------------------------------+ + | rules | list | A list of l7rule objects. | + +-----------------------+--------+------------------------------------------+ + + *Creating a Fully Populated L7 policy* + + If the "rules" option is specified, the provider driver will create all of + the child objects in addition to creating the L7 policy instance. + +* **delete** + + Deletes an existing L7 policy. + + Octavia will pass in the L7 policy ID as a parameter. + + The l7policy will be in the ``PENDING_DELETE`` provisioning_status when + it is passed to the driver. The driver will notify Octavia that the delete + was successful by setting the provisioning_status to ``DELETED``. If the + delete failed, the driver will update the provisioning_status to ``ERROR``. + +* **update** + + Modifies an existing L7 policy using the values supplied in the l7policy + object. + + Octavia will pass in an L7 policy object with the fields to be updated. + + As of the writing of this specification the update L7 policy object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | action | string | The L7 policy action. One of | + | | | REDIRECT_TO_POOL, REDIRECT_TO_URL, or | + | | | REJECT. | + +-----------------------+--------+------------------------------------------+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | description | string | A human-readable description for the | + | | | L7 policy. | + +-----------------------+--------+------------------------------------------+ + | l7policy_id | string | The ID of the L7 policy. | + +-----------------------+--------+------------------------------------------+ + | name | string | Human-readable name of the L7 policy. | + +-----------------------+--------+------------------------------------------+ + | position | int | The position of this policy on the | + | | | listener. Positions start at 1. | + +-----------------------+--------+------------------------------------------+ + | redirect_pool_id | string | Requests matching this policy will be | + | | | redirected to the pool with this ID. | + | | | Only valid if action is REDIRECT_TO_POOL.| + +-----------------------+--------+------------------------------------------+ + | redirect_url | string | Requests matching this policy will be | + | | | redirected to this URL. Only valid if | + | | | action is REDIRECT_TO_URL. | + +-----------------------+--------+------------------------------------------+ + + The L7 policy will be in the ``PENDING_UPDATE`` provisioning_status when + it is passed to the driver. The driver will update the provisioning_status + of the L7 policy to either ``ACTIVE`` if successfully updated, or ``ERROR`` + if the update was not successful. + + The driver is expected to validate that the driver supports the request. + The method will then return or raise an exception if the request cannot be + accepted. + +**Abstract class definition** + +.. code-block:: python + + class Driver(object): + def l7policy_create(self, l7policy): + """Creates a new L7 policy. + + :param l7policy (object): The l7policy object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + + def l7policy_delete(self, l7policy_id): + """Deletes an L7 policy. + + :param l7policy_id (string): ID of the L7 policy to delete. + :return: Nothing if the delete request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + """ + raise NotImplementedError() + + def l7policy_update(self, l7policy): + """Updates an L7 policy. + + :param l7policy (object): The l7policy object. + :return: Nothing if the update request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + +L7 Rule +^^^^^^^ + +* **create** + + Creates a new L7 rule for an existing L7 policy. + + Octavia will pass in the L7 rule object with all requested settings. + + The L7 rule will be in the ``PENDING_CREATE`` provisioning_status and + ``OFFLINE`` operating_status when it is passed to the driver. The driver + will be responsible for updating the provisioning status of the L7 rule + to either ``ACTIVE`` if successfully created, or ``ERROR`` if not created. + + The Octavia API will accept and do basic API validation of the create + request from the user. The l7rule python object representing the + request body will be passed to the driver create method as it was received + and validated with the following exceptions: + + 1. The project_id will be removed, if present, as this field is now + deprecated. The listener will inherit the project_id from the parent + load balancer. + + .. _L7rule objects: + + **L7rule object** + + As of the writing of this specification the create l7rule object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | compare_type | string | The comparison type for the L7 rule. One | + | | | of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, | + | | | or STARTS_WITH. | + +-----------------------+--------+------------------------------------------+ + | invert | bool | When True the logic of the rule is | + | | | inverted. For example, with invert True, | + | | | equal to would become not equal to. | + +-----------------------+--------+------------------------------------------+ + | key | string | The key to use for the comparison. For | + | | | example, the name of the cookie to | + | | | evaluate. | + +-----------------------+--------+------------------------------------------+ + | l7policy_id | string | The ID of the L7 policy. | + +-----------------------+--------+------------------------------------------+ + | l7rule_id | string | The ID of the L7 rule. | + +-----------------------+--------+------------------------------------------+ + | type | string | The L7 rule type. One of COOKIE, | + | | | FILE_TYPE, HEADER, HOST_NAME, or PATH. | + +-----------------------+--------+------------------------------------------+ + | value | string | The value to use for the comparison. For | + | | | example, the file type to compare. | + +-----------------------+--------+------------------------------------------+ + +* **delete** + + Deletes an existing L7 rule. + + Octavia will pass in the L7 rule ID as a parameter. + + The L7 rule will be in the ``PENDING_DELETE`` provisioning_status when + it is passed to the driver. The driver will notify Octavia that the delete + was successful by setting the provisioning_status to ``DELETED``. If the + delete failed, the driver will update the provisioning_status to ``ERROR``. + +* **update** + + Modifies an existing L7 rule using the values supplied in the l7rule object. + + Octavia will pass in an L7 rule object with the fields to be updated. + + As of the writing of this specification the update L7 rule object may + contain the following: + + +-----------------------+--------+------------------------------------------+ + | Name | Type | Description | + +=======================+========+==========================================+ + | admin_state_up | bool | Admin state: True if up, False if down. | + +-----------------------+--------+------------------------------------------+ + | compare_type | string | The comparison type for the L7 rule. One | + | | | of CONTAINS, ENDS_WITH, EQUAL_TO, REGEX, | + | | | or STARTS_WITH. | + +-----------------------+--------+------------------------------------------+ + | invert | bool | When True the logic of the rule is | + | | | inverted. For example, with invert True, | + | | | equal to would become not equal to. | + +-----------------------+--------+------------------------------------------+ + | key | string | The key to use for the comparison. For | + | | | example, the name of the cookie to | + | | | evaluate. | + +-----------------------+--------+------------------------------------------+ + | l7rule_id | string | The ID of the L7 rule. | + +-----------------------+--------+------------------------------------------+ + | type | string | The L7 rule type. One of COOKIE, | + | | | FILE_TYPE, HEADER, HOST_NAME, or PATH. | + +-----------------------+--------+------------------------------------------+ + | value | string | The value to use for the comparison. For | + | | | example, the file type to compare. | + +-----------------------+--------+------------------------------------------+ + + The L7 rule will be in the ``PENDING_UPDATE`` provisioning_status when + it is passed to the driver. The driver will update the provisioning_status + of the L7 rule to either ``ACTIVE`` if successfully updated, or ``ERROR`` + if the update was not successful. + + The driver is expected to validate that the driver supports the request. + The method will then return or raise an exception if the request cannot be + accepted. + +**Abstract class definition** + +.. code-block:: python + + class Driver(object): + def l7rule_create(self, l7rule): + + """Creates a new L7 rule. + + :param l7rule (object): The L7 rule object. + :return: Nothing if the create request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + + def l7rule_delete(self, l7rule_id): + + """Deletes an L7 rule. + + :param l7rule_id (string): ID of the L7 rule to delete. + :return: Nothing if the delete request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + """ + raise NotImplementedError() + + def l7rule_update(self, l7rule): + + """Updates an L7 rule. + + :param l7rule (object): The L7 rule object. + :return: Nothing if the update request was accepted. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: if driver does not support request. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + +Flavor +^^^^^^ + +Octavia flavors are defined in a separate specification (see References below). +Support for flavors will be provided through two provider driver interfaces, +one to query supported flavor metadata keys and another to validate that a +flavor is supported. Both functions are synchronous. + +* **get_supported_flavor_keys** + + Retrieves a dictionary of supported flavor keys and their description. + + .. code-block:: python + + {"topology": "The load balancer topology for the flavor. One of: SINGLE, ACTIVE_STANDBY", + "compute_flavor": "The compute driver flavor to use for the load balancer instances"} + +* **validate_flavor** + + Validates that the driver supports the flavor metadata dictionary. + + The validate_flavor method will be passed a flavor metadata dictionary that + the driver will validate. This is used when an operator uploads a new flavor + that applies to the driver. + + The validate_flavor method will either return or raise a + ``UnsupportedOptionError`` exception. + +Following are interface definitions for flavor support: + +.. code-block:: python + + def get_supported_flavor_metadata(): + """Returns a dictionary of flavor metadata keys supported by this driver. + + The returned dictionary will include key/value pairs, 'name' and + 'description.' + + :returns: The flavor metadata dictionary + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: The driver does not support flavors. + """ + raise NotImplementedError() + +.. code-block:: python + + def validate_flavor(flavor_metadata): + """Validates if driver can support flavor as defined in flavor_metadata. + + :param flavor_metadata (dict): Dictionary with flavor metadata. + :return: Nothing if the flavor is valid and supported. + :raises DriverError: An unexpected error occurred in the driver. + :raises NotImplementedError: The driver does not support flavors. + :raises UnsupportedOptionError: if driver does not + support one of the configuration options. + """ + raise NotImplementedError() + +Exception Model +^^^^^^^^^^^^^^^ + +DriverError +""""""""""" + +This is a catch all exception that drivers can return if there is an +unexpected error. An example might be a delete call for a load balancer the +driver does not recognize. This exception includes two strings: The user fault +string and the optional operator fault string. The user fault string, +"user_fault_string", will be provided to the API requester. The operator fault +string, "operator_fault_string", will be logged in the Octavia API log file +for the operator to use when debugging. + +.. code-block:: python + + class DriverError(Exception): + user_fault_string = _("An unknown driver error occurred.") + operator_fault_string = _("An unknown driver error occurred.") + + def __init__(self, *args, **kwargs): + self.user_fault_string = kwargs.pop('user_fault_string', + self.user_fault_string) + self.operator_fault_string = kwargs.pop('operator_fault_string', + self.operator_fault_string) + + super(DriverError, self).__init__(*args, **kwargs) + +NotImplementedError +""""""""""""""""""" + +Driver implementations may not support all operations, and are free to reject +a request. If the driver does not implement an API function, the driver will +raise a NotImplementedError exception. + +.. code-block:: python + + class NotImplementedError(Exception): + user_fault_string = _("A feature is not implemented by this driver.") + operator_fault_string = _("A feature is not implemented by this driver.") + + def __init__(self, *args, **kwargs): + self.user_fault_string = kwargs.pop('user_fault_string', + self.user_fault_string) + self.operator_fault_string = kwargs.pop('operator_fault_string', + self.operator_fault_string) + + super(NotImplementedError, self).__init__(*args, **kwargs) + +UnsupportedOptionError +"""""""""""""""""""""" + +Provider drivers will validate that they can complete the request -- that all +options are supported by the driver. If the request fails validation, drivers +will raise an UnsupportedOptionError exception. For example, if a driver does +not support a flavor passed as an option to load balancer create(), the driver +will raise an UnsupportedOptionError and include a message parameter providing +an explanation of the failure. + +.. code-block:: python + + class UnsupportedOptionError(Exception): + user_fault_string = _("A specified option is not supported by this driver.") + operator_fault_string = _("A specified option is not supported by this driver.") + + def __init__(self, *args, **kwargs): + self.user_fault_string = kwargs.pop('user_fault_string', + self.user_fault_string) + self.operator_fault_string = kwargs.pop('operator_fault_string', + self.operator_fault_string) + + super(UnsupportedOptionError, self).__init__(*args, **kwargs) + + +Driver Support Library +---------------------- + +Provider drivers need support for updating provisioning status, operating +status, and statistics. Drivers will not directly use database operations, +and instead will callback to Octavia using a new API. + +.. warning:: + + The methods listed here are the only callable methods for drivers. + All other interfaces are not considered stable or safe for drivers to + access. + +Update provisioning and operating status API +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The update status API defined below can be used by provider drivers +to update the provisioning and/or operating status of Octavia resources +(load balancer, listener, pool, member, health monitor, L7 policy, or L7 +rule). + +For the following status API, valid values for provisioning status +and operating status parameters are as defined by Octavia status codes. If an +existing object is not included in the input parameter, the status remains +unchanged. + +provisioning_status: status associated with lifecycle of the +resource. See `Octavia Provisioning Status Codes `_. + +operating_status: the observed status of the resource. See `Octavia +Operating Status Codes `_. + +The dictionary takes this form: + +.. code-block:: python + + { "loadbalancers": [{"id": "123", + "provisioning_status": "ACTIVE", + "operating_status": "ONLINE"},...], + "healthmonitors": [], + "l7policies": [], + "l7rules": [], + "listeners": [], + "members": [], + "pools": [] + } + +.. code-block:: python + + def update_loadbalancer_status(status): + """Update load balancer status. + + :param status (dict): dictionary defining the provisioning status and + operating status for load balancer objects, including pools, + members, listeners, L7 policies, and L7 rules. + :raises: UpdateStatusError + :returns: None + """ + raise NotImplementedError() + +Update statistics API +^^^^^^^^^^^^^^^^^^^^^ + +Provider drivers can update statistics for load balancers and listeners using +the following API. Similar to the status function above, a single dictionary +with multiple load balancer and/or listener statistics is used to update +statistics in a single call. If an existing load balancer or listener is not +included, the statistics those objects remain unchanged. + +The general form of the input dictionary is a list of load balancer and +listener statistics: + +.. code-block:: python + + { "loadbalancers": [{"id": "123", + "active_connections": 12, + "bytes_in": 238908, + "bytes_out": 290234}, + "request_errors": 0, + "total_connections": 3530},...] + "listeners": [] + } + +.. code-block:: python + + def update_loadbalancer_statistics(statistics): + """Update load balancer statistics. + + :param statistics (dict): Statistics for loadbalancers and listeners: + id (string): ID for load balancer or listener. + active_connections (int): Number of currently active connections. + bytes_in (int): Total bytes received. + bytes_out (int): Total bytes sent. + request_errors (int): Total requests not fulfilled. + total_connections (int): The total connections handled. + :raises: UpdateStatisticsError + :returns: None + """ + raise NotImplementedError() + +Get Resource Support +^^^^^^^^^^^^^^^^^^^^ + +Provider drivers may need to get information about an Octavia resource. +As an example of its use, a provider driver may need to sync with Octavia, +and therefore need to fetch all of the Octavia resources it is responsible +for managing. Provider drivers can use the existing Octavia API to get these +resources. See the `Octavia API Reference `_. + +API Exception Model +^^^^^^^^^^^^^^^^^^^ + +The driver support API will include two Exceptions, one for each of the +two API groups: + +* UpdateStatusError +* UpdateStatisticsError + +Each exception class will include a message field that describes the error and +references to the failed record if available. + +.. code-block:: python + + class UpdateStatusError(Exception): + fault_string = _("The status update had an unknown error.") + status_object = None + status_object_id = None + status_record = None + + def __init__(self, *args, **kwargs): + self.fault_string = kwargs.pop('fault_string', + self.fault_string) + self.status_object = kwargs.pop('status_object', None) + self.status_object_id = kwargs.pop('status_object_id', None) + self.status_record = kwargs.pop('status_record', None) + + super(UnsupportedOptionError, self).__init__(*args, **kwargs) + + class UpdateStatisticsError(Exception): + fault_string = _("The statistics update had an unknown error.") + stats_object = None + stats_object_id = None + stats_record = None + + def __init__(self, *args, **kwargs): + self.fault_string = kwargs.pop('fault_string', + self.fault_string) + self.stats_object = kwargs.pop('stats_object', None) + self.stats_object_id = kwargs.pop('stats_object_id', None) + self.stats_record = kwargs.pop('stats_record', None) + + super(UnsupportedOptionError, self).__init__(*args, **kwargs) + + +Alternatives +------------ +**Driver Support Library** + +An alternative to this library is a REST interface that drivers use directly. +A REST implementation can still be used within the library, but wrapping it +in an API simplifies the programming interface. + +Data model impact +----------------- +None, the required data model changes are already present. + +REST API impact +--------------- +None, the required REST API changes are already present. + +Security impact +--------------- +None. + +Notifications impact +-------------------- +None. + +Other end user impact +--------------------- +Users will be able to direct requests to specific backends using the *provider* +parameter. Users may want to understand the availability of provider drivers, +and can use Octavia APIs to do so. + +Performance Impact +------------------ +The performance impact on Octavia should be minimal. Driver requests will need +to be scheduled, and Octavia will process driver callbacks through a REST +interface. As provider drivers are loaded by Octavia, calls into drivers are +through direct interfaces. + +Other deployer impact +--------------------- +Minimal configuration is needed to support provider drivers. The work required +is adding a driver name to Octavia's configuration file, and installing +provider drivers supplied by third parties. + +Developer impact +---------------- +The proposal defines interaction between Octavia and backend drivers, so no +developer impact is expected. + +Implementation +============== + +Assignee(s) +----------- + +Work Items +---------- + +* Implement loading drivers defined the Octavia configuration. +* Implement scheduling requests to drivers. +* Implement validating flavors with provider drivers. +* Implement getting and testing flavors with provider drivers. +* Implement a no-op driver for testing. +* Implement driver support library functions: + + * Update status functions + * Update statistics functions + +* Migrate the existing Octavia reference driver to use this interface. + +Dependencies +============ +* Octavia API: + https://developer.openstack.org/api-ref/load-balancer/ +* Flavors: + https://docs.openstack.org/octavia/latest/contributor/specs/version1.0/flavors.html + +Testing +======= +Tempest tests should be added for testing: + +* Scheduling: test that Octavia effectively schedules to drivers besides + the default driver. +* Request validation: test request validation API. +* Flavor profile validation: test flavor validation. +* Flavor queries: test flavor queries. +* Statistics updates + +Functional API tests should be updated to test the provider API. + +Documentation Impact +==================== +A driver developer guide should be created. + +References +========== +Octavia API + https://developer.openstack.org/api-ref/load-balancer/v2/index.html + +Octavia Flavors Specification + https://docs.openstack.org/octavia/latest/contributor/specs/version1.0/flavors.html