octavia 12.0.0.0rc2__py3-none-any.whl → 13.0.0.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. octavia/amphorae/backends/agent/api_server/osutils.py +1 -0
  2. octavia/amphorae/backends/agent/api_server/plug.py +21 -7
  3. octavia/amphorae/backends/agent/api_server/templates/amphora-netns.systemd.j2 +2 -2
  4. octavia/amphorae/backends/agent/api_server/util.py +21 -0
  5. octavia/amphorae/backends/health_daemon/health_daemon.py +9 -3
  6. octavia/amphorae/backends/health_daemon/health_sender.py +2 -0
  7. octavia/amphorae/backends/utils/interface.py +14 -6
  8. octavia/amphorae/backends/utils/interface_file.py +6 -3
  9. octavia/amphorae/backends/utils/keepalivedlvs_query.py +8 -9
  10. octavia/amphorae/drivers/driver_base.py +1 -2
  11. octavia/amphorae/drivers/haproxy/rest_api_driver.py +11 -25
  12. octavia/amphorae/drivers/health/heartbeat_udp.py +34 -24
  13. octavia/amphorae/drivers/keepalived/jinja/jinja_cfg.py +3 -12
  14. octavia/amphorae/drivers/noop_driver/driver.py +3 -5
  15. octavia/api/common/pagination.py +4 -4
  16. octavia/api/drivers/amphora_driver/v2/driver.py +11 -5
  17. octavia/api/drivers/driver_agent/driver_get.py +22 -14
  18. octavia/api/drivers/driver_agent/driver_updater.py +8 -4
  19. octavia/api/drivers/utils.py +4 -2
  20. octavia/api/healthcheck/healthcheck_plugins.py +4 -2
  21. octavia/api/root_controller.py +4 -1
  22. octavia/api/v2/controllers/amphora.py +35 -38
  23. octavia/api/v2/controllers/availability_zone_profiles.py +43 -33
  24. octavia/api/v2/controllers/availability_zones.py +22 -18
  25. octavia/api/v2/controllers/flavor_profiles.py +37 -28
  26. octavia/api/v2/controllers/flavors.py +19 -15
  27. octavia/api/v2/controllers/health_monitor.py +44 -33
  28. octavia/api/v2/controllers/l7policy.py +52 -40
  29. octavia/api/v2/controllers/l7rule.py +68 -55
  30. octavia/api/v2/controllers/listener.py +88 -61
  31. octavia/api/v2/controllers/load_balancer.py +52 -34
  32. octavia/api/v2/controllers/member.py +63 -52
  33. octavia/api/v2/controllers/pool.py +55 -42
  34. octavia/api/v2/controllers/quotas.py +5 -3
  35. octavia/api/v2/types/listener.py +15 -0
  36. octavia/cmd/octavia_worker.py +0 -3
  37. octavia/cmd/status.py +1 -4
  38. octavia/common/clients.py +25 -45
  39. octavia/common/config.py +64 -22
  40. octavia/common/constants.py +3 -2
  41. octavia/common/data_models.py +7 -1
  42. octavia/common/jinja/haproxy/combined_listeners/jinja_cfg.py +12 -1
  43. octavia/common/jinja/haproxy/combined_listeners/templates/macros.j2 +5 -2
  44. octavia/common/jinja/lvs/jinja_cfg.py +4 -2
  45. octavia/common/keystone.py +58 -5
  46. octavia/common/validate.py +35 -0
  47. octavia/compute/drivers/noop_driver/driver.py +6 -0
  48. octavia/controller/healthmanager/health_manager.py +3 -6
  49. octavia/controller/housekeeping/house_keeping.py +36 -37
  50. octavia/controller/worker/amphora_rate_limit.py +5 -4
  51. octavia/controller/worker/task_utils.py +57 -41
  52. octavia/controller/worker/v2/controller_worker.py +160 -103
  53. octavia/controller/worker/v2/flows/listener_flows.py +3 -0
  54. octavia/controller/worker/v2/flows/load_balancer_flows.py +9 -14
  55. octavia/controller/worker/v2/tasks/amphora_driver_tasks.py +152 -91
  56. octavia/controller/worker/v2/tasks/compute_tasks.py +4 -2
  57. octavia/controller/worker/v2/tasks/database_tasks.py +542 -400
  58. octavia/controller/worker/v2/tasks/network_tasks.py +119 -79
  59. octavia/db/api.py +26 -23
  60. octavia/db/base_models.py +2 -2
  61. octavia/db/healthcheck.py +2 -1
  62. octavia/db/migration/alembic_migrations/versions/632152d2d32e_add_http_strict_transport_security_.py +42 -0
  63. octavia/db/models.py +12 -2
  64. octavia/db/prepare.py +2 -0
  65. octavia/db/repositories.py +462 -482
  66. octavia/hacking/checks.py +1 -1
  67. octavia/network/base.py +0 -14
  68. octavia/network/drivers/neutron/allowed_address_pairs.py +92 -135
  69. octavia/network/drivers/neutron/base.py +65 -77
  70. octavia/network/drivers/neutron/utils.py +69 -85
  71. octavia/network/drivers/noop_driver/driver.py +0 -7
  72. octavia/statistics/drivers/update_db.py +10 -10
  73. octavia/tests/common/constants.py +91 -84
  74. octavia/tests/common/sample_data_models.py +13 -1
  75. octavia/tests/fixtures.py +32 -0
  76. octavia/tests/functional/amphorae/backend/agent/api_server/test_server.py +9 -10
  77. octavia/tests/functional/api/drivers/driver_agent/test_driver_agent.py +260 -15
  78. octavia/tests/functional/api/test_root_controller.py +3 -28
  79. octavia/tests/functional/api/v2/base.py +5 -3
  80. octavia/tests/functional/api/v2/test_amphora.py +18 -5
  81. octavia/tests/functional/api/v2/test_availability_zone_profiles.py +1 -0
  82. octavia/tests/functional/api/v2/test_listener.py +51 -19
  83. octavia/tests/functional/api/v2/test_load_balancer.py +10 -1
  84. octavia/tests/functional/db/base.py +31 -16
  85. octavia/tests/functional/db/test_models.py +27 -28
  86. octavia/tests/functional/db/test_repositories.py +407 -50
  87. octavia/tests/unit/amphorae/backends/agent/api_server/test_amphora_info.py +2 -0
  88. octavia/tests/unit/amphorae/backends/agent/api_server/test_osutils.py +1 -1
  89. octavia/tests/unit/amphorae/backends/agent/api_server/test_plug.py +54 -6
  90. octavia/tests/unit/amphorae/backends/agent/api_server/test_util.py +35 -0
  91. octavia/tests/unit/amphorae/backends/health_daemon/test_health_daemon.py +8 -0
  92. octavia/tests/unit/amphorae/backends/health_daemon/test_health_sender.py +18 -0
  93. octavia/tests/unit/amphorae/backends/utils/test_interface.py +81 -0
  94. octavia/tests/unit/amphorae/backends/utils/test_interface_file.py +2 -0
  95. octavia/tests/unit/amphorae/backends/utils/test_keepalivedlvs_query.py +129 -5
  96. octavia/tests/unit/amphorae/drivers/haproxy/test_rest_api_driver_1_0.py +42 -20
  97. octavia/tests/unit/amphorae/drivers/health/test_heartbeat_udp.py +18 -20
  98. octavia/tests/unit/amphorae/drivers/keepalived/jinja/test_jinja_cfg.py +4 -4
  99. octavia/tests/unit/amphorae/drivers/noop_driver/test_driver.py +4 -1
  100. octavia/tests/unit/api/drivers/driver_agent/test_driver_get.py +3 -3
  101. octavia/tests/unit/api/drivers/driver_agent/test_driver_updater.py +11 -13
  102. octavia/tests/unit/base.py +6 -0
  103. octavia/tests/unit/cmd/test_interface.py +2 -2
  104. octavia/tests/unit/cmd/test_status.py +2 -2
  105. octavia/tests/unit/common/jinja/haproxy/combined_listeners/test_jinja_cfg.py +152 -1
  106. octavia/tests/unit/common/sample_configs/sample_configs_combined.py +10 -3
  107. octavia/tests/unit/common/test_clients.py +0 -39
  108. octavia/tests/unit/common/test_keystone.py +54 -0
  109. octavia/tests/unit/common/test_validate.py +67 -0
  110. octavia/tests/unit/controller/healthmanager/test_health_manager.py +8 -22
  111. octavia/tests/unit/controller/housekeeping/test_house_keeping.py +3 -64
  112. octavia/tests/unit/controller/worker/test_amphora_rate_limit.py +1 -1
  113. octavia/tests/unit/controller/worker/test_task_utils.py +44 -24
  114. octavia/tests/unit/controller/worker/v2/flows/test_load_balancer_flows.py +0 -1
  115. octavia/tests/unit/controller/worker/v2/tasks/test_amphora_driver_tasks.py +49 -26
  116. octavia/tests/unit/controller/worker/v2/tasks/test_database_tasks.py +399 -196
  117. octavia/tests/unit/controller/worker/v2/tasks/test_database_tasks_quota.py +37 -64
  118. octavia/tests/unit/controller/worker/v2/tasks/test_network_tasks.py +3 -14
  119. octavia/tests/unit/controller/worker/v2/test_controller_worker.py +2 -2
  120. octavia/tests/unit/network/drivers/neutron/test_allowed_address_pairs.py +456 -561
  121. octavia/tests/unit/network/drivers/neutron/test_base.py +181 -194
  122. octavia/tests/unit/network/drivers/neutron/test_utils.py +14 -30
  123. octavia/tests/unit/statistics/drivers/test_update_db.py +7 -5
  124. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/README.rst +1 -1
  125. {octavia-12.0.0.0rc2.dist-info → octavia-13.0.0.0rc1.dist-info}/AUTHORS +4 -0
  126. {octavia-12.0.0.0rc2.dist-info → octavia-13.0.0.0rc1.dist-info}/METADATA +4 -4
  127. {octavia-12.0.0.0rc2.dist-info → octavia-13.0.0.0rc1.dist-info}/RECORD +141 -189
  128. {octavia-12.0.0.0rc2.dist-info → octavia-13.0.0.0rc1.dist-info}/entry_points.txt +1 -2
  129. octavia-13.0.0.0rc1.dist-info/pbr.json +1 -0
  130. octavia/api/drivers/amphora_driver/v1/__init__.py +0 -11
  131. octavia/api/drivers/amphora_driver/v1/driver.py +0 -547
  132. octavia/controller/queue/v1/__init__.py +0 -11
  133. octavia/controller/queue/v1/consumer.py +0 -64
  134. octavia/controller/queue/v1/endpoints.py +0 -160
  135. octavia/controller/worker/v1/__init__.py +0 -11
  136. octavia/controller/worker/v1/controller_worker.py +0 -1157
  137. octavia/controller/worker/v1/flows/__init__.py +0 -11
  138. octavia/controller/worker/v1/flows/amphora_flows.py +0 -610
  139. octavia/controller/worker/v1/flows/health_monitor_flows.py +0 -105
  140. octavia/controller/worker/v1/flows/l7policy_flows.py +0 -94
  141. octavia/controller/worker/v1/flows/l7rule_flows.py +0 -100
  142. octavia/controller/worker/v1/flows/listener_flows.py +0 -128
  143. octavia/controller/worker/v1/flows/load_balancer_flows.py +0 -692
  144. octavia/controller/worker/v1/flows/member_flows.py +0 -230
  145. octavia/controller/worker/v1/flows/pool_flows.py +0 -127
  146. octavia/controller/worker/v1/tasks/__init__.py +0 -11
  147. octavia/controller/worker/v1/tasks/amphora_driver_tasks.py +0 -453
  148. octavia/controller/worker/v1/tasks/cert_task.py +0 -51
  149. octavia/controller/worker/v1/tasks/compute_tasks.py +0 -335
  150. octavia/controller/worker/v1/tasks/database_tasks.py +0 -2756
  151. octavia/controller/worker/v1/tasks/lifecycle_tasks.py +0 -173
  152. octavia/controller/worker/v1/tasks/model_tasks.py +0 -41
  153. octavia/controller/worker/v1/tasks/network_tasks.py +0 -970
  154. octavia/controller/worker/v1/tasks/retry_tasks.py +0 -74
  155. octavia/tests/unit/api/drivers/amphora_driver/v1/__init__.py +0 -11
  156. octavia/tests/unit/api/drivers/amphora_driver/v1/test_driver.py +0 -824
  157. octavia/tests/unit/controller/queue/v1/__init__.py +0 -11
  158. octavia/tests/unit/controller/queue/v1/test_consumer.py +0 -61
  159. octavia/tests/unit/controller/queue/v1/test_endpoints.py +0 -189
  160. octavia/tests/unit/controller/worker/v1/__init__.py +0 -11
  161. octavia/tests/unit/controller/worker/v1/flows/__init__.py +0 -11
  162. octavia/tests/unit/controller/worker/v1/flows/test_amphora_flows.py +0 -474
  163. octavia/tests/unit/controller/worker/v1/flows/test_health_monitor_flows.py +0 -72
  164. octavia/tests/unit/controller/worker/v1/flows/test_l7policy_flows.py +0 -67
  165. octavia/tests/unit/controller/worker/v1/flows/test_l7rule_flows.py +0 -67
  166. octavia/tests/unit/controller/worker/v1/flows/test_listener_flows.py +0 -91
  167. octavia/tests/unit/controller/worker/v1/flows/test_load_balancer_flows.py +0 -431
  168. octavia/tests/unit/controller/worker/v1/flows/test_member_flows.py +0 -106
  169. octavia/tests/unit/controller/worker/v1/flows/test_pool_flows.py +0 -77
  170. octavia/tests/unit/controller/worker/v1/tasks/__init__.py +0 -11
  171. octavia/tests/unit/controller/worker/v1/tasks/test_amphora_driver_tasks.py +0 -792
  172. octavia/tests/unit/controller/worker/v1/tasks/test_cert_task.py +0 -46
  173. octavia/tests/unit/controller/worker/v1/tasks/test_compute_tasks.py +0 -634
  174. octavia/tests/unit/controller/worker/v1/tasks/test_database_tasks.py +0 -2615
  175. octavia/tests/unit/controller/worker/v1/tasks/test_database_tasks_quota.py +0 -415
  176. octavia/tests/unit/controller/worker/v1/tasks/test_lifecycle_tasks.py +0 -401
  177. octavia/tests/unit/controller/worker/v1/tasks/test_model_tasks.py +0 -44
  178. octavia/tests/unit/controller/worker/v1/tasks/test_network_tasks.py +0 -1788
  179. octavia/tests/unit/controller/worker/v1/tasks/test_retry_tasks.py +0 -47
  180. octavia/tests/unit/controller/worker/v1/test_controller_worker.py +0 -2096
  181. octavia-12.0.0.0rc2.dist-info/pbr.json +0 -1
  182. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/LICENSE +0 -0
  183. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/diskimage-create/README.rst +0 -0
  184. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/diskimage-create/diskimage-create.sh +0 -0
  185. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/diskimage-create/image-tests.sh +0 -0
  186. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/diskimage-create/requirements.txt +0 -0
  187. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/diskimage-create/test-requirements.txt +0 -0
  188. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/diskimage-create/tox.ini +0 -0
  189. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/data/share/octavia/diskimage-create/version.txt +0 -0
  190. {octavia-12.0.0.0rc2.data → octavia-13.0.0.0rc1.data}/scripts/octavia-wsgi +0 -0
  191. {octavia-12.0.0.0rc2.dist-info → octavia-13.0.0.0rc1.dist-info}/LICENSE +0 -0
  192. {octavia-12.0.0.0rc2.dist-info → octavia-13.0.0.0rc1.dist-info}/WHEEL +0 -0
  193. {octavia-12.0.0.0rc2.dist-info → octavia-13.0.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -185,6 +185,7 @@ class TestAmphoraInfo(base.TestCase):
185
185
  u'packages': {},
186
186
  u'topology': u'SINGLE',
187
187
  u'topology_status': u'OK'}
188
+ self.useFixture(test_utils.OpenFixture('/etc/tuned/active_profile'))
188
189
  actual = self.amp_info.compile_amphora_details()
189
190
  self.assertEqual(expected_dict, actual.json)
190
191
  m_count.assert_called_once_with(sorted(mget_loadbalancers()))
@@ -277,6 +278,7 @@ class TestAmphoraInfo(base.TestCase):
277
278
  u'packages': {},
278
279
  u'topology': u'SINGLE',
279
280
  u'topology_status': u'OK'}
281
+ self.useFixture(test_utils.OpenFixture('/etc/tuned/active_profile'))
280
282
  actual = self.amp_info.compile_amphora_details(self.lvs_driver)
281
283
  self.assertEqual(expected_dict, actual.json)
282
284
  api_server.VERSION = original_version
@@ -106,7 +106,7 @@ class TestOSUtils(base.TestCase):
106
106
  '192.0.2.2', 16)
107
107
 
108
108
  mock_interface_file.assert_called_once_with(
109
- name='eth1',
109
+ name='eth1', if_type="lo",
110
110
  addresses=[{"address": "192.0.2.2", "prefixlen": 16}])
111
111
  mock_interface.write.assert_called_once()
112
112
 
@@ -265,7 +265,10 @@ class TestPlug(base.TestCase):
265
265
  "BaseOS.write_port_interface_file")
266
266
  @mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
267
267
  "BaseOS.bring_interface_up")
268
- def test_plug_network(self, mock_if_up, mock_write_port_interface,
268
+ @mock.patch("octavia.amphorae.backends.agent.api_server.util."
269
+ "send_member_advertisements")
270
+ def test_plug_network(self, mock_send_member_adv,
271
+ mock_if_up, mock_write_port_interface,
269
272
  mock_netns, mock_iproute,
270
273
  mock_by_mac, mock_interface_exists, mock_webob):
271
274
  fixed_ips = [
@@ -282,12 +285,13 @@ class TestPlug(base.TestCase):
282
285
  self.test_plug.plug_network(FAKE_MAC_ADDRESS, fixed_ips, 1400)
283
286
 
284
287
  mock_write_port_interface.assert_called_once_with(
285
- interface='eth0', fixed_ips=fixed_ips, mtu=mtu)
286
- mock_if_up.assert_called_once_with('eth0', 'network')
288
+ interface='eth2', fixed_ips=fixed_ips, mtu=mtu)
289
+ mock_if_up.assert_called_once_with('eth2', 'network')
290
+ mock_send_member_adv.assert_called_once_with(fixed_ips)
287
291
 
288
292
  mock_webob.Response.assert_any_call(
289
293
  json={'message': 'OK',
290
- 'details': 'Plugged on interface eth0'},
294
+ 'details': 'Plugged on interface eth2'},
291
295
  status=202)
292
296
 
293
297
  @mock.patch.object(plug, "webob")
@@ -300,7 +304,10 @@ class TestPlug(base.TestCase):
300
304
  "BaseOS.write_port_interface_file")
301
305
  @mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
302
306
  "BaseOS.bring_interface_up")
303
- def test_plug_network_existing_interface(self, mock_if_up,
307
+ @mock.patch("octavia.amphorae.backends.agent.api_server.util."
308
+ "send_member_advertisements")
309
+ def test_plug_network_existing_interface(self, mock_send_member_adv,
310
+ mock_if_up,
304
311
  mock_write_port_interface,
305
312
  mock_netns, mock_by_mac,
306
313
  mock_interface_exists,
@@ -327,6 +334,7 @@ class TestPlug(base.TestCase):
327
334
  mock_write_port_interface.assert_called_once_with(
328
335
  interface=FAKE_INTERFACE, fixed_ips=fixed_ips, mtu=mtu)
329
336
  mock_if_up.assert_called_once_with(FAKE_INTERFACE, 'network')
337
+ mock_send_member_adv.assert_called_once_with(fixed_ips)
330
338
 
331
339
  mock_webob.Response.assert_any_call(
332
340
  json={'message': 'OK',
@@ -344,8 +352,10 @@ class TestPlug(base.TestCase):
344
352
  "BaseOS.write_vip_interface_file")
345
353
  @mock.patch("octavia.amphorae.backends.agent.api_server.osutils."
346
354
  "BaseOS.bring_interface_up")
355
+ @mock.patch("octavia.amphorae.backends.agent.api_server.util."
356
+ "send_member_advertisements")
347
357
  def test_plug_network_on_vip(
348
- self, mock_if_up, mock_write_vip_interface,
358
+ self, mock_send_member_adv, mock_if_up, mock_write_vip_interface,
349
359
  mock_netns, mock_by_mac, mock_interface_exists, mock_webob):
350
360
  fixed_ips = [
351
361
  {'ip_address': FAKE_IP_IPV4,
@@ -394,9 +404,47 @@ class TestPlug(base.TestCase):
394
404
  fixed_ips=fixed_ips, mtu=mtu)
395
405
 
396
406
  mock_if_up.assert_called_once_with(FAKE_INTERFACE, 'vip')
407
+ mock_send_member_adv.assert_called_once_with(fixed_ips)
397
408
 
398
409
  mock_webob.Response.assert_any_call(
399
410
  json={'message': 'OK',
400
411
  'details': 'Updated existing interface {}'.format(
401
412
  FAKE_INTERFACE)},
402
413
  status=202)
414
+
415
+ @mock.patch('pyroute2.NetNS', create=True)
416
+ def test__netns_get_next_interface(self, mock_netns):
417
+ netns_handle = mock_netns.return_value.__enter__.return_value
418
+
419
+ netns_handle.get_links.return_value = [
420
+ {'attrs': [['IFLA_IFNAME', 'lo']]},
421
+ ]
422
+
423
+ ifname = self.test_plug._netns_get_next_interface()
424
+ self.assertEqual('eth2', ifname)
425
+
426
+ netns_handle.get_links.return_value = [
427
+ {'attrs': [['IFLA_IFNAME', 'lo']]},
428
+ {'attrs': [['IFLA_IFNAME', 'eth1']]},
429
+ {'attrs': [['IFLA_IFNAME', 'eth2']]},
430
+ {'attrs': [['IFLA_IFNAME', 'eth3']]},
431
+ ]
432
+
433
+ ifname = self.test_plug._netns_get_next_interface()
434
+ self.assertEqual('eth4', ifname)
435
+
436
+ netns_handle.get_links.return_value = [
437
+ {'attrs': [['IFLA_IFNAME', 'lo']]},
438
+ {'attrs': [['IFLA_IFNAME', 'eth1']]},
439
+ {'attrs': [['IFLA_IFNAME', 'eth3']]},
440
+ ]
441
+
442
+ ifname = self.test_plug._netns_get_next_interface()
443
+ self.assertEqual('eth2', ifname)
444
+
445
+ netns_handle.get_links.return_value = [
446
+ {'attrs': [['IFLA_IFNAME', f'eth{idx}']]}
447
+ for idx in range(2, 1000)]
448
+
449
+ ifname = self.test_plug._netns_get_next_interface()
450
+ self.assertEqual('eth1000', ifname)
@@ -406,3 +406,38 @@ class TestUtil(base.TestCase):
406
406
  util.send_vip_advertisements(LB_ID1)
407
407
  mock_get_int_name.assert_not_called()
408
408
  mock_send_advert.assert_not_called()
409
+
410
+ @mock.patch('octavia.amphorae.backends.utils.ip_advertisement.'
411
+ 'send_ip_advertisement')
412
+ @mock.patch('octavia.amphorae.backends.utils.network_utils.'
413
+ 'get_interface_name')
414
+ def test_send_member_advertisements(self, mock_get_int_name,
415
+ mock_send_advert):
416
+ # IPv4 fixed_ips
417
+ mock_get_int_name.side_effect = ['fake0', 'fake1']
418
+ fixed_ips = [{'ip_address': '192.0.2.1'},
419
+ {'ip_address': '192.2.0.2'}]
420
+ util.send_member_advertisements(fixed_ips)
421
+ mock_send_advert.assert_has_calls(
422
+ [mock.call('fake0', fixed_ips[0]['ip_address'],
423
+ net_ns=consts.AMPHORA_NAMESPACE),
424
+ mock.call('fake1', fixed_ips[1]['ip_address'],
425
+ net_ns=consts.AMPHORA_NAMESPACE)])
426
+
427
+ # Mixed IPv4/IPv6
428
+ mock_send_advert.reset_mock()
429
+ mock_get_int_name.side_effect = ['fake0', 'fake1']
430
+ fixed_ips = [{'ip_address': '192.0.2.1'},
431
+ {'ip_address': '2001:db8::2'}]
432
+ util.send_member_advertisements(fixed_ips)
433
+ mock_send_advert.assert_has_calls(
434
+ [mock.call('fake0', fixed_ips[0]['ip_address'],
435
+ net_ns=consts.AMPHORA_NAMESPACE),
436
+ mock.call('fake1', fixed_ips[1]['ip_address'],
437
+ net_ns=consts.AMPHORA_NAMESPACE)])
438
+
439
+ # Exception
440
+ mock_send_advert.reset_mock()
441
+ mock_get_int_name.side_effect = Exception('ERROR')
442
+ util.send_member_advertisements(fixed_ips)
443
+ mock_send_advert.assert_not_called()
@@ -319,6 +319,14 @@ class TestHealthDaemon(base.TestCase):
319
319
  stats_query_mock.show_stat.assert_called_once_with()
320
320
  stats_query_mock.get_pool_status.assert_called_once_with()
321
321
 
322
+ @mock.patch('octavia.amphorae.backends.utils.haproxy_query.HAProxyQuery')
323
+ def test_get_stats_exception(self, mock_query):
324
+ mock_query.side_effect = Exception('Boom')
325
+
326
+ stats, pool_status = health_daemon.get_stats('TEST')
327
+ self.assertEqual([], stats)
328
+ self.assertEqual({}, pool_status)
329
+
322
330
  @mock.patch('octavia.amphorae.backends.agent.api_server.'
323
331
  'util.is_lb_running')
324
332
  @mock.patch('octavia.amphorae.backends.health_daemon.'
@@ -93,6 +93,24 @@ class TestHealthSender(base.TestCase):
93
93
 
94
94
  sendto_mock.reset_mock()
95
95
 
96
+ # Test IPv6 path enclosed within square brackets ("[" and "]").
97
+ self.conf.config(group="health_manager",
98
+ controller_ip_port_list=['[2001:0db8::f00d]:80'])
99
+ mock_getaddrinfo.return_value = [(socket.AF_INET6,
100
+ socket.SOCK_DGRAM,
101
+ socket.IPPROTO_UDP,
102
+ '',
103
+ ('2001:db8::f00d', 80, 0, 0))]
104
+
105
+ sender = health_sender.UDPStatusSender()
106
+
107
+ sender.dosend(SAMPLE_MSG)
108
+
109
+ sendto_mock.assert_called_once_with(SAMPLE_MSG_BIN,
110
+ ('2001:db8::f00d', 80, 0, 0))
111
+
112
+ sendto_mock.reset_mock()
113
+
96
114
  # Test IPv6 link-local address path
97
115
  self.conf.config(
98
116
  group="health_manager",
@@ -74,6 +74,7 @@ class TestInterface(base.TestCase):
74
74
  '],\n'
75
75
  '"mtu": 1450,\n'
76
76
  '"name": "eth1",\n'
77
+ '"if_type": "mytype",\n'
77
78
  '"routes": [\n'
78
79
  '{"dst": "0.0.0.0/0",\n'
79
80
  '"gateway": "10.0.0.1"},\n'
@@ -107,6 +108,7 @@ class TestInterface(base.TestCase):
107
108
 
108
109
  expected_dict = {
109
110
  consts.NAME: "eth1",
111
+ consts.IF_TYPE: "mytype",
110
112
  consts.MTU: 1450,
111
113
  consts.ADDRESSES: [{
112
114
  consts.ADDRESS: "10.0.0.2",
@@ -331,6 +333,7 @@ class TestInterface(base.TestCase):
331
333
  mock_link, mock_addr, mock_route, mock_rule):
332
334
  iface = interface_file.InterfaceFile(
333
335
  name="eth1",
336
+ if_type="vip",
334
337
  mtu=1450,
335
338
  addresses=[{
336
339
  consts.ADDRESS: '1.2.3.4',
@@ -445,6 +448,78 @@ class TestInterface(base.TestCase):
445
448
  mock.call(["post-up", "eth1"])
446
449
  ])
447
450
 
451
+ @mock.patch('pyroute2.IPRoute.rule')
452
+ @mock.patch('pyroute2.IPRoute.route')
453
+ @mock.patch('pyroute2.IPRoute.addr')
454
+ @mock.patch('pyroute2.IPRoute.link')
455
+ @mock.patch('pyroute2.IPRoute.get_links')
456
+ @mock.patch('pyroute2.IPRoute.link_lookup')
457
+ @mock.patch('pyroute2.IPRoute.get_rules')
458
+ @mock.patch('subprocess.check_output')
459
+ def test_up_backend(self, mock_check_output, mock_get_rules,
460
+ mock_link_lookup, mock_get_links, mock_link, mock_addr,
461
+ mock_route, mock_rule):
462
+ iface = interface_file.InterfaceFile(
463
+ name="eth1",
464
+ if_type="backend",
465
+ mtu=1450,
466
+ addresses=[{
467
+ consts.ADDRESS: '1.2.3.4',
468
+ consts.PREFIXLEN: 24
469
+ }],
470
+ routes=[],
471
+ rules=[],
472
+ scripts={
473
+ consts.IFACE_UP: [{
474
+ consts.COMMAND: "post-up eth1"
475
+ }],
476
+ consts.IFACE_DOWN: [{
477
+ consts.COMMAND: "post-down eth1"
478
+ }],
479
+ })
480
+
481
+ idx = mock.MagicMock()
482
+ mock_link_lookup.return_value = [idx]
483
+
484
+ mock_get_links.return_value = [{
485
+ consts.STATE: consts.IFACE_DOWN
486
+ }]
487
+ mock_get_rules.return_value = [{
488
+ 'src_len': 32,
489
+ 'attrs': {
490
+ 'FRA_SRC': '1.1.1.1',
491
+ 'FRA_TABLE': 20,
492
+ 'FRA_PROTOCOL': 0
493
+ }
494
+ }]
495
+
496
+ controller = interface.InterfaceController()
497
+ controller.up(iface)
498
+
499
+ mock_link.assert_called_once_with(
500
+ controller.SET,
501
+ index=idx,
502
+ state=consts.IFACE_UP,
503
+ mtu=1450)
504
+
505
+ mock_addr.assert_has_calls([
506
+ mock.call(controller.ADD,
507
+ index=idx,
508
+ address='1.2.3.4',
509
+ prefixlen=24,
510
+ family=socket.AF_INET),
511
+ ])
512
+
513
+ mock_route.assert_called_once_with(
514
+ 'dump', family=mock.ANY, match=mock.ANY)
515
+
516
+ # for 'backend' iface, we don't update the rules
517
+ mock_rule.assert_not_called()
518
+
519
+ mock_check_output.assert_has_calls([
520
+ mock.call(["post-up", "eth1"])
521
+ ])
522
+
448
523
  @mock.patch('pyroute2.IPRoute.rule')
449
524
  @mock.patch('pyroute2.IPRoute.route')
450
525
  @mock.patch('pyroute2.IPRoute.addr')
@@ -463,6 +538,7 @@ class TestInterface(base.TestCase):
463
538
  mock_route, mock_rule):
464
539
  iface = interface_file.InterfaceFile(
465
540
  name="eth1",
541
+ if_type="vip",
466
542
  mtu=1450,
467
543
  addresses=[{
468
544
  consts.ADDRESS: '1.2.3.4',
@@ -658,6 +734,7 @@ class TestInterface(base.TestCase):
658
734
  mock_route, mock_rule):
659
735
  iface = interface_file.InterfaceFile(
660
736
  name="eth1",
737
+ if_type="vip",
661
738
  mtu=1450,
662
739
  addresses=[{
663
740
  consts.DHCP: True,
@@ -722,6 +799,7 @@ class TestInterface(base.TestCase):
722
799
  mock_link, mock_addr, mock_route, mock_rule):
723
800
  iface = interface_file.InterfaceFile(
724
801
  name="eth1",
802
+ if_type="vip",
725
803
  mtu=1450,
726
804
  addresses=[{
727
805
  consts.ADDRESS: '1.2.3.4',
@@ -848,6 +926,7 @@ class TestInterface(base.TestCase):
848
926
  mock_addr, mock_route, mock_rule):
849
927
  iface = interface_file.InterfaceFile(
850
928
  name="eth1",
929
+ if_type="vip",
851
930
  mtu=1450,
852
931
  addresses=[{
853
932
  consts.ADDRESS: '1.2.3.4',
@@ -997,6 +1076,7 @@ class TestInterface(base.TestCase):
997
1076
  mock_route, mock_rule):
998
1077
  iface = interface_file.InterfaceFile(
999
1078
  name="eth1",
1079
+ if_type="vip",
1000
1080
  mtu=1450,
1001
1081
  addresses=[{
1002
1082
  consts.ADDRESS: '1.2.3.4',
@@ -1069,6 +1149,7 @@ class TestInterface(base.TestCase):
1069
1149
  mock_addr, mock_route, mock_rule):
1070
1150
  iface = interface_file.InterfaceFile(
1071
1151
  name="eth1",
1152
+ if_type="vip",
1072
1153
  mtu=1450,
1073
1154
  addresses=[{
1074
1155
  consts.DHCP: True,
@@ -691,6 +691,7 @@ class TestInterfaceFile(base.TestCase):
691
691
  '"prefixlen": 26}\n'
692
692
  '],\n'
693
693
  '"mtu": 1450,\n'
694
+ '"if_type": "mytype",\n'
694
695
  '"name": "eth1",\n'
695
696
  '"routes": [\n'
696
697
  '{"dst": "0.0.0.0/0",\n'
@@ -717,6 +718,7 @@ class TestInterfaceFile(base.TestCase):
717
718
 
718
719
  expected_dict = {
719
720
  consts.NAME: "eth1",
721
+ consts.IF_TYPE: "mytype",
720
722
  consts.MTU: 1450,
721
723
  consts.ADDRESSES: [{
722
724
  consts.ADDRESS: "10.0.0.181",
@@ -52,6 +52,24 @@ KERNAL_FILE_SAMPLE_V6 = (
52
52
  " -> [fd79:35e2::8f3f]:115C "
53
53
  "Masq 2 0 0")
54
54
 
55
+ KERNEL_FILE_SAMPLE_MIXED = (
56
+ "IP Virtual Server version 1.2.1 (size=4096)\n"
57
+ "Prot LocalAddress:Port Scheduler Flags\n"
58
+ " -> RemoteAddress:Port Forward Weight ActiveConn InActConn\n"
59
+ "UDP [fd79:35e2:9963:0000:f816:3eff:fe6d:7a2a]:1E61 rr\n"
60
+ " -> [fd79:35e2:9963:0000:f816:3eff:feca:b7bf]:08AE "
61
+ "Masq 3 0 0\n"
62
+ " -> [fd79:35e2:9963:0000:f816:3eff:fe9d:94df]:0D05 "
63
+ "Masq 2 0 0\n"
64
+ " -> [fd79:35e2::8f3f]:115C "
65
+ "Masq 2 0 0\n"
66
+ "UDP 0A000025:1E61 rr\n"
67
+ " -> 0A000023:0D05 Masq 2 0 0\n"
68
+ " -> 0A000019:08AE Masq 3 0 0\n"
69
+ "UDP 0A000025:FF12 rr\n"
70
+ " -> 0A000023:12AB Masq 2 0 0\n"
71
+ " -> 0A000019:BF2E Masq 3 0 0")
72
+
55
73
  CFG_FILE_TEMPLATE_v4 = (
56
74
  "# Configuration for Listener %(listener_id)s\n\n"
57
75
  "net_namespace %(ns_name)s\n\n"
@@ -190,6 +208,37 @@ CFG_FILE_TEMPLATE_mixed = (
190
208
  " # Member %(member_id5)s is disabled\n\n"
191
209
  "}")
192
210
 
211
+ CFG_FILE_TEMPLATE_mixed_no_ipv6_member = (
212
+ "# Configuration for Listener %(listener_id)s\n\n"
213
+ "net_namespace %(ns_name)s\n\n"
214
+ "virtual_server_group ipv4-group {\n"
215
+ " 10.0.0.37 7777\n"
216
+ "}\n\n"
217
+ "virtual_server group ipv4-group {\n"
218
+ " lb_algo rr\n"
219
+ " lvs_method NAT\n"
220
+ " protocol udp\n\n\n"
221
+ " # Configuration for Pool %(pool_id)s\n"
222
+ " # Configuration for Member %(member_id1)s\n"
223
+ " real_server 10.0.0.25 2222 {\n"
224
+ " weight 3\n"
225
+ " MISC_CHECK {\n\n"
226
+ " misc_path \"/usr/bin/check_script.sh\"\n\n"
227
+ " misc_timeout 5\n\n"
228
+ " }\n\n"
229
+ " }\n\n"
230
+ " # Member %(member_id2)s is disabled\n\n"
231
+ "}\n"
232
+ "virtual_server_group ipv6-group {\n"
233
+ " fd79:35e2:9963:0:f816:3eff:fe6d:7a2a 7777\n"
234
+ "}\n\n"
235
+ "virtual_server group ipv6-group {\n"
236
+ " lb_algo rr\n"
237
+ " lvs_method NAT\n"
238
+ " protocol udp\n\n\n"
239
+ " # Configuration for Pool %(pool_id)s\n"
240
+ "}")
241
+
193
242
  CFG_FILE_TEMPLATE_DISABLED_LISTENER = (
194
243
  "# Listener %(listener_id)s is disabled \n\n"
195
244
  "net_namespace %(ns_name)s\n\n"
@@ -233,6 +282,7 @@ class LvsQueryTestCase(base.TestCase):
233
282
  self.member_id4_v6 = uuidutils.generate_uuid()
234
283
  self.member_id5_v6 = uuidutils.generate_uuid()
235
284
  self.listener_id_mixed = uuidutils.generate_uuid()
285
+ self.listener_id_mixed_no_ipv6_member = uuidutils.generate_uuid()
236
286
  self.pool_id_mixed = uuidutils.generate_uuid()
237
287
  self.disabled_listener_id = uuidutils.generate_uuid()
238
288
  cfg_content_v4 = CFG_FILE_TEMPLATE_v4 % {
@@ -264,6 +314,15 @@ class LvsQueryTestCase(base.TestCase):
264
314
  'member_id4': self.member_id4_v6,
265
315
  'member_id5': self.member_id5_v6
266
316
  }
317
+ cfg_content_mixed_no_ipv6_member = (
318
+ CFG_FILE_TEMPLATE_mixed_no_ipv6_member % {
319
+ 'listener_id': self.listener_id_mixed,
320
+ 'ns_name': constants.AMPHORA_NAMESPACE,
321
+ 'pool_id': self.pool_id_mixed,
322
+ 'member_id1': self.member_id1_v4,
323
+ 'member_id2': self.member_id2_v4
324
+ }
325
+ )
267
326
  cfg_content_disabled_listener = (
268
327
  CFG_FILE_TEMPLATE_DISABLED_LISTENER % {
269
328
  'listener_id': self.listener_id_v6,
@@ -277,6 +336,10 @@ class LvsQueryTestCase(base.TestCase):
277
336
  self.useFixture(test_utils.OpenFixture(
278
337
  util.keepalived_lvs_cfg_path(self.listener_id_mixed),
279
338
  cfg_content_mixed))
339
+ self.useFixture(test_utils.OpenFixture(
340
+ util.keepalived_lvs_cfg_path(
341
+ self.listener_id_mixed_no_ipv6_member),
342
+ cfg_content_mixed_no_ipv6_member))
280
343
  self.useFixture(test_utils.OpenFixture(
281
344
  util.keepalived_lvs_cfg_path(self.disabled_listener_id),
282
345
  cfg_content_disabled_listener))
@@ -300,7 +363,7 @@ class LvsQueryTestCase(base.TestCase):
300
363
  'Weight': '2',
301
364
  'ActiveConn': '0',
302
365
  'InActConn': '0'}}
303
- self.assertEqual((True, expected), result)
366
+ self.assertEqual(expected, result)
304
367
 
305
368
  # Ipv6 resolver
306
369
  input_listener_ip_port = [
@@ -327,7 +390,45 @@ class LvsQueryTestCase(base.TestCase):
327
390
  'Weight': '2',
328
391
  'ActiveConn': '0',
329
392
  'InActConn': '0'}}
330
- self.assertEqual((True, expected), result)
393
+ self.assertEqual(expected, result)
394
+
395
+ # mixed resolver
396
+ input_listener_ip_port = [
397
+ '[fd79:35e2:9963:0:f816:3eff:fe6d:7a2a]:7777',
398
+ '10.0.0.37:7777']
399
+ mock_check_output.return_value = KERNEL_FILE_SAMPLE_MIXED
400
+ result = lvs_query.get_listener_realserver_mapping(
401
+ target_ns, input_listener_ip_port,
402
+ health_monitor_enabled=True)
403
+ expected = {'[fd79:35e2:9963:0:f816:3eff:feca:b7bf]:2222':
404
+ {'status': constants.UP,
405
+ 'Forward': 'Masq',
406
+ 'Weight': '3',
407
+ 'ActiveConn': '0',
408
+ 'InActConn': '0'},
409
+ '[fd79:35e2:9963:0:f816:3eff:fe9d:94df]:3333':
410
+ {'status': constants.UP,
411
+ 'Forward': 'Masq',
412
+ 'Weight': '2',
413
+ 'ActiveConn': '0',
414
+ 'InActConn': '0'},
415
+ '[fd79:35e2::8f3f]:4444':
416
+ {'status': constants.UP,
417
+ 'Forward': 'Masq',
418
+ 'Weight': '2',
419
+ 'ActiveConn': '0',
420
+ 'InActConn': '0'},
421
+ '10.0.0.25:2222': {'status': 'UP',
422
+ 'Forward': 'Masq',
423
+ 'Weight': '3',
424
+ 'ActiveConn': '0',
425
+ 'InActConn': '0'},
426
+ '10.0.0.35:3333': {'status': 'UP',
427
+ 'Forward': 'Masq',
428
+ 'Weight': '2',
429
+ 'ActiveConn': '0',
430
+ 'InActConn': '0'}}
431
+ self.assertEqual(expected, result)
331
432
 
332
433
  # negetive cases
333
434
  mock_check_output.return_value = KERNAL_FILE_SAMPLE_V4
@@ -335,7 +436,7 @@ class LvsQueryTestCase(base.TestCase):
335
436
  result = lvs_query.get_listener_realserver_mapping(
336
437
  target_ns, [listener_ip_port],
337
438
  health_monitor_enabled=True)
338
- self.assertEqual((False, {}), result)
439
+ self.assertEqual({}, result)
339
440
 
340
441
  mock_check_output.return_value = KERNAL_FILE_SAMPLE_V6
341
442
  for listener_ip_port in [
@@ -344,7 +445,7 @@ class LvsQueryTestCase(base.TestCase):
344
445
  result = lvs_query.get_listener_realserver_mapping(
345
446
  target_ns, [listener_ip_port],
346
447
  health_monitor_enabled=True)
347
- self.assertEqual((False, {}), result)
448
+ self.assertEqual({}, result)
348
449
 
349
450
  def test_get_lvs_listener_resource_ipports_nsname(self):
350
451
  # ipv4
@@ -526,7 +627,7 @@ class LvsQueryTestCase(base.TestCase):
526
627
  mock.Mock(st_mtime=1234),
527
628
  mock.Mock(st_mtime=1234),
528
629
  )
529
- mock_get_mapping.return_value = (False, {})
630
+ mock_get_mapping.return_value = {}
530
631
  res = lvs_query.get_lvs_listener_pool_status(self.listener_id_v4)
531
632
  expected = {
532
633
  'lvs':
@@ -629,6 +730,29 @@ class LvsQueryTestCase(base.TestCase):
629
730
  res = lvs_query.get_lvs_listeners_stats()
630
731
  self.assertEqual({}, res)
631
732
 
733
+ # listener for both ipv4 and ipv6, but no member in the ipv6 pool
734
+ mock_is_running.return_value = True
735
+ mock_get_listener.return_value = [
736
+ self.listener_id_mixed_no_ipv6_member]
737
+ output_list = list()
738
+ output_list.append(IPVSADM_OUTPUT_TEMPLATE % {
739
+ "listener_ipport": "10.0.0.37:7777",
740
+ "member1_ipport": "10.0.0.25:2222",
741
+ "member2_ipport": "10.0.0.35:3333"})
742
+ output_list.append(IPVSADM_STATS_OUTPUT_TEMPLATE % {
743
+ "listener_ipport": "10.0.0.37:7777",
744
+ "member1_ipport": "10.0.0.25:2222",
745
+ "member2_ipport": "10.0.0.35:3333"})
746
+ mock_check_output.side_effect = output_list
747
+ res = lvs_query.get_lvs_listeners_stats()
748
+ # We can check the expected result reference the stats sample,
749
+ # that means this func can compute the stats info of single listener.
750
+ expected = {self.listener_id_mixed_no_ipv6_member: {
751
+ 'status': constants.OPEN,
752
+ 'stats': {'bin': 6387472, 'stot': 5, 'bout': 7490,
753
+ 'ereq': 0, 'scur': 0}}}
754
+ self.assertEqual(expected, res)
755
+
632
756
  @mock.patch('subprocess.check_output')
633
757
  @mock.patch("octavia.amphorae.backends.agent.api_server.util."
634
758
  "is_lvs_listener_running", return_value=True)