OpenStack Dashboard (Horizon)
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.

tests.py 41KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. # Copyright 2012, Nachi Ueno, NTT MCL, 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 copy
  15. import django
  16. from django.core.urlresolvers import reverse
  17. from django import http
  18. from mox3.mox import IgnoreArg
  19. from mox3.mox import IsA
  20. import six
  21. from openstack_dashboard import api
  22. from openstack_dashboard.test import helpers as test
  23. from openstack_dashboard.usage import quotas
  24. INDEX_TEMPLATE = 'horizon/common/_data_table_view.html'
  25. class RouterMixin(object):
  26. @test.create_stubs({
  27. api.neutron: ('router_get', 'port_list',
  28. 'network_get', 'is_extension_supported',
  29. 'list_l3_agent_hosting_router'),
  30. })
  31. def _get_detail(self, router, extraroute=True, lookup_l3=False,
  32. support_l3_agent=True):
  33. api.neutron.is_extension_supported(IsA(http.HttpRequest), 'extraroute')\
  34. .MultipleTimes().AndReturn(extraroute)
  35. if lookup_l3:
  36. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  37. 'l3_agent_scheduler')\
  38. .AndReturn(support_l3_agent)
  39. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  40. .AndReturn(router)
  41. api.neutron.port_list(IsA(http.HttpRequest),
  42. device_id=router.id)\
  43. .AndReturn([self.ports.first()])
  44. self._mock_external_network_get(router)
  45. if lookup_l3 and support_l3_agent:
  46. agent = self.agents.list()[1]
  47. api.neutron.list_l3_agent_hosting_router(IsA(http.HttpRequest), router.id)\
  48. .AndReturn([agent])
  49. self.mox.ReplayAll()
  50. res = self.client.get(reverse('horizon:%s'
  51. ':routers:detail' % self.DASHBOARD,
  52. args=[router.id]))
  53. return res
  54. def _mock_external_network_list(self, alter_ids=False):
  55. search_opts = {'router:external': True}
  56. ext_nets = [n for n in self.networks.list() if n['router:external']]
  57. if alter_ids:
  58. for ext_net in ext_nets:
  59. ext_net.id += 'some extra garbage'
  60. api.neutron.network_list(
  61. IsA(http.HttpRequest),
  62. **search_opts).AndReturn(ext_nets)
  63. def _mock_external_network_get(self, router):
  64. ext_net_id = router.external_gateway_info['network_id']
  65. ext_net = self.networks.list()[2]
  66. api.neutron.network_get(IsA(http.HttpRequest), ext_net_id,
  67. expand_subnet=False).AndReturn(ext_net)
  68. def _mock_network_list(self, tenant_id):
  69. api.neutron.network_list(
  70. IsA(http.HttpRequest),
  71. shared=False,
  72. tenant_id=tenant_id).AndReturn(self.networks.list())
  73. api.neutron.network_list(
  74. IsA(http.HttpRequest),
  75. shared=True).AndReturn([])
  76. class RouterTests(RouterMixin, test.TestCase):
  77. DASHBOARD = 'project'
  78. INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
  79. DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
  80. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  81. quotas: ('tenant_quota_usages',)})
  82. def test_index(self):
  83. quota_data = self.neutron_quota_usages.first()
  84. api.neutron.router_list(
  85. IsA(http.HttpRequest),
  86. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  87. quotas.tenant_quota_usages(
  88. IsA(http.HttpRequest), targets=('routers', )) \
  89. .MultipleTimes().AndReturn(quota_data)
  90. self._mock_external_network_list()
  91. self.mox.ReplayAll()
  92. res = self.client.get(self.INDEX_URL)
  93. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  94. routers = res.context['table'].data
  95. self.assertItemsEqual(routers, self.routers.list())
  96. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  97. quotas: ('tenant_quota_usages',)})
  98. def test_index_router_list_exception(self):
  99. quota_data = self.neutron_quota_usages.first()
  100. api.neutron.router_list(
  101. IsA(http.HttpRequest),
  102. tenant_id=self.tenant.id).MultipleTimes().AndRaise(
  103. self.exceptions.neutron)
  104. quotas.tenant_quota_usages(
  105. IsA(http.HttpRequest), targets=('routers', )) \
  106. .MultipleTimes().AndReturn(quota_data)
  107. self._mock_external_network_list()
  108. self.mox.ReplayAll()
  109. res = self.client.get(self.INDEX_URL)
  110. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  111. self.assertEqual(len(res.context['table'].data), 0)
  112. self.assertMessageCount(res, error=1)
  113. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  114. quotas: ('tenant_quota_usages',)})
  115. def test_set_external_network_empty(self):
  116. router = self.routers.first()
  117. quota_data = self.neutron_quota_usages.first()
  118. api.neutron.router_list(
  119. IsA(http.HttpRequest),
  120. tenant_id=self.tenant.id).MultipleTimes().AndReturn([router])
  121. quotas.tenant_quota_usages(
  122. IsA(http.HttpRequest), targets=('routers', )) \
  123. .MultipleTimes().AndReturn(quota_data)
  124. self._mock_external_network_list(alter_ids=True)
  125. self.mox.ReplayAll()
  126. res = self.client.get(self.INDEX_URL)
  127. table_data = res.context['table'].data
  128. self.assertEqual(len(table_data), 1)
  129. self.assertIn('(Not Found)',
  130. table_data[0]['external_gateway_info']['network'])
  131. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  132. self.assertMessageCount(res, error=1)
  133. def test_router_detail(self):
  134. router = self.routers.first()
  135. res = self._get_detail(router)
  136. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  137. ports = res.context['interfaces_table'].data
  138. self.assertItemsEqual(ports, [self.ports.first()])
  139. @test.create_stubs({api.neutron: ('router_get',)})
  140. def test_router_detail_exception(self):
  141. router = self.routers.first()
  142. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  143. .AndRaise(self.exceptions.neutron)
  144. self.mox.ReplayAll()
  145. res = self.client.get(reverse('horizon:%s'
  146. ':routers:detail' % self.DASHBOARD,
  147. args=[router.id]))
  148. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  149. @test.create_stubs({api.neutron: ('router_list', 'network_list',
  150. 'port_list', 'router_delete',),
  151. quotas: ('tenant_quota_usages',)})
  152. def test_router_delete(self):
  153. router = self.routers.first()
  154. quota_data = self.neutron_quota_usages.first()
  155. api.neutron.router_list(
  156. IsA(http.HttpRequest),
  157. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  158. quotas.tenant_quota_usages(
  159. IsA(http.HttpRequest), targets=('routers', )) \
  160. .MultipleTimes().AndReturn(quota_data)
  161. self._mock_external_network_list()
  162. api.neutron.router_list(
  163. IsA(http.HttpRequest),
  164. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  165. self._mock_external_network_list()
  166. api.neutron.router_list(
  167. IsA(http.HttpRequest),
  168. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  169. self._mock_external_network_list()
  170. api.neutron.port_list(IsA(http.HttpRequest),
  171. device_id=router.id, device_owner=IgnoreArg())\
  172. .AndReturn([])
  173. api.neutron.router_delete(IsA(http.HttpRequest), router.id)
  174. self.mox.ReplayAll()
  175. res = self.client.get(self.INDEX_URL)
  176. formData = {'action': 'routers__delete__' + router.id}
  177. res = self.client.post(self.INDEX_URL, formData, follow=True)
  178. self.assertNoFormErrors(res)
  179. self.assertMessageCount(response=res, success=1)
  180. self.assertIn('Deleted Router: ' + router.name,
  181. res.content.decode('utf-8'))
  182. @test.create_stubs({api.neutron: ('router_list', 'network_list',
  183. 'port_list', 'router_remove_interface',
  184. 'router_delete',),
  185. quotas: ('tenant_quota_usages',)})
  186. def test_router_with_interface_delete(self):
  187. router = self.routers.first()
  188. ports = self.ports.list()
  189. quota_data = self.neutron_quota_usages.first()
  190. api.neutron.router_list(
  191. IsA(http.HttpRequest),
  192. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  193. quotas.tenant_quota_usages(
  194. IsA(http.HttpRequest), targets=('routers', )) \
  195. .MultipleTimes().AndReturn(quota_data)
  196. self._mock_external_network_list()
  197. api.neutron.router_list(
  198. IsA(http.HttpRequest),
  199. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  200. self._mock_external_network_list()
  201. api.neutron.router_list(
  202. IsA(http.HttpRequest),
  203. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  204. self._mock_external_network_list()
  205. api.neutron.port_list(IsA(http.HttpRequest),
  206. device_id=router.id, device_owner=IgnoreArg())\
  207. .AndReturn(ports)
  208. for port in ports:
  209. api.neutron.router_remove_interface(IsA(http.HttpRequest),
  210. router.id, port_id=port.id)
  211. api.neutron.router_delete(IsA(http.HttpRequest), router.id)
  212. self.mox.ReplayAll()
  213. res = self.client.get(self.INDEX_URL)
  214. formData = {'action': 'routers__delete__' + router.id}
  215. res = self.client.post(self.INDEX_URL, formData, follow=True)
  216. self.assertNoFormErrors(res)
  217. self.assertMessageCount(response=res, success=1)
  218. self.assertIn('Deleted Router: ' + router.name,
  219. res.content.decode('utf-8'))
  220. class RouterActionTests(RouterMixin, test.TestCase):
  221. DASHBOARD = 'project'
  222. INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
  223. DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
  224. @test.create_stubs({api.neutron: ('router_create',
  225. 'get_feature_permission',
  226. 'network_list',
  227. 'is_extension_supported')})
  228. def test_router_create_post(self):
  229. router = self.routers.first()
  230. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  231. "dvr", "create")\
  232. .AndReturn(False)
  233. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  234. "l3-ha", "create")\
  235. .AndReturn(False)
  236. api.neutron.network_list(IsA(http.HttpRequest))\
  237. .AndReturn(self.networks.list())
  238. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  239. "router_availability_zone")\
  240. .AndReturn(False)
  241. params = {'name': router.name,
  242. 'admin_state_up': router.admin_state_up}
  243. api.neutron.router_create(IsA(http.HttpRequest), **params)\
  244. .AndReturn(router)
  245. self.mox.ReplayAll()
  246. form_data = {'name': router.name,
  247. 'admin_state_up': router.admin_state_up}
  248. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  249. res = self.client.post(url, form_data)
  250. self.assertNoFormErrors(res)
  251. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  252. @test.create_stubs({api.neutron: ('router_create',
  253. 'get_feature_permission',
  254. 'network_list',
  255. 'is_extension_supported')})
  256. def test_router_create_post_mode_server_default(self):
  257. router = self.routers.first()
  258. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  259. "dvr", "create")\
  260. .AndReturn(True)
  261. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  262. "l3-ha", "create")\
  263. .AndReturn(True)
  264. api.neutron.network_list(IsA(http.HttpRequest))\
  265. .AndReturn(self.networks.list())
  266. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  267. "router_availability_zone")\
  268. .AndReturn(False)
  269. params = {'name': router.name,
  270. 'admin_state_up': router.admin_state_up}
  271. api.neutron.router_create(IsA(http.HttpRequest), **params)\
  272. .AndReturn(router)
  273. self.mox.ReplayAll()
  274. form_data = {'name': router.name,
  275. 'mode': 'server_default',
  276. 'ha': 'server_default',
  277. 'admin_state_up': router.admin_state_up}
  278. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  279. res = self.client.post(url, form_data)
  280. self.assertNoFormErrors(res)
  281. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  282. @test.create_stubs({api.neutron: ('router_create',
  283. 'get_feature_permission',
  284. 'network_list',
  285. 'is_extension_supported')})
  286. def test_dvr_ha_router_create_post(self):
  287. router = self.routers.first()
  288. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  289. "dvr", "create")\
  290. .MultipleTimes().AndReturn(True)
  291. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  292. "l3-ha", "create")\
  293. .MultipleTimes().AndReturn(True)
  294. api.neutron.network_list(IsA(http.HttpRequest))\
  295. .AndReturn(self.networks.list())
  296. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  297. "router_availability_zone")\
  298. .AndReturn(False)
  299. param = {'name': router.name,
  300. 'distributed': True,
  301. 'ha': True,
  302. 'admin_state_up': router.admin_state_up}
  303. api.neutron.router_create(IsA(http.HttpRequest), **param)\
  304. .AndReturn(router)
  305. self.mox.ReplayAll()
  306. form_data = {'name': router.name,
  307. 'mode': 'distributed',
  308. 'ha': 'enabled',
  309. 'admin_state_up': router.admin_state_up}
  310. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  311. res = self.client.post(url, form_data)
  312. self.assertNoFormErrors(res)
  313. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  314. @test.create_stubs({api.neutron: ('router_create',
  315. 'get_feature_permission',
  316. 'network_list',
  317. 'is_extension_supported',
  318. 'list_availability_zones')})
  319. def test_az_router_create_post(self):
  320. router = self.routers.first()
  321. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  322. "dvr", "create")\
  323. .MultipleTimes().AndReturn(False)
  324. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  325. "l3-ha", "create")\
  326. .AndReturn(False)
  327. api.neutron.network_list(IsA(http.HttpRequest))\
  328. .AndReturn(self.networks.list())
  329. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  330. "router_availability_zone")\
  331. .AndReturn(True)
  332. api.neutron.list_availability_zones(IsA(http.HttpRequest),
  333. "router", "available")\
  334. .AndReturn(self.neutron_availability_zones.list())
  335. param = {'name': router.name,
  336. 'availability_zone_hints': ['nova'],
  337. 'admin_state_up': router.admin_state_up}
  338. api.neutron.router_create(IsA(http.HttpRequest), **param)\
  339. .AndReturn(router)
  340. self.mox.ReplayAll()
  341. form_data = {'name': router.name,
  342. 'mode': 'server_default',
  343. 'ha': 'server_default',
  344. 'az_hints': 'nova',
  345. 'admin_state_up': router.admin_state_up}
  346. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  347. res = self.client.post(url, form_data)
  348. self.assertNoFormErrors(res)
  349. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  350. @test.create_stubs({api.neutron: ('router_create',
  351. 'get_feature_permission',
  352. 'network_list',
  353. 'is_extension_supported')})
  354. def test_router_create_post_exception_error_case_409(self):
  355. router = self.routers.first()
  356. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  357. "dvr", "create")\
  358. .MultipleTimes().AndReturn(False)
  359. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  360. "l3-ha", "create")\
  361. .AndReturn(False)
  362. self.exceptions.neutron.status_code = 409
  363. api.neutron.network_list(IsA(http.HttpRequest))\
  364. .MultipleTimes().AndReturn(self.networks.list())
  365. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  366. "router_availability_zone")\
  367. .AndReturn(False)
  368. params = {'name': router.name,
  369. 'admin_state_up': router.admin_state_up}
  370. api.neutron.router_create(IsA(http.HttpRequest), **params)\
  371. .AndRaise(self.exceptions.neutron)
  372. self.mox.ReplayAll()
  373. form_data = {'name': router.name,
  374. 'admin_state_up': router.admin_state_up}
  375. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  376. res = self.client.post(url, form_data)
  377. self.assertNoFormErrors(res)
  378. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  379. @test.create_stubs({api.neutron: ('router_create',
  380. 'get_feature_permission',
  381. 'is_extension_supported',
  382. 'network_list')})
  383. def test_router_create_post_exception_error_case_non_409(self):
  384. router = self.routers.first()
  385. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  386. "dvr", "create")\
  387. .MultipleTimes().AndReturn(False)
  388. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  389. "l3-ha", "create")\
  390. .MultipleTimes().AndReturn(False)
  391. self.exceptions.neutron.status_code = 999
  392. api.neutron.network_list(IsA(http.HttpRequest))\
  393. .MultipleTimes().AndReturn(self.networks.list())
  394. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  395. "router_availability_zone")\
  396. .MultipleTimes().AndReturn(False)
  397. params = {'name': router.name,
  398. 'admin_state_up': router.admin_state_up}
  399. api.neutron.router_create(IsA(http.HttpRequest), **params)\
  400. .AndRaise(self.exceptions.neutron)
  401. self.mox.ReplayAll()
  402. form_data = {'name': router.name,
  403. 'admin_state_up': router.admin_state_up}
  404. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  405. res = self.client.post(url, form_data)
  406. self.assertNoFormErrors(res)
  407. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  408. @test.create_stubs({api.neutron: ('router_get',
  409. 'get_feature_permission')})
  410. def _test_router_update_get(self, dvr_enabled=False,
  411. current_dvr=False,
  412. ha_enabled=False):
  413. router = [r for r in self.routers.list()
  414. if r.distributed == current_dvr][0]
  415. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  416. .AndReturn(router)
  417. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  418. "dvr", "update")\
  419. .AndReturn(dvr_enabled)
  420. # TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
  421. # PUT operation. It will be fixed in Kilo cycle.
  422. # api.neutron.get_feature_permission(IsA(http.HttpRequest),
  423. # "l3-ha", "update")\
  424. # .AndReturn(ha_enabled)
  425. self.mox.ReplayAll()
  426. url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
  427. args=[router.id])
  428. return self.client.get(url)
  429. def test_router_update_get_dvr_disabled(self):
  430. res = self._test_router_update_get(dvr_enabled=False)
  431. self.assertTemplateUsed(res, 'project/routers/update.html')
  432. self.assertNotContains(res, 'Router Type')
  433. self.assertNotContains(res, 'id="id_mode"')
  434. def test_router_update_get_dvr_enabled_mode_centralized(self):
  435. res = self._test_router_update_get(dvr_enabled=True, current_dvr=False)
  436. self.assertTemplateUsed(res, 'project/routers/update.html')
  437. self.assertContains(res, 'Router Type')
  438. # Check both menu are displayed.
  439. self.assertContains(
  440. res,
  441. '<option value="centralized" selected="selected">'
  442. 'Centralized</option>',
  443. html=True)
  444. self.assertContains(
  445. res,
  446. '<option value="distributed">Distributed</option>',
  447. html=True)
  448. def test_router_update_get_dvr_enabled_mode_distributed(self):
  449. res = self._test_router_update_get(dvr_enabled=True, current_dvr=True)
  450. self.assertTemplateUsed(res, 'project/routers/update.html')
  451. self.assertContains(res, 'Router Type')
  452. if django.VERSION >= (1, 10):
  453. pattern = ('<input class="form-control" id="id_mode" name="mode" '
  454. 'readonly="readonly" type="text" value="distributed" '
  455. 'required/>')
  456. else:
  457. pattern = ('<input class="form-control" id="id_mode" name="mode" '
  458. 'readonly="readonly" type="text" '
  459. 'value="distributed" />')
  460. self.assertContains(res, pattern, html=True)
  461. self.assertNotContains(res, 'centralized')
  462. @test.create_stubs({api.neutron: ('router_get',
  463. 'router_update',
  464. 'get_feature_permission')})
  465. def test_router_update_post_dvr_ha_disabled(self):
  466. router = self.routers.first()
  467. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  468. "dvr", "update")\
  469. .AndReturn(False)
  470. # TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
  471. # PUT operation. It will be fixed in Kilo cycle.
  472. # api.neutron.get_feature_permission(IsA(http.HttpRequest),
  473. # "l3-ha", "update")\
  474. # .AndReturn(False)
  475. api.neutron.router_update(IsA(http.HttpRequest), router.id,
  476. name=router.name,
  477. admin_state_up=router.admin_state_up)\
  478. .AndReturn(router)
  479. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  480. .AndReturn(router)
  481. self.mox.ReplayAll()
  482. form_data = {'router_id': router.id,
  483. 'name': router.name,
  484. 'admin_state': router.admin_state_up}
  485. url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
  486. args=[router.id])
  487. res = self.client.post(url, form_data)
  488. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  489. @test.create_stubs({api.neutron: ('router_get',
  490. 'router_update',
  491. 'get_feature_permission')})
  492. def test_router_update_post_dvr_ha_enabled(self):
  493. router = self.routers.first()
  494. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  495. "dvr", "update")\
  496. .AndReturn(True)
  497. # TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
  498. # PUT operation. It will be fixed in Kilo cycle.
  499. # api.neutron.get_feature_permission(IsA(http.HttpRequest),
  500. # "l3-ha", "update")\
  501. # .AndReturn(True)
  502. api.neutron.router_update(IsA(http.HttpRequest), router.id,
  503. name=router.name,
  504. admin_state_up=router.admin_state_up,
  505. # ha=True,
  506. distributed=True).AndReturn(router)
  507. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  508. .AndReturn(router)
  509. self.mox.ReplayAll()
  510. form_data = {'router_id': router.id,
  511. 'name': router.name,
  512. 'admin_state': router.admin_state_up,
  513. 'mode': 'distributed',
  514. 'ha': True}
  515. url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
  516. args=[router.id])
  517. res = self.client.post(url, form_data)
  518. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  519. def _test_router_addinterface(self, raise_error=False):
  520. router = self.routers.first()
  521. subnet = self.subnets.first()
  522. port = self.ports.first()
  523. add_interface = api.neutron.router_add_interface(
  524. IsA(http.HttpRequest), router.id, subnet_id=subnet.id)
  525. if raise_error:
  526. add_interface.AndRaise(self.exceptions.neutron)
  527. else:
  528. add_interface.AndReturn({'subnet_id': subnet.id,
  529. 'port_id': port.id})
  530. api.neutron.port_get(IsA(http.HttpRequest), port.id)\
  531. .AndReturn(port)
  532. self._check_router_addinterface(router, subnet)
  533. def _check_router_addinterface(self, router, subnet, ip_address=''):
  534. # mock APIs used to show router detail
  535. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  536. .AndReturn(router)
  537. api.neutron.port_list(IsA(http.HttpRequest), device_id=router.id)\
  538. .AndReturn([])
  539. self._mock_network_list(router['tenant_id'])
  540. self.mox.ReplayAll()
  541. form_data = {'router_id': router.id,
  542. 'router_name': router.name,
  543. 'subnet_id': subnet.id,
  544. 'ip_address': ip_address}
  545. url = reverse('horizon:%s:routers:addinterface' % self.DASHBOARD,
  546. args=[router.id])
  547. res = self.client.post(url, form_data)
  548. self.assertNoFormErrors(res)
  549. detail_url = reverse(self.DETAIL_PATH, args=[router.id])
  550. self.assertRedirectsNoFollow(res, detail_url)
  551. @test.create_stubs({api.neutron: ('router_get',
  552. 'router_add_interface',
  553. 'port_get',
  554. 'network_list',
  555. 'port_list')})
  556. def test_router_addinterface(self):
  557. self._test_router_addinterface()
  558. @test.create_stubs({api.neutron: ('router_get',
  559. 'router_add_interface',
  560. 'network_list',
  561. 'port_list')})
  562. def test_router_addinterface_exception(self):
  563. self._test_router_addinterface(raise_error=True)
  564. def _test_router_addinterface_ip_addr(self, errors=None):
  565. errors = errors or []
  566. router = self.routers.first()
  567. subnet = self.subnets.first()
  568. port = self.ports.first()
  569. ip_addr = port['fixed_ips'][0]['ip_address']
  570. self._setup_mock_addinterface_ip_addr(router, subnet, port,
  571. ip_addr, errors)
  572. self._check_router_addinterface(router, subnet, ip_addr)
  573. def _setup_mock_addinterface_ip_addr(self, router, subnet, port,
  574. ip_addr, errors=None):
  575. errors = errors or []
  576. subnet_get = api.neutron.subnet_get(IsA(http.HttpRequest), subnet.id)
  577. if 'subnet_get' in errors:
  578. subnet_get.AndRaise(self.exceptions.neutron)
  579. return
  580. subnet_get.AndReturn(subnet)
  581. params = {'network_id': subnet.network_id,
  582. 'fixed_ips': [{'subnet_id': subnet.id,
  583. 'ip_address': ip_addr}]}
  584. port_create = api.neutron.port_create(IsA(http.HttpRequest), **params)
  585. if 'port_create' in errors:
  586. port_create.AndRaise(self.exceptions.neutron)
  587. return
  588. port_create.AndReturn(port)
  589. add_inf = api.neutron.router_add_interface(
  590. IsA(http.HttpRequest), router.id, port_id=port.id)
  591. if 'add_interface' not in errors:
  592. return
  593. add_inf.AndRaise(self.exceptions.neutron)
  594. port_delete = api.neutron.port_delete(IsA(http.HttpRequest), port.id)
  595. if 'port_delete' in errors:
  596. port_delete.AndRaise(self.exceptions.neutron)
  597. @test.create_stubs({api.neutron: ('router_add_interface', 'subnet_get',
  598. 'port_create',
  599. 'router_get', 'network_list',
  600. 'port_list')})
  601. def test_router_addinterface_ip_addr(self):
  602. self._test_router_addinterface_ip_addr()
  603. @test.create_stubs({api.neutron: ('subnet_get', 'router_get',
  604. 'network_list', 'port_list')})
  605. def test_router_addinterface_ip_addr_exception_subnet_get(self):
  606. self._test_router_addinterface_ip_addr(errors=['subnet_get'])
  607. @test.create_stubs({api.neutron: ('subnet_get', 'port_create',
  608. 'router_get', 'network_list',
  609. 'port_list')})
  610. def test_router_addinterface_ip_addr_exception_port_create(self):
  611. self._test_router_addinterface_ip_addr(errors=['port_create'])
  612. @test.create_stubs({api.neutron: ('router_add_interface', 'subnet_get',
  613. 'port_create', 'port_delete',
  614. 'router_get', 'network_list',
  615. 'port_list')})
  616. def test_router_addinterface_ip_addr_exception_add_interface(self):
  617. self._test_router_addinterface_ip_addr(errors=['add_interface'])
  618. @test.create_stubs({api.neutron: ('router_add_interface', 'subnet_get',
  619. 'port_create', 'port_delete',
  620. 'router_get', 'network_list',
  621. 'port_list')})
  622. def test_router_addinterface_ip_addr_exception_port_delete(self):
  623. self._test_router_addinterface_ip_addr(errors=['add_interface',
  624. 'port_delete'])
  625. @test.create_stubs({api.neutron: ('router_get',
  626. 'router_add_gateway',
  627. 'network_list')})
  628. def test_router_add_gateway(self):
  629. router = self.routers.first()
  630. network = self.networks.first()
  631. api.neutron.router_add_gateway(
  632. IsA(http.HttpRequest),
  633. router.id,
  634. network.id).AndReturn(None)
  635. api.neutron.router_get(
  636. IsA(http.HttpRequest), router.id).AndReturn(router)
  637. search_opts = {'router:external': True}
  638. api.neutron.network_list(
  639. IsA(http.HttpRequest), **search_opts).AndReturn([network])
  640. self.mox.ReplayAll()
  641. form_data = {'router_id': router.id,
  642. 'router_name': router.name,
  643. 'network_id': network.id}
  644. url = reverse('horizon:%s:routers:setgateway' % self.DASHBOARD,
  645. args=[router.id])
  646. res = self.client.post(url, form_data)
  647. self.assertNoFormErrors(res)
  648. detail_url = self.INDEX_URL
  649. self.assertRedirectsNoFollow(res, detail_url)
  650. @test.create_stubs({api.neutron: ('router_get',
  651. 'router_add_gateway',
  652. 'network_list')})
  653. def test_router_add_gateway_exception(self):
  654. router = self.routers.first()
  655. network = self.networks.first()
  656. api.neutron.router_add_gateway(
  657. IsA(http.HttpRequest),
  658. router.id,
  659. network.id).AndRaise(self.exceptions.neutron)
  660. api.neutron.router_get(
  661. IsA(http.HttpRequest), router.id).AndReturn(router)
  662. search_opts = {'router:external': True}
  663. api.neutron.network_list(
  664. IsA(http.HttpRequest), **search_opts).AndReturn([network])
  665. self.mox.ReplayAll()
  666. form_data = {'router_id': router.id,
  667. 'router_name': router.name,
  668. 'network_id': network.id}
  669. url = reverse('horizon:%s:routers:setgateway' % self.DASHBOARD,
  670. args=[router.id])
  671. res = self.client.post(url, form_data)
  672. self.assertNoFormErrors(res)
  673. detail_url = self.INDEX_URL
  674. self.assertRedirectsNoFollow(res, detail_url)
  675. class RouterRouteTests(RouterMixin, test.TestCase):
  676. DASHBOARD = 'project'
  677. INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
  678. DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
  679. def test_extension_hides_without_routes(self):
  680. router = self.routers_with_routes.first()
  681. res = self._get_detail(router, extraroute=False)
  682. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  683. self.assertNotIn('extra_routes_table', res.context)
  684. def test_routerroute_detail(self):
  685. router = self.routers_with_routes.first()
  686. res = self._get_detail(router, extraroute=True)
  687. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  688. routes = res.context['extra_routes_table'].data
  689. routes_dict = [r._apidict for r in routes]
  690. self.assertItemsEqual(routes_dict, router['routes'])
  691. @test.create_stubs({api.neutron: ('router_get', 'router_update')})
  692. def _test_router_addrouterroute(self, raise_error=False):
  693. pre_router = self.routers_with_routes.first()
  694. post_router = copy.deepcopy(pre_router)
  695. route = {'nexthop': '10.0.0.5', 'destination': '40.0.1.0/24'}
  696. post_router['routes'].insert(0, route)
  697. api.neutron.router_get(IsA(http.HttpRequest), pre_router.id)\
  698. .MultipleTimes().AndReturn(pre_router)
  699. params = {}
  700. params['routes'] = post_router['routes']
  701. router_update = api.neutron.router_update(IsA(http.HttpRequest),
  702. pre_router.id, **params)
  703. if raise_error:
  704. router_update.AndRaise(self.exceptions.neutron)
  705. else:
  706. router_update.AndReturn({'router': post_router})
  707. self.mox.ReplayAll()
  708. form_data = copy.deepcopy(route)
  709. form_data['router_id'] = pre_router.id
  710. url = reverse('horizon:%s:routers:addrouterroute' % self.DASHBOARD,
  711. args=[pre_router.id])
  712. res = self.client.post(url, form_data)
  713. self.assertNoFormErrors(res)
  714. detail_url = reverse(self.DETAIL_PATH, args=[pre_router.id])
  715. self.assertRedirectsNoFollow(res, detail_url)
  716. def test_router_addrouterroute(self):
  717. if self.DASHBOARD == 'project':
  718. self._test_router_addrouterroute()
  719. self.assertMessageCount(success=1)
  720. def test_router_addrouterroute_exception(self):
  721. if self.DASHBOARD == 'project':
  722. self._test_router_addrouterroute(raise_error=True)
  723. self.assertMessageCount(error=1)
  724. @test.create_stubs({api.neutron: ('router_get', 'router_update',
  725. 'network_get', 'port_list',
  726. 'is_extension_supported')})
  727. def test_router_removeroute(self):
  728. if self.DASHBOARD == 'admin':
  729. return
  730. pre_router = self.routers_with_routes.first()
  731. post_router = copy.deepcopy(pre_router)
  732. route = post_router['routes'].pop()
  733. api.neutron.is_extension_supported(IsA(http.HttpRequest), 'extraroute')\
  734. .MultipleTimes().AndReturn(True)
  735. api.neutron.router_get(IsA(http.HttpRequest),
  736. pre_router.id).AndReturn(pre_router)
  737. params = {}
  738. params['routes'] = post_router['routes']
  739. api.neutron.router_get(IsA(http.HttpRequest),
  740. pre_router.id).AndReturn(pre_router)
  741. router_update = api.neutron.router_update(IsA(http.HttpRequest),
  742. pre_router.id, **params)
  743. router_update.AndReturn({'router': post_router})
  744. api.neutron.port_list(IsA(http.HttpRequest),
  745. device_id=pre_router.id)\
  746. .AndReturn([self.ports.first()])
  747. self._mock_external_network_get(pre_router)
  748. self.mox.ReplayAll()
  749. form_route_id = route['nexthop'] + ":" + route['destination']
  750. form_data = {'action': 'extra_routes__delete__%s' % form_route_id}
  751. url = reverse(self.DETAIL_PATH, args=[pre_router.id])
  752. res = self.client.post(url, form_data)
  753. self.assertNoFormErrors(res)
  754. class RouterViewTests(RouterMixin, test.TestCase):
  755. DASHBOARD = 'project'
  756. INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
  757. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  758. quotas: ('tenant_quota_usages',)})
  759. def test_create_button_disabled_when_quota_exceeded(self):
  760. quota_data = self.neutron_quota_usages.first()
  761. quota_data['routers']['available'] = 0
  762. api.neutron.router_list(
  763. IsA(http.HttpRequest),
  764. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  765. quotas.tenant_quota_usages(
  766. IsA(http.HttpRequest), targets=('routers', )) \
  767. .MultipleTimes().AndReturn(quota_data)
  768. self._mock_external_network_list()
  769. self.mox.ReplayAll()
  770. res = self.client.get(self.INDEX_URL)
  771. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  772. routers = res.context['routers_table'].data
  773. self.assertItemsEqual(routers, self.routers.list())
  774. create_action = self.getAndAssertTableAction(res, 'routers', 'create')
  775. self.assertIn('disabled', create_action.classes,
  776. 'Create button is not disabled')
  777. self.assertEqual('Create Router (Quota exceeded)',
  778. create_action.verbose_name)
  779. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  780. quotas: ('tenant_quota_usages',)})
  781. def test_create_button_shown_when_quota_disabled(self):
  782. quota_data = self.neutron_quota_usages.first()
  783. quota_data['routers'].pop('available')
  784. api.neutron.router_list(
  785. IsA(http.HttpRequest),
  786. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  787. quotas.tenant_quota_usages(
  788. IsA(http.HttpRequest), targets=('routers', )) \
  789. .MultipleTimes().AndReturn(quota_data)
  790. self._mock_external_network_list()
  791. self.mox.ReplayAll()
  792. res = self.client.get(self.INDEX_URL)
  793. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  794. routers = res.context['routers_table'].data
  795. self.assertItemsEqual(routers, self.routers.list())
  796. create_action = self.getAndAssertTableAction(res, 'routers', 'create')
  797. self.assertFalse('disabled' in create_action.classes,
  798. 'Create button should not be disabled')
  799. self.assertEqual('Create Router',
  800. create_action.verbose_name)
  801. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  802. quotas: ('tenant_quota_usages',)})
  803. def test_create_button_attributes(self):
  804. quota_data = self.neutron_quota_usages.first()
  805. quota_data['routers']['available'] = 10
  806. api.neutron.router_list(
  807. IsA(http.HttpRequest),
  808. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  809. quotas.tenant_quota_usages(
  810. IsA(http.HttpRequest), targets=('routers', )) \
  811. .MultipleTimes().AndReturn(quota_data)
  812. self._mock_external_network_list()
  813. self.mox.ReplayAll()
  814. res = self.client.get(self.INDEX_URL)
  815. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  816. routers = res.context['routers_table'].data
  817. self.assertItemsEqual(routers, self.routers.list())
  818. create_action = self.getAndAssertTableAction(res, 'routers', 'create')
  819. self.assertEqual(set(['ajax-modal']), set(create_action.classes))
  820. self.assertEqual('Create Router',
  821. six.text_type(create_action.verbose_name))
  822. self.assertEqual('horizon:project:routers:create', create_action.url)
  823. self.assertEqual((('network', 'create_router'),),
  824. create_action.policy_rules)