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 38KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  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. def test_router_create_post(self):
  228. router = self.routers.first()
  229. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  230. "dvr", "create")\
  231. .AndReturn(False)
  232. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  233. "l3-ha", "create")\
  234. .AndReturn(False)
  235. api.neutron.network_list(IsA(http.HttpRequest))\
  236. .AndReturn(self.networks.list())
  237. params = {'name': router.name,
  238. 'admin_state_up': router.admin_state_up}
  239. api.neutron.router_create(IsA(http.HttpRequest), **params)\
  240. .AndReturn(router)
  241. self.mox.ReplayAll()
  242. form_data = {'name': router.name,
  243. 'admin_state_up': router.admin_state_up}
  244. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  245. res = self.client.post(url, form_data)
  246. self.assertNoFormErrors(res)
  247. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  248. @test.create_stubs({api.neutron: ('router_create',
  249. 'get_feature_permission',
  250. 'network_list')})
  251. def test_router_create_post_mode_server_default(self):
  252. router = self.routers.first()
  253. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  254. "dvr", "create")\
  255. .AndReturn(True)
  256. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  257. "l3-ha", "create")\
  258. .AndReturn(True)
  259. api.neutron.network_list(IsA(http.HttpRequest))\
  260. .AndReturn(self.networks.list())
  261. params = {'name': router.name,
  262. 'admin_state_up': router.admin_state_up}
  263. api.neutron.router_create(IsA(http.HttpRequest), **params)\
  264. .AndReturn(router)
  265. self.mox.ReplayAll()
  266. form_data = {'name': router.name,
  267. 'mode': 'server_default',
  268. 'ha': 'server_default',
  269. 'admin_state_up': router.admin_state_up}
  270. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  271. res = self.client.post(url, form_data)
  272. self.assertNoFormErrors(res)
  273. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  274. @test.create_stubs({api.neutron: ('router_create',
  275. 'get_feature_permission',
  276. 'network_list')})
  277. def test_dvr_ha_router_create_post(self):
  278. router = self.routers.first()
  279. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  280. "dvr", "create")\
  281. .MultipleTimes().AndReturn(True)
  282. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  283. "l3-ha", "create")\
  284. .MultipleTimes().AndReturn(True)
  285. api.neutron.network_list(IsA(http.HttpRequest))\
  286. .AndReturn(self.networks.list())
  287. param = {'name': router.name,
  288. 'distributed': True,
  289. 'ha': True,
  290. 'admin_state_up': router.admin_state_up}
  291. api.neutron.router_create(IsA(http.HttpRequest), **param)\
  292. .AndReturn(router)
  293. self.mox.ReplayAll()
  294. form_data = {'name': router.name,
  295. 'mode': 'distributed',
  296. 'ha': 'enabled',
  297. 'admin_state_up': router.admin_state_up}
  298. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  299. res = self.client.post(url, form_data)
  300. self.assertNoFormErrors(res)
  301. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  302. @test.create_stubs({api.neutron: ('router_create',
  303. 'get_feature_permission',
  304. 'network_list')})
  305. def test_router_create_post_exception_error_case_409(self):
  306. router = self.routers.first()
  307. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  308. "dvr", "create")\
  309. .MultipleTimes().AndReturn(False)
  310. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  311. "l3-ha", "create")\
  312. .AndReturn(False)
  313. self.exceptions.neutron.status_code = 409
  314. api.neutron.network_list(IsA(http.HttpRequest))\
  315. .MultipleTimes().AndReturn(self.networks.list())
  316. params = {'name': router.name,
  317. 'admin_state_up': router.admin_state_up}
  318. api.neutron.router_create(IsA(http.HttpRequest), **params)\
  319. .AndRaise(self.exceptions.neutron)
  320. self.mox.ReplayAll()
  321. form_data = {'name': router.name,
  322. 'admin_state_up': router.admin_state_up}
  323. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  324. res = self.client.post(url, form_data)
  325. self.assertNoFormErrors(res)
  326. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  327. @test.create_stubs({api.neutron: ('router_create',
  328. 'get_feature_permission',
  329. 'network_list')})
  330. def test_router_create_post_exception_error_case_non_409(self):
  331. router = self.routers.first()
  332. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  333. "dvr", "create")\
  334. .MultipleTimes().AndReturn(False)
  335. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  336. "l3-ha", "create")\
  337. .MultipleTimes().AndReturn(False)
  338. self.exceptions.neutron.status_code = 999
  339. api.neutron.network_list(IsA(http.HttpRequest))\
  340. .MultipleTimes().AndReturn(self.networks.list())
  341. params = {'name': router.name,
  342. 'admin_state_up': router.admin_state_up}
  343. api.neutron.router_create(IsA(http.HttpRequest), **params)\
  344. .AndRaise(self.exceptions.neutron)
  345. self.mox.ReplayAll()
  346. form_data = {'name': router.name,
  347. 'admin_state_up': router.admin_state_up}
  348. url = reverse('horizon:%s:routers:create' % self.DASHBOARD)
  349. res = self.client.post(url, form_data)
  350. self.assertNoFormErrors(res)
  351. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  352. @test.create_stubs({api.neutron: ('router_get',
  353. 'get_feature_permission')})
  354. def _test_router_update_get(self, dvr_enabled=False,
  355. current_dvr=False,
  356. ha_enabled=False):
  357. router = [r for r in self.routers.list()
  358. if r.distributed == current_dvr][0]
  359. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  360. .AndReturn(router)
  361. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  362. "dvr", "update")\
  363. .AndReturn(dvr_enabled)
  364. # TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
  365. # PUT operation. It will be fixed in Kilo cycle.
  366. # api.neutron.get_feature_permission(IsA(http.HttpRequest),
  367. # "l3-ha", "update")\
  368. # .AndReturn(ha_enabled)
  369. self.mox.ReplayAll()
  370. url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
  371. args=[router.id])
  372. return self.client.get(url)
  373. def test_router_update_get_dvr_disabled(self):
  374. res = self._test_router_update_get(dvr_enabled=False)
  375. self.assertTemplateUsed(res, 'project/routers/update.html')
  376. self.assertNotContains(res, 'Router Type')
  377. self.assertNotContains(res, 'id="id_mode"')
  378. def test_router_update_get_dvr_enabled_mode_centralized(self):
  379. res = self._test_router_update_get(dvr_enabled=True, current_dvr=False)
  380. self.assertTemplateUsed(res, 'project/routers/update.html')
  381. self.assertContains(res, 'Router Type')
  382. # Check both menu are displayed.
  383. self.assertContains(
  384. res,
  385. '<option value="centralized" selected="selected">'
  386. 'Centralized</option>',
  387. html=True)
  388. self.assertContains(
  389. res,
  390. '<option value="distributed">Distributed</option>',
  391. html=True)
  392. def test_router_update_get_dvr_enabled_mode_distributed(self):
  393. res = self._test_router_update_get(dvr_enabled=True, current_dvr=True)
  394. self.assertTemplateUsed(res, 'project/routers/update.html')
  395. self.assertContains(res, 'Router Type')
  396. if django.VERSION >= (1, 10):
  397. pattern = ('<input class="form-control" id="id_mode" name="mode" '
  398. 'readonly="readonly" type="text" value="distributed" '
  399. 'required/>')
  400. else:
  401. pattern = ('<input class="form-control" id="id_mode" name="mode" '
  402. 'readonly="readonly" type="text" '
  403. 'value="distributed" />')
  404. self.assertContains(res, pattern, html=True)
  405. self.assertNotContains(res, 'centralized')
  406. @test.create_stubs({api.neutron: ('router_get',
  407. 'router_update',
  408. 'get_feature_permission')})
  409. def test_router_update_post_dvr_ha_disabled(self):
  410. router = self.routers.first()
  411. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  412. "dvr", "update")\
  413. .AndReturn(False)
  414. # TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
  415. # PUT operation. It will be fixed in Kilo cycle.
  416. # api.neutron.get_feature_permission(IsA(http.HttpRequest),
  417. # "l3-ha", "update")\
  418. # .AndReturn(False)
  419. api.neutron.router_update(IsA(http.HttpRequest), router.id,
  420. name=router.name,
  421. admin_state_up=router.admin_state_up)\
  422. .AndReturn(router)
  423. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  424. .AndReturn(router)
  425. self.mox.ReplayAll()
  426. form_data = {'router_id': router.id,
  427. 'name': router.name,
  428. 'admin_state': router.admin_state_up}
  429. url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
  430. args=[router.id])
  431. res = self.client.post(url, form_data)
  432. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  433. @test.create_stubs({api.neutron: ('router_get',
  434. 'router_update',
  435. 'get_feature_permission')})
  436. def test_router_update_post_dvr_ha_enabled(self):
  437. router = self.routers.first()
  438. api.neutron.get_feature_permission(IsA(http.HttpRequest),
  439. "dvr", "update")\
  440. .AndReturn(True)
  441. # TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
  442. # PUT operation. It will be fixed in Kilo cycle.
  443. # api.neutron.get_feature_permission(IsA(http.HttpRequest),
  444. # "l3-ha", "update")\
  445. # .AndReturn(True)
  446. api.neutron.router_update(IsA(http.HttpRequest), router.id,
  447. name=router.name,
  448. admin_state_up=router.admin_state_up,
  449. # ha=True,
  450. distributed=True).AndReturn(router)
  451. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  452. .AndReturn(router)
  453. self.mox.ReplayAll()
  454. form_data = {'router_id': router.id,
  455. 'name': router.name,
  456. 'admin_state': router.admin_state_up,
  457. 'mode': 'distributed',
  458. 'ha': True}
  459. url = reverse('horizon:%s:routers:update' % self.DASHBOARD,
  460. args=[router.id])
  461. res = self.client.post(url, form_data)
  462. self.assertRedirectsNoFollow(res, self.INDEX_URL)
  463. def _test_router_addinterface(self, raise_error=False):
  464. router = self.routers.first()
  465. subnet = self.subnets.first()
  466. port = self.ports.first()
  467. add_interface = api.neutron.router_add_interface(
  468. IsA(http.HttpRequest), router.id, subnet_id=subnet.id)
  469. if raise_error:
  470. add_interface.AndRaise(self.exceptions.neutron)
  471. else:
  472. add_interface.AndReturn({'subnet_id': subnet.id,
  473. 'port_id': port.id})
  474. api.neutron.port_get(IsA(http.HttpRequest), port.id)\
  475. .AndReturn(port)
  476. self._check_router_addinterface(router, subnet)
  477. def _check_router_addinterface(self, router, subnet, ip_address=''):
  478. # mock APIs used to show router detail
  479. api.neutron.router_get(IsA(http.HttpRequest), router.id)\
  480. .AndReturn(router)
  481. api.neutron.port_list(IsA(http.HttpRequest), device_id=router.id)\
  482. .AndReturn([])
  483. self._mock_network_list(router['tenant_id'])
  484. self.mox.ReplayAll()
  485. form_data = {'router_id': router.id,
  486. 'router_name': router.name,
  487. 'subnet_id': subnet.id,
  488. 'ip_address': ip_address}
  489. url = reverse('horizon:%s:routers:addinterface' % self.DASHBOARD,
  490. args=[router.id])
  491. res = self.client.post(url, form_data)
  492. self.assertNoFormErrors(res)
  493. detail_url = reverse(self.DETAIL_PATH, args=[router.id])
  494. self.assertRedirectsNoFollow(res, detail_url)
  495. @test.create_stubs({api.neutron: ('router_get',
  496. 'router_add_interface',
  497. 'port_get',
  498. 'network_list',
  499. 'port_list')})
  500. def test_router_addinterface(self):
  501. self._test_router_addinterface()
  502. @test.create_stubs({api.neutron: ('router_get',
  503. 'router_add_interface',
  504. 'network_list',
  505. 'port_list')})
  506. def test_router_addinterface_exception(self):
  507. self._test_router_addinterface(raise_error=True)
  508. def _test_router_addinterface_ip_addr(self, errors=None):
  509. errors = errors or []
  510. router = self.routers.first()
  511. subnet = self.subnets.first()
  512. port = self.ports.first()
  513. ip_addr = port['fixed_ips'][0]['ip_address']
  514. self._setup_mock_addinterface_ip_addr(router, subnet, port,
  515. ip_addr, errors)
  516. self._check_router_addinterface(router, subnet, ip_addr)
  517. def _setup_mock_addinterface_ip_addr(self, router, subnet, port,
  518. ip_addr, errors=None):
  519. errors = errors or []
  520. subnet_get = api.neutron.subnet_get(IsA(http.HttpRequest), subnet.id)
  521. if 'subnet_get' in errors:
  522. subnet_get.AndRaise(self.exceptions.neutron)
  523. return
  524. subnet_get.AndReturn(subnet)
  525. params = {'network_id': subnet.network_id,
  526. 'fixed_ips': [{'subnet_id': subnet.id,
  527. 'ip_address': ip_addr}]}
  528. port_create = api.neutron.port_create(IsA(http.HttpRequest), **params)
  529. if 'port_create' in errors:
  530. port_create.AndRaise(self.exceptions.neutron)
  531. return
  532. port_create.AndReturn(port)
  533. add_inf = api.neutron.router_add_interface(
  534. IsA(http.HttpRequest), router.id, port_id=port.id)
  535. if 'add_interface' not in errors:
  536. return
  537. add_inf.AndRaise(self.exceptions.neutron)
  538. port_delete = api.neutron.port_delete(IsA(http.HttpRequest), port.id)
  539. if 'port_delete' in errors:
  540. port_delete.AndRaise(self.exceptions.neutron)
  541. @test.create_stubs({api.neutron: ('router_add_interface', 'subnet_get',
  542. 'port_create',
  543. 'router_get', 'network_list',
  544. 'port_list')})
  545. def test_router_addinterface_ip_addr(self):
  546. self._test_router_addinterface_ip_addr()
  547. @test.create_stubs({api.neutron: ('subnet_get', 'router_get',
  548. 'network_list', 'port_list')})
  549. def test_router_addinterface_ip_addr_exception_subnet_get(self):
  550. self._test_router_addinterface_ip_addr(errors=['subnet_get'])
  551. @test.create_stubs({api.neutron: ('subnet_get', 'port_create',
  552. 'router_get', 'network_list',
  553. 'port_list')})
  554. def test_router_addinterface_ip_addr_exception_port_create(self):
  555. self._test_router_addinterface_ip_addr(errors=['port_create'])
  556. @test.create_stubs({api.neutron: ('router_add_interface', 'subnet_get',
  557. 'port_create', 'port_delete',
  558. 'router_get', 'network_list',
  559. 'port_list')})
  560. def test_router_addinterface_ip_addr_exception_add_interface(self):
  561. self._test_router_addinterface_ip_addr(errors=['add_interface'])
  562. @test.create_stubs({api.neutron: ('router_add_interface', 'subnet_get',
  563. 'port_create', 'port_delete',
  564. 'router_get', 'network_list',
  565. 'port_list')})
  566. def test_router_addinterface_ip_addr_exception_port_delete(self):
  567. self._test_router_addinterface_ip_addr(errors=['add_interface',
  568. 'port_delete'])
  569. @test.create_stubs({api.neutron: ('router_get',
  570. 'router_add_gateway',
  571. 'network_list')})
  572. def test_router_add_gateway(self):
  573. router = self.routers.first()
  574. network = self.networks.first()
  575. api.neutron.router_add_gateway(
  576. IsA(http.HttpRequest),
  577. router.id,
  578. network.id).AndReturn(None)
  579. api.neutron.router_get(
  580. IsA(http.HttpRequest), router.id).AndReturn(router)
  581. search_opts = {'router:external': True}
  582. api.neutron.network_list(
  583. IsA(http.HttpRequest), **search_opts).AndReturn([network])
  584. self.mox.ReplayAll()
  585. form_data = {'router_id': router.id,
  586. 'router_name': router.name,
  587. 'network_id': network.id}
  588. url = reverse('horizon:%s:routers:setgateway' % self.DASHBOARD,
  589. args=[router.id])
  590. res = self.client.post(url, form_data)
  591. self.assertNoFormErrors(res)
  592. detail_url = self.INDEX_URL
  593. self.assertRedirectsNoFollow(res, detail_url)
  594. @test.create_stubs({api.neutron: ('router_get',
  595. 'router_add_gateway',
  596. 'network_list')})
  597. def test_router_add_gateway_exception(self):
  598. router = self.routers.first()
  599. network = self.networks.first()
  600. api.neutron.router_add_gateway(
  601. IsA(http.HttpRequest),
  602. router.id,
  603. network.id).AndRaise(self.exceptions.neutron)
  604. api.neutron.router_get(
  605. IsA(http.HttpRequest), router.id).AndReturn(router)
  606. search_opts = {'router:external': True}
  607. api.neutron.network_list(
  608. IsA(http.HttpRequest), **search_opts).AndReturn([network])
  609. self.mox.ReplayAll()
  610. form_data = {'router_id': router.id,
  611. 'router_name': router.name,
  612. 'network_id': network.id}
  613. url = reverse('horizon:%s:routers:setgateway' % self.DASHBOARD,
  614. args=[router.id])
  615. res = self.client.post(url, form_data)
  616. self.assertNoFormErrors(res)
  617. detail_url = self.INDEX_URL
  618. self.assertRedirectsNoFollow(res, detail_url)
  619. class RouterRouteTests(RouterMixin, test.TestCase):
  620. DASHBOARD = 'project'
  621. INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
  622. DETAIL_PATH = 'horizon:%s:routers:detail' % DASHBOARD
  623. def test_extension_hides_without_routes(self):
  624. router = self.routers_with_routes.first()
  625. res = self._get_detail(router, extraroute=False)
  626. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  627. self.assertNotIn('extra_routes_table', res.context)
  628. def test_routerroute_detail(self):
  629. router = self.routers_with_routes.first()
  630. res = self._get_detail(router, extraroute=True)
  631. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  632. routes = res.context['extra_routes_table'].data
  633. routes_dict = [r._apidict for r in routes]
  634. self.assertItemsEqual(routes_dict, router['routes'])
  635. @test.create_stubs({api.neutron: ('router_get', 'router_update')})
  636. def _test_router_addrouterroute(self, raise_error=False):
  637. pre_router = self.routers_with_routes.first()
  638. post_router = copy.deepcopy(pre_router)
  639. route = {'nexthop': '10.0.0.5', 'destination': '40.0.1.0/24'}
  640. post_router['routes'].insert(0, route)
  641. api.neutron.router_get(IsA(http.HttpRequest), pre_router.id)\
  642. .MultipleTimes().AndReturn(pre_router)
  643. params = {}
  644. params['routes'] = post_router['routes']
  645. router_update = api.neutron.router_update(IsA(http.HttpRequest),
  646. pre_router.id, **params)
  647. if raise_error:
  648. router_update.AndRaise(self.exceptions.neutron)
  649. else:
  650. router_update.AndReturn({'router': post_router})
  651. self.mox.ReplayAll()
  652. form_data = copy.deepcopy(route)
  653. form_data['router_id'] = pre_router.id
  654. url = reverse('horizon:%s:routers:addrouterroute' % self.DASHBOARD,
  655. args=[pre_router.id])
  656. res = self.client.post(url, form_data)
  657. self.assertNoFormErrors(res)
  658. detail_url = reverse(self.DETAIL_PATH, args=[pre_router.id])
  659. self.assertRedirectsNoFollow(res, detail_url)
  660. def test_router_addrouterroute(self):
  661. if self.DASHBOARD == 'project':
  662. self._test_router_addrouterroute()
  663. self.assertMessageCount(success=1)
  664. def test_router_addrouterroute_exception(self):
  665. if self.DASHBOARD == 'project':
  666. self._test_router_addrouterroute(raise_error=True)
  667. self.assertMessageCount(error=1)
  668. @test.create_stubs({api.neutron: ('router_get', 'router_update',
  669. 'network_get', 'port_list',
  670. 'is_extension_supported')})
  671. def test_router_removeroute(self):
  672. if self.DASHBOARD == 'admin':
  673. return
  674. pre_router = self.routers_with_routes.first()
  675. post_router = copy.deepcopy(pre_router)
  676. route = post_router['routes'].pop()
  677. api.neutron.is_extension_supported(IsA(http.HttpRequest), 'extraroute')\
  678. .MultipleTimes().AndReturn(True)
  679. api.neutron.router_get(IsA(http.HttpRequest),
  680. pre_router.id).AndReturn(pre_router)
  681. params = {}
  682. params['routes'] = post_router['routes']
  683. api.neutron.router_get(IsA(http.HttpRequest),
  684. pre_router.id).AndReturn(pre_router)
  685. router_update = api.neutron.router_update(IsA(http.HttpRequest),
  686. pre_router.id, **params)
  687. router_update.AndReturn({'router': post_router})
  688. api.neutron.port_list(IsA(http.HttpRequest),
  689. device_id=pre_router.id)\
  690. .AndReturn([self.ports.first()])
  691. self._mock_external_network_get(pre_router)
  692. self.mox.ReplayAll()
  693. form_route_id = route['nexthop'] + ":" + route['destination']
  694. form_data = {'action': 'extra_routes__delete__%s' % form_route_id}
  695. url = reverse(self.DETAIL_PATH, args=[pre_router.id])
  696. res = self.client.post(url, form_data)
  697. self.assertNoFormErrors(res)
  698. class RouterViewTests(RouterMixin, test.TestCase):
  699. DASHBOARD = 'project'
  700. INDEX_URL = reverse('horizon:%s:routers:index' % DASHBOARD)
  701. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  702. quotas: ('tenant_quota_usages',)})
  703. def test_create_button_disabled_when_quota_exceeded(self):
  704. quota_data = self.neutron_quota_usages.first()
  705. quota_data['routers']['available'] = 0
  706. api.neutron.router_list(
  707. IsA(http.HttpRequest),
  708. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  709. quotas.tenant_quota_usages(
  710. IsA(http.HttpRequest), targets=('routers', )) \
  711. .MultipleTimes().AndReturn(quota_data)
  712. self._mock_external_network_list()
  713. self.mox.ReplayAll()
  714. res = self.client.get(self.INDEX_URL)
  715. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  716. routers = res.context['routers_table'].data
  717. self.assertItemsEqual(routers, self.routers.list())
  718. create_action = self.getAndAssertTableAction(res, 'routers', 'create')
  719. self.assertIn('disabled', create_action.classes,
  720. 'Create button is not disabled')
  721. self.assertEqual('Create Router (Quota exceeded)',
  722. create_action.verbose_name)
  723. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  724. quotas: ('tenant_quota_usages',)})
  725. def test_create_button_shown_when_quota_disabled(self):
  726. quota_data = self.neutron_quota_usages.first()
  727. quota_data['routers'].pop('available')
  728. api.neutron.router_list(
  729. IsA(http.HttpRequest),
  730. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  731. quotas.tenant_quota_usages(
  732. IsA(http.HttpRequest), targets=('routers', )) \
  733. .MultipleTimes().AndReturn(quota_data)
  734. self._mock_external_network_list()
  735. self.mox.ReplayAll()
  736. res = self.client.get(self.INDEX_URL)
  737. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  738. routers = res.context['routers_table'].data
  739. self.assertItemsEqual(routers, self.routers.list())
  740. create_action = self.getAndAssertTableAction(res, 'routers', 'create')
  741. self.assertFalse('disabled' in create_action.classes,
  742. 'Create button should not be disabled')
  743. self.assertEqual('Create Router',
  744. create_action.verbose_name)
  745. @test.create_stubs({api.neutron: ('router_list', 'network_list'),
  746. quotas: ('tenant_quota_usages',)})
  747. def test_create_button_attributes(self):
  748. quota_data = self.neutron_quota_usages.first()
  749. quota_data['routers']['available'] = 10
  750. api.neutron.router_list(
  751. IsA(http.HttpRequest),
  752. tenant_id=self.tenant.id).AndReturn(self.routers.list())
  753. quotas.tenant_quota_usages(
  754. IsA(http.HttpRequest), targets=('routers', )) \
  755. .MultipleTimes().AndReturn(quota_data)
  756. self._mock_external_network_list()
  757. self.mox.ReplayAll()
  758. res = self.client.get(self.INDEX_URL)
  759. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  760. routers = res.context['routers_table'].data
  761. self.assertItemsEqual(routers, self.routers.list())
  762. create_action = self.getAndAssertTableAction(res, 'routers', 'create')
  763. self.assertEqual(set(['ajax-modal']), set(create_action.classes))
  764. self.assertEqual('Create Router',
  765. six.text_type(create_action.verbose_name))
  766. self.assertEqual('horizon:project:routers:create', create_action.url)
  767. self.assertEqual((('network', 'create_router'),),
  768. create_action.policy_rules)