From 7b1621789c545adedcffdd65bc953ee618c96ba0 Mon Sep 17 00:00:00 2001 From: Michael Johnson Date: Mon, 13 Nov 2017 15:56:10 -0800 Subject: [PATCH] ACTIVE-ACTIVE: Initial distributor driver This patch is the initial implementation of a distributor driver for Octavia Active/Active topology support. This patch is a decompostion of the following patch: https://review.openstack.org/#/c/313006 Story: 2001288 Task: 5836 Depends-On: I97b52b80efb33749647229a55147a08afa112dd2 Change-Id: I65e4a533caee692e1c98e8c6586c2e2132f2e34c Co-Authored-By: Valeria Perelman --- etc/octavia.conf | 5 + octavia/common/config.py | 3 + octavia/distributor/__init__.py | 0 octavia/distributor/drivers/__init__.py | 0 octavia/distributor/drivers/driver_base.py | 144 ++++++++++++++++++ .../drivers/noop_driver/__init__.py | 0 .../distributor/drivers/noop_driver/driver.py | 124 +++++++++++++++ .../drivers/single_VIP_amphora/__init__.py | 0 setup.cfg | 3 + 9 files changed, 279 insertions(+) create mode 100644 octavia/distributor/__init__.py create mode 100644 octavia/distributor/drivers/__init__.py create mode 100644 octavia/distributor/drivers/driver_base.py create mode 100644 octavia/distributor/drivers/noop_driver/__init__.py create mode 100644 octavia/distributor/drivers/noop_driver/driver.py create mode 100644 octavia/distributor/drivers/single_VIP_amphora/__init__.py diff --git a/etc/octavia.conf b/etc/octavia.conf index b59dbbb025..c90da6162e 100644 --- a/etc/octavia.conf +++ b/etc/octavia.conf @@ -222,6 +222,11 @@ # # network_driver = network_noop_driver # +# Distributor driver options are distributor_noop_driver +# single_VIP_amphora +# +# distributor_driver = distributor_noop_driver +# # Load balancer topology options are SINGLE, ACTIVE_STANDBY # loadbalancer_topology = SINGLE # user_data_config_drive = False diff --git a/octavia/common/config.py b/octavia/common/config.py index 3f55002020..28794cbd43 100644 --- a/octavia/common/config.py +++ b/octavia/common/config.py @@ -331,6 +331,9 @@ controller_worker_opts = [ cfg.StrOpt('network_driver', default='network_noop_driver', help=_('Name of the network driver to use')), + cfg.StrOpt('distributor_driver', + default='distributor_noop_driver', + help=_('Name of the distributor driver to use')), cfg.StrOpt('loadbalancer_topology', default=constants.TOPOLOGY_SINGLE, choices=constants.SUPPORTED_LB_TOPOLOGIES, diff --git a/octavia/distributor/__init__.py b/octavia/distributor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/octavia/distributor/drivers/__init__.py b/octavia/distributor/drivers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/octavia/distributor/drivers/driver_base.py b/octavia/distributor/drivers/driver_base.py new file mode 100644 index 0000000000..ca3933b593 --- /dev/null +++ b/octavia/distributor/drivers/driver_base.py @@ -0,0 +1,144 @@ +# Copyright 2016 IBM Corp. +# Copyright 2017 Rackspace, US Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import abc + +import six + +# This class describes the abstraction of a distributor interface. +# Distributor implementations may be: a noop, a single hardware device, +# a single amphora, or multiple amphora among other options. + + +@six.add_metaclass(abc.ABCMeta) +class DistributorDriver(object): + @abc.abstractmethod + def get_create_distributor_subflow(self): + """Get a subflow to create a distributor + + :requires: **load_balancer** (object) - Load balancer object + associated with this distributor + :provides: **distributor_id** (string) - The created distributor ID + :returns: A TaskFlow Flow that will create the distributor + + This method will setup the TaskFlow Flow required to setup the + database fields and create a distributor should the driver need to + instantiate one. + The flow must store the generated distibutor ID in the flow. + """ + pass + + @abc.abstractmethod + def get_delete_distributor_subflow(self): + """Get a subflow that deletes a distributor + + :requires: **distributor_id** (string) - The ID of the distributor + to delete + :returns: A TaskFlow Flow that will delete the distributor + + This method will return a TaskFlow Flow that deletes the distributor + (if applicable for the driver) and cleans up any associated database + records. + """ + pass + + @abc.abstractmethod + def get_add_vip_subflow(self): + """Get a subflow that adds a VIP to a distributor + + :requires: **distributor_id** (string) - The ID of the distributor + to create the VIP on. + :requires: **vip** (object) - The VIP object to create on the + distributor. + :requires: **vip_alg** (string) - The optional algorithm to use for + this VIP. + :requires: **vip_persistence** (string) - The persistence type for + this VIP. + :returns: A TaskFlow Flow that will add a VIP to the distributor + + This method will return a TaskFlow Flow that adds a VIP to the + distributor by perfoming the necessary steps to plug the VIP and + configure the distributor to start receiving requests on this VIP. + """ + pass + + @abc.abstractmethod + def get_remove_vip_subflow(self): + """Get a subflow that removes a VIP from a distributor + + :requires: **distributor_id** (string) - The ID of the distributor + to remove the VIP from. + :requires: **vip** (object) - The VIP object to remove from the + distributor. + :returns: A TaskFlow Flow that will remove a VIP from the distributor + + This method will return a TaskFlow Flow that removes the VIP from the + distributor by reconfiguring the distributor and unplugging the + associated port. + """ + pass + + @abc.abstractmethod + def get_register_amphorae_subflow(self): + """Get a subflow that Registers amphorae with the distributor + + :requires: **distributor_id** (string) - The ID of the distributor + to register the amphora on + :requires: **amphorae** (tuple) - Tuple of amphora objects to + register with the distributor. + :returns: A TaskFlow Flow that will register amphorae with the + distributor + + This method will return a TaskFlow Flow that registers amphorae with + the distributor so it can begin to receive requests from the + distributor. Amphora should be ready to receive requests prior to + this call being made. + """ + pass + + @abc.abstractmethod + def get_drain_amphorae_subflow(self): + """Get a subflow that drains connections from amphorae + + :requires: **distributor_id** (string) - The ID of the distributor + to drain amphorae from + :requires: **amphorae** (tuple) - Tuple of amphora objects to drain + from distributor. + :returns: A TaskFlow Flow that will drain the listed amphorae on the + distributor + + This method will return a TaskFlow Flow that configures the + distributor to stop sending new connections to the amphorae in the + list. Existing connections will continue to pass traffic to the + amphorae in this list. + """ + pass + + @abc.abstractmethod + def get_unregister_amphorae_subflow(self): + """Get a subflow that unregisters amphorae from a distributor + + :requires: **distributor_id** (string) - The ID of the distributor + to unregister amphorae from + :requires: **amphorae** (tuple) - Tuple of amphora objects to + unregister from distributor. + :returns: A TaskFlow Flow that will unregister amphorae from the + distributor + + This method will return a TaskFlow Flow that unregisters amphorae + from the distributor. Amphorae in this list will immediately stop + receiving traffic. + """ + pass diff --git a/octavia/distributor/drivers/noop_driver/__init__.py b/octavia/distributor/drivers/noop_driver/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/octavia/distributor/drivers/noop_driver/driver.py b/octavia/distributor/drivers/noop_driver/driver.py new file mode 100644 index 0000000000..132696186e --- /dev/null +++ b/octavia/distributor/drivers/noop_driver/driver.py @@ -0,0 +1,124 @@ +# Copyright 2016 IBM Corp. +# Copyright 2017 Rackspace, US Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from taskflow.patterns import linear_flow +from taskflow import task + +from oslo_log import log as logging +from oslo_utils import uuidutils + +from octavia.distributor.drivers import driver_base + +LOG = logging.getLogger(__name__) + + +class NoopProvidesRequiresTask(task.Task): + def __init__(self, name, provides_dicts=None, requires=None): + if provides_dicts is None: + provides_dicts = {} + super(NoopProvidesRequiresTask, self).__init__( + name=name, + provides=list(provides_dicts), + requires=requires) + self.provides_dict = provides_dicts + + def execute(self, *args, **kwargs): + return self.provides_dict.values() + + +class NoopManager(object): + def __init__(self): + super(NoopManager, self).__init__() + + def get_create_distributor_subflow(self): + LOG.debug('Distributor %s create_distributor', self.__class__.__name__) + create_distributor_flow = linear_flow.Flow('create-distributor') + create_distributor_flow.add(NoopProvidesRequiresTask( + 'create-distributor-task', + requires=('load_balancer'), + provides_dicts={'distributor_id': uuidutils.generate_uuid()})) + return create_distributor_flow + + def get_delete_distributor_subflow(self): + LOG.debug('Distributor %s delete_distributor', self.__class__.__name__) + delete_distributor_flow = linear_flow.Flow('delete-distributor') + delete_distributor_flow.add(NoopProvidesRequiresTask( + 'delete-distributor-task', requires=('distributor_id'))) + return delete_distributor_flow + + def get_add_vip_subflow(self): + LOG.debug('Distributor %s add_vip', self.__class__.__name__) + add_vip_flow = linear_flow.Flow('add-vip') + add_vip_flow.add(NoopProvidesRequiresTask( + 'add-vip-task', requires=('distributor_id', 'vip', + 'vip_alg', 'vip_persistence'))) + return add_vip_flow + + def get_remove_vip_subflow(self): + LOG.debug('Distributor %s remove_vip', self.__class__.__name__) + remove_vip_flow = linear_flow.Flow('remove-vip') + remove_vip_flow.add(NoopProvidesRequiresTask('remove-vip-task', + requires=('distributor_id', 'vip'))) + return remove_vip_flow + + def get_register_amphorae_subflow(self): + LOG.debug('Distributor %s register_amphorae', self.__class__.__name__) + register_amphorae_flow = linear_flow.Flow('register_amphorae') + register_amphorae_flow.add(NoopProvidesRequiresTask( + 'register_amphorae_task', requires=('distributor_id', 'amphorae'))) + return register_amphorae_flow + + def get_drain_amphorae_subflow(self): + LOG.debug('Distributor %s drain_amphorae', self.__class__.__name__) + drain_amphorae_flow = linear_flow.Flow('drain-amphorae') + drain_amphorae_flow.add(NoopProvidesRequiresTask( + 'drain_amphorae_task', requires=('distributor_id', 'amphorae'))) + return drain_amphorae_flow + + def get_unregister_amphorae_subflow(self): + LOG.debug('Distributor %s unregister_amphorae', + self.__class__.__name__) + unregister_amphorae_flow = linear_flow.Flow('unregister_amphora') + unregister_amphorae_flow.add(NoopProvidesRequiresTask( + 'unregister_amphorae_task', requires=('distributor_id', + 'amphorae'))) + return unregister_amphorae_flow + + +class NoopDistributorDriver(driver_base.DistributorDriver): + def __init__(self): + super(NoopDistributorDriver, self).__init__() + self.driver = NoopManager() + + def get_create_distributor_subflow(self): + return self.driver.get_create_distributor_subflow() + + def get_delete_distributor_subflow(self): + return self.driver.get_delete_distributor_subflow() + + def get_add_vip_flow(self): + return self.driver.get_add_vip_subflow() + + def get_remove_vip_subflow(self): + return self.driver.get_remove_vip_subflow() + + def get_register_amphorae_subflow(self): + return self.driver.get_register_amphorae_subflow() + + def get_drain_amphorae_subflow(self): + self.driver.get_drain_amphorae_subflow() + + def get_unregister_amphorae_subflow(self): + self.driver.get_unregister_amphorae_subflow() diff --git a/octavia/distributor/drivers/single_VIP_amphora/__init__.py b/octavia/distributor/drivers/single_VIP_amphora/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/setup.cfg b/setup.cfg index 5b4f1a7d44..ddd1037ced 100644 --- a/setup.cfg +++ b/setup.cfg @@ -85,6 +85,9 @@ octavia.network.drivers = network_noop_driver = octavia.network.drivers.noop_driver.driver:NoopNetworkDriver allowed_address_pairs_driver = octavia.network.drivers.neutron.allowed_address_pairs:AllowedAddressPairsDriver containers_driver = octavia.network.drivers.neutron.containers:ContainersDriver +octavia.distributor.drivers = + distributor_noop_driver = octavia.distributor.drivers.noop_driver.driver:NoopDistributorDriver + single_VIP_amphora = octavia.distributor.drivers.single_VIP_amphora.driver:SingleVIPAmpDistributorDriver octavia.cert_generator = local_cert_generator = octavia.certificates.generator.local:LocalCertGenerator anchor_cert_generator = octavia.certificates.generator.anchor:AnchorCertGenerator