Skip to content

Commit 68f2695

Browse files
vperelmaxabedad
authored andcommittedMar 6, 2017
ACTIVE-ACTIVE Topology: Initial Distributor Noop Driver
Implements: blueprint https://review.openstack.org/#/c/234639 Change-Id: Ic143c3b5a24cdeb6444f26f965fc57c1a88e1fb8 Co-Authored-By: Valeria Perelman <perelman@il.ibm.com>
1 parent 214ffb0 commit 68f2695

File tree

18 files changed

+825
-6
lines changed

18 files changed

+825
-6
lines changed
 

‎doc/source/api/distributor-api.rst

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
===========================
2+
Octavia Distributor API
3+
===========================
4+
5+
Introduction
6+
============
7+
This document describes the API interface between the reference
8+
distributor driver and its corresponding distributor module.
9+
10+
Octavia reference distributor uses a web service API for configuration and
11+
control. This API should be secured through the use of TLS encryption as well
12+
as bi-directional verification of client- and server-side certificates. (The
13+
exact process for generating and distributing these certificates should be
14+
covered in another document.)
15+
16+
In addition to the web service configuration and control interface, the
17+
distributor may use an HMAC-signed UDP protocol for communicating regular,
18+
less-vital information to the controller (ex. statistics updates and
19+
health checks).
20+
Information on this will also be covered in another document.
21+
22+
23+
.. contents::
24+
25+
Versioning
26+
----------
27+
All Octavia APIs (including internal APIs like this one) are versioned. For the
28+
purposes of this document, the initial version of this API shall be v0.1. (So,
29+
any reference to a *:version* variable should be replaced with the literal
30+
string 'v0.1'.)
31+
32+
Response codes
33+
--------------
34+
Typical response codes are:
35+
36+
* 200 OK - Operation was completed as requested.
37+
* 201 Created - Operation successfully resulted in the creation / processing
38+
of a file.
39+
* 202 Accepted - Command was accepted but is not completed. (Note that this is
40+
used for asynchronous processing.)
41+
* 400 Bad Request - API handler was unable to complete request.
42+
* 401 Unauthorized - Authentication of the client certificate failed.
43+
* 404 Not Found - The requested file was not found.
44+
* 500 Internal Server Error - Usually indicates a permissions problem
45+
46+
API
47+
===
48+
49+
Get distributor info
50+
--------------------
51+
* **URL:** /info
52+
* **Method:** GET
53+
* **URL params:** none
54+
* **Data params:** none
55+
* **Success Response:**
56+
57+
* Code: 200
58+
59+
* Content: JSON formatted listing of several basic distributor data.
60+
61+
* **Error Response:**
62+
63+
* none
64+
65+
JSON Response attributes:
66+
67+
* *hostname* - distributor hostname
68+
* *api_version* - Version of distributor API in use
69+
70+
**Notes:** The data in this request is used by the controller for determining
71+
the distributor and API version numbers.
72+
73+
It's also worth noting that this is the only API command that doesn't have a
74+
version string prepended to it.
75+
76+
**Examples:**
77+
78+
* Success code 200:
79+
80+
::
81+
82+
{
83+
'hostname': 'octavia-haproxy-img-00328.local',
84+
'api_version': '0.1',
85+
}
86+
87+
Get distributor diagnostics
88+
---------------------------
89+
90+
* **URL:** /*:version*/diagnostics
91+
* **Method:** GET
92+
* **URL params:** none
93+
* **Data params:** none
94+
* **Success Response:**
95+
96+
* Code: 200
97+
98+
* Content: JSON formatted listing of various amphora statistics.
99+
100+
* **Error Response:**
101+
102+
* none
103+
104+
JSON Response attributes:
105+
106+
* *hostname* - amphora hostname
107+
* *api_version* - Version of API/agent in use
108+
* *network_tx* - Current total outbound bandwidth in bytes/sec (30-second
109+
snapshot)
110+
* *network_rx* - Current total inbound bandwidth in bytes/sec (30-second
111+
snapshot)
112+
* *active* - is distributor role Active
113+
* *cpu* - list of percent CPU usage broken down into:
114+
115+
* total
116+
* user
117+
* system
118+
* soft_irq
119+
120+
* *memory* - memory usage in kilobytes broken down into:
121+
122+
* total
123+
* free
124+
* available
125+
* buffers
126+
* cached
127+
* swap_used
128+
* shared
129+
* slab
130+
131+
* *disk* - disk usage in kilobytes for root filesystem, listed as:
132+
133+
* used
134+
* available
135+
136+
* *load* - System load (list)
137+
* *topology* - Current topology
138+
* *topology_status* - Is topology status valid
139+
* *packages* - list of load-balancing related packages installed with versions
140+
(eg. OpenSSL, haproxy, nginx, etc.)
141+
142+
**Notes:** The data in this request is meant to provide intelligence for an
143+
auto-scaling orchestration controller (heat) in order to determine whether
144+
additional (or fewer) virtual amphorae are necessary to handle load. As such,
145+
we may add additional parameters to the JSON listing above if they prove to be
146+
useful for making these decisions.
147+
148+
The data in this request is also used by the controller for determining overall
149+
health of the amphora, currently-configured topology and role, etc.
150+
151+
**Examples**
152+
153+
* Success code 200:
154+
155+
::
156+
157+
{
158+
'hostname': 'octavia-haproxy-img-00328.local',
159+
'api_version': '0.1',
160+
'networks': {
161+
'eth0': {
162+
'network_tx': 3300138,
163+
'network_rx': 982001, }}
164+
'cpu':{
165+
'total': 0.43,
166+
'user': 0.30,
167+
'system': 0.05,
168+
'soft_irq': 0.08,
169+
},
170+
'memory':{
171+
'total': 4087402,
172+
'free': 760656,
173+
'available': 2655901,
174+
'buffers': 90980,
175+
'cached': 1830143,
176+
'swap_used': 943,
177+
'shared': 105792,
178+
'slab': 158819,
179+
'committed_as': 2643480,
180+
},
181+
'disk':{
182+
'used': 1234567,
183+
'available': 5242880,
184+
},
185+
'load': [0.50, 0.45, 0.47],
186+
'packages':[
187+
{'bash': '4.3.23'},
188+
{'lighttpd': '1.4.33-1'},
189+
{'openssl': '1.0.1f'},
190+
<cut for brevity>
191+
],
192+
}

‎octavia/common/constants.py

+41
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Copyright 2014 Rackspace
2+
# Copyright 2016 IBM Corp.
23
#
34
# Licensed under the Apache License, Version 2.0 (the "License"); you may
45
# not use this file except in compliance with the License. You may obtain
@@ -173,6 +174,7 @@
173174
ADDED_PORTS = 'added_ports'
174175
PORTS = 'ports'
175176
MEMBER_PORTS = 'member_ports'
177+
176178
LOADBALANCER_TOPOLOGY = 'topology'
177179

178180
CERT_ROTATE_AMPHORA_FLOW = 'octavia-cert-rotate-amphora-flow'
@@ -200,6 +202,7 @@
200202
DELETE_L7RULE_FLOW = 'octavia-delete-l7policy-flow'
201203
FAILOVER_AMPHORA_FLOW = 'octavia-failover-amphora-flow'
202204
LOADBALANCER_NETWORKING_SUBFLOW = 'octavia-new-loadbalancer-net-subflow'
205+
203206
UPDATE_HEALTH_MONITOR_FLOW = 'octavia-update-health-monitor-flow'
204207
UPDATE_LISTENER_FLOW = 'octavia-update-listener-flow'
205208
UPDATE_LOADBALANCER_FLOW = 'octavia-update-loadbalancer-flow'
@@ -371,3 +374,41 @@
371374
RH_AMP_NET_DIR_TEMPLATE = '/etc/netns/{netns}/sysconfig/network-scripts/'
372375
UBUNTU = 'ubuntu'
373376
CENTOS = 'centos'
377+
# Distributor related constants
378+
DISTRIBUTOR = 'distributor'
379+
DISTRIBUTOR_ID = 'distributor_id'
380+
DISTRIBUTOR_DATA = 'distributor_data'
381+
DISTRIBUTOR_NETWORK_CONFIG = 'distributor_network_config'
382+
DISTRIBUTOR_NETWORKING_SUBFLOW = 'octavia-new-distributor-net-subflow'
383+
CREATE_DISTRIBUTOR_FLOW = 'octavia-create-distributor-flow'
384+
WAIT_FOR_DISTRIBUTOR = 'wait-for-distributor'
385+
RELOAD_LB_BEFORE_PLUG_VIP_TO_DISTRIBUTOR = (
386+
'reload-lb-before-plug-vip-to-distributor')
387+
DISTRIBUTOR_AGENT_CONF_TEMPLATE = 'distributor_agent_conf.template'
388+
DISTRIBUTOR_AGENT_API_TEMPLATES = '/templates'
389+
390+
DISTRIBUTOR_BOOTING = 'DISTRIBUTOR_BOOTING'
391+
392+
# Distributor has been allocated
393+
DISTRIBUTOR_ALLOCATED = 'DISTRIBUTOR_ALLOCATED'
394+
# Distributor is ready
395+
DISTRIBUTOR_READY = 'DISTRIBUTOR_READY'
396+
DISTRIBUTOR_ACTIVE = 'DISTRIBUTOR_ACTIVE'
397+
DISTRIBUTOR_PENDING_DELETE = 'DISTRIBUTOR_PENDING_DELETE'
398+
DISTRIBUTOR_PENDING_UPDATE = 'DISTRIBUTOR_PENDING_UPDATE'
399+
DISTRIBUTOR_PENDING_CREATE = 'DISTRIBUTOR_PENDING_CREATE'
400+
DISTRIBUTOR_DELETED = 'DISTRIBUTOR_DELETED'
401+
DISTRIBUTOR_ERROR = 'DISTRIBUTOR_ERROR'
402+
SUPPORTED_DISTRIBUTOR_PROVISIONING_STATUSES = (DISTRIBUTOR_ACTIVE,
403+
DISTRIBUTOR_ALLOCATED,
404+
DISTRIBUTOR_BOOTING,
405+
DISTRIBUTOR_READY,
406+
DISTRIBUTOR_DELETED,
407+
DISTRIBUTOR_ERROR,
408+
DISTRIBUTOR_PENDING_CREATE,
409+
DISTRIBUTOR_PENDING_DELETE,
410+
DISTRIBUTOR_PENDING_UPDATE)
411+
412+
SUPPORTED_DISTRIBUTOR_STATUSES = (DISTRIBUTOR_ALLOCATED, DISTRIBUTOR_BOOTING,
413+
DISTRIBUTOR_READY, DISTRIBUTOR_DELETED,
414+
DISTRIBUTOR_PENDING_DELETE)

‎octavia/common/data_models.py

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright (c) 2014 Rackspace
22
# Copyright (c) 2016 Blue Box, an IBM Company
3+
# Copyright 2016 IBM Corp.
34
# All Rights Reserved.
45
#
56
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -481,6 +482,16 @@ def delete(self):
481482
break
482483

483484

485+
class Distributor(BaseDataModel):
486+
487+
def __init__(self, id=None, compute_id=None, lb_network_ip=None,
488+
status=None):
489+
self.id = id
490+
self.compute_id = compute_id
491+
self.lb_network_ip = lb_network_ip
492+
self.status = status
493+
494+
484495
class AmphoraHealth(BaseDataModel):
485496

486497
def __init__(self, amphora_id=None, last_update=None, busy=False):

‎octavia/db/base_models.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def _get_unique_key(obj):
3030
# objects.
3131
if obj.__class__.__name__ in ['Member', 'Pool', 'LoadBalancer',
3232
'Listener', 'Amphora', 'L7Policy',
33-
'L7Rule']:
33+
'L7Rule', 'Distributor']:
3434
return obj.__class__.__name__ + obj.id
3535
elif obj.__class__.__name__ in ['SessionPersistence', 'HealthMonitor']:
3636
return obj.__class__.__name__ + obj.pool_id
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright 2016 IBM Corp.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
"""create distributor table
16+
17+
Revision ID: 94135f95e292
18+
Revises: 443fe6676637
19+
Create Date: 2016-05-04 16:50:45.781429
20+
21+
"""
22+
23+
# revision identifiers, used by Alembic.
24+
revision = '94135f95e292'
25+
down_revision = '443fe6676637'
26+
27+
from alembic import op
28+
import sqlalchemy as sa
29+
from sqlalchemy import sql
30+
31+
32+
def upgrade():
33+
34+
op.create_table(
35+
u'distributor_provisioning_status',
36+
sa.Column(u'name', sa.String(30), primary_key=True),
37+
sa.Column(u'description', sa.String(255), nullable=True)
38+
)
39+
40+
insert_table = sql.table(
41+
u'distributor_provisioning_status',
42+
sql.column(u'name', sa.String),
43+
sql.column(u'description', sa.String)
44+
)
45+
46+
op.bulk_insert(
47+
insert_table,
48+
[
49+
{'name': 'DISTRIBUTOR_ACTIVE'},
50+
{'name': 'DISTRIBUTOR_ALLOCATED'},
51+
{'name': 'DISTRIBUTOR_BOOTING'},
52+
{'name': 'DISTRIBUTOR_READY'},
53+
{'name': 'DISTRIBUTOR_PENDING_CREATE'},
54+
{'name': 'DISTRIBUTOR_PENDING_UPDATE'},
55+
{'name': 'DISTRIBUTOR_PENDING_DELETE'},
56+
{'name': 'DISTRIBUTOR_DELETED'},
57+
{'name': 'DISTRIBUTOR_ERROR'}
58+
]
59+
)
60+
61+
op.create_table(
62+
u'distributor',
63+
sa.Column(u'id', sa.String(36), nullable=False),
64+
sa.Column(u'compute_id', sa.String(36), nullable=True),
65+
sa.Column(u'lb_network_ip', sa.String(64), nullable=True),
66+
sa.Column(u'status', sa.String(36), nullable=False),
67+
sa.PrimaryKeyConstraint(u'id'),
68+
sa.ForeignKeyConstraint(
69+
[u'status'],
70+
[u'distributor_provisioning_status.name'],
71+
name=u'fk_distributor_provisioning_status_name'
72+
)
73+
)

‎octavia/db/models.py

+21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright 2014 Rackspace
22
# Copyright 2016 Blue Box, an IBM Company
3+
# Copyright 2016 IBM Corp.
34
#
45
# Licensed under the Apache License, Version 2.0 (the "License"); you may
56
# not use this file except in compliance with the License. You may obtain
@@ -29,6 +30,12 @@ class ProvisioningStatus(base_models.BASE, base_models.LookupTableMixin):
2930
__tablename__ = "provisioning_status"
3031

3132

33+
class DistributorProvisioningStatus(base_models.BASE,
34+
base_models.LookupTableMixin):
35+
36+
__tablename__ = "distributor_provisioning_status"
37+
38+
3239
class OperatingStatus(base_models.BASE, base_models.LookupTableMixin):
3340

3441
__tablename__ = "operating_status"
@@ -463,6 +470,20 @@ class Amphora(base_models.BASE, base_models.IdMixin):
463470
vrrp_priority = sa.Column(sa.Integer(), nullable=True)
464471

465472

473+
class Distributor(base_models.BASE, base_models.IdMixin):
474+
475+
__data_model__ = data_models.Distributor
476+
477+
__tablename__ = "distributor"
478+
479+
compute_id = sa.Column(sa.String(36), nullable=True)
480+
lb_network_ip = sa.Column(sa.String(64), nullable=True)
481+
status = sa.Column(
482+
sa.String(36),
483+
sa.ForeignKey("distributor_provisioning_status.name",
484+
name="fk_distributor_provisioning_status_name"))
485+
486+
466487
class AmphoraHealth(base_models.BASE):
467488
__data_model__ = data_models.AmphoraHealth
468489
__tablename__ = "amphora_health"

‎octavia/db/repositories.py

+14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright 2014 Rackspace
22
# Copyright 2016 Blue Box, an IBM Company
3+
# Copyright 2016 IBM Corp.
34
#
45
# Licensed under the Apache License, Version 2.0 (the "License"); you may
56
# not use this file except in compliance with the License. You may obtain
@@ -138,6 +139,7 @@ def __init__(self):
138139
self.listener = ListenerRepository()
139140
self.listener_stats = ListenerStatisticsRepository()
140141
self.amphora = AmphoraRepository()
142+
self.distributor = DistributorRepository()
141143
self.sni = SNIRepository()
142144
self.amphorahealth = AmphoraHealthRepository()
143145
self.vrrpgroup = VRRPGroupRepository()
@@ -989,6 +991,18 @@ def get_stale_amphora(self, session):
989991
return amp.to_data_model()
990992

991993

994+
class DistributorRepository(BaseRepository):
995+
model_class = models.Distributor
996+
997+
def get_distributor_by_id(self, session, id):
998+
with session.begin(subtransactions=True):
999+
distributor = session.query(models.Distributor).filter_by(
1000+
id=id).first()
1001+
if distributor is None:
1002+
return None
1003+
return distributor.to_data_model()
1004+
1005+
9921006
class VRRPGroupRepository(BaseRepository):
9931007
model_class = models.VRRPGroup
9941008

‎octavia/distributor/__init__.py

Whitespace-only changes.

‎octavia/distributor/drivers/__init__.py

Whitespace-only changes.
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Copyright 2016 IBM Corp.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import abc
16+
17+
import six
18+
19+
20+
@six.add_metaclass(abc.ABCMeta)
21+
class DistributorDriver(object):
22+
@abc.abstractmethod
23+
def get_info(self, distributor):
24+
"""Returns information about the Distributor
25+
26+
:param distributor: distributor object, need to use its id property
27+
:type distributor: object
28+
:returns: return a value list (distributor.id, status flag--'get_info')
29+
30+
At this moment, we just build the basic structure for testing, will
31+
add more function along with the development, eventually, we want it
32+
to return information as:
33+
{"Rest Interface": "1.0", "Distributor": "1.0"}
34+
"""
35+
pass
36+
37+
@abc.abstractmethod
38+
def get_diagnostics(self, distributor):
39+
"""Return ready health set of amphorae
40+
41+
:param distributor: distributor object, need to use its id property
42+
:type distributor: object
43+
:returns: return a value list (distributor.id, status flag--'ge
44+
t_diagnostics')
45+
46+
At this moment, we just build the basic structure for testing, will
47+
add more function along with the development, eventually, we want it
48+
run some expensive self tests to determine if the distributor and lbs
49+
are healthy the idea is that those tests are triggered more infrequent
50+
than the health gathering
51+
"""
52+
pass
53+
54+
@abc.abstractmethod
55+
def post_vip_plug(self, distributor, load_balancer, distributor_mac,
56+
cluster_alg_type, cluster_min_size):
57+
"""Called after network driver has allocated and plugged the VIP
58+
59+
:param distributor: distributor object, need to use its id property
60+
:type distributor: object
61+
:param load_balancer: A load balancer that just had its vip allocated
62+
and plugged in the network driver.
63+
:type load_balancer: octavia.network.data_models.LoadBalancer
64+
:param distributor_mac: distributor mac for this VIP
65+
:type distributor_mac: string
66+
:param cluster_alg_type: specific algorithm type
67+
(currently supported are ACTIVE_ACTIVE and PLUG_DEBUG)
68+
:type arg_type: basestring
69+
:param culster_min_size: minimal size of cluster
70+
:type cluster_min_size: int
71+
72+
:returns: None
73+
"""
74+
pass
75+
76+
@abc.abstractmethod
77+
def pre_vip_unplug(self, distributor, load_balancer):
78+
"""Called before network driver has deallocated and unplugged the VIP
79+
80+
:param distributor: distributor object, need to use its id property
81+
:type distributor: object
82+
:param load_balancer: A load balancer that is to be removed.
83+
:type load_balancer: octavia.network.data_models.LoadBalancer
84+
:returns: None
85+
"""
86+
pass
87+
88+
@abc.abstractmethod
89+
def register_amphora(self, distributor, load_balancer, amphora,
90+
cluster_alg_type, cluster_min_size):
91+
"""Called after amphora is ready to add amphora MAC
92+
93+
:param distributor: distributor object
94+
:type distributor: object
95+
:param load_balancer: A load balancer to which the amphora is attached
96+
:type load_balancer: octavia.network.data_models.LoadBalancer
97+
:param amphora: An amphora object
98+
:type: amphora: an object that defines amphora to be registered
99+
:param cluster_alg_type: specific algorithm type
100+
(currently supported are ACTIVE_ACTIVE and PLUG_DEBUG)
101+
:type arg_type: basestring
102+
:param culster_min_size: minimal size of cluster
103+
:type cluster_min_size: int
104+
:returns: None
105+
"""
106+
pass
107+
108+
@abc.abstractmethod
109+
def unregister_amphora(self, distributor, load_balancer,
110+
amphora, cluster_alg_type, cluster_min_size):
111+
"""Called after amphora is ready to be removed
112+
113+
:param distributor: distributor object, need to use its id property
114+
:type distributor: object
115+
:param load_balancer: A load balancer from which the amphora is
116+
detached
117+
:type load_balancer: octavia.network.data_models.LoadBalancer
118+
:param amphora: An amphora object
119+
:type: amphora: an object that defines amphora to be unregistered
120+
:param cluster_alg_type: specific algorithm type
121+
(currently supported are ACTIVE_ACTIVE and PLUG_DEBUG)
122+
:type arg_type: basestring
123+
:param culster_min_size: minimal size of cluster
124+
:type cluster_min_size: int
125+
:returns: None
126+
"""
127+
pass

‎octavia/distributor/drivers/noop_driver/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Copyright 2016 IBM Corp.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from oslo_log import log as logging
16+
17+
from octavia.distributor.drivers import driver_base as driver_base
18+
19+
LOG = logging.getLogger(__name__)
20+
21+
22+
class NoopManager(object):
23+
def __init__(self):
24+
super(NoopManager, self).__init__()
25+
self.distributor_config = {}
26+
27+
def get_info(self, distributor):
28+
LOG.debug("distributor %s no-op, info distributor %s",
29+
self.__class__.__name__, distributor.id)
30+
self.distributor_config[distributor.id] = (distributor.id, 'get_info')
31+
32+
def get_diagnostics(self, distributor):
33+
LOG.debug("distributor %s no-op, get diagnostics distributor %s",
34+
self.__class__.__name__, distributor.id)
35+
self.distributor_config[distributor.id] = (distributor.id,
36+
'get_diagnostics')
37+
38+
def register_amphora(self, distributor, load_balancer, amphora,
39+
cluster_alg_type, cluster_min_size):
40+
LOG.debug("distributor %s no-op, register amphora %s with LB %s",
41+
self.__class__.__name__, amphora.id, load_balancer.id)
42+
self.distributor_config[(amphora.id,
43+
distributor.id,
44+
load_balancer.id,
45+
cluster_alg_type,
46+
cluster_min_size)] = (amphora.id,
47+
distributor.id,
48+
load_balancer.id,
49+
cluster_alg_type,
50+
cluster_min_size,
51+
'register_amphora')
52+
53+
def unregister_amphora(self, distributor, load_balancer, amphora,
54+
cluster_alg_type, cluster_min_size):
55+
LOG.debug("distributor %s no-op, unregister amphora %s, LB:%s",
56+
self.__class__.__name__, amphora.id, load_balancer.id)
57+
self.distributor_config[(amphora.id,
58+
distributor.id,
59+
load_balancer.id,
60+
cluster_alg_type,
61+
cluster_min_size)] = (amphora.id,
62+
distributor.id,
63+
load_balancer.id,
64+
cluster_alg_type,
65+
cluster_min_size,
66+
'unregister_amphora')
67+
68+
def post_vip_plug(self, distributor, load_balancer, distributor_mac,
69+
cluster_alg_type, cluster_min_size):
70+
LOG.debug("distributor %s no-op, post vip plug load balancer %s",
71+
self.__class__.__name__, load_balancer.id)
72+
self.distributor_config[(distributor.id,
73+
load_balancer.id,
74+
distributor_mac,
75+
cluster_alg_type,
76+
cluster_min_size)] = (distributor.id,
77+
load_balancer.id,
78+
distributor_mac,
79+
cluster_alg_type,
80+
cluster_min_size,
81+
'post_vip_plug')
82+
83+
def pre_vip_unplug(self, distributor, load_balancer):
84+
LOG.debug("distributor %s no-op, pre vip unplug load balancer %s",
85+
self.__class__.__name__, load_balancer.id)
86+
self.distributor_config[(load_balancer.id,
87+
distributor.id)] = (load_balancer.id,
88+
distributor.id,
89+
'pre_vip_unplug')
90+
91+
92+
class NoopDistributorDriver(driver_base.DistributorDriver):
93+
def __init__(self):
94+
super(NoopDistributorDriver, self).__init__()
95+
self.driver = NoopManager()
96+
97+
def get_info(self, distributor):
98+
self.driver.get_info(distributor)
99+
100+
def get_diagnostics(self, distributor):
101+
self.driver.get_diagnostics(distributor)
102+
103+
def register_amphora(self, distributor, load_balancer, amphora,
104+
cluster_alg_type, cluster_min_size):
105+
self.driver.register_amphora(distributor,
106+
load_balancer,
107+
amphora,
108+
cluster_alg_type,
109+
cluster_min_size)
110+
111+
def unregister_amphora(self, distributor, load_balancer, amphora,
112+
cluster_alg_type, cluster_min_size):
113+
self.driver.unregister_amphora(distributor,
114+
load_balancer,
115+
amphora,
116+
cluster_alg_type,
117+
cluster_min_size)
118+
119+
def post_vip_plug(self, distributor, load_balancer, distributor_mac,
120+
cluster_alg_type, cluster_min_size):
121+
self.driver.post_vip_plug(distributor,
122+
load_balancer,
123+
distributor_mac,
124+
cluster_alg_type,
125+
cluster_min_size)
126+
127+
def pre_vip_unplug(self, distributor, load_balancer):
128+
self.driver.pre_vip_unplug(distributor, load_balancer)

‎octavia/tests/functional/db/base.py

+3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ def _seed_lookup_tables(self, session):
5151
self._seed_lookup_table(
5252
session, constants.SUPPORTED_PROVISIONING_STATUSES,
5353
models.ProvisioningStatus)
54+
self._seed_lookup_table(
55+
session, constants.SUPPORTED_DISTRIBUTOR_PROVISIONING_STATUSES,
56+
models.DistributorProvisioningStatus)
5457
self._seed_lookup_table(
5558
session, constants.SUPPORTED_HEALTH_MONITOR_TYPES,
5659
models.HealthMonitorType)

‎octavia/tests/functional/db/test_models.py

+39
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Copyright 2014 Rackspace
2+
# Copyright 2016 IBM Corp.
23
#
34
# Licensed under the Apache License, Version 2.0 (the "License"); you may
45
# not use this file except in compliance with the License. You may obtain
@@ -137,6 +138,14 @@ def create_amphora(self, session, **overrides):
137138
kwargs.update(overrides)
138139
return self._insert(session, models.Amphora, kwargs)
139140

141+
def create_distributor(self, session, **overrides):
142+
kwargs = {'id': self.FAKE_UUID_1,
143+
'compute_id': self.FAKE_UUID_1,
144+
'lb_network_ip': self.FAKE_IP,
145+
'status': constants.DISTRIBUTOR_ACTIVE}
146+
kwargs.update(overrides)
147+
return self._insert(session, models.Distributor, kwargs)
148+
140149
def create_amphora_health(self, session, **overrides):
141150
kwargs = {'amphora_id': self.FAKE_UUID_1,
142151
'last_update': datetime.date.today(),
@@ -612,6 +621,31 @@ def test_load_balancer_relationship(self):
612621
self.assertIsInstance(new_amphora.load_balancer, models.LoadBalancer)
613622

614623

624+
class DistributorModelTest(base.OctaviaDBTestBase, ModelTestMixin):
625+
626+
def setUp(self):
627+
super(DistributorModelTest, self).setUp()
628+
629+
def test_create(self):
630+
self.create_distributor(self.session)
631+
632+
def test_update(self):
633+
distributor = self.create_distributor(self.session)
634+
distributor.distributor_id = self.FAKE_UUID_2
635+
new_distributor = self.session.query(models.Distributor).filter_by(
636+
id=distributor.id).first()
637+
self.assertEqual(self.FAKE_UUID_2, new_distributor.distributor_id)
638+
639+
def test_delete(self):
640+
distributor = self.create_distributor(self.session)
641+
with self.session.begin():
642+
self.session.delete(distributor)
643+
self.session.flush()
644+
new_distributor = self.session.query(
645+
models.Distributor).filter_by(id=distributor.id).first()
646+
self.assertIsNone(new_distributor)
647+
648+
615649
class AmphoraHealthModelTest(base.OctaviaDBTestBase, ModelTestMixin):
616650
def setUp(self):
617651
super(AmphoraHealthModelTest, self).setUp()
@@ -1255,6 +1289,11 @@ def check_load_balancer_amphora_data_model(self, amphora):
12551289
self.assertEqual(self.FAKE_UUID_1, amphora.amphora_id)
12561290
self.assertEqual(self.FAKE_UUID_1, amphora.load_balancer_id)
12571291

1292+
def check_distributor_data_model(self, distributor):
1293+
self.assertEqual(self.FAKE_UUID_1, distributor.id)
1294+
self.assertEqual(self.FAKE_UUID_1, distributor.compute_id)
1295+
self.assertEqual(constants.ACTIVE, distributor.status)
1296+
12581297

12591298
class TestDataModelManipulations(base.OctaviaDBTestBase, ModelTestMixin):
12601299

‎octavia/tests/functional/db/test_repositories.py

+64-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Copyright 2014 Rackspace
2+
# Copyright 2016 IBM Corp.
23
#
34
# Licensed under the Apache License, Version 2.0 (the "License"); you may
45
# not use this file except in compliance with the License. You may obtain
@@ -52,6 +53,7 @@ def setUp(self):
5253
self.hm_repo = repo.HealthMonitorRepository()
5354
self.sni_repo = repo.SNIRepository()
5455
self.amphora_repo = repo.AmphoraRepository()
56+
self.distributor_repo = repo.DistributorRepository()
5557
self.amphora_health_repo = repo.AmphoraHealthRepository()
5658
self.vrrp_group_repo = repo.VRRPGroupRepository()
5759
self.l7policy_repo = repo.L7PolicyRepository()
@@ -93,11 +95,12 @@ def setUp(self):
9395
load_balancer_id=self.load_balancer.id)
9496

9597
def test_all_repos_has_correct_repos(self):
96-
repo_attr_names = ('load_balancer', 'vip', 'health_monitor',
97-
'session_persistence', 'pool', 'member', 'listener',
98-
'listener_stats', 'amphora', 'sni',
99-
'amphorahealth', 'vrrpgroup', 'l7rule', 'l7policy',
100-
'quotas')
98+
repo_attr_names = (
99+
'load_balancer', 'vip', 'health_monitor', 'distributor',
100+
'session_persistence', 'pool', 'member', 'listener',
101+
'listener_stats', 'amphora', 'sni',
102+
'amphorahealth', 'vrrpgroup', 'l7rule', 'l7policy',
103+
'quotas')
101104
for repo_attr in repo_attr_names:
102105
single_repo = getattr(self.repos, repo_attr, None)
103106
message = ("Class Repositories should have %s instance"
@@ -2894,6 +2897,62 @@ def test_get_cert_expired_amphora(self):
28942897
self.assertEqual(cert_expired_amphora.id, amphora2.id)
28952898

28962899

2900+
class DistributorRepositoryTest(BaseRepositoryTest):
2901+
2902+
def setUp(self):
2903+
super(DistributorRepositoryTest, self).setUp()
2904+
self.distributor = self.distributor_repo.create(
2905+
self.session, id=self.FAKE_UUID_1, compute_id=self.FAKE_UUID_2,
2906+
lb_network_ip=self.FAKE_IP, status=constants.DISTRIBUTOR_READY)
2907+
2908+
def create_distributor(self, distributor_id):
2909+
dist_ready = constants.DISTRIBUTOR_READY
2910+
distributor = self.distributor_repo.create(
2911+
self.session,
2912+
id=distributor_id,
2913+
compute_id=self.FAKE_UUID_4,
2914+
status=dist_ready,
2915+
lb_network_ip=self.FAKE_IP)
2916+
return distributor
2917+
2918+
def test_get(self):
2919+
distributor = self.create_distributor(self.FAKE_UUID_2)
2920+
new_distributor = self.distributor_repo.get(
2921+
self.session,
2922+
id=distributor.id)
2923+
self.assertIsInstance(new_distributor, models.Distributor)
2924+
self.assertEqual(distributor, new_distributor)
2925+
2926+
def test_create(self):
2927+
distributor = self.create_distributor(self.FAKE_UUID_2)
2928+
self.assertEqual(self.FAKE_UUID_2, distributor.id)
2929+
self.assertEqual(self.FAKE_UUID_4, distributor.compute_id)
2930+
self.assertEqual(constants.DISTRIBUTOR_READY, distributor.status)
2931+
self.assertEqual(self.FAKE_IP, distributor.lb_network_ip)
2932+
2933+
def test_update(self):
2934+
distributor = self.create_distributor(self.FAKE_UUID_2)
2935+
status_change = constants.DISTRIBUTOR_PENDING_UPDATE
2936+
self.distributor_repo.update(self.session, distributor.id,
2937+
status=status_change)
2938+
new_distributor = self.distributor_repo.get(self.session,
2939+
id=distributor.id)
2940+
self.assertEqual(status_change, new_distributor.status)
2941+
2942+
def test_delete(self):
2943+
distributor = self.create_distributor(self.FAKE_UUID_2)
2944+
self.distributor_repo.delete(self.session, id=distributor.id)
2945+
self.assertIsNone(self.distributor_repo.get(self.session,
2946+
id=distributor.id))
2947+
2948+
def test_get_distributor_by_id(self):
2949+
self.create_distributor(self.FAKE_UUID_2)
2950+
new_distributor = self.distributor_repo.get_distributor_by_id(
2951+
self.session, self.FAKE_UUID_2)
2952+
self.assertIsNotNone(new_distributor)
2953+
self.assertIsInstance(new_distributor, models.Distributor)
2954+
2955+
28972956
class AmphoraHealthRepositoryTest(BaseRepositoryTest):
28982957
def setUp(self):
28992958
super(AmphoraHealthRepositoryTest, self).setUp()

‎octavia/tests/unit/distributor/__init__.py

Whitespace-only changes.

‎octavia/tests/unit/distributor/drivers/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Copyright (c) 2016 IBM Corp.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from oslo_utils import uuidutils
16+
17+
from octavia.common import data_models
18+
from octavia.db import models as models
19+
from octavia.distributor.drivers.noop_driver import driver as driver
20+
import octavia.tests.unit.base as base
21+
22+
23+
class TestDistributorNoopDriver(base.TestCase):
24+
FAKE_UUID_1 = uuidutils.generate_uuid()
25+
FAKE_UUID_2 = uuidutils.generate_uuid()
26+
FAKE_MAC_ADDRESS = '123'
27+
28+
def setUp(self):
29+
super(TestDistributorNoopDriver, self).setUp()
30+
self.driver = driver.NoopDistributorDriver()
31+
32+
self.amphora = data_models.Amphora()
33+
self.amphora.id = self.FAKE_UUID_1
34+
self.load_balancer = data_models.LoadBalancer(
35+
id=self.FAKE_UUID_1, amphorae=[self.amphora])
36+
self.distributor = models.Distributor()
37+
self.distributor.id = self.FAKE_UUID_2
38+
self.cluster_alg_type = "TEST"
39+
self.cluster_min_size = 0
40+
41+
def test_get_info(self):
42+
self.driver.get_info(self.distributor)
43+
self.assertEqual((self.distributor.id, 'get_info'),
44+
self.driver.driver.distributor_config[
45+
self.distributor.id])
46+
47+
def test_get_diagnostics(self):
48+
self.driver.get_diagnostics(self.distributor)
49+
self.assertEqual((self.distributor.id, 'get_diagnostics'),
50+
self.driver.driver.distributor_config[
51+
self.distributor.id])
52+
53+
def test_register_amphora(self):
54+
self.driver.register_amphora(self.distributor,
55+
self.load_balancer,
56+
self.amphora,
57+
self.cluster_alg_type,
58+
self.cluster_min_size)
59+
self.assertEqual((self.amphora.id, self.distributor.id,
60+
self.load_balancer.id,
61+
self.cluster_alg_type,
62+
self.cluster_min_size,
63+
'register_amphora'),
64+
self.driver.driver.distributor_config[(
65+
self.amphora.id,
66+
self.distributor.id,
67+
self.load_balancer.id,
68+
self.cluster_alg_type,
69+
self.cluster_min_size)])
70+
71+
def test_unregister_amphora(self):
72+
self.driver.unregister_amphora(self.distributor,
73+
self.load_balancer,
74+
self.amphora,
75+
self.cluster_alg_type,
76+
self.cluster_min_size)
77+
self.assertEqual((self.amphora.id, self.distributor.id,
78+
self.load_balancer.id,
79+
self.cluster_alg_type,
80+
self.cluster_min_size,
81+
'unregister_amphora'),
82+
self.driver.driver.distributor_config[(
83+
self.amphora.id,
84+
self.distributor.id,
85+
self.load_balancer.id,
86+
self.cluster_alg_type,
87+
self.cluster_min_size)])
88+
89+
def test_post_vip_plug(self):
90+
self.driver.post_vip_plug(self.distributor, self.load_balancer,
91+
self.FAKE_MAC_ADDRESS,
92+
self.cluster_alg_type, self.cluster_min_size)
93+
self.assertEqual((self.distributor.id,
94+
self.load_balancer.id,
95+
self.FAKE_MAC_ADDRESS,
96+
self.cluster_alg_type,
97+
self.cluster_min_size,
98+
'post_vip_plug'),
99+
self.driver.driver.distributor_config[(
100+
self.distributor.id,
101+
self.load_balancer.id,
102+
self.FAKE_MAC_ADDRESS,
103+
self.cluster_alg_type,
104+
self.cluster_min_size)])
105+
106+
def test_pre_vip_unplug(self):
107+
self.driver.pre_vip_unplug(self.distributor, self.load_balancer)
108+
self.assertEqual((self.load_balancer.id, self.distributor.id,
109+
'pre_vip_unplug'),
110+
self.driver.driver.distributor_config[(
111+
self.load_balancer.id, self.distributor.id)])

0 commit comments

Comments
 (0)
Please sign in to comment.