octavia 13.0.0__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.
Files changed (135) hide show
  1. octavia/amphorae/backends/agent/api_server/lvs_listener_base.py +1 -1
  2. octavia/amphorae/backends/agent/api_server/osutils.py +5 -5
  3. octavia/amphorae/backends/agent/api_server/plug.py +3 -2
  4. octavia/amphorae/backends/agent/api_server/rules_schema.py +52 -0
  5. octavia/amphorae/backends/agent/api_server/server.py +28 -1
  6. octavia/amphorae/backends/utils/interface.py +45 -6
  7. octavia/amphorae/backends/utils/interface_file.py +9 -6
  8. octavia/amphorae/backends/utils/nftable_utils.py +125 -0
  9. octavia/amphorae/drivers/driver_base.py +27 -0
  10. octavia/amphorae/drivers/haproxy/rest_api_driver.py +42 -10
  11. octavia/amphorae/drivers/health/heartbeat_udp.py +2 -2
  12. octavia/amphorae/drivers/keepalived/vrrp_rest_driver.py +2 -1
  13. octavia/amphorae/drivers/noop_driver/driver.py +25 -0
  14. octavia/api/app.py +3 -0
  15. octavia/api/common/pagination.py +2 -2
  16. octavia/api/drivers/amphora_driver/flavor_schema.py +6 -1
  17. octavia/api/root_controller.py +4 -1
  18. octavia/api/v2/controllers/health_monitor.py +0 -1
  19. octavia/api/v2/controllers/l7policy.py +0 -1
  20. octavia/api/v2/controllers/l7rule.py +0 -1
  21. octavia/api/v2/controllers/listener.py +0 -1
  22. octavia/api/v2/controllers/load_balancer.py +13 -7
  23. octavia/api/v2/controllers/member.py +6 -3
  24. octavia/api/v2/controllers/pool.py +6 -7
  25. octavia/api/v2/types/load_balancer.py +5 -1
  26. octavia/api/v2/types/pool.py +1 -1
  27. octavia/certificates/common/pkcs12.py +9 -9
  28. octavia/certificates/manager/barbican.py +24 -16
  29. octavia/certificates/manager/castellan_mgr.py +12 -7
  30. octavia/certificates/manager/local.py +4 -4
  31. octavia/certificates/manager/noop.py +106 -0
  32. octavia/cmd/driver_agent.py +1 -1
  33. octavia/cmd/health_checker.py +0 -4
  34. octavia/cmd/health_manager.py +1 -5
  35. octavia/cmd/house_keeping.py +1 -1
  36. octavia/cmd/interface.py +0 -4
  37. octavia/cmd/octavia_worker.py +0 -4
  38. octavia/cmd/prometheus_proxy.py +0 -5
  39. octavia/cmd/status.py +0 -6
  40. octavia/common/base_taskflow.py +1 -1
  41. octavia/common/clients.py +15 -3
  42. octavia/common/config.py +24 -6
  43. octavia/common/constants.py +34 -0
  44. octavia/common/data_models.py +3 -1
  45. octavia/common/exceptions.py +11 -0
  46. octavia/common/jinja/haproxy/combined_listeners/templates/macros.j2 +7 -5
  47. octavia/common/keystone.py +7 -7
  48. octavia/common/tls_utils/cert_parser.py +24 -10
  49. octavia/common/utils.py +6 -0
  50. octavia/common/validate.py +2 -2
  51. octavia/compute/drivers/nova_driver.py +23 -5
  52. octavia/controller/worker/task_utils.py +28 -6
  53. octavia/controller/worker/v2/controller_worker.py +49 -15
  54. octavia/controller/worker/v2/flows/amphora_flows.py +120 -21
  55. octavia/controller/worker/v2/flows/flow_utils.py +15 -13
  56. octavia/controller/worker/v2/flows/listener_flows.py +95 -5
  57. octavia/controller/worker/v2/flows/load_balancer_flows.py +74 -30
  58. octavia/controller/worker/v2/taskflow_jobboard_driver.py +17 -1
  59. octavia/controller/worker/v2/tasks/amphora_driver_tasks.py +145 -24
  60. octavia/controller/worker/v2/tasks/compute_tasks.py +1 -1
  61. octavia/controller/worker/v2/tasks/database_tasks.py +72 -41
  62. octavia/controller/worker/v2/tasks/lifecycle_tasks.py +97 -41
  63. octavia/controller/worker/v2/tasks/network_tasks.py +57 -60
  64. octavia/controller/worker/v2/tasks/shim_tasks.py +28 -0
  65. octavia/db/migration/alembic_migrations/versions/55874a4ceed6_add_l7policy_action_redirect_prefix.py +1 -1
  66. octavia/db/migration/alembic_migrations/versions/5a3ee5472c31_add_cert_expiration__infor_in_amphora_table.py +1 -1
  67. octavia/db/migration/alembic_migrations/versions/6742ca1b27c2_add_l7policy_redirect_http_code.py +1 -1
  68. octavia/db/migration/alembic_migrations/versions/db2a73e82626_add_vnic_type_for_vip.py +36 -0
  69. octavia/db/models.py +1 -0
  70. octavia/db/prepare.py +1 -1
  71. octavia/db/repositories.py +53 -34
  72. octavia/distributor/drivers/driver_base.py +1 -1
  73. octavia/network/base.py +3 -16
  74. octavia/network/data_models.py +4 -1
  75. octavia/network/drivers/neutron/allowed_address_pairs.py +27 -26
  76. octavia/network/drivers/noop_driver/driver.py +10 -23
  77. octavia/tests/common/sample_certs.py +115 -0
  78. octavia/tests/common/sample_haproxy_prometheus +1 -1
  79. octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py +37 -0
  80. octavia/tests/functional/api/test_healthcheck.py +2 -2
  81. octavia/tests/functional/api/v2/base.py +1 -1
  82. octavia/tests/functional/api/v2/test_listener.py +45 -0
  83. octavia/tests/functional/api/v2/test_load_balancer.py +17 -0
  84. octavia/tests/functional/db/base.py +9 -0
  85. octavia/tests/functional/db/test_models.py +2 -1
  86. octavia/tests/functional/db/test_repositories.py +55 -99
  87. octavia/tests/unit/amphorae/backends/agent/api_server/test_osutils.py +4 -2
  88. octavia/tests/unit/amphorae/backends/utils/test_interface.py +201 -1
  89. octavia/tests/unit/amphorae/backends/utils/test_keepalivedlvs_query.py +1 -1
  90. octavia/tests/unit/amphorae/backends/utils/test_nftable_utils.py +194 -0
  91. octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver.py +27 -5
  92. octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_1_0.py +15 -2
  93. octavia/tests/unit/amphorae/drivers/keepalived/test_vrrp_rest_driver.py +17 -0
  94. octavia/tests/unit/amphorae/drivers/noop_driver/test_driver.py +2 -1
  95. octavia/tests/unit/api/v2/types/test_pool.py +71 -0
  96. octavia/tests/unit/certificates/manager/test_barbican.py +3 -3
  97. octavia/tests/unit/certificates/manager/test_noop.py +53 -0
  98. octavia/tests/unit/common/jinja/haproxy/combined_listeners/test_jinja_cfg.py +16 -17
  99. octavia/tests/unit/common/sample_configs/sample_configs_combined.py +5 -3
  100. octavia/tests/unit/common/test_config.py +35 -0
  101. octavia/tests/unit/common/test_keystone.py +32 -0
  102. octavia/tests/unit/common/test_utils.py +39 -0
  103. octavia/tests/unit/compute/drivers/test_nova_driver.py +22 -0
  104. octavia/tests/unit/controller/worker/test_task_utils.py +58 -2
  105. octavia/tests/unit/controller/worker/v2/flows/test_amphora_flows.py +28 -5
  106. octavia/tests/unit/controller/worker/v2/flows/test_listener_flows.py +64 -16
  107. octavia/tests/unit/controller/worker/v2/flows/test_load_balancer_flows.py +49 -9
  108. octavia/tests/unit/controller/worker/v2/tasks/test_amphora_driver_tasks.py +265 -17
  109. octavia/tests/unit/controller/worker/v2/tasks/test_database_tasks.py +101 -1
  110. octavia/tests/unit/controller/worker/v2/tasks/test_database_tasks_quota.py +19 -19
  111. octavia/tests/unit/controller/worker/v2/tasks/test_network_tasks.py +105 -42
  112. octavia/tests/unit/controller/worker/v2/tasks/test_shim_tasks.py +33 -0
  113. octavia/tests/unit/controller/worker/v2/test_controller_worker.py +85 -42
  114. octavia/tests/unit/network/drivers/neutron/test_allowed_address_pairs.py +48 -51
  115. octavia/tests/unit/network/drivers/neutron/test_utils.py +2 -0
  116. octavia/tests/unit/network/drivers/noop_driver/test_driver.py +0 -7
  117. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/README.rst +6 -1
  118. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/diskimage-create.sh +10 -4
  119. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/requirements.txt +0 -2
  120. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/tox.ini +30 -13
  121. {octavia-13.0.0.dist-info → octavia-14.0.0.dist-info}/AUTHORS +5 -0
  122. {octavia-13.0.0.dist-info → octavia-14.0.0.dist-info}/METADATA +6 -6
  123. {octavia-13.0.0.dist-info → octavia-14.0.0.dist-info}/RECORD +134 -126
  124. {octavia-13.0.0.dist-info → octavia-14.0.0.dist-info}/entry_points.txt +1 -1
  125. octavia-14.0.0.dist-info/pbr.json +1 -0
  126. octavia-13.0.0.dist-info/pbr.json +0 -1
  127. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/LICENSE +0 -0
  128. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/README.rst +0 -0
  129. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/image-tests.sh +0 -0
  130. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/test-requirements.txt +0 -0
  131. {octavia-13.0.0.data → octavia-14.0.0.data}/data/share/octavia/diskimage-create/version.txt +0 -0
  132. {octavia-13.0.0.data → octavia-14.0.0.data}/scripts/octavia-wsgi +0 -0
  133. {octavia-13.0.0.dist-info → octavia-14.0.0.dist-info}/LICENSE +0 -0
  134. {octavia-13.0.0.dist-info → octavia-14.0.0.dist-info}/WHEEL +0 -0
  135. {octavia-13.0.0.dist-info → octavia-14.0.0.dist-info}/top_level.txt +0 -0
@@ -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 enteries in self.params
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
- # Tranlate arguments from API standard to data model's field name
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
  }
@@ -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', 'CURRENT',
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}
@@ -238,7 +238,6 @@ class HealthMonitorController(base.BaseController):
238
238
  context.session.begin()
239
239
  try:
240
240
  if self.repositories.check_quota_met(
241
- context.session,
242
241
  context.session,
243
242
  data_models.HealthMonitor,
244
243
  health_monitor.project_id):
@@ -151,7 +151,6 @@ class L7PolicyController(base.BaseController):
151
151
  lock_session.begin()
152
152
  try:
153
153
  if self.repositories.check_quota_met(
154
- context.session,
155
154
  lock_session,
156
155
  data_models.L7Policy,
157
156
  l7policy.project_id):
@@ -154,7 +154,6 @@ class L7RuleController(base.BaseController):
154
154
  context.session.begin()
155
155
  try:
156
156
  if self.repositories.check_quota_met(
157
- context.session,
158
157
  context.session,
159
158
  data_models.L7Rule,
160
159
  l7rule.project_id):
@@ -372,7 +372,6 @@ class ListenersController(base.BaseController):
372
372
  context.session.begin()
373
373
  try:
374
374
  if self.repositories.check_quota_met(
375
- context.session,
376
375
  context.session,
377
376
  data_models.Listener,
378
377
  listener.project_id):
@@ -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
- context.session, lock_session, db_lb, listeners, pools)
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, lock_session, db_lb, listeners, pools):
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, lock_session, data_models.Pool, db_lb.project_id,
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, lock_session, p))
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, lock_session, data_models.Listener, db_lb.project_id,
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
- lock_session, li, pool_name_ids=pool_name_ids))
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, context.session, data_models.Member,
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, lock_session, pool_dict):
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
- lock_session, pool_dict)
313
+ session, pool_dict)
315
314
 
316
315
  # Check quotas for healthmonitors
317
316
  if hm and self.repositories.check_quota_met(
318
- session, lock_session, data_models.HealthMonitor,
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
- lock_session, hm)
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, lock_session, data_models.Member,
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
- lock_session, m))
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]
@@ -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 pool.healthmonitor:
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 OpenSSL import crypto
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 = crypto.load_pkcs12(certbag)
32
- except crypto.Error as e:
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.get_certificate()
35
- self.intermediates = p12.get_ca_certificates()
36
- self.private_key = p12.get_privatekey()
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.to_cryptography().public_bytes(
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.to_cryptography().public_bytes(
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.to_cryptography_key().private_bytes(
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 OpenSSL import crypto
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
- p12 = crypto.PKCS12()
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=p12.export(),
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 OpenSSL import crypto
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(p12.export(), name=name)
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 type(certificate) == bytes:
53
+ if isinstance(certificate, bytes):
54
54
  certificate = certificate.decode('utf-8')
55
- if type(private_key) == bytes:
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 type(intermediates) == bytes:
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 type(private_key_passphrase) == bytes:
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
@@ -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 succesfully shutdown.',
158
+ LOG.info('Provider agent "%s" has successfully shutdown.',
159
159
  proc.name)
160
160
 
161
161
  signal.signal(signal.SIGTERM, process_cleanup)
@@ -262,7 +262,3 @@ def main():
262
262
  else:
263
263
  print("Unsupported protocol '{}'".format(protocol))
264
264
  sys.exit(1)
265
-
266
-
267
- if __name__ == '__main__':
268
- main()
@@ -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 recieved HUP signal, mutating config.")
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()
@@ -72,7 +72,7 @@ def cert_rotation():
72
72
 
73
73
 
74
74
  def _mutate_config(*args, **kwargs):
75
- LOG.info("Housekeeping recieved HUP signal, mutating config.")
75
+ LOG.info("Housekeeping received HUP signal, mutating config.")
76
76
  CONF.mutate_config_files()
77
77
 
78
78
 
octavia/cmd/interface.py CHANGED
@@ -84,7 +84,3 @@ def main():
84
84
  except Exception as e:
85
85
  print("Error: {}".format(e))
86
86
  sys.exit(2)
87
-
88
-
89
- if __name__ == "__main__":
90
- main()
@@ -36,7 +36,3 @@ def main():
36
36
  workers=CONF.controller_worker.workers, args=(CONF,))
37
37
  oslo_config_glue.setup(sm, CONF, reload_method="mutate")
38
38
  sm.run()
39
-
40
-
41
- if __name__ == "__main__":
42
- main()
@@ -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()