OpenStack Networking (Neutron)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_l3_agent.py 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. # Copyright 2015 Red Hat, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. import functools
  15. import os
  16. import time
  17. import netaddr
  18. from oslo_utils import uuidutils
  19. from neutron.agent.l3 import ha_router
  20. from neutron.agent.l3 import namespaces
  21. from neutron.agent.linux import ip_lib
  22. from neutron.common import utils as common_utils
  23. from neutron.tests import base as tests_base
  24. from neutron.tests.common.exclusive_resources import ip_network
  25. from neutron.tests.common import machine_fixtures
  26. from neutron.tests.fullstack import base
  27. from neutron.tests.fullstack.resources import environment
  28. from neutron.tests.fullstack.resources import machine
  29. from neutron.tests.unit import testlib_api
  30. load_tests = testlib_api.module_load_tests
  31. class TestL3Agent(base.BaseFullStackTestCase):
  32. def _create_external_network_and_subnet(self, tenant_id):
  33. network = self.safe_client.create_network(
  34. tenant_id, name='public', external=True)
  35. cidr = self.useFixture(
  36. ip_network.ExclusiveIPNetwork(
  37. "240.0.0.0", "240.255.255.255", "24")).network
  38. subnet = self.safe_client.create_subnet(tenant_id, network['id'], cidr)
  39. return network, subnet
  40. def block_until_port_status_active(self, port_id):
  41. def is_port_status_active():
  42. port = self.client.show_port(port_id)
  43. return port['port']['status'] == 'ACTIVE'
  44. common_utils.wait_until_true(lambda: is_port_status_active(), sleep=1)
  45. def _create_and_attach_subnet(
  46. self, tenant_id, subnet_cidr, network_id, router_id):
  47. subnet = self.safe_client.create_subnet(
  48. tenant_id, network_id, subnet_cidr)
  49. router_interface_info = self.safe_client.add_router_interface(
  50. router_id, subnet['id'])
  51. self.block_until_port_status_active(
  52. router_interface_info['port_id'])
  53. def _boot_fake_vm_in_network(self, host, tenant_id, network_id, wait=True):
  54. vm = self.useFixture(
  55. machine.FakeFullstackMachine(
  56. host, network_id, tenant_id, self.safe_client, use_dhcp=True))
  57. if wait:
  58. vm.block_until_boot()
  59. return vm
  60. def _create_net_subnet_and_vm(self, tenant_id, subnet_cidrs, host, router):
  61. network = self.safe_client.create_network(tenant_id)
  62. for cidr in subnet_cidrs:
  63. self._create_and_attach_subnet(
  64. tenant_id, cidr, network['id'], router['id'])
  65. return self._boot_fake_vm_in_network(host, tenant_id, network['id'])
  66. class TestLegacyL3Agent(TestL3Agent):
  67. def setUp(self):
  68. host_descriptions = [
  69. environment.HostDescription(l3_agent=True, dhcp_agent=True),
  70. environment.HostDescription()]
  71. env = environment.Environment(
  72. environment.EnvironmentDescription(
  73. network_type='vlan', l2_pop=False),
  74. host_descriptions)
  75. super(TestLegacyL3Agent, self).setUp(env)
  76. def _get_namespace(self, router_id):
  77. return namespaces.build_ns_name(namespaces.NS_PREFIX, router_id)
  78. def _assert_namespace_exists(self, ns_name):
  79. common_utils.wait_until_true(
  80. lambda: ip_lib.network_namespace_exists(ns_name))
  81. def test_namespace_exists(self):
  82. tenant_id = uuidutils.generate_uuid()
  83. router = self.safe_client.create_router(tenant_id)
  84. network = self.safe_client.create_network(tenant_id)
  85. subnet = self.safe_client.create_subnet(
  86. tenant_id, network['id'], '20.0.0.0/24', gateway_ip='20.0.0.1')
  87. self.safe_client.add_router_interface(router['id'], subnet['id'])
  88. namespace = "%s@%s" % (
  89. self._get_namespace(router['id']),
  90. self.environment.hosts[0].l3_agent.get_namespace_suffix(), )
  91. self._assert_namespace_exists(namespace)
  92. def test_mtu_update(self):
  93. tenant_id = uuidutils.generate_uuid()
  94. router = self.safe_client.create_router(tenant_id)
  95. network = self.safe_client.create_network(tenant_id)
  96. subnet = self.safe_client.create_subnet(
  97. tenant_id, network['id'], '20.0.0.0/24', gateway_ip='20.0.0.1')
  98. self.safe_client.add_router_interface(router['id'], subnet['id'])
  99. namespace = "%s@%s" % (
  100. self._get_namespace(router['id']),
  101. self.environment.hosts[0].l3_agent.get_namespace_suffix(), )
  102. self._assert_namespace_exists(namespace)
  103. ip = ip_lib.IPWrapper(namespace)
  104. common_utils.wait_until_true(lambda: ip.get_devices())
  105. devices = ip.get_devices()
  106. self.assertEqual(1, len(devices))
  107. ri_dev = devices[0]
  108. mtu = ri_dev.link.mtu
  109. self.assertEqual(1500, mtu)
  110. mtu -= 1
  111. network = self.safe_client.update_network(network['id'], mtu=mtu)
  112. common_utils.wait_until_true(lambda: ri_dev.link.mtu == mtu)
  113. def test_east_west_traffic(self):
  114. tenant_id = uuidutils.generate_uuid()
  115. router = self.safe_client.create_router(tenant_id)
  116. vm1 = self._create_net_subnet_and_vm(
  117. tenant_id, ['20.0.0.0/24', '2001:db8:aaaa::/64'],
  118. self.environment.hosts[0], router)
  119. vm2 = self._create_net_subnet_and_vm(
  120. tenant_id, ['21.0.0.0/24', '2001:db8:bbbb::/64'],
  121. self.environment.hosts[1], router)
  122. vm1.block_until_ping(vm2.ip)
  123. # Verify ping6 from vm2 to vm1 IPv6 Address
  124. vm2.block_until_ping(vm1.ipv6)
  125. def test_north_south_traffic(self):
  126. # This function creates an external network which is connected to
  127. # central_external_bridge and spawns an external_vm on it.
  128. # The external_vm is configured with the gateway_ip (both v4 & v6
  129. # addresses) of external subnet. Later, it creates a tenant router,
  130. # a tenant network and two tenant subnets (v4 and v6). The tenant
  131. # router is associated with tenant network and external network to
  132. # provide north-south connectivity to the VMs.
  133. # We validate the following in this testcase.
  134. # 1. SNAT support: using ping from tenant VM to external_vm
  135. # 2. Floating IP support: using ping from external_vm to VM floating ip
  136. # 3. IPv6 ext connectivity: using ping6 from tenant vm to external_vm.
  137. tenant_id = uuidutils.generate_uuid()
  138. ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id)
  139. external_vm = self.useFixture(
  140. machine_fixtures.FakeMachine(
  141. self.environment.central_external_bridge,
  142. common_utils.ip_to_cidr(ext_sub['gateway_ip'], 24)))
  143. # Create an IPv6 subnet in the external network
  144. v6network = self.useFixture(
  145. ip_network.ExclusiveIPNetwork(
  146. "2001:db8:1234::1", "2001:db8:1234::10", "64")).network
  147. ext_v6sub = self.safe_client.create_subnet(
  148. tenant_id, ext_net['id'], v6network)
  149. router = self.safe_client.create_router(tenant_id,
  150. external_network=ext_net['id'])
  151. # Configure the gateway_ip of external v6subnet on the external_vm.
  152. external_vm.ipv6_cidr = common_utils.ip_to_cidr(
  153. ext_v6sub['gateway_ip'], 64)
  154. # Configure an IPv6 downstream route to the v6Address of router gw port
  155. for fixed_ip in router['external_gateway_info']['external_fixed_ips']:
  156. if netaddr.IPNetwork(fixed_ip['ip_address']).version == 6:
  157. external_vm.set_default_gateway(fixed_ip['ip_address'])
  158. vm = self._create_net_subnet_and_vm(
  159. tenant_id, ['20.0.0.0/24', '2001:db8:aaaa::/64'],
  160. self.environment.hosts[1], router)
  161. # ping external vm to test snat
  162. vm.block_until_ping(external_vm.ip)
  163. fip = self.safe_client.create_floatingip(
  164. tenant_id, ext_net['id'], vm.ip, vm.neutron_port['id'])
  165. # ping floating ip from external vm
  166. external_vm.block_until_ping(fip['floating_ip_address'])
  167. # Verify VM is able to reach the router interface.
  168. vm.block_until_ping(vm.gateway_ipv6)
  169. # Verify north-south connectivity using ping6 to external_vm.
  170. vm.block_until_ping(external_vm.ipv6)
  171. # Now let's remove and create again phys bridge and check connectivity
  172. # once again
  173. br_phys = self.environment.hosts[0].br_phys
  174. br_phys.destroy()
  175. br_phys.create()
  176. self.environment.hosts[0].connect_to_internal_network_via_vlans(
  177. br_phys)
  178. # ping floating ip from external vm
  179. external_vm.block_until_ping(fip['floating_ip_address'])
  180. # Verify VM is able to reach the router interface.
  181. vm.block_until_ping(vm.gateway_ipv6)
  182. # Verify north-south connectivity using ping6 to external_vm.
  183. vm.block_until_ping(external_vm.ipv6)
  184. class TestHAL3Agent(TestL3Agent):
  185. def setUp(self):
  186. host_descriptions = [
  187. environment.HostDescription(l3_agent=True, dhcp_agent=True)
  188. for _ in range(2)]
  189. env = environment.Environment(
  190. environment.EnvironmentDescription(
  191. network_type='vxlan', l2_pop=True),
  192. host_descriptions)
  193. super(TestHAL3Agent, self).setUp(env)
  194. def _is_ha_router_active_on_one_agent(self, router_id):
  195. agents = self.client.list_l3_agent_hosting_routers(router_id)
  196. return (
  197. agents['agents'][0]['ha_state'] != agents['agents'][1]['ha_state'])
  198. def test_ha_router(self):
  199. # TODO(amuller): Test external connectivity before and after a
  200. # failover, see: https://review.openstack.org/#/c/196393/
  201. tenant_id = uuidutils.generate_uuid()
  202. router = self.safe_client.create_router(tenant_id, ha=True)
  203. common_utils.wait_until_true(
  204. lambda:
  205. len(self.client.list_l3_agent_hosting_routers(
  206. router['id'])['agents']) == 2,
  207. timeout=90)
  208. common_utils.wait_until_true(
  209. functools.partial(
  210. self._is_ha_router_active_on_one_agent,
  211. router['id']),
  212. timeout=90)
  213. def _get_keepalived_state(self, keepalived_state_file):
  214. with open(keepalived_state_file, "r") as fd:
  215. return fd.read()
  216. def _get_state_file_for_master_agent(self, router_id):
  217. for host in self.environment.hosts:
  218. keepalived_state_file = os.path.join(
  219. host.neutron_config.state_path, "ha_confs", router_id, "state")
  220. if self._get_keepalived_state(keepalived_state_file) == "master":
  221. return keepalived_state_file
  222. def test_keepalived_multiple_sighups_does_not_forfeit_mastership(self):
  223. """Setup a complete "Neutron stack" - both an internal and an external
  224. network+subnet, and a router connected to both.
  225. """
  226. tenant_id = uuidutils.generate_uuid()
  227. ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id)
  228. router = self.safe_client.create_router(tenant_id, ha=True,
  229. external_network=ext_net['id'])
  230. common_utils.wait_until_true(
  231. lambda:
  232. len(self.client.list_l3_agent_hosting_routers(
  233. router['id'])['agents']) == 2,
  234. timeout=90)
  235. common_utils.wait_until_true(
  236. functools.partial(
  237. self._is_ha_router_active_on_one_agent,
  238. router['id']),
  239. timeout=90)
  240. keepalived_state_file = self._get_state_file_for_master_agent(
  241. router['id'])
  242. self.assertIsNotNone(keepalived_state_file)
  243. network = self.safe_client.create_network(tenant_id)
  244. self._create_and_attach_subnet(
  245. tenant_id, '13.37.0.0/24', network['id'], router['id'])
  246. # Create 10 fake VMs, each with a floating ip. Each floating ip
  247. # association should send a SIGHUP to the keepalived's parent process,
  248. # unless the Throttler works.
  249. host = self.environment.hosts[0]
  250. vms = [self._boot_fake_vm_in_network(host, tenant_id, network['id'],
  251. wait=False)
  252. for i in range(10)]
  253. for vm in vms:
  254. self.safe_client.create_floatingip(
  255. tenant_id, ext_net['id'], vm.ip, vm.neutron_port['id'])
  256. # Check that the keepalived's state file has not changed and is still
  257. # master. This will indicate that the Throttler works. We want to check
  258. # for ha_vrrp_advert_int (the default is 2 seconds), plus a bit more.
  259. time_to_stop = (time.time() +
  260. (common_utils.DEFAULT_THROTTLER_VALUE *
  261. ha_router.THROTTLER_MULTIPLIER * 1.3))
  262. while True:
  263. if time.time() > time_to_stop:
  264. break
  265. self.assertEqual(
  266. "master",
  267. self._get_keepalived_state(keepalived_state_file))
  268. @tests_base.unstable_test("bug 1776459")
  269. def test_ha_router_restart_agents_no_packet_lost(self):
  270. tenant_id = uuidutils.generate_uuid()
  271. ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id)
  272. router = self.safe_client.create_router(tenant_id, ha=True,
  273. external_network=ext_net['id'])
  274. external_vm = self.useFixture(
  275. machine_fixtures.FakeMachine(
  276. self.environment.central_external_bridge,
  277. common_utils.ip_to_cidr(ext_sub['gateway_ip'], 24)))
  278. common_utils.wait_until_true(
  279. lambda:
  280. len(self.client.list_l3_agent_hosting_routers(
  281. router['id'])['agents']) == 2,
  282. timeout=90)
  283. common_utils.wait_until_true(
  284. functools.partial(
  285. self._is_ha_router_active_on_one_agent,
  286. router['id']),
  287. timeout=90)
  288. router_ip = router['external_gateway_info'][
  289. 'external_fixed_ips'][0]['ip_address']
  290. l3_agents = [host.agents['l3'] for host in self.environment.hosts]
  291. self._assert_ping_during_agents_restart(
  292. l3_agents, external_vm.namespace, [router_ip], count=60)