octavia 13.0.0.0rc1__py3-none-any.whl → 14.0.0__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/lvs_listener_base.py +1 -1
- octavia/amphorae/backends/agent/api_server/osutils.py +5 -5
- octavia/amphorae/backends/agent/api_server/plug.py +3 -2
- octavia/amphorae/backends/agent/api_server/rules_schema.py +52 -0
- octavia/amphorae/backends/agent/api_server/server.py +28 -1
- octavia/amphorae/backends/utils/interface.py +45 -6
- octavia/amphorae/backends/utils/interface_file.py +9 -6
- octavia/amphorae/backends/utils/nftable_utils.py +125 -0
- octavia/amphorae/drivers/driver_base.py +27 -0
- octavia/amphorae/drivers/haproxy/rest_api_driver.py +42 -10
- octavia/amphorae/drivers/health/heartbeat_udp.py +2 -2
- octavia/amphorae/drivers/keepalived/vrrp_rest_driver.py +2 -1
- octavia/amphorae/drivers/noop_driver/driver.py +25 -0
- octavia/api/app.py +3 -0
- octavia/api/common/pagination.py +2 -2
- octavia/api/drivers/amphora_driver/flavor_schema.py +6 -1
- octavia/api/root_controller.py +4 -1
- octavia/api/v2/controllers/health_monitor.py +0 -1
- octavia/api/v2/controllers/l7policy.py +0 -1
- octavia/api/v2/controllers/l7rule.py +0 -1
- octavia/api/v2/controllers/listener.py +0 -1
- octavia/api/v2/controllers/load_balancer.py +13 -7
- octavia/api/v2/controllers/member.py +6 -3
- octavia/api/v2/controllers/pool.py +6 -7
- octavia/api/v2/types/load_balancer.py +5 -1
- octavia/api/v2/types/pool.py +1 -1
- octavia/certificates/common/pkcs12.py +9 -9
- octavia/certificates/manager/barbican.py +24 -16
- octavia/certificates/manager/castellan_mgr.py +12 -7
- octavia/certificates/manager/local.py +4 -4
- octavia/certificates/manager/noop.py +106 -0
- octavia/cmd/driver_agent.py +1 -1
- octavia/cmd/health_checker.py +0 -4
- octavia/cmd/health_manager.py +1 -5
- octavia/cmd/house_keeping.py +1 -1
- octavia/cmd/interface.py +0 -4
- octavia/cmd/octavia_worker.py +0 -4
- octavia/cmd/prometheus_proxy.py +0 -5
- octavia/cmd/status.py +0 -6
- octavia/common/base_taskflow.py +1 -1
- octavia/common/clients.py +15 -3
- octavia/common/config.py +24 -6
- octavia/common/constants.py +34 -0
- octavia/common/data_models.py +3 -1
- octavia/common/exceptions.py +11 -0
- octavia/common/jinja/haproxy/combined_listeners/templates/macros.j2 +7 -5
- octavia/common/keystone.py +7 -7
- octavia/common/tls_utils/cert_parser.py +24 -10
- octavia/common/utils.py +6 -0
- octavia/common/validate.py +2 -2
- octavia/compute/drivers/nova_driver.py +23 -5
- octavia/controller/worker/task_utils.py +28 -6
- octavia/controller/worker/v2/controller_worker.py +49 -15
- octavia/controller/worker/v2/flows/amphora_flows.py +120 -21
- octavia/controller/worker/v2/flows/flow_utils.py +15 -13
- octavia/controller/worker/v2/flows/listener_flows.py +95 -5
- octavia/controller/worker/v2/flows/load_balancer_flows.py +74 -30
- octavia/controller/worker/v2/taskflow_jobboard_driver.py +17 -1
- octavia/controller/worker/v2/tasks/amphora_driver_tasks.py +145 -24
- octavia/controller/worker/v2/tasks/compute_tasks.py +1 -1
- octavia/controller/worker/v2/tasks/database_tasks.py +72 -41
- octavia/controller/worker/v2/tasks/lifecycle_tasks.py +97 -41
- octavia/controller/worker/v2/tasks/network_tasks.py +57 -60
- octavia/controller/worker/v2/tasks/shim_tasks.py +28 -0
- octavia/db/migration/alembic_migrations/versions/55874a4ceed6_add_l7policy_action_redirect_prefix.py +1 -1
- octavia/db/migration/alembic_migrations/versions/5a3ee5472c31_add_cert_expiration__infor_in_amphora_table.py +1 -1
- octavia/db/migration/alembic_migrations/versions/6742ca1b27c2_add_l7policy_redirect_http_code.py +1 -1
- octavia/db/migration/alembic_migrations/versions/db2a73e82626_add_vnic_type_for_vip.py +36 -0
- octavia/db/models.py +1 -0
- octavia/db/prepare.py +1 -1
- octavia/db/repositories.py +53 -34
- octavia/distributor/drivers/driver_base.py +1 -1
- octavia/network/base.py +3 -16
- octavia/network/data_models.py +4 -1
- octavia/network/drivers/neutron/allowed_address_pairs.py +27 -26
- octavia/network/drivers/noop_driver/driver.py +10 -23
- octavia/tests/common/sample_certs.py +115 -0
- octavia/tests/common/sample_haproxy_prometheus +1 -1
- octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py +37 -0
- octavia/tests/functional/api/test_healthcheck.py +2 -2
- octavia/tests/functional/api/v2/base.py +1 -1
- octavia/tests/functional/api/v2/test_listener.py +45 -0
- octavia/tests/functional/api/v2/test_load_balancer.py +17 -0
- octavia/tests/functional/db/base.py +9 -0
- octavia/tests/functional/db/test_models.py +2 -1
- octavia/tests/functional/db/test_repositories.py +55 -99
- octavia/tests/unit/amphorae/backends/agent/api_server/test_osutils.py +4 -2
- octavia/tests/unit/amphorae/backends/utils/test_interface.py +201 -1
- octavia/tests/unit/amphorae/backends/utils/test_keepalivedlvs_query.py +1 -1
- octavia/tests/unit/amphorae/backends/utils/test_nftable_utils.py +194 -0
- octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py +27 -5
- octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_1_0.py +15 -2
- octavia/tests/unit/amphorae/drivers/keepalived/test_vrrp_rest_driver.py +17 -0
- octavia/tests/unit/amphorae/drivers/noop_driver/test_driver.py +2 -1
- octavia/tests/unit/api/v2/types/test_pool.py +71 -0
- octavia/tests/unit/certificates/manager/test_barbican.py +3 -3
- octavia/tests/unit/certificates/manager/test_noop.py +53 -0
- octavia/tests/unit/common/jinja/haproxy/combined_listeners/test_jinja_cfg.py +16 -17
- octavia/tests/unit/common/sample_configs/sample_configs_combined.py +5 -3
- octavia/tests/unit/common/test_config.py +35 -0
- octavia/tests/unit/common/test_keystone.py +32 -0
- octavia/tests/unit/common/test_utils.py +39 -0
- octavia/tests/unit/compute/drivers/test_nova_driver.py +22 -0
- octavia/tests/unit/controller/worker/test_task_utils.py +58 -2
- octavia/tests/unit/controller/worker/v2/flows/test_amphora_flows.py +28 -5
- octavia/tests/unit/controller/worker/v2/flows/test_listener_flows.py +64 -16
- octavia/tests/unit/controller/worker/v2/flows/test_load_balancer_flows.py +49 -9
- octavia/tests/unit/controller/worker/v2/tasks/test_amphora_driver_tasks.py +265 -17
- octavia/tests/unit/controller/worker/v2/tasks/test_database_tasks.py +101 -1
- octavia/tests/unit/controller/worker/v2/tasks/test_database_tasks_quota.py +19 -19
- octavia/tests/unit/controller/worker/v2/tasks/test_network_tasks.py +105 -42
- octavia/tests/unit/controller/worker/v2/tasks/test_shim_tasks.py +33 -0
- octavia/tests/unit/controller/worker/v2/test_controller_worker.py +85 -42
- octavia/tests/unit/network/drivers/neutron/test_allowed_address_pairs.py +48 -51
- octavia/tests/unit/network/drivers/neutron/test_utils.py +2 -0
- octavia/tests/unit/network/drivers/noop_driver/test_driver.py +0 -7
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/README.rst +6 -1
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/diskimage-create.sh +10 -4
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/requirements.txt +0 -2
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/tox.ini +30 -13
- {octavia-13.0.0.0rc1.dist-info → octavia-14.0.0.dist-info}/AUTHORS +5 -0
- {octavia-13.0.0.0rc1.dist-info → octavia-14.0.0.dist-info}/METADATA +6 -6
- {octavia-13.0.0.0rc1.dist-info → octavia-14.0.0.dist-info}/RECORD +134 -126
- {octavia-13.0.0.0rc1.dist-info → octavia-14.0.0.dist-info}/entry_points.txt +1 -1
- octavia-14.0.0.dist-info/pbr.json +1 -0
- octavia-13.0.0.0rc1.dist-info/pbr.json +0 -1
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/LICENSE +0 -0
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/README.rst +0 -0
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/image-tests.sh +0 -0
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/test-requirements.txt +0 -0
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/version.txt +0 -0
- {octavia-13.0.0.0rc1.data → octavia-14.0.0.data}/scripts/octavia-wsgi +0 -0
- {octavia-13.0.0.0rc1.dist-info → octavia-14.0.0.dist-info}/LICENSE +0 -0
- {octavia-13.0.0.0rc1.dist-info → octavia-14.0.0.dist-info}/WHEEL +0 -0
- {octavia-13.0.0.0rc1.dist-info → octavia-14.0.0.dist-info}/top_level.txt +0 -0
octavia/api/common/pagination.py
CHANGED
@@ -244,7 +244,7 @@ class PaginationHelper(object):
|
|
244
244
|
:param query: the query object to which we should add
|
245
245
|
paging/sorting/filtering
|
246
246
|
:param model: the ORM model class
|
247
|
-
:param enforce_valid_params: check for invalid
|
247
|
+
:param enforce_valid_params: check for invalid entries in self.params
|
248
248
|
|
249
249
|
:rtype: sqlalchemy.orm.query.Query
|
250
250
|
:returns: The query with sorting/pagination/filtering added.
|
@@ -259,7 +259,7 @@ class PaginationHelper(object):
|
|
259
259
|
secondary_query_filter = filter_params.pop(
|
260
260
|
"project_id", None) if (model == models.Amphora) else None
|
261
261
|
|
262
|
-
#
|
262
|
+
# Translate arguments from API standard to data model's field name
|
263
263
|
filter_params = (
|
264
264
|
model.__v2_wsme__.translate_dict_keys_to_data_model(
|
265
265
|
filter_params)
|
@@ -47,6 +47,11 @@ SUPPORTED_FLAVOR_SCHEMA = {
|
|
47
47
|
consts.AMP_IMAGE_TAG: {
|
48
48
|
"type": "string",
|
49
49
|
"description": "The amphora image tag."
|
50
|
-
}
|
50
|
+
},
|
51
|
+
consts.SRIOV_VIP: {
|
52
|
+
"type": "boolean",
|
53
|
+
"description": "When true, the VIP port will be created using an "
|
54
|
+
"SR-IOV VF port."
|
55
|
+
},
|
51
56
|
}
|
52
57
|
}
|
octavia/api/root_controller.py
CHANGED
@@ -146,6 +146,9 @@ class RootController(object):
|
|
146
146
|
self._add_a_version(versions, 'v2.26', 'v2', 'SUPPORTED',
|
147
147
|
'2022-08-29T00:00:00Z', host_url)
|
148
148
|
# HTTP Strict Transport Security (HSTS)
|
149
|
-
self._add_a_version(versions, 'v2.27', 'v2', '
|
149
|
+
self._add_a_version(versions, 'v2.27', 'v2', 'SUPPORTED',
|
150
150
|
'2023-05-05T00:00:00Z', host_url)
|
151
|
+
# Add port vnic_type for SR-IOV
|
152
|
+
self._add_a_version(versions, 'v2.28', 'v2', 'CURRENT',
|
153
|
+
'2023-11-08T00:00:00Z', host_url)
|
151
154
|
return {'versions': versions}
|
@@ -465,7 +465,6 @@ class LoadBalancersController(base.BaseController):
|
|
465
465
|
lock_session.begin()
|
466
466
|
try:
|
467
467
|
if self.repositories.check_quota_met(
|
468
|
-
context.session,
|
469
468
|
lock_session,
|
470
469
|
data_models.LoadBalancer,
|
471
470
|
load_balancer.project_id):
|
@@ -497,6 +496,13 @@ class LoadBalancersController(base.BaseController):
|
|
497
496
|
load_balancer.vip_network_id,
|
498
497
|
valid_networks=az_dict.get(constants.VALID_VIP_NETWORKS))
|
499
498
|
|
499
|
+
# Apply the anticipated vNIC type so the create will return the
|
500
|
+
# right vip_vnic_type
|
501
|
+
if flavor_dict and flavor_dict.get(constants.SRIOV_VIP, False):
|
502
|
+
vip_dict[constants.VNIC_TYPE] = constants.VNIC_TYPE_DIRECT
|
503
|
+
else:
|
504
|
+
vip_dict[constants.VNIC_TYPE] = constants.VNIC_TYPE_NORMAL
|
505
|
+
|
500
506
|
db_lb = self.repositories.create_load_balancer_and_vip(
|
501
507
|
lock_session, lb_dict, vip_dict, additional_vip_dicts)
|
502
508
|
|
@@ -553,7 +559,7 @@ class LoadBalancersController(base.BaseController):
|
|
553
559
|
|
554
560
|
if listeners or pools:
|
555
561
|
db_pools, db_lists = self._graph_create(
|
556
|
-
|
562
|
+
lock_session, db_lb, listeners, pools)
|
557
563
|
|
558
564
|
# Prepare the data for the driver data model
|
559
565
|
driver_lb_dict = driver_utils.lb_dict_to_provider_dict(
|
@@ -583,7 +589,7 @@ class LoadBalancersController(base.BaseController):
|
|
583
589
|
db_lb, lb_types.LoadBalancerFullResponse)
|
584
590
|
return lb_types.LoadBalancerFullRootResponse(loadbalancer=result)
|
585
591
|
|
586
|
-
def _graph_create(self, session,
|
592
|
+
def _graph_create(self, session, db_lb, listeners, pools):
|
587
593
|
# Track which pools must have a full specification
|
588
594
|
pools_required = set()
|
589
595
|
# Look through listeners and find any extra pools, and move them to the
|
@@ -642,7 +648,7 @@ class LoadBalancersController(base.BaseController):
|
|
642
648
|
|
643
649
|
# Check quotas for pools.
|
644
650
|
if pools and self.repositories.check_quota_met(
|
645
|
-
session,
|
651
|
+
session, data_models.Pool, db_lb.project_id,
|
646
652
|
count=len(pools)):
|
647
653
|
raise exceptions.QuotaException(resource=data_models.Pool._name())
|
648
654
|
|
@@ -661,13 +667,13 @@ class LoadBalancersController(base.BaseController):
|
|
661
667
|
p['load_balancer_id'] = db_lb.id
|
662
668
|
p['project_id'] = db_lb.project_id
|
663
669
|
new_pool = (pool.PoolsController()._graph_create(
|
664
|
-
session,
|
670
|
+
session, p))
|
665
671
|
new_pools.append(new_pool)
|
666
672
|
pool_name_ids[new_pool.name] = new_pool.id
|
667
673
|
|
668
674
|
# Now check quotas for listeners
|
669
675
|
if listeners and self.repositories.check_quota_met(
|
670
|
-
session,
|
676
|
+
session, data_models.Listener, db_lb.project_id,
|
671
677
|
count=len(listeners)):
|
672
678
|
raise exceptions.QuotaException(
|
673
679
|
resource=data_models.Listener._name())
|
@@ -687,7 +693,7 @@ class LoadBalancersController(base.BaseController):
|
|
687
693
|
li['load_balancer_id'] = db_lb.id
|
688
694
|
li['project_id'] = db_lb.project_id
|
689
695
|
new_lists.append(listener.ListenersController()._graph_create(
|
690
|
-
|
696
|
+
session, li, pool_name_ids=pool_name_ids))
|
691
697
|
|
692
698
|
return new_pools, new_lists
|
693
699
|
|
@@ -165,7 +165,6 @@ class MemberController(base.BaseController):
|
|
165
165
|
context.session.begin()
|
166
166
|
try:
|
167
167
|
if self.repositories.check_quota_met(
|
168
|
-
context.session,
|
169
168
|
context.session,
|
170
169
|
data_models.Member,
|
171
170
|
member.project_id):
|
@@ -336,7 +335,6 @@ class MembersController(MemberController):
|
|
336
335
|
|
337
336
|
with context.session.begin():
|
338
337
|
db_pool = self._get_db_pool(context.session, self.pool_id)
|
339
|
-
old_members = db_pool.members
|
340
338
|
|
341
339
|
project_id, provider = self._get_lb_project_id_provider(
|
342
340
|
context.session, db_pool.load_balancer_id)
|
@@ -354,6 +352,11 @@ class MembersController(MemberController):
|
|
354
352
|
with context.session.begin():
|
355
353
|
self._test_lb_and_listener_and_pool_statuses(context.session)
|
356
354
|
|
355
|
+
# Reload the pool, the members may have been updated between the
|
356
|
+
# first query in this function and the lock of the loadbalancer
|
357
|
+
db_pool = self._get_db_pool(context.session, self.pool_id)
|
358
|
+
old_members = db_pool.members
|
359
|
+
|
357
360
|
old_member_uniques = {
|
358
361
|
(m.ip_address, m.protocol_port): m.id for m in old_members}
|
359
362
|
new_member_uniques = [
|
@@ -387,7 +390,7 @@ class MembersController(MemberController):
|
|
387
390
|
else:
|
388
391
|
member_count_diff = len(new_members) - len(deleted_members)
|
389
392
|
if member_count_diff > 0 and self.repositories.check_quota_met(
|
390
|
-
context.session,
|
393
|
+
context.session, data_models.Member,
|
391
394
|
db_pool.project_id, count=member_count_diff):
|
392
395
|
raise exceptions.QuotaException(
|
393
396
|
resource=data_models.Member._name())
|
@@ -260,7 +260,6 @@ class PoolsController(base.BaseController):
|
|
260
260
|
context.session.begin()
|
261
261
|
try:
|
262
262
|
if self.repositories.check_quota_met(
|
263
|
-
context.session,
|
264
263
|
context.session,
|
265
264
|
data_models.Pool,
|
266
265
|
pool.project_id):
|
@@ -304,18 +303,18 @@ class PoolsController(base.BaseController):
|
|
304
303
|
result = self._convert_db_to_type(db_pool, pool_types.PoolResponse)
|
305
304
|
return pool_types.PoolRootResponse(pool=result)
|
306
305
|
|
307
|
-
def _graph_create(self, session,
|
306
|
+
def _graph_create(self, session, pool_dict):
|
308
307
|
load_balancer_id = pool_dict['load_balancer_id']
|
309
308
|
pool_dict = db_prepare.create_pool(
|
310
309
|
pool_dict, load_balancer_id)
|
311
310
|
members = pool_dict.pop('members', []) or []
|
312
311
|
hm = pool_dict.pop('health_monitor', None)
|
313
312
|
db_pool = self._validate_create_pool(
|
314
|
-
|
313
|
+
session, pool_dict)
|
315
314
|
|
316
315
|
# Check quotas for healthmonitors
|
317
316
|
if hm and self.repositories.check_quota_met(
|
318
|
-
session,
|
317
|
+
session, data_models.HealthMonitor,
|
319
318
|
db_pool.project_id):
|
320
319
|
raise exceptions.QuotaException(
|
321
320
|
resource=data_models.HealthMonitor._name())
|
@@ -325,7 +324,7 @@ class PoolsController(base.BaseController):
|
|
325
324
|
hm[constants.POOL_ID] = db_pool.id
|
326
325
|
hm[constants.PROJECT_ID] = db_pool.project_id
|
327
326
|
new_hm = health_monitor.HealthMonitorController()._graph_create(
|
328
|
-
|
327
|
+
session, hm)
|
329
328
|
if db_pool.protocol in (constants.PROTOCOL_UDP,
|
330
329
|
lib_consts.PROTOCOL_SCTP):
|
331
330
|
health_monitor.HealthMonitorController(
|
@@ -344,7 +343,7 @@ class PoolsController(base.BaseController):
|
|
344
343
|
|
345
344
|
# Now check quotas for members
|
346
345
|
if members and self.repositories.check_quota_met(
|
347
|
-
session,
|
346
|
+
session, data_models.Member,
|
348
347
|
db_pool.project_id, count=len(members)):
|
349
348
|
raise exceptions.QuotaException(
|
350
349
|
resource=data_models.Member._name())
|
@@ -357,7 +356,7 @@ class PoolsController(base.BaseController):
|
|
357
356
|
m['project_id'] = db_pool.project_id
|
358
357
|
new_members.append(
|
359
358
|
member.MembersController(db_pool.id)._graph_create(
|
360
|
-
|
359
|
+
session, m))
|
361
360
|
db_pool.members = new_members
|
362
361
|
return db_pool
|
363
362
|
|
@@ -25,13 +25,15 @@ class BaseLoadBalancerType(types.BaseType):
|
|
25
25
|
'vip_port_id': 'vip.port_id',
|
26
26
|
'vip_network_id': 'vip.network_id',
|
27
27
|
'vip_qos_policy_id': 'vip.qos_policy_id',
|
28
|
+
'vip_vnic_type': 'vip.vnic_type',
|
28
29
|
'admin_state_up': 'enabled'}
|
29
30
|
_child_map = {'vip': {
|
30
31
|
'ip_address': 'vip_address',
|
31
32
|
'subnet_id': 'vip_subnet_id',
|
32
33
|
'port_id': 'vip_port_id',
|
33
34
|
'network_id': 'vip_network_id',
|
34
|
-
'qos_policy_id': 'vip_qos_policy_id'
|
35
|
+
'qos_policy_id': 'vip_qos_policy_id',
|
36
|
+
'vnic_type': 'vip_vnic_type'}}
|
35
37
|
|
36
38
|
|
37
39
|
class AdditionalVipsType(types.BaseType):
|
@@ -63,6 +65,7 @@ class LoadBalancerResponse(BaseLoadBalancerType):
|
|
63
65
|
vip_qos_policy_id = wtypes.wsattr(wtypes.UuidType())
|
64
66
|
tags = wtypes.wsattr(wtypes.ArrayType(wtypes.StringType()))
|
65
67
|
availability_zone = wtypes.wsattr(wtypes.StringType())
|
68
|
+
vip_vnic_type = wtypes.wsattr(wtypes.StringType())
|
66
69
|
|
67
70
|
@classmethod
|
68
71
|
def from_data_model(cls, data_model, children=False):
|
@@ -74,6 +77,7 @@ class LoadBalancerResponse(BaseLoadBalancerType):
|
|
74
77
|
result.vip_address = data_model.vip.ip_address
|
75
78
|
result.vip_network_id = data_model.vip.network_id
|
76
79
|
result.vip_qos_policy_id = data_model.vip.qos_policy_id
|
80
|
+
result.vip_vnic_type = data_model.vip.vnic_type
|
77
81
|
result.additional_vips = [
|
78
82
|
AdditionalVipsType.from_data_model(i)
|
79
83
|
for i in data_model.additional_vips]
|
octavia/api/v2/types/pool.py
CHANGED
@@ -106,7 +106,7 @@ class PoolResponse(BasePoolType):
|
|
106
106
|
if cls._full_response():
|
107
107
|
del pool.loadbalancers
|
108
108
|
member_model = member.MemberFullResponse
|
109
|
-
if
|
109
|
+
if data_model.health_monitor:
|
110
110
|
pool.healthmonitor = (
|
111
111
|
health_monitor.HealthMonitorFullResponse
|
112
112
|
.from_data_model(data_model.health_monitor))
|
@@ -18,7 +18,7 @@ Common classes for pkcs12 based certificate handling
|
|
18
18
|
"""
|
19
19
|
|
20
20
|
from cryptography.hazmat.primitives import serialization
|
21
|
-
from
|
21
|
+
from cryptography.hazmat.primitives.serialization import pkcs12
|
22
22
|
|
23
23
|
from octavia.certificates.common import cert
|
24
24
|
from octavia.common import exceptions
|
@@ -28,21 +28,21 @@ class PKCS12Cert(cert.Cert):
|
|
28
28
|
"""Representation of a Cert for local storage."""
|
29
29
|
def __init__(self, certbag):
|
30
30
|
try:
|
31
|
-
p12 =
|
32
|
-
except
|
31
|
+
p12 = pkcs12.load_pkcs12(certbag, None)
|
32
|
+
except (TypeError, ValueError) as e:
|
33
33
|
raise exceptions.UnreadablePKCS12(error=str(e))
|
34
|
-
self.certificate = p12.
|
35
|
-
self.intermediates = p12.
|
36
|
-
self.private_key = p12.
|
34
|
+
self.certificate = p12.cert
|
35
|
+
self.intermediates = p12.additional_certs
|
36
|
+
self.private_key = p12.key
|
37
37
|
|
38
38
|
def get_certificate(self):
|
39
|
-
return self.certificate.
|
39
|
+
return self.certificate.certificate.public_bytes(
|
40
40
|
encoding=serialization.Encoding.PEM).strip()
|
41
41
|
|
42
42
|
def get_intermediates(self):
|
43
43
|
if self.intermediates:
|
44
44
|
int_data = [
|
45
|
-
ic.
|
45
|
+
ic.certificate.public_bytes(
|
46
46
|
encoding=serialization.Encoding.PEM).strip()
|
47
47
|
for ic in self.intermediates
|
48
48
|
]
|
@@ -50,7 +50,7 @@ class PKCS12Cert(cert.Cert):
|
|
50
50
|
return None
|
51
51
|
|
52
52
|
def get_private_key(self):
|
53
|
-
return self.private_key.
|
53
|
+
return self.private_key.private_bytes(
|
54
54
|
encoding=serialization.Encoding.PEM,
|
55
55
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
56
56
|
encryption_algorithm=serialization.NoEncryption()).strip()
|
@@ -17,8 +17,9 @@
|
|
17
17
|
"""
|
18
18
|
Cert manager implementation for Barbican using a single PKCS12 secret
|
19
19
|
"""
|
20
|
-
from
|
21
|
-
|
20
|
+
from cryptography.hazmat.primitives import serialization
|
21
|
+
from cryptography.hazmat.primitives.serialization import pkcs12 as c_pkcs12
|
22
|
+
from cryptography import x509
|
22
23
|
from oslo_config import cfg
|
23
24
|
from oslo_log import log as logging
|
24
25
|
from oslo_utils import encodeutils
|
@@ -64,25 +65,29 @@ class BarbicanCertManager(cert_mgr.CertManager):
|
|
64
65
|
connection = self.auth.get_barbican_client(context.project_id)
|
65
66
|
|
66
67
|
LOG.info("Storing certificate secret '%s' in Barbican.", name)
|
67
|
-
|
68
|
-
p12.set_friendlyname(encodeutils.to_utf8(name))
|
69
|
-
x509_cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificate)
|
70
|
-
p12.set_certificate(x509_cert)
|
71
|
-
x509_pk = crypto.load_privatekey(crypto.FILETYPE_PEM, private_key)
|
72
|
-
p12.set_privatekey(x509_pk)
|
73
|
-
if intermediates:
|
74
|
-
cert_ints = list(cert_parser.get_intermediates_pems(intermediates))
|
75
|
-
x509_ints = [
|
76
|
-
crypto.load_certificate(crypto.FILETYPE_PEM, ci)
|
77
|
-
for ci in cert_ints]
|
78
|
-
p12.set_ca_certificates(x509_ints)
|
68
|
+
|
79
69
|
if private_key_passphrase:
|
80
70
|
raise exceptions.CertificateStorageException(
|
81
71
|
"Passphrase protected PKCS12 certificates are not supported.")
|
82
72
|
|
73
|
+
x509_cert = x509.load_pem_x509_certificate(certificate)
|
74
|
+
x509_pk = serialization.load_pem_private_key(private_key, None)
|
75
|
+
cas = None
|
76
|
+
if intermediates:
|
77
|
+
cert_ints = list(cert_parser.get_intermediates_pems(intermediates))
|
78
|
+
cas = [
|
79
|
+
x509.load_pem_x509_certificate(ci)
|
80
|
+
for ci in cert_ints]
|
81
|
+
|
83
82
|
try:
|
84
83
|
certificate_secret = connection.secrets.create(
|
85
|
-
payload=
|
84
|
+
payload=c_pkcs12.serialize_key_and_certificates(
|
85
|
+
name=encodeutils.safe_encode(name),
|
86
|
+
key=x509_pk,
|
87
|
+
cert=x509_cert,
|
88
|
+
cas=cas,
|
89
|
+
encryption_algorithm=serialization.NoEncryption()
|
90
|
+
),
|
86
91
|
expiration=expiration,
|
87
92
|
name=name
|
88
93
|
)
|
@@ -115,7 +120,10 @@ class BarbicanCertManager(cert_mgr.CertManager):
|
|
115
120
|
return pkcs12.PKCS12Cert(cert_secret.payload)
|
116
121
|
except exceptions.UnreadablePKCS12:
|
117
122
|
raise
|
118
|
-
except Exception:
|
123
|
+
except Exception as e:
|
124
|
+
LOG.warning('Failed to load PKCS12Cert for secret %s with %s',
|
125
|
+
cert_ref, str(e))
|
126
|
+
LOG.warning('Falling back to the barbican_legacy implementation.')
|
119
127
|
# If our get fails, try with the legacy driver.
|
120
128
|
# TODO(rm_work): Remove this code when the deprecation cycle for
|
121
129
|
# the legacy driver is complete.
|
@@ -18,7 +18,8 @@ Cert manager implementation for Castellan
|
|
18
18
|
"""
|
19
19
|
from castellan.common.objects import opaque_data
|
20
20
|
from castellan import key_manager
|
21
|
-
from
|
21
|
+
from cryptography.hazmat.primitives import serialization
|
22
|
+
from cryptography.hazmat.primitives.serialization import pkcs12 as c_pkcs12
|
22
23
|
from oslo_config import cfg
|
23
24
|
from oslo_log import log as logging
|
24
25
|
|
@@ -41,16 +42,20 @@ class CastellanCertManager(cert_mgr.CertManager):
|
|
41
42
|
def store_cert(self, context, certificate, private_key, intermediates=None,
|
42
43
|
private_key_passphrase=None, expiration=None,
|
43
44
|
name="PKCS12 Certificate Bundle"):
|
44
|
-
p12 = crypto.PKCS12()
|
45
|
-
p12.set_certificate(certificate)
|
46
|
-
p12.set_privatekey(private_key)
|
47
|
-
if intermediates:
|
48
|
-
p12.set_ca_certificates(intermediates)
|
49
45
|
if private_key_passphrase:
|
50
46
|
raise exceptions.CertificateStorageException(
|
51
47
|
"Passphrases protected PKCS12 certificates are not supported.")
|
52
48
|
|
53
|
-
p12_data = opaque_data.OpaqueData(
|
49
|
+
p12_data = opaque_data.OpaqueData(
|
50
|
+
c_pkcs12.serialize_key_and_certificates(
|
51
|
+
name=None,
|
52
|
+
key=private_key,
|
53
|
+
cert=certificate,
|
54
|
+
cas=intermediates,
|
55
|
+
encryption_algorithm=serialization.NoEncryption()
|
56
|
+
),
|
57
|
+
name=name
|
58
|
+
)
|
54
59
|
self.manager.store(context, p12_data)
|
55
60
|
|
56
61
|
def get_cert(self, context, cert_ref, resource_ref=None, check_only=False,
|
@@ -50,9 +50,9 @@ class LocalCertManager(cert_mgr.CertManager):
|
|
50
50
|
"""
|
51
51
|
cert_ref = str(uuid.uuid4())
|
52
52
|
filename_base = os.path.join(CONF.certificates.storage_path, cert_ref)
|
53
|
-
if
|
53
|
+
if isinstance(certificate, bytes):
|
54
54
|
certificate = certificate.decode('utf-8')
|
55
|
-
if
|
55
|
+
if isinstance(private_key, bytes):
|
56
56
|
private_key = private_key.decode('utf-8')
|
57
57
|
|
58
58
|
LOG.info("Storing certificate data on the local filesystem.")
|
@@ -71,7 +71,7 @@ class LocalCertManager(cert_mgr.CertManager):
|
|
71
71
|
|
72
72
|
if intermediates:
|
73
73
|
filename_intermediates = "{0}.int".format(filename_base)
|
74
|
-
if
|
74
|
+
if isinstance(intermediates, bytes):
|
75
75
|
intermediates = intermediates.decode('utf-8')
|
76
76
|
with os.fdopen(os.open(
|
77
77
|
filename_intermediates, flags, mode), 'w') as int_file:
|
@@ -79,7 +79,7 @@ class LocalCertManager(cert_mgr.CertManager):
|
|
79
79
|
|
80
80
|
if private_key_passphrase:
|
81
81
|
filename_pkp = "{0}.pass".format(filename_base)
|
82
|
-
if
|
82
|
+
if isinstance(private_key_passphrase, bytes):
|
83
83
|
private_key_passphrase = private_key_passphrase.decode(
|
84
84
|
'utf-8')
|
85
85
|
with os.fdopen(os.open(
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Copyright (c) 2023 Red Hat
|
2
|
+
# All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
5
|
+
# not use this file except in compliance with the License. You may obtain
|
6
|
+
# a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
12
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
13
|
+
# License for the specific language governing permissions and limitations
|
14
|
+
# under the License.
|
15
|
+
import uuid
|
16
|
+
|
17
|
+
from oslo_log import log as logging
|
18
|
+
|
19
|
+
from octavia.certificates.common import cert
|
20
|
+
from octavia.certificates.common import local
|
21
|
+
from octavia.certificates.manager import cert_mgr
|
22
|
+
from octavia.common.tls_utils import cert_parser
|
23
|
+
from octavia.tests.common import sample_certs
|
24
|
+
|
25
|
+
LOG = logging.getLogger(__name__)
|
26
|
+
|
27
|
+
|
28
|
+
class NoopCertManager(cert_mgr.CertManager):
|
29
|
+
"""Cert manager implementation for no-op operations
|
30
|
+
|
31
|
+
"""
|
32
|
+
def __init__(self):
|
33
|
+
super().__init__()
|
34
|
+
self._local_cert = None
|
35
|
+
|
36
|
+
@property
|
37
|
+
def local_cert(self):
|
38
|
+
if self._local_cert is None:
|
39
|
+
self._local_cert = self.store_cert(
|
40
|
+
None,
|
41
|
+
sample_certs.X509_CERT,
|
42
|
+
sample_certs.X509_CERT_KEY_ENCRYPTED,
|
43
|
+
sample_certs.X509_IMDS,
|
44
|
+
private_key_passphrase=sample_certs.X509_CERT_KEY_PASSPHRASE)
|
45
|
+
return self._local_cert
|
46
|
+
|
47
|
+
def store_cert(self, context, certificate, private_key, intermediates=None,
|
48
|
+
private_key_passphrase=None, **kwargs) -> cert.Cert:
|
49
|
+
"""Stores (i.e., registers) a cert with the cert manager.
|
50
|
+
|
51
|
+
This method stores the specified cert to the filesystem and returns
|
52
|
+
a UUID that can be used to retrieve it.
|
53
|
+
|
54
|
+
:param context: Ignored in this implementation
|
55
|
+
:param certificate: PEM encoded TLS certificate
|
56
|
+
:param private_key: private key for the supplied certificate
|
57
|
+
:param intermediates: ordered and concatenated intermediate certs
|
58
|
+
:param private_key_passphrase: optional passphrase for the supplied key
|
59
|
+
|
60
|
+
:returns: the UUID of the stored cert
|
61
|
+
:raises CertificateStorageException: if certificate storage fails
|
62
|
+
"""
|
63
|
+
cert_ref = str(uuid.uuid4())
|
64
|
+
if isinstance(certificate, bytes):
|
65
|
+
certificate = certificate.decode('utf-8')
|
66
|
+
if isinstance(private_key, bytes):
|
67
|
+
private_key = private_key.decode('utf-8')
|
68
|
+
|
69
|
+
LOG.debug('Driver %s no-op, store_cert certificate %s, cert_ref %s',
|
70
|
+
self.__class__.__name__, certificate, cert_ref)
|
71
|
+
|
72
|
+
cert_data = {'certificate': certificate, 'private_key': private_key}
|
73
|
+
if intermediates:
|
74
|
+
if isinstance(intermediates, bytes):
|
75
|
+
intermediates = intermediates.decode('utf-8')
|
76
|
+
cert_data['intermediates'] = list(
|
77
|
+
cert_parser.get_intermediates_pems(intermediates))
|
78
|
+
if private_key_passphrase:
|
79
|
+
if isinstance(private_key_passphrase, bytes):
|
80
|
+
private_key_passphrase = private_key_passphrase.decode('utf-8')
|
81
|
+
cert_data['private_key_passphrase'] = private_key_passphrase
|
82
|
+
|
83
|
+
return local.LocalCert(**cert_data)
|
84
|
+
|
85
|
+
def get_cert(self, context, cert_ref, check_only=True, **kwargs) -> (
|
86
|
+
cert.Cert):
|
87
|
+
LOG.debug('Driver %s no-op, get_cert with cert_ref %s',
|
88
|
+
self.__class__.__name__, cert_ref)
|
89
|
+
return self.local_cert
|
90
|
+
|
91
|
+
def delete_cert(self, context, cert_ref, resource_ref, service_name=None):
|
92
|
+
LOG.debug('Driver %s no-op, delete_cert with cert_ref %s',
|
93
|
+
self.__class__.__name__, cert_ref)
|
94
|
+
|
95
|
+
def set_acls(self, context, cert_ref):
|
96
|
+
LOG.debug('Driver %s no-op, set_acls with cert_ref %s',
|
97
|
+
self.__class__.__name__, cert_ref)
|
98
|
+
|
99
|
+
def unset_acls(self, context, cert_ref):
|
100
|
+
LOG.debug('Driver %s no-op, unset_acls with cert_ref %s',
|
101
|
+
self.__class__.__name__, cert_ref)
|
102
|
+
|
103
|
+
def get_secret(self, context, secret_ref) -> cert.Cert:
|
104
|
+
LOG.debug('Driver %s no-op, get_secret with secret_ref %s',
|
105
|
+
self.__class__.__name__, secret_ref)
|
106
|
+
return self.local_cert
|
octavia/cmd/driver_agent.py
CHANGED
@@ -155,7 +155,7 @@ def main():
|
|
155
155
|
'ignoring and continuing shutdown process.',
|
156
156
|
str(e), proc.name)
|
157
157
|
else:
|
158
|
-
LOG.info('Provider agent "%s" has
|
158
|
+
LOG.info('Provider agent "%s" has successfully shutdown.',
|
159
159
|
proc.name)
|
160
160
|
|
161
161
|
signal.signal(signal.SIGTERM, process_cleanup)
|
octavia/cmd/health_checker.py
CHANGED
octavia/cmd/health_manager.py
CHANGED
@@ -78,7 +78,7 @@ def hm_health_check(exit_event):
|
|
78
78
|
|
79
79
|
|
80
80
|
def _handle_mutate_config(listener_proc_pid, check_proc_pid, *args, **kwargs):
|
81
|
-
LOG.info("Health Manager
|
81
|
+
LOG.info("Health Manager received HUP signal, mutating config.")
|
82
82
|
_mutate_config()
|
83
83
|
os.kill(listener_proc_pid, signal.SIGHUP)
|
84
84
|
os.kill(check_proc_pid, signal.SIGHUP)
|
@@ -125,7 +125,3 @@ def main():
|
|
125
125
|
process.join()
|
126
126
|
except KeyboardInterrupt:
|
127
127
|
process_cleanup()
|
128
|
-
|
129
|
-
|
130
|
-
if __name__ == "__main__":
|
131
|
-
main()
|
octavia/cmd/house_keeping.py
CHANGED
octavia/cmd/interface.py
CHANGED
octavia/cmd/octavia_worker.py
CHANGED
octavia/cmd/prometheus_proxy.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#!/usr/bin/python3
|
2
1
|
# Copyright 2022 Red Hat
|
3
2
|
#
|
4
3
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
@@ -810,7 +809,3 @@ def main():
|
|
810
809
|
httpd.serve_forever()
|
811
810
|
except Exception:
|
812
811
|
time.sleep(1)
|
813
|
-
|
814
|
-
|
815
|
-
if __name__ == "__main__":
|
816
|
-
main()
|