octavia 15.0.0__py3-none-any.whl → 16.0.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- octavia/amphorae/backends/agent/api_server/keepalivedlvs.py +9 -0
- octavia/amphorae/backends/agent/api_server/loadbalancer.py +6 -6
- octavia/amphorae/backends/agent/api_server/plug.py +1 -1
- octavia/amphorae/backends/agent/api_server/util.py +35 -2
- octavia/amphorae/backends/health_daemon/status_message.py +1 -2
- octavia/amphorae/drivers/haproxy/rest_api_driver.py +12 -7
- octavia/api/drivers/amphora_driver/flavor_schema.py +5 -0
- octavia/api/drivers/noop_driver/driver.py +2 -1
- octavia/api/drivers/utils.py +12 -0
- octavia/api/root_controller.py +8 -2
- octavia/api/v2/controllers/base.py +8 -4
- octavia/api/v2/controllers/listener.py +12 -2
- octavia/api/v2/controllers/load_balancer.py +33 -1
- octavia/api/v2/controllers/member.py +58 -4
- octavia/api/v2/types/load_balancer.py +7 -1
- octavia/api/v2/types/member.py +3 -0
- octavia/common/base_taskflow.py +19 -10
- octavia/common/clients.py +8 -2
- octavia/common/config.py +17 -2
- octavia/common/constants.py +6 -0
- octavia/common/data_models.py +32 -2
- octavia/common/exceptions.py +5 -0
- octavia/common/utils.py +4 -1
- octavia/common/validate.py +16 -0
- octavia/compute/drivers/noop_driver/driver.py +30 -1
- octavia/controller/healthmanager/health_manager.py +7 -0
- octavia/controller/worker/v2/flows/amphora_flows.py +3 -5
- octavia/controller/worker/v2/flows/listener_flows.py +2 -1
- octavia/controller/worker/v2/flows/load_balancer_flows.py +38 -0
- octavia/controller/worker/v2/taskflow_jobboard_driver.py +34 -6
- octavia/controller/worker/v2/tasks/compute_tasks.py +9 -5
- octavia/controller/worker/v2/tasks/database_tasks.py +26 -6
- octavia/controller/worker/v2/tasks/network_tasks.py +118 -70
- octavia/db/base_models.py +29 -5
- octavia/db/migration/alembic_migrations/versions/3097e55493ae_add_sg_id_to_vip_table.py +39 -0
- octavia/db/migration/alembic_migrations/versions/8db7a6443785_add_member_vnic_type.py +36 -0
- octavia/db/migration/alembic_migrations/versions/fabf4983846b_add_member_port_table.py +40 -0
- octavia/db/models.py +43 -1
- octavia/db/repositories.py +88 -9
- octavia/network/base.py +29 -12
- octavia/network/data_models.py +2 -1
- octavia/network/drivers/neutron/allowed_address_pairs.py +55 -46
- octavia/network/drivers/neutron/base.py +28 -16
- octavia/network/drivers/neutron/utils.py +2 -2
- octavia/network/drivers/noop_driver/driver.py +150 -29
- octavia/policies/__init__.py +4 -0
- octavia/policies/advanced_rbac.py +95 -0
- octavia/policies/base.py +5 -101
- octavia/policies/keystone_default_roles.py +81 -0
- octavia/policies/loadbalancer.py +13 -0
- octavia/tests/common/constants.py +2 -1
- octavia/tests/common/sample_data_models.py +27 -14
- octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py +5 -4
- octavia/tests/functional/api/drivers/driver_agent/test_driver_agent.py +2 -1
- octavia/tests/functional/api/v2/test_health_monitor.py +1 -1
- octavia/tests/functional/api/v2/test_l7policy.py +1 -1
- octavia/tests/functional/api/v2/test_listener.py +1 -1
- octavia/tests/functional/api/v2/test_load_balancer.py +150 -4
- octavia/tests/functional/api/v2/test_member.py +50 -0
- octavia/tests/functional/api/v2/test_pool.py +1 -1
- octavia/tests/functional/api/v2/test_quotas.py +5 -8
- octavia/tests/functional/db/base.py +6 -6
- octavia/tests/functional/db/test_models.py +124 -1
- octavia/tests/functional/db/test_repositories.py +237 -19
- octavia/tests/unit/amphorae/backends/agent/api_server/test_util.py +89 -1
- octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_1_0.py +10 -7
- octavia/tests/unit/api/drivers/test_utils.py +6 -1
- octavia/tests/unit/certificates/generator/test_local.py +1 -1
- octavia/tests/unit/common/test_base_taskflow.py +4 -3
- octavia/tests/unit/compute/drivers/noop_driver/test_driver.py +28 -2
- octavia/tests/unit/controller/worker/v2/flows/test_load_balancer_flows.py +27 -1
- octavia/tests/unit/controller/worker/v2/tasks/test_database_tasks.py +28 -6
- octavia/tests/unit/controller/worker/v2/tasks/test_network_tasks.py +100 -79
- octavia/tests/unit/controller/worker/v2/test_taskflow_jobboard_driver.py +8 -0
- octavia/tests/unit/network/drivers/neutron/test_allowed_address_pairs.py +62 -45
- octavia/tests/unit/network/drivers/neutron/test_base.py +7 -7
- octavia/tests/unit/network/drivers/noop_driver/test_driver.py +55 -42
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/diskimage-create/tox.ini +0 -1
- {octavia-15.0.0.dist-info → octavia-16.0.0.0rc1.dist-info}/AUTHORS +3 -0
- octavia-16.0.0.0rc1.dist-info/METADATA +156 -0
- {octavia-15.0.0.dist-info → octavia-16.0.0.0rc1.dist-info}/RECORD +95 -90
- {octavia-15.0.0.dist-info → octavia-16.0.0.0rc1.dist-info}/WHEEL +1 -1
- {octavia-15.0.0.dist-info → octavia-16.0.0.0rc1.dist-info}/entry_points.txt +1 -1
- octavia-16.0.0.0rc1.dist-info/pbr.json +1 -0
- octavia-15.0.0.dist-info/METADATA +0 -156
- octavia-15.0.0.dist-info/pbr.json +0 -1
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/LICENSE +0 -0
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/README.rst +0 -0
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/diskimage-create/README.rst +0 -0
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/diskimage-create/diskimage-create.sh +0 -0
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/diskimage-create/image-tests.sh +0 -0
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/diskimage-create/requirements.txt +0 -0
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/diskimage-create/test-requirements.txt +0 -0
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/data/share/octavia/diskimage-create/version.txt +0 -0
- {octavia-15.0.0.data → octavia-16.0.0.0rc1.data}/scripts/octavia-wsgi +0 -0
- {octavia-15.0.0.dist-info → octavia-16.0.0.0rc1.dist-info}/LICENSE +0 -0
- {octavia-15.0.0.dist-info → octavia-16.0.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -17,16 +17,19 @@ import time
|
|
17
17
|
from oslo_config import cfg
|
18
18
|
from oslo_log import log as logging
|
19
19
|
from oslo_utils import excutils
|
20
|
+
from sqlalchemy.orm import exc as sa_exception
|
20
21
|
from taskflow import task
|
21
22
|
from taskflow.types import failure
|
22
23
|
import tenacity
|
23
24
|
|
24
25
|
from octavia.common import constants
|
25
26
|
from octavia.common import data_models
|
27
|
+
from octavia.common import exceptions
|
26
28
|
from octavia.common import utils
|
27
29
|
from octavia.controller.worker import task_utils
|
28
30
|
from octavia.db import api as db_apis
|
29
31
|
from octavia.db import repositories as repo
|
32
|
+
from octavia.i18n import _
|
30
33
|
from octavia.network import base
|
31
34
|
from octavia.network import data_models as n_data_models
|
32
35
|
|
@@ -43,6 +46,7 @@ class BaseNetworkTask(task.Task):
|
|
43
46
|
self.task_utils = task_utils.TaskUtils()
|
44
47
|
self.loadbalancer_repo = repo.LoadBalancerRepository()
|
45
48
|
self.amphora_repo = repo.AmphoraRepository()
|
49
|
+
self.amphora_member_port_repo = repo.AmphoraMemberPortRepository()
|
46
50
|
|
47
51
|
@property
|
48
52
|
def network_driver(self):
|
@@ -78,6 +82,7 @@ class CalculateAmphoraDelta(BaseNetworkTask):
|
|
78
82
|
loadbalancer[constants.VIP_NETWORK_ID]
|
79
83
|
}
|
80
84
|
|
85
|
+
net_vnic_type_map = {}
|
81
86
|
for pool in db_lb.pools:
|
82
87
|
for member in pool.members:
|
83
88
|
if (member.subnet_id and
|
@@ -85,6 +90,8 @@ class CalculateAmphoraDelta(BaseNetworkTask):
|
|
85
90
|
constants.PENDING_DELETE):
|
86
91
|
member_network = self.network_driver.get_subnet(
|
87
92
|
member.subnet_id).network_id
|
93
|
+
net_vnic_type_map[member_network] = getattr(
|
94
|
+
member, 'vnic_type', constants.VNIC_TYPE_NORMAL)
|
88
95
|
desired_subnet_to_net_map[member.subnet_id] = (
|
89
96
|
member_network)
|
90
97
|
|
@@ -117,7 +124,8 @@ class CalculateAmphoraDelta(BaseNetworkTask):
|
|
117
124
|
n_data_models.FixedIP(
|
118
125
|
subnet_id=subnet_id)
|
119
126
|
for subnet_id, net_id in desired_subnet_to_net_map.items()
|
120
|
-
if net_id == add_net_id]
|
127
|
+
if net_id == add_net_id],
|
128
|
+
vnic_type=net_vnic_type_map[add_net_id])
|
121
129
|
for add_net_id in add_ids]
|
122
130
|
|
123
131
|
# Calculate member Subnet deltas
|
@@ -215,45 +223,6 @@ class GetPlumbedNetworks(BaseNetworkTask):
|
|
215
223
|
amphora[constants.COMPUTE_ID])
|
216
224
|
|
217
225
|
|
218
|
-
class PlugNetworks(BaseNetworkTask):
|
219
|
-
"""Task to plug the networks.
|
220
|
-
|
221
|
-
This uses the delta to add all missing networks/nics
|
222
|
-
"""
|
223
|
-
|
224
|
-
def execute(self, amphora, delta):
|
225
|
-
"""Update the amphora networks for the delta."""
|
226
|
-
|
227
|
-
LOG.debug("Plug or unplug networks for amphora id: %s",
|
228
|
-
amphora[constants.ID])
|
229
|
-
|
230
|
-
if not delta:
|
231
|
-
LOG.debug("No network deltas for amphora id: %s",
|
232
|
-
amphora[constants.ID])
|
233
|
-
return
|
234
|
-
|
235
|
-
# add nics
|
236
|
-
for nic in delta[constants.ADD_NICS]:
|
237
|
-
self.network_driver.plug_network(amphora[constants.COMPUTE_ID],
|
238
|
-
nic[constants.NETWORK_ID])
|
239
|
-
|
240
|
-
def revert(self, amphora, delta, *args, **kwargs):
|
241
|
-
"""Handle a failed network plug by removing all nics added."""
|
242
|
-
|
243
|
-
LOG.warning("Unable to plug networks for amp id %s",
|
244
|
-
amphora[constants.ID])
|
245
|
-
if not delta:
|
246
|
-
return
|
247
|
-
|
248
|
-
for nic in delta[constants.ADD_NICS]:
|
249
|
-
try:
|
250
|
-
self.network_driver.unplug_network(
|
251
|
-
amphora[constants.COMPUTE_ID],
|
252
|
-
nic[constants.NETWORK_ID])
|
253
|
-
except base.NetworkNotFound:
|
254
|
-
pass
|
255
|
-
|
256
|
-
|
257
226
|
class UnPlugNetworks(BaseNetworkTask):
|
258
227
|
"""Task to unplug the networks
|
259
228
|
|
@@ -316,6 +285,14 @@ class HandleNetworkDelta(BaseNetworkTask):
|
|
316
285
|
fixed_ip.subnet = self.network_driver.get_subnet(
|
317
286
|
fixed_ip.subnet_id)
|
318
287
|
|
288
|
+
def _cleanup_port(self, port_id, compute_id):
|
289
|
+
try:
|
290
|
+
self.network_driver.delete_port(port_id)
|
291
|
+
except Exception:
|
292
|
+
LOG.error(f'Unable to delete port {port_id} after failing to plug '
|
293
|
+
f'the port into compute {compute_id}. This port '
|
294
|
+
f'may now be abandoned in neutron.')
|
295
|
+
|
319
296
|
def execute(self, amphora, delta):
|
320
297
|
"""Handle network plugging based off deltas."""
|
321
298
|
session = db_apis.get_session()
|
@@ -324,20 +301,43 @@ class HandleNetworkDelta(BaseNetworkTask):
|
|
324
301
|
id=amphora.get(constants.ID))
|
325
302
|
updated_ports = {}
|
326
303
|
for nic in delta[constants.ADD_NICS]:
|
304
|
+
network_id = nic[constants.NETWORK_ID]
|
327
305
|
subnet_id = nic[constants.FIXED_IPS][0][constants.SUBNET_ID]
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
306
|
+
|
307
|
+
try:
|
308
|
+
port = self.network_driver.create_port(
|
309
|
+
network_id,
|
310
|
+
name=f'octavia-lb-member-{amphora.get(constants.ID)}',
|
311
|
+
vnic_type=nic[constants.VNIC_TYPE])
|
312
|
+
except exceptions.NotFound as e:
|
313
|
+
if 'Network' in str(e):
|
314
|
+
raise base.NetworkNotFound(str(e))
|
315
|
+
raise base.CreatePortException(str(e))
|
316
|
+
except Exception as e:
|
317
|
+
message = _(f'Error creating a port on network {network_id}. ')
|
318
|
+
LOG.exception(message)
|
319
|
+
raise base.CreatePortException(message) from e
|
320
|
+
|
321
|
+
try:
|
322
|
+
self.network_driver.plug_port(db_amp, port)
|
323
|
+
except exceptions.NotFound as e:
|
324
|
+
self._cleanup_port(port.id, db_amp.compute_id)
|
325
|
+
if 'Instance' in str(e):
|
326
|
+
raise base.AmphoraNotFound(str(e))
|
327
|
+
raise base.PlugNetworkException(str(e))
|
328
|
+
except Exception as e:
|
329
|
+
self._cleanup_port(port.id, db_amp.compute_id)
|
330
|
+
message = _('Error plugging amphora (compute_id: '
|
331
|
+
'{compute_id}) into network {network_id}.').format(
|
332
|
+
compute_id=db_amp.compute_id, network_id=network_id)
|
333
|
+
LOG.exception(message)
|
334
|
+
raise base.PlugNetworkException(message) from e
|
335
|
+
with session.begin():
|
336
|
+
self.amphora_member_port_repo.create(
|
337
|
+
session, port_id=port.id,
|
338
|
+
amphora_id=amphora.get(constants.ID),
|
339
|
+
network_id=network_id)
|
340
|
+
|
341
341
|
self._fill_port_info(port)
|
342
342
|
updated_ports[port.network_id] = port.to_dict(recurse=True)
|
343
343
|
|
@@ -395,6 +395,14 @@ class HandleNetworkDelta(BaseNetworkTask):
|
|
395
395
|
self.network_driver.delete_port(port_id)
|
396
396
|
except Exception:
|
397
397
|
LOG.exception("Unable to delete the port")
|
398
|
+
try:
|
399
|
+
with session.begin():
|
400
|
+
self.amphora_member_port_repo.delete(session,
|
401
|
+
port_id=port_id)
|
402
|
+
except sa_exception.NoResultFound:
|
403
|
+
# Passively fail here for upgrade compatibility
|
404
|
+
LOG.warning("No Amphora member port records found for "
|
405
|
+
"port_id: %s", port_id)
|
398
406
|
|
399
407
|
updated_ports.pop(network_id, None)
|
400
408
|
return {amphora[constants.ID]: list(updated_ports.values())}
|
@@ -497,6 +505,20 @@ class UpdateVIPSecurityGroup(BaseNetworkTask):
|
|
497
505
|
return sg_id
|
498
506
|
|
499
507
|
|
508
|
+
class UpdateAmphoraSecurityGroup(BaseNetworkTask):
|
509
|
+
"""Task to update SGs for an Amphora."""
|
510
|
+
|
511
|
+
def execute(self, loadbalancer_id: str):
|
512
|
+
session = db_apis.get_session()
|
513
|
+
with session.begin():
|
514
|
+
db_lb = self.loadbalancer_repo.get(
|
515
|
+
session, id=loadbalancer_id)
|
516
|
+
for amp in db_lb.amphorae:
|
517
|
+
self.network_driver.update_aap_port_sg(db_lb,
|
518
|
+
amp,
|
519
|
+
db_lb.vip)
|
520
|
+
|
521
|
+
|
500
522
|
class GetSubnetFromVIP(BaseNetworkTask):
|
501
523
|
"""Task to plumb a VIP."""
|
502
524
|
|
@@ -536,11 +558,10 @@ class PlugVIPAmphora(BaseNetworkTask):
|
|
536
558
|
"""Handle a failure to plumb a vip."""
|
537
559
|
if isinstance(result, failure.Failure):
|
538
560
|
return
|
561
|
+
lb_id = loadbalancer[constants.LOADBALANCER_ID]
|
539
562
|
LOG.warning("Unable to plug VIP for amphora id %s "
|
540
563
|
"load balancer id %s",
|
541
|
-
amphora.get(constants.ID),
|
542
|
-
loadbalancer[constants.LOADBALANCER_ID])
|
543
|
-
|
564
|
+
amphora.get(constants.ID), lb_id)
|
544
565
|
try:
|
545
566
|
session = db_apis.get_session()
|
546
567
|
with session.begin():
|
@@ -550,15 +571,16 @@ class PlugVIPAmphora(BaseNetworkTask):
|
|
550
571
|
db_amp.ha_port_id = result[constants.HA_PORT_ID]
|
551
572
|
db_subnet = self.network_driver.get_subnet(
|
552
573
|
subnet[constants.ID])
|
553
|
-
db_lb = self.loadbalancer_repo.get(
|
554
|
-
session,
|
555
|
-
id=loadbalancer[constants.LOADBALANCER_ID])
|
556
|
-
|
574
|
+
db_lb = self.loadbalancer_repo.get(session, id=lb_id)
|
557
575
|
self.network_driver.unplug_aap_port(db_lb.vip,
|
558
576
|
db_amp, db_subnet)
|
559
577
|
except Exception as e:
|
560
|
-
LOG.error(
|
561
|
-
|
578
|
+
LOG.error(
|
579
|
+
'Failed to unplug AAP port for load balancer: %s. '
|
580
|
+
'Resources may still be in use for VRRP port: %s. '
|
581
|
+
'Due to error: %s',
|
582
|
+
lb_id, result[constants.VRRP_PORT_ID], str(e)
|
583
|
+
)
|
562
584
|
|
563
585
|
|
564
586
|
class UnplugVIP(BaseNetworkTask):
|
@@ -906,24 +928,29 @@ class DeletePort(BaseNetworkTask):
|
|
906
928
|
"""Delete the network port."""
|
907
929
|
if port_id is None:
|
908
930
|
return
|
909
|
-
|
931
|
+
# tenacity 8.5.0 moves statistics from the retry object to the function
|
932
|
+
try:
|
933
|
+
retry_statistics = self.execute.statistics
|
934
|
+
except AttributeError:
|
935
|
+
retry_statistics = self.execute.retry.statistics
|
936
|
+
|
937
|
+
if retry_statistics.get(constants.ATTEMPT_NUMBER, 1) == 1:
|
910
938
|
LOG.debug("Deleting network port %s", port_id)
|
911
939
|
else:
|
912
940
|
LOG.warning('Retrying network port %s delete attempt %s of %s.',
|
913
941
|
port_id,
|
914
|
-
|
915
|
-
constants.ATTEMPT_NUMBER],
|
942
|
+
retry_statistics[constants.ATTEMPT_NUMBER],
|
916
943
|
self.execute.retry.stop.max_attempt_number)
|
917
944
|
# Let the Taskflow engine know we are working and alive
|
918
945
|
# Don't use get with a default for 'attempt_number', we need to fail
|
919
946
|
# if that number is missing.
|
920
947
|
self.update_progress(
|
921
|
-
|
948
|
+
retry_statistics[constants.ATTEMPT_NUMBER] /
|
922
949
|
self.execute.retry.stop.max_attempt_number)
|
923
950
|
try:
|
924
951
|
self.network_driver.delete_port(port_id)
|
925
952
|
except Exception:
|
926
|
-
if (
|
953
|
+
if (retry_statistics[constants.ATTEMPT_NUMBER] !=
|
927
954
|
self.execute.retry.stop.max_attempt_number):
|
928
955
|
LOG.warning('Network port delete for port id: %s failed. '
|
929
956
|
'Retrying.', port_id)
|
@@ -950,6 +977,22 @@ class DeletePort(BaseNetworkTask):
|
|
950
977
|
raise
|
951
978
|
|
952
979
|
|
980
|
+
class DeleteAmphoraMemberPorts(BaseNetworkTask):
|
981
|
+
"""Task to delete all of the member ports on an Amphora."""
|
982
|
+
|
983
|
+
def execute(self, amphora_id, passive_failure=False):
|
984
|
+
delete_port = DeletePort()
|
985
|
+
session = db_apis.get_session()
|
986
|
+
|
987
|
+
with session.begin():
|
988
|
+
ports = self.amphora_member_port_repo.get_port_ids(
|
989
|
+
session, amphora_id)
|
990
|
+
for port in ports:
|
991
|
+
delete_port.execute(port, passive_failure)
|
992
|
+
with session.begin():
|
993
|
+
self.amphora_member_port_repo.delete(session, port_id=port)
|
994
|
+
|
995
|
+
|
953
996
|
class CreateVIPBasePort(BaseNetworkTask):
|
954
997
|
"""Task to create the VIP base port for an amphora."""
|
955
998
|
|
@@ -963,16 +1006,21 @@ class CreateVIPBasePort(BaseNetworkTask):
|
|
963
1006
|
def execute(self, vip, vip_sg_id, amphora_id, additional_vips):
|
964
1007
|
port_name = constants.AMP_BASE_PORT_PREFIX + amphora_id
|
965
1008
|
fixed_ips = [{constants.SUBNET_ID: vip[constants.SUBNET_ID]}]
|
966
|
-
|
1009
|
+
sg_ids = []
|
1010
|
+
# NOTE(gthiemonge) clarification:
|
1011
|
+
# - vip_sg_id is the ID of the SG created and managed by Octavia.
|
1012
|
+
# - vip['sg_ids'] are the IDs of the SGs provided by the user.
|
967
1013
|
if vip_sg_id:
|
968
|
-
|
1014
|
+
sg_ids = [vip_sg_id]
|
1015
|
+
if vip["sg_ids"]:
|
1016
|
+
sg_ids += vip["sg_ids"]
|
969
1017
|
secondary_ips = [vip[constants.IP_ADDRESS]]
|
970
1018
|
for add_vip in additional_vips:
|
971
1019
|
secondary_ips.append(add_vip[constants.IP_ADDRESS])
|
972
1020
|
port = self.network_driver.create_port(
|
973
1021
|
vip[constants.NETWORK_ID], name=port_name, fixed_ips=fixed_ips,
|
974
1022
|
secondary_ips=secondary_ips,
|
975
|
-
security_group_ids=
|
1023
|
+
security_group_ids=sg_ids,
|
976
1024
|
qos_policy_id=vip[constants.QOS_POLICY_ID])
|
977
1025
|
LOG.info('Created port %s with ID %s for amphora %s',
|
978
1026
|
port_name, port.id, amphora_id)
|
octavia/db/base_models.py
CHANGED
@@ -11,6 +11,7 @@
|
|
11
11
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
12
12
|
# License for the specific language governing permissions and limitations
|
13
13
|
# under the License.
|
14
|
+
from typing import Optional
|
14
15
|
|
15
16
|
from wsme import types as wtypes
|
16
17
|
|
@@ -58,9 +59,14 @@ class OctaviaBase(models.ModelBase):
|
|
58
59
|
if obj.__class__.__name__ in ['AdditionalVip']:
|
59
60
|
return (obj.__class__.__name__ +
|
60
61
|
obj.load_balancer_id + obj.subnet_id)
|
62
|
+
if obj.__class__.__name__ in ['VipSecurityGroup']:
|
63
|
+
return obj.__class__.__name__ + obj.load_balancer_id + obj.sg_id
|
64
|
+
if obj.__class__.__name__ in ['AmphoraMemberPort']:
|
65
|
+
return obj.__class__.__name__ + obj.port_id
|
61
66
|
raise NotImplementedError
|
62
67
|
|
63
|
-
def to_data_model(
|
68
|
+
def to_data_model(
|
69
|
+
self, _graph_nodes=None, recursion_depth: Optional[int] = None):
|
64
70
|
"""Converts to a data model graph.
|
65
71
|
|
66
72
|
In order to make the resulting data model graph usable no matter how
|
@@ -71,6 +77,13 @@ class OctaviaBase(models.ModelBase):
|
|
71
77
|
method. Should not be called from the outside.
|
72
78
|
Contains a dictionary of all OctaviaBase type
|
73
79
|
objects in the generated graph
|
80
|
+
:param recursion_depth: Used only for configuring recursion.
|
81
|
+
This option allows to limit recursion depth.
|
82
|
+
It could be used when we need only main node
|
83
|
+
and its first level relationships.
|
84
|
+
It allows to save time on recursion calls for
|
85
|
+
huge graphs, when only main object is
|
86
|
+
necessary.
|
74
87
|
"""
|
75
88
|
_graph_nodes = _graph_nodes or {}
|
76
89
|
if not self.__data_model__:
|
@@ -88,9 +101,16 @@ class OctaviaBase(models.ModelBase):
|
|
88
101
|
dm_self = self.__data_model__(**dm_kwargs)
|
89
102
|
dm_key = self._get_unique_key(dm_self)
|
90
103
|
_graph_nodes.update({dm_key: dm_self})
|
104
|
+
new_depth = recursion_depth
|
105
|
+
need_recursion = recursion_depth is None or recursion_depth > 0
|
106
|
+
# decrease depth of recursion on new recursion call
|
107
|
+
if new_depth:
|
108
|
+
new_depth -= 1
|
91
109
|
for attr_name in attr_names:
|
92
110
|
attr = getattr(self, attr_name)
|
93
|
-
if
|
111
|
+
if (need_recursion and
|
112
|
+
isinstance(attr, OctaviaBase) and
|
113
|
+
attr.__class__):
|
94
114
|
# If this attr is already in the graph node list, just
|
95
115
|
# reference it there and don't recurse.
|
96
116
|
ukey = self._get_unique_key(attr)
|
@@ -98,18 +118,22 @@ class OctaviaBase(models.ModelBase):
|
|
98
118
|
setattr(dm_self, attr_name, _graph_nodes[ukey])
|
99
119
|
else:
|
100
120
|
setattr(dm_self, attr_name, attr.to_data_model(
|
101
|
-
_graph_nodes=_graph_nodes
|
121
|
+
_graph_nodes=_graph_nodes,
|
122
|
+
recursion_depth=new_depth))
|
102
123
|
elif isinstance(attr, (collections.InstrumentedList, list)):
|
103
124
|
setattr(dm_self, attr_name, [])
|
104
125
|
listref = getattr(dm_self, attr_name)
|
105
126
|
for item in attr:
|
106
|
-
if
|
127
|
+
if (need_recursion and
|
128
|
+
isinstance(item, OctaviaBase) and
|
129
|
+
item.__class__):
|
107
130
|
ukey = self._get_unique_key(item)
|
108
131
|
if ukey in _graph_nodes.keys():
|
109
132
|
listref.append(_graph_nodes[ukey])
|
110
133
|
else:
|
111
134
|
listref.append(
|
112
|
-
item.to_data_model(_graph_nodes=_graph_nodes
|
135
|
+
item.to_data_model(_graph_nodes=_graph_nodes,
|
136
|
+
recursion_depth=new_depth))
|
113
137
|
elif not isinstance(item, OctaviaBase):
|
114
138
|
listref.append(item)
|
115
139
|
return dm_self
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
2
|
+
# not use this file except in compliance with the License. You may obtain
|
3
|
+
# a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
9
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
10
|
+
# License for the specific language governing permissions and limitations
|
11
|
+
# under the License.
|
12
|
+
|
13
|
+
"""add sg_id to vip table
|
14
|
+
|
15
|
+
Revision ID: 3097e55493ae
|
16
|
+
Revises: db2a73e82626
|
17
|
+
Create Date: 2024-04-05 10:04:32.015445
|
18
|
+
|
19
|
+
"""
|
20
|
+
|
21
|
+
from alembic import op
|
22
|
+
import sqlalchemy as sa
|
23
|
+
|
24
|
+
|
25
|
+
# revision identifiers, used by Alembic.
|
26
|
+
revision = '3097e55493ae'
|
27
|
+
down_revision = 'db2a73e82626'
|
28
|
+
|
29
|
+
|
30
|
+
def upgrade():
|
31
|
+
op.create_table(
|
32
|
+
"vip_security_group",
|
33
|
+
sa.Column("load_balancer_id", sa.String(36), nullable=False),
|
34
|
+
sa.Column("sg_id", sa.String(36), nullable=False),
|
35
|
+
sa.ForeignKeyConstraint(["load_balancer_id"],
|
36
|
+
["vip.load_balancer_id"],
|
37
|
+
name="fk_vip_sg_vip_lb_id"),
|
38
|
+
sa.PrimaryKeyConstraint("load_balancer_id", "sg_id")
|
39
|
+
)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
2
|
+
# not use this file except in compliance with the License. You may obtain
|
3
|
+
# a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
9
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
10
|
+
# License for the specific language governing permissions and limitations
|
11
|
+
# under the License.
|
12
|
+
|
13
|
+
"""Add member vnic_type
|
14
|
+
|
15
|
+
Revision ID: 8db7a6443785
|
16
|
+
Revises: 3097e55493ae
|
17
|
+
Create Date: 2024-03-29 20:34:37.263847
|
18
|
+
|
19
|
+
"""
|
20
|
+
|
21
|
+
from alembic import op
|
22
|
+
import sqlalchemy as sa
|
23
|
+
|
24
|
+
from octavia.common import constants
|
25
|
+
|
26
|
+
# revision identifiers, used by Alembic.
|
27
|
+
revision = '8db7a6443785'
|
28
|
+
down_revision = '3097e55493ae'
|
29
|
+
|
30
|
+
|
31
|
+
def upgrade():
|
32
|
+
op.add_column(
|
33
|
+
u'member',
|
34
|
+
sa.Column(u'vnic_type', sa.String(64), nullable=False,
|
35
|
+
server_default=constants.VNIC_TYPE_NORMAL)
|
36
|
+
)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
2
|
+
# not use this file except in compliance with the License. You may obtain
|
3
|
+
# a copy of the License at
|
4
|
+
#
|
5
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
#
|
7
|
+
# Unless required by applicable law or agreed to in writing, software
|
8
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
9
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
10
|
+
# License for the specific language governing permissions and limitations
|
11
|
+
# under the License.
|
12
|
+
|
13
|
+
"""add_member_port_table
|
14
|
+
|
15
|
+
Revision ID: fabf4983846b
|
16
|
+
Revises: 8db7a6443785
|
17
|
+
Create Date: 2024-08-30 23:12:01.713217
|
18
|
+
|
19
|
+
"""
|
20
|
+
from alembic import op
|
21
|
+
import sqlalchemy as sa
|
22
|
+
|
23
|
+
# revision identifiers, used by Alembic.
|
24
|
+
revision = 'fabf4983846b'
|
25
|
+
down_revision = '8db7a6443785'
|
26
|
+
|
27
|
+
|
28
|
+
def upgrade():
|
29
|
+
op.create_table(
|
30
|
+
'amphora_member_port',
|
31
|
+
sa.Column('port_id', sa.String(36), primary_key=True),
|
32
|
+
sa.Column('amphora_id', sa.String(36), nullable=False, index=True),
|
33
|
+
sa.Column('network_id', sa.String(36)),
|
34
|
+
sa.Column('created_at', sa.DateTime()),
|
35
|
+
sa.Column('updated_at', sa.DateTime())
|
36
|
+
)
|
37
|
+
op.create_foreign_key(
|
38
|
+
'fk_member_port_amphora_id', 'amphora_member_port',
|
39
|
+
'amphora', ['amphora_id'], ['id']
|
40
|
+
)
|
octavia/db/models.py
CHANGED
@@ -229,6 +229,7 @@ class Member(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin,
|
|
229
229
|
nullable=False)
|
230
230
|
enabled = sa.Column(sa.Boolean(), nullable=False)
|
231
231
|
pool = orm.relationship("Pool", back_populates="members")
|
232
|
+
vnic_type = sa.Column(sa.String(64), nullable=True)
|
232
233
|
|
233
234
|
_tags = orm.relationship(
|
234
235
|
'Tags',
|
@@ -246,7 +247,7 @@ class Member(base_models.BASE, base_models.IdMixin, base_models.ProjectMixin,
|
|
246
247
|
f"ip_address={self.ip_address!r}, "
|
247
248
|
f"protocol_port={self.protocol_port!r}, "
|
248
249
|
f"operating_status={self.operating_status!r}, "
|
249
|
-
f"weight={self.weight!r})")
|
250
|
+
f"weight={self.weight!r}, vnic_type={self.vnic_type!r})")
|
250
251
|
|
251
252
|
|
252
253
|
class HealthMonitor(base_models.BASE, base_models.IdMixin,
|
@@ -508,6 +509,14 @@ class Vip(base_models.BASE):
|
|
508
509
|
octavia_owned = sa.Column(sa.Boolean(), nullable=True)
|
509
510
|
vnic_type = sa.Column(sa.String(64), nullable=True)
|
510
511
|
|
512
|
+
sgs = orm.relationship(
|
513
|
+
"VipSecurityGroup", cascade="all,delete-orphan",
|
514
|
+
uselist=True, backref=orm.backref("vip", uselist=False))
|
515
|
+
|
516
|
+
@property
|
517
|
+
def sg_ids(self) -> list[str]:
|
518
|
+
return [sg.sg_id for sg in self.sgs]
|
519
|
+
|
511
520
|
|
512
521
|
class AdditionalVip(base_models.BASE):
|
513
522
|
|
@@ -976,3 +985,36 @@ class ListenerCidr(base_models.BASE):
|
|
976
985
|
sa.ForeignKey("listener.id", name="fk_listener_cidr_listener_id"),
|
977
986
|
nullable=False)
|
978
987
|
cidr = sa.Column(sa.String(64), nullable=False)
|
988
|
+
|
989
|
+
|
990
|
+
class VipSecurityGroup(base_models.BASE):
|
991
|
+
|
992
|
+
__data_model__ = data_models.VipSecurityGroup
|
993
|
+
|
994
|
+
__tablename__ = "vip_security_group"
|
995
|
+
__table_args__ = (
|
996
|
+
sa.PrimaryKeyConstraint('load_balancer_id', 'sg_id'),
|
997
|
+
)
|
998
|
+
|
999
|
+
load_balancer_id = sa.Column(
|
1000
|
+
sa.String(36),
|
1001
|
+
sa.ForeignKey("vip.load_balancer_id", name="fk_vip_sg_vip_lb_id"),
|
1002
|
+
nullable=False)
|
1003
|
+
sg_id = sa.Column(sa.String(64), nullable=False)
|
1004
|
+
|
1005
|
+
|
1006
|
+
class AmphoraMemberPort(base_models.BASE, models.TimestampMixin):
|
1007
|
+
|
1008
|
+
__data_model__ = data_models.AmphoraMemberPort
|
1009
|
+
|
1010
|
+
__tablename__ = "amphora_member_port"
|
1011
|
+
|
1012
|
+
port_id = sa.Column(
|
1013
|
+
sa.String(36),
|
1014
|
+
primary_key=True)
|
1015
|
+
amphora_id = sa.Column(
|
1016
|
+
sa.String(36),
|
1017
|
+
sa.ForeignKey("amphora.id", name="fk_member_port_amphora_id"),
|
1018
|
+
nullable=False)
|
1019
|
+
network_id = sa.Column(
|
1020
|
+
sa.String(36))
|