octavia 15.0.0.0rc1__py3-none-any.whl → 16.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 (97) hide show
  1. octavia/amphorae/backends/agent/api_server/keepalivedlvs.py +9 -0
  2. octavia/amphorae/backends/agent/api_server/loadbalancer.py +6 -6
  3. octavia/amphorae/backends/agent/api_server/plug.py +1 -1
  4. octavia/amphorae/backends/agent/api_server/util.py +35 -2
  5. octavia/amphorae/backends/health_daemon/status_message.py +1 -2
  6. octavia/amphorae/drivers/haproxy/rest_api_driver.py +12 -7
  7. octavia/api/drivers/amphora_driver/flavor_schema.py +5 -0
  8. octavia/api/drivers/noop_driver/driver.py +2 -1
  9. octavia/api/drivers/utils.py +12 -0
  10. octavia/api/root_controller.py +8 -2
  11. octavia/api/v2/controllers/base.py +8 -4
  12. octavia/api/v2/controllers/listener.py +12 -2
  13. octavia/api/v2/controllers/load_balancer.py +33 -1
  14. octavia/api/v2/controllers/member.py +58 -4
  15. octavia/api/v2/types/load_balancer.py +7 -1
  16. octavia/api/v2/types/member.py +3 -0
  17. octavia/common/base_taskflow.py +19 -10
  18. octavia/common/clients.py +8 -2
  19. octavia/common/config.py +17 -2
  20. octavia/common/constants.py +6 -0
  21. octavia/common/data_models.py +32 -2
  22. octavia/common/exceptions.py +5 -0
  23. octavia/common/utils.py +4 -1
  24. octavia/common/validate.py +16 -0
  25. octavia/compute/drivers/noop_driver/driver.py +30 -1
  26. octavia/controller/healthmanager/health_manager.py +7 -0
  27. octavia/controller/worker/v2/flows/amphora_flows.py +3 -5
  28. octavia/controller/worker/v2/flows/listener_flows.py +2 -1
  29. octavia/controller/worker/v2/flows/load_balancer_flows.py +38 -0
  30. octavia/controller/worker/v2/taskflow_jobboard_driver.py +34 -6
  31. octavia/controller/worker/v2/tasks/compute_tasks.py +9 -5
  32. octavia/controller/worker/v2/tasks/database_tasks.py +26 -6
  33. octavia/controller/worker/v2/tasks/network_tasks.py +118 -70
  34. octavia/db/base_models.py +29 -5
  35. octavia/db/migration/alembic_migrations/versions/3097e55493ae_add_sg_id_to_vip_table.py +39 -0
  36. octavia/db/migration/alembic_migrations/versions/8db7a6443785_add_member_vnic_type.py +36 -0
  37. octavia/db/migration/alembic_migrations/versions/fabf4983846b_add_member_port_table.py +40 -0
  38. octavia/db/models.py +43 -1
  39. octavia/db/repositories.py +88 -9
  40. octavia/network/base.py +29 -12
  41. octavia/network/data_models.py +2 -1
  42. octavia/network/drivers/neutron/allowed_address_pairs.py +55 -46
  43. octavia/network/drivers/neutron/base.py +28 -16
  44. octavia/network/drivers/neutron/utils.py +2 -2
  45. octavia/network/drivers/noop_driver/driver.py +150 -29
  46. octavia/policies/__init__.py +4 -0
  47. octavia/policies/advanced_rbac.py +95 -0
  48. octavia/policies/base.py +5 -101
  49. octavia/policies/keystone_default_roles.py +81 -0
  50. octavia/policies/loadbalancer.py +13 -0
  51. octavia/tests/common/constants.py +2 -1
  52. octavia/tests/common/sample_data_models.py +27 -14
  53. octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py +5 -4
  54. octavia/tests/functional/api/drivers/driver_agent/test_driver_agent.py +2 -1
  55. octavia/tests/functional/api/v2/test_health_monitor.py +1 -1
  56. octavia/tests/functional/api/v2/test_l7policy.py +1 -1
  57. octavia/tests/functional/api/v2/test_listener.py +1 -1
  58. octavia/tests/functional/api/v2/test_load_balancer.py +150 -4
  59. octavia/tests/functional/api/v2/test_member.py +50 -0
  60. octavia/tests/functional/api/v2/test_pool.py +1 -1
  61. octavia/tests/functional/api/v2/test_quotas.py +5 -8
  62. octavia/tests/functional/db/base.py +6 -6
  63. octavia/tests/functional/db/test_models.py +124 -1
  64. octavia/tests/functional/db/test_repositories.py +237 -19
  65. octavia/tests/unit/amphorae/backends/agent/api_server/test_util.py +89 -1
  66. octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_1_0.py +10 -7
  67. octavia/tests/unit/api/drivers/test_utils.py +6 -1
  68. octavia/tests/unit/certificates/generator/test_local.py +1 -1
  69. octavia/tests/unit/common/test_base_taskflow.py +4 -3
  70. octavia/tests/unit/compute/drivers/noop_driver/test_driver.py +28 -2
  71. octavia/tests/unit/controller/worker/v2/flows/test_load_balancer_flows.py +27 -1
  72. octavia/tests/unit/controller/worker/v2/tasks/test_database_tasks.py +28 -6
  73. octavia/tests/unit/controller/worker/v2/tasks/test_network_tasks.py +100 -79
  74. octavia/tests/unit/controller/worker/v2/test_taskflow_jobboard_driver.py +8 -0
  75. octavia/tests/unit/network/drivers/neutron/test_allowed_address_pairs.py +62 -45
  76. octavia/tests/unit/network/drivers/neutron/test_base.py +7 -7
  77. octavia/tests/unit/network/drivers/noop_driver/test_driver.py +55 -42
  78. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/diskimage-create/tox.ini +0 -1
  79. {octavia-15.0.0.0rc1.dist-info → octavia-16.0.0.dist-info}/AUTHORS +3 -0
  80. octavia-16.0.0.dist-info/METADATA +156 -0
  81. {octavia-15.0.0.0rc1.dist-info → octavia-16.0.0.dist-info}/RECORD +95 -90
  82. {octavia-15.0.0.0rc1.dist-info → octavia-16.0.0.dist-info}/WHEEL +1 -1
  83. {octavia-15.0.0.0rc1.dist-info → octavia-16.0.0.dist-info}/entry_points.txt +1 -1
  84. octavia-16.0.0.dist-info/pbr.json +1 -0
  85. octavia-15.0.0.0rc1.dist-info/METADATA +0 -156
  86. octavia-15.0.0.0rc1.dist-info/pbr.json +0 -1
  87. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/LICENSE +0 -0
  88. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/README.rst +0 -0
  89. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/diskimage-create/README.rst +0 -0
  90. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/diskimage-create/diskimage-create.sh +0 -0
  91. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/diskimage-create/image-tests.sh +0 -0
  92. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/diskimage-create/requirements.txt +0 -0
  93. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/diskimage-create/test-requirements.txt +0 -0
  94. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/data/share/octavia/diskimage-create/version.txt +0 -0
  95. {octavia-15.0.0.0rc1.data → octavia-16.0.0.data}/scripts/octavia-wsgi +0 -0
  96. {octavia-15.0.0.0rc1.dist-info → octavia-16.0.0.dist-info}/LICENSE +0 -0
  97. {octavia-15.0.0.0rc1.dist-info → octavia-16.0.0.dist-info}/top_level.txt +0 -0
@@ -115,10 +115,19 @@ class BaseRepository:
115
115
  session.query(self.model_class).filter_by(
116
116
  id=id).update(model_kwargs)
117
117
 
118
- def get(self, session, **filters):
118
+ def get(self, session, limited_graph=False, **filters):
119
119
  """Retrieves an entity from the database.
120
120
 
121
121
  :param session: A Sql Alchemy database session.
122
+ :param limited_graph: Option controls number of processed nodes
123
+ in the graph. Default (with False) behaviour
124
+ is recursion iteration through all nodes
125
+ in the graph via to_data_model. With True value
126
+ recursion will stop at the first child node.
127
+ It means, that only limited number of nodes be
128
+ converted. This logic could be used for specific
129
+ cases, where information about full graph
130
+ is unnecessary.
122
131
  :param filters: Filters to decide which entity should be retrieved.
123
132
  :returns: octavia.common.data_model
124
133
  """
@@ -138,16 +147,26 @@ class BaseRepository:
138
147
  if not model:
139
148
  return None
140
149
 
141
- return model.to_data_model()
150
+ recursion_depth = 0 if limited_graph else None
151
+ return model.to_data_model(recursion_depth=recursion_depth)
142
152
 
143
153
  def get_all(self, session, pagination_helper=None,
144
- query_options=None, **filters):
154
+ query_options=None, limited_graph=False, **filters):
145
155
 
146
156
  """Retrieves a list of entities from the database.
147
157
 
148
158
  :param session: A Sql Alchemy database session.
149
159
  :param pagination_helper: Helper to apply pagination and sorting.
150
160
  :param query_options: Optional query options to apply.
161
+ :param limited_graph: Option controls number of processed nodes
162
+ in the graph. Default (with False) behaviour
163
+ is recursion iteration through all nodes
164
+ in the graph via to_data_model. With True value
165
+ recursion will stop at the first child node.
166
+ It means, that only limited number of nodes be
167
+ converted. This logic could be used for specific
168
+ cases, where information about full graph
169
+ is unnecessary.
151
170
  :param filters: Filters to decide which entities should be retrieved.
152
171
  :returns: [octavia.common.data_model]
153
172
  """
@@ -170,8 +189,11 @@ class BaseRepository:
170
189
  else:
171
190
  links = None
172
191
  model_list = query.all()
173
-
174
- data_model_list = [model.to_data_model() for model in model_list]
192
+ recursion_depth = 1 if limited_graph else None
193
+ data_model_list = [
194
+ model.to_data_model(recursion_depth=recursion_depth)
195
+ for model in model_list
196
+ ]
175
197
  return data_model_list, links
176
198
 
177
199
  def exists(self, session, id):
@@ -234,6 +256,7 @@ class Repositories:
234
256
  self.flavor_profile = FlavorProfileRepository()
235
257
  self.availability_zone = AvailabilityZoneRepository()
236
258
  self.availability_zone_profile = AvailabilityZoneProfileRepository()
259
+ self.amphora_member_port = AmphoraMemberPortRepository()
237
260
 
238
261
  def create_load_balancer_and_vip(self, session, lb_dict, vip_dict,
239
262
  additional_vip_dicts=None):
@@ -253,9 +276,17 @@ class Repositories:
253
276
  lb_dict['id'] = uuidutils.generate_uuid()
254
277
  lb = models.LoadBalancer(**lb_dict)
255
278
  session.add(lb)
279
+ vip_sg_ids = vip_dict.pop(consts.SG_IDS, [])
256
280
  vip_dict['load_balancer_id'] = lb_dict['id']
257
281
  vip = models.Vip(**vip_dict)
258
282
  session.add(vip)
283
+ if vip_sg_ids:
284
+ vip_dict[consts.SG_IDS] = vip_sg_ids
285
+ for vip_sg_id in vip_sg_ids:
286
+ vip_sg = models.VipSecurityGroup(
287
+ load_balancer_id=lb_dict['id'],
288
+ sg_id=vip_sg_id)
289
+ session.add(vip_sg)
259
290
  for add_vip_dict in additional_vip_dicts:
260
291
  add_vip_dict['load_balancer_id'] = lb_dict['id']
261
292
  add_vip_dict['network_id'] = vip_dict.get('network_id')
@@ -712,6 +743,8 @@ class LoadBalancerRepository(BaseRepository):
712
743
  query_options = (
713
744
  subqueryload(models.LoadBalancer.vip),
714
745
  subqueryload(models.LoadBalancer.additional_vips),
746
+ (subqueryload(models.LoadBalancer.vip).
747
+ subqueryload(models.Vip.sgs)),
715
748
  subqueryload(models.LoadBalancer.amphorae),
716
749
  subqueryload(models.LoadBalancer.pools),
717
750
  subqueryload(models.LoadBalancer.listeners),
@@ -789,8 +822,24 @@ class VipRepository(BaseRepository):
789
822
 
790
823
  def update(self, session, load_balancer_id, **model_kwargs):
791
824
  """Updates a vip entity in the database by load_balancer_id."""
792
- session.query(self.model_class).filter_by(
793
- load_balancer_id=load_balancer_id).update(model_kwargs)
825
+ sg_ids = model_kwargs.pop(consts.SG_IDS, None)
826
+
827
+ vip = session.query(self.model_class).filter_by(
828
+ load_balancer_id=load_balancer_id)
829
+ if model_kwargs:
830
+ vip.update(model_kwargs)
831
+
832
+ # NOTE(gthiemonge) the vip must be updated when sg_ids is []
833
+ # (removal of current sg_ids)
834
+ if sg_ids is not None:
835
+ vip = vip.first()
836
+ vip.sgs = [
837
+ models.VipSecurityGroup(
838
+ load_balancer_id=load_balancer_id,
839
+ sg_id=sg_id)
840
+ for sg_id in sg_ids]
841
+
842
+ session.flush()
794
843
 
795
844
 
796
845
  class AdditionalVipRepository(BaseRepository):
@@ -916,7 +965,8 @@ class PoolRepository(BaseRepository):
916
965
  class MemberRepository(BaseRepository):
917
966
  model_class = models.Member
918
967
 
919
- def get_all_API_list(self, session, pagination_helper=None, **filters):
968
+ def get_all_API_list(self, session, pagination_helper=None,
969
+ limited_graph=False, **filters):
920
970
  """Get a list of members for the API list call.
921
971
 
922
972
  This get_all returns a data set that is only one level deep
@@ -925,6 +975,8 @@ class MemberRepository(BaseRepository):
925
975
 
926
976
  :param session: A Sql Alchemy database session.
927
977
  :param pagination_helper: Helper to apply pagination and sorting.
978
+ :param limited_graph: Option to avoid recursion iteration through all
979
+ nodes in the graph via to_data_model
928
980
  :param filters: Filters to decide which entities should be retrieved.
929
981
  :returns: [octavia.common.data_model]
930
982
  """
@@ -938,7 +990,8 @@ class MemberRepository(BaseRepository):
938
990
 
939
991
  return super().get_all(
940
992
  session, pagination_helper=pagination_helper,
941
- query_options=query_options, **filters)
993
+ query_options=query_options, limited_graph=limited_graph,
994
+ **filters)
942
995
 
943
996
  def delete_members(self, session, member_ids):
944
997
  """Batch deletes members from a pool."""
@@ -1402,6 +1455,20 @@ class AmphoraRepository(BaseRepository):
1402
1455
  amp.status = consts.PENDING_DELETE
1403
1456
  lock_session.flush()
1404
1457
 
1458
+ def get_amphorae_ids_on_lb(self, session, lb_id):
1459
+ """Returns a list of amphora IDs associated with the load balancer
1460
+
1461
+ :param session: A Sql Alchemy database session.
1462
+ :param lb_id: A load balancer ID.
1463
+ :returns: A list of amphora IDs
1464
+ """
1465
+ return session.scalars(
1466
+ select(
1467
+ self.model_class.id
1468
+ ).where(
1469
+ self.model_class.load_balancer_id == lb_id
1470
+ )).all()
1471
+
1405
1472
 
1406
1473
  class AmphoraBuildReqRepository(BaseRepository):
1407
1474
  model_class = models.AmphoraBuildRequest
@@ -2081,3 +2148,15 @@ class AvailabilityZoneRepository(_GetALLExceptDELETEDIdMixin, BaseRepository):
2081
2148
  class AvailabilityZoneProfileRepository(_GetALLExceptDELETEDIdMixin,
2082
2149
  BaseRepository):
2083
2150
  model_class = models.AvailabilityZoneProfile
2151
+
2152
+
2153
+ class AmphoraMemberPortRepository(BaseRepository):
2154
+ model_class = models.AmphoraMemberPort
2155
+
2156
+ def get_port_ids(self, session, amphora_id):
2157
+ return session.scalars(
2158
+ select(
2159
+ self.model_class.port_id
2160
+ ).where(
2161
+ self.model_class.amphora_id == amphora_id
2162
+ )).all()
octavia/network/base.py CHANGED
@@ -13,10 +13,16 @@
13
13
  # under the License.
14
14
 
15
15
  import abc
16
+ import typing
16
17
 
17
18
  from octavia.common import constants
19
+ from octavia.common import data_models
18
20
  from octavia.common import exceptions
19
21
 
22
+ if typing.TYPE_CHECKING:
23
+ from octavia.common import context
24
+ import octavia.network.data_models as n_data_models
25
+
20
26
 
21
27
  class NetworkException(exceptions.OctaviaException):
22
28
  pass
@@ -94,7 +100,8 @@ class AbstractNetworkDriver(metaclass=abc.ABCMeta):
94
100
  """
95
101
 
96
102
  @abc.abstractmethod
97
- def allocate_vip(self, load_balancer):
103
+ def allocate_vip(self, load_balancer: data_models.LoadBalancer) -> (
104
+ tuple[data_models.Vip, list[data_models.AdditionalVip]]):
98
105
  """Allocates a virtual ip.
99
106
 
100
107
  Reserves it for later use as the frontend connection of a load
@@ -156,16 +163,6 @@ class AbstractNetworkDriver(metaclass=abc.ABCMeta):
156
163
  :raises: UnplugVIPException, PluggedVIPNotFound
157
164
  """
158
165
 
159
- @abc.abstractmethod
160
- def plug_network(self, compute_id, network_id):
161
- """Connects an existing amphora to an existing network.
162
-
163
- :param compute_id: id of an amphora in the compute service
164
- :param network_id: id of a network
165
- :return: octavia.network.data_models.Interface instance
166
- :raises: PlugNetworkException, AmphoraNotFound, NetworkNotFound
167
- """
168
-
169
166
  @abc.abstractmethod
170
167
  def unplug_network(self, compute_id, network_id):
171
168
  """Disconnects an existing amphora from an existing network.
@@ -294,13 +291,25 @@ class AbstractNetworkDriver(metaclass=abc.ABCMeta):
294
291
 
295
292
  @abc.abstractmethod
296
293
  def get_security_group(self, sg_name):
297
- """Retrieves the security group by it's name.
294
+ """Retrieves the security group by its name.
298
295
 
299
296
  :param sg_name: The security group name.
300
297
  :return: octavia.network.data_models.SecurityGroup, None if not enabled
301
298
  :raises: NetworkException, SecurityGroupNotFound
302
299
  """
303
300
 
301
+ @abc.abstractmethod
302
+ def get_security_group_by_id(self, sg_id: str,
303
+ context: 'context.RequestContext' = None) -> (
304
+ 'n_data_models.SecurityGroup'):
305
+ """Retrieves the security group by its id.
306
+
307
+ :param sg_id: The security group ID.
308
+ :param context: A request context
309
+ :return: octavia.network.data_models.SecurityGroup, None if not enabled
310
+ :raises: NetworkException, SecurityGroupNotFound
311
+ """
312
+
304
313
  @abc.abstractmethod
305
314
  def failover_preparation(self, amphora):
306
315
  """Prepare an amphora for failover.
@@ -349,6 +358,14 @@ class AbstractNetworkDriver(metaclass=abc.ABCMeta):
349
358
  :param vip: The VIP to plug
350
359
  """
351
360
 
361
+ @abc.abstractmethod
362
+ def update_aap_port_sg(self, load_balancer: data_models.LoadBalancer,
363
+ amphora: data_models.Amphora,
364
+ vip: data_models.Vip):
365
+ """Updates the security group of the AAP port of an amphora
366
+
367
+ """
368
+
352
369
  @abc.abstractmethod
353
370
  def plug_aap_port(self, load_balancer, vip, amphora, subnet):
354
371
  """Plugs the AAP port to the amp
@@ -19,12 +19,13 @@ from octavia.common import data_models
19
19
  class Interface(data_models.BaseDataModel):
20
20
 
21
21
  def __init__(self, id=None, compute_id=None, network_id=None,
22
- fixed_ips=None, port_id=None):
22
+ fixed_ips=None, port_id=None, vnic_type=None):
23
23
  self.id = id
24
24
  self.compute_id = compute_id
25
25
  self.network_id = network_id
26
26
  self.port_id = port_id
27
27
  self.fixed_ips = fixed_ips
28
+ self.vnic_type = vnic_type
28
29
 
29
30
 
30
31
  class Delta(data_models.BaseDataModel):
@@ -80,7 +80,7 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
80
80
  return interface
81
81
  return None
82
82
 
83
- def _plug_amphora_vip(self, amphora, subnet):
83
+ def _plug_amphora_vip(self, amphora, subnet, vip: data_models.Vip):
84
84
  # We need a vip port owned by Octavia for Act/Stby and failover
85
85
  try:
86
86
  port = {
@@ -89,7 +89,9 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
89
89
  constants.FIXED_IPS: [{'subnet_id': subnet.id}],
90
90
  constants.ADMIN_STATE_UP: True,
91
91
  constants.DEVICE_OWNER: constants.OCTAVIA_OWNER,
92
+ constants.SECURITY_GROUP_IDS: vip.sg_ids
92
93
  }
94
+
93
95
  new_port = self.network_proxy.create_port(**port)
94
96
  new_port = utils.convert_port_to_model(new_port)
95
97
 
@@ -149,7 +151,12 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
149
151
  net = ipaddress.ip_network(cidr)
150
152
  return 'IPv6' if net.version == 6 else 'IPv4'
151
153
 
152
- def _update_security_group_rules(self, load_balancer, sec_grp_id):
154
+ def _update_security_group_rules(self,
155
+ load_balancer: data_models.LoadBalancer,
156
+ sec_grp_id):
157
+ # Skip adding listener rules if sgs is not None or not empty
158
+ skip_listener_rules = load_balancer.vip.sg_ids
159
+
153
160
  rules = tuple(self.network_proxy.security_group_rules(
154
161
  security_group_id=sec_grp_id))
155
162
 
@@ -160,19 +167,20 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
160
167
  constants.DELETED]):
161
168
  continue
162
169
 
163
- protocol = constants.PROTOCOL_TCP.lower()
164
- if listener.protocol == constants.PROTOCOL_UDP:
165
- protocol = constants.PROTOCOL_UDP.lower()
166
- elif listener.protocol == lib_consts.PROTOCOL_SCTP:
167
- protocol = lib_consts.PROTOCOL_SCTP.lower()
168
-
169
- if listener.allowed_cidrs:
170
- for ac in listener.allowed_cidrs:
171
- port = (listener.protocol_port, protocol, ac.cidr)
170
+ if not skip_listener_rules:
171
+ protocol = constants.PROTOCOL_TCP.lower()
172
+ if listener.protocol == constants.PROTOCOL_UDP:
173
+ protocol = constants.PROTOCOL_UDP.lower()
174
+ elif listener.protocol == lib_consts.PROTOCOL_SCTP:
175
+ protocol = lib_consts.PROTOCOL_SCTP.lower()
176
+
177
+ if listener.allowed_cidrs:
178
+ for ac in listener.allowed_cidrs:
179
+ port = (listener.protocol_port, protocol, ac.cidr)
180
+ updated_ports.append(port)
181
+ else:
182
+ port = (listener.protocol_port, protocol, None)
172
183
  updated_ports.append(port)
173
- else:
174
- port = (listener.protocol_port, protocol, None)
175
- updated_ports.append(port)
176
184
 
177
185
  listener_peer_ports.append(listener.peer_port)
178
186
 
@@ -194,12 +202,13 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
194
202
  # Don't remove egress rules and don't confuse other protocols with
195
203
  # None ports with the egress rules. VRRP uses protocol 51 and 112
196
204
  if (rule.get('direction') == 'egress' or
197
- rule.get('protocol').upper() not in
205
+ rule.get('protocol') is None or
206
+ rule['protocol'].upper() not in
198
207
  [constants.PROTOCOL_TCP, constants.PROTOCOL_UDP,
199
208
  lib_consts.PROTOCOL_SCTP]):
200
209
  continue
201
210
  old_ports.append((rule.get('port_range_max'),
202
- rule.get('protocol').lower(),
211
+ rule['protocol'].lower(),
203
212
  rule.get('remote_ip_prefix')))
204
213
 
205
214
  add_ports = set(updated_ports) - set(old_ports)
@@ -262,12 +271,15 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
262
271
  raise base.PlugVIPException(str(e))
263
272
 
264
273
  def _add_vip_security_group_to_port(self, load_balancer_id, port_id,
265
- sec_grp_id=None):
266
- sec_grp_id = (sec_grp_id or
267
- self._get_lb_security_group(load_balancer_id).get(
268
- constants.ID))
274
+ sec_grp_id: str = None,
275
+ vip_sg_ids: list[str] = None):
276
+ sec_grp_ids = [sec_grp_id or
277
+ self._get_lb_security_group(load_balancer_id).get(
278
+ constants.ID)]
279
+ if vip_sg_ids:
280
+ sec_grp_ids += vip_sg_ids
269
281
  try:
270
- self._add_security_group_to_port(sec_grp_id, port_id)
282
+ self._update_security_groups(sec_grp_ids, port_id)
271
283
  except base.PortNotFound:
272
284
  raise
273
285
  except base.NetworkException as e:
@@ -409,15 +421,28 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
409
421
  self._update_security_group_rules(load_balancer,
410
422
  sec_grp.get(constants.ID))
411
423
  self._add_vip_security_group_to_port(load_balancer.id, vip.port_id,
412
- sec_grp.get(constants.ID))
424
+ sec_grp.get(constants.ID),
425
+ vip_sg_ids=vip.sg_ids)
413
426
  return sec_grp.get(constants.ID)
414
427
  return None
415
428
 
429
+ def update_aap_port_sg(self,
430
+ load_balancer: data_models.LoadBalancer,
431
+ amphora: data_models.Amphora,
432
+ vip: data_models.Vip):
433
+ if self.sec_grp_enabled:
434
+ sec_grp = self._get_lb_security_group(load_balancer.id)
435
+ if sec_grp:
436
+ self._add_vip_security_group_to_port(load_balancer.id,
437
+ amphora.vrrp_port_id,
438
+ sec_grp.get(constants.ID),
439
+ vip_sg_ids=vip.sg_ids)
440
+
416
441
  def plug_aap_port(self, load_balancer, vip, amphora, subnet):
417
442
  interface = self._get_plugged_interface(
418
443
  amphora.compute_id, subnet.network_id, amphora.lb_network_ip)
419
444
  if not interface:
420
- interface = self._plug_amphora_vip(amphora, subnet)
445
+ interface = self._plug_amphora_vip(amphora, subnet, vip)
421
446
 
422
447
  aap_address_list = [vip.ip_address]
423
448
  for add_vip in load_balancer.additional_vips:
@@ -426,7 +451,8 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
426
451
 
427
452
  if self.sec_grp_enabled:
428
453
  self._add_vip_security_group_to_port(load_balancer.id,
429
- interface.port_id)
454
+ interface.port_id,
455
+ vip_sg_ids=vip.sg_ids)
430
456
  vrrp_ip = None
431
457
  for fixed_ip in interface.fixed_ips:
432
458
  is_correct_subnet = fixed_ip.subnet_id == subnet.id
@@ -466,7 +492,7 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
466
492
  list_of_dicts.append(fixed_ip.to_dict())
467
493
  return list_of_dicts
468
494
 
469
- def allocate_vip(self, load_balancer):
495
+ def allocate_vip(self, load_balancer: data_models.LoadBalancer):
470
496
  """Allocates a virtual ip.
471
497
 
472
498
  Reserves the IP for later use as the frontend connection of a load
@@ -558,6 +584,9 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
558
584
  constants.DEVICE_OWNER: constants.OCTAVIA_OWNER,
559
585
  project_id_key: load_balancer.project_id}
560
586
 
587
+ if load_balancer.vip.sg_ids:
588
+ port[constants.SECURITY_GROUP_IDS] = load_balancer.vip.sg_ids
589
+
561
590
  if fixed_ips:
562
591
  port[constants.FIXED_IPS] = fixed_ips
563
592
  try:
@@ -626,26 +655,6 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
626
655
  load_balancer.amphorae):
627
656
  self.unplug_aap_port(vip, amphora, subnet)
628
657
 
629
- def plug_network(self, compute_id, network_id):
630
- try:
631
- interface = self.compute.attach_network_or_port(
632
- compute_id=compute_id, network_id=network_id)
633
- except exceptions.NotFound as e:
634
- if 'Instance' in str(e):
635
- raise base.AmphoraNotFound(str(e))
636
- if 'Network' in str(e):
637
- raise base.NetworkNotFound(str(e))
638
- raise base.PlugNetworkException(str(e))
639
- except Exception as e:
640
- message = _('Error plugging amphora (compute_id: {compute_id}) '
641
- 'into network {network_id}.').format(
642
- compute_id=compute_id,
643
- network_id=network_id)
644
- LOG.exception(message)
645
- raise base.PlugNetworkException(message) from e
646
-
647
- return self._nova_interface_to_octavia_interface(compute_id, interface)
648
-
649
658
  def unplug_network(self, compute_id, network_id):
650
659
  interfaces = self.get_plugged_networks(compute_id)
651
660
  if not interfaces:
@@ -876,7 +885,7 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
876
885
  raise base.CreatePortException(message)
877
886
 
878
887
  def get_security_group(self, sg_name):
879
- """Retrieves the security group by it's name.
888
+ """Retrieves the security group by its name.
880
889
 
881
890
  :param sg_name: The security group name.
882
891
  :return: octavia.network.data_models.SecurityGroup, None if not enabled
@@ -12,6 +12,8 @@
12
12
  # License for the specific language governing permissions and limitations
13
13
  # under the License.
14
14
 
15
+ import typing
16
+
15
17
  from openstack.connection import Connection
16
18
  import openstack.exceptions as os_exceptions
17
19
  from openstack.network.v2._proxy import Proxy
@@ -25,6 +27,9 @@ from octavia.network import base
25
27
  from octavia.network import data_models as network_models
26
28
  from octavia.network.drivers.neutron import utils
27
29
 
30
+ if typing.TYPE_CHECKING:
31
+ from octavia.common import context
32
+
28
33
  LOG = logging.getLogger(__name__)
29
34
  DNS_INT_EXT_ALIAS = 'dns-integration'
30
35
  SEC_GRP_EXT_ALIAS = 'security-group'
@@ -76,21 +81,22 @@ class BaseNeutronDriver(base.AbstractNetworkDriver):
76
81
  fixed_ip = port_fixed_ip
77
82
  else:
78
83
  additional_ips.append(port_fixed_ip)
84
+ kwargs = {
85
+ 'ip_address': None,
86
+ 'subnet_id': None
87
+ }
79
88
  if fixed_ip:
80
- primary_vip = data_models.Vip(ip_address=fixed_ip.ip_address,
81
- subnet_id=fixed_ip.subnet_id,
82
- network_id=port.network_id,
83
- port_id=port.id,
84
- load_balancer=load_balancer,
85
- load_balancer_id=load_balancer.id,
86
- octavia_owned=octavia_owned)
87
- else:
88
- primary_vip = data_models.Vip(ip_address=None, subnet_id=None,
89
- network_id=port.network_id,
90
- port_id=port.id,
91
- load_balancer=load_balancer,
92
- load_balancer_id=load_balancer.id,
93
- octavia_owned=octavia_owned)
89
+ kwargs['ip_address'] = fixed_ip.ip_address
90
+ kwargs['subnet_id'] = fixed_ip.subnet_id
91
+
92
+ primary_vip = data_models.Vip(
93
+ network_id=port.network_id,
94
+ port_id=port.id,
95
+ load_balancer=load_balancer,
96
+ load_balancer_id=load_balancer.id,
97
+ octavia_owned=octavia_owned,
98
+ sg_ids=load_balancer.vip.sg_ids,
99
+ **kwargs)
94
100
  additional_vips = [
95
101
  data_models.AdditionalVip(
96
102
  ip_address=add_fixed_ip.ip_address,
@@ -123,11 +129,12 @@ class BaseNeutronDriver(base.AbstractNetworkDriver):
123
129
  self.network_proxy.update_port(port_id,
124
130
  allowed_address_pairs=aap)
125
131
 
126
- def _add_security_group_to_port(self, sec_grp_id, port_id):
132
+ def _update_security_groups(self, sec_grp_ids: list[str],
133
+ port_id: str):
127
134
  # Note: Neutron accepts the SG even if it already exists
128
135
  try:
129
136
  self.network_proxy.update_port(
130
- port_id, security_groups=[sec_grp_id])
137
+ port_id, security_groups=sec_grp_ids)
131
138
  except os_exceptions.NotFoundException as e:
132
139
  raise base.PortNotFound(str(e))
133
140
  except Exception as e:
@@ -252,6 +259,11 @@ class BaseNeutronDriver(base.AbstractNetworkDriver):
252
259
  def get_port(self, port_id, context=None):
253
260
  return self._get_resource('port', port_id, context=context)
254
261
 
262
+ def get_security_group_by_id(self, sg_id: str,
263
+ context: 'context.RequestContext' = None) -> (
264
+ 'network_models.SecurityGroup'):
265
+ return self._get_resource('security_group', sg_id, context=context)
266
+
255
267
  def get_network_by_name(self, network_name):
256
268
  return self._get_resources_by_filters(
257
269
  'network', unique_item=True, name=network_name)
@@ -12,7 +12,6 @@
12
12
  # License for the specific language governing permissions and limitations
13
13
  # under the License.
14
14
 
15
-
16
15
  from openstack.network.v2.network_ip_availability import NetworkIPAvailability
17
16
 
18
17
  from octavia.network import data_models as network_models
@@ -52,7 +51,8 @@ def convert_port_to_model(port):
52
51
  admin_state_up=port.is_admin_state_up,
53
52
  fixed_ips=fixed_ips,
54
53
  qos_policy_id=port.qos_policy_id,
55
- security_group_ids=port.security_group_ids
54
+ security_group_ids=port.security_group_ids,
55
+ vnic_type=port.binding_vnic_type
56
56
  )
57
57
 
58
58