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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350
  1. # Copyright 2012 NEC Corporation
  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. from django.core.urlresolvers import reverse
  15. from django import http
  16. from django.utils.html import escape
  17. from django.utils.http import urlunquote
  18. from mox3.mox import IsA
  19. import six
  20. from horizon.workflows import views
  21. from openstack_dashboard import api
  22. from openstack_dashboard.dashboards.project.networks import tables\
  23. as networks_tables
  24. from openstack_dashboard.dashboards.project.networks import workflows
  25. from openstack_dashboard.test import helpers as test
  26. from openstack_dashboard.usage import quotas
  27. INDEX_TEMPLATE = 'horizon/common/_data_table_view.html'
  28. INDEX_URL = reverse('horizon:project:networks:index')
  29. def form_data_subnet(subnet,
  30. name=None, cidr=None, ip_version=None,
  31. gateway_ip='', enable_dhcp=None,
  32. allocation_pools=None,
  33. dns_nameservers=None,
  34. host_routes=None):
  35. def get_value(value, default):
  36. return default if value is None else value
  37. data = {}
  38. data['subnet_name'] = get_value(name, subnet.name)
  39. data['cidr'] = get_value(cidr, subnet.cidr)
  40. data['ip_version'] = get_value(ip_version, subnet.ip_version)
  41. gateway_ip = subnet.gateway_ip if gateway_ip == '' else gateway_ip
  42. data['gateway_ip'] = gateway_ip or ''
  43. data['no_gateway'] = (gateway_ip is None)
  44. data['enable_dhcp'] = get_value(enable_dhcp, subnet.enable_dhcp)
  45. if data['ip_version'] == 6:
  46. data['ipv6_modes'] = subnet.ipv6_modes
  47. pools = get_value(allocation_pools, subnet.allocation_pools)
  48. data['allocation_pools'] = _str_allocation_pools(pools)
  49. nameservers = get_value(dns_nameservers, subnet.dns_nameservers)
  50. data['dns_nameservers'] = _str_dns_nameservers(nameservers)
  51. routes = get_value(host_routes, subnet.host_routes)
  52. data['host_routes'] = _str_host_routes(routes)
  53. return data
  54. def form_data_no_subnet():
  55. return {'subnet_name': '',
  56. 'cidr': '',
  57. 'ip_version': 4,
  58. 'gateway_ip': '',
  59. 'no_gateway': False,
  60. 'enable_dhcp': True,
  61. 'allocation_pools': '',
  62. 'dns_nameservers': '',
  63. 'host_routes': ''}
  64. def _str_allocation_pools(allocation_pools):
  65. if isinstance(allocation_pools, str):
  66. return allocation_pools
  67. return '\n'.join(['%s,%s' % (pool['start'], pool['end'])
  68. for pool in allocation_pools])
  69. def _str_dns_nameservers(dns_nameservers):
  70. if isinstance(dns_nameservers, str):
  71. return dns_nameservers
  72. return '\n'.join(dns_nameservers)
  73. def _str_host_routes(host_routes):
  74. if isinstance(host_routes, str):
  75. return host_routes
  76. return '\n'.join(['%s,%s' % (route['destination'], route['nexthop'])
  77. for route in host_routes])
  78. class NetworkStubMixin(object):
  79. def _stub_net_list(self):
  80. all_networks = self.networks.list()
  81. api.neutron.network_list(
  82. IsA(http.HttpRequest),
  83. tenant_id=self.tenant.id,
  84. shared=False).AndReturn([
  85. network for network in all_networks
  86. if network['tenant_id'] == self.tenant.id
  87. ])
  88. api.neutron.network_list(
  89. IsA(http.HttpRequest),
  90. shared=True).AndReturn([
  91. network for network in all_networks
  92. if network.get('shared')
  93. ])
  94. api.neutron.network_list(
  95. IsA(http.HttpRequest),
  96. **{'router:external': True}).AndReturn([
  97. network for network in all_networks
  98. if network.get('router:external')
  99. ])
  100. class NetworkTests(test.TestCase, NetworkStubMixin):
  101. @test.create_stubs({api.neutron: ('network_list',
  102. 'is_extension_supported'),
  103. quotas: ('tenant_quota_usages',)})
  104. def test_index(self):
  105. quota_data = self.quota_usages.first()
  106. quota_data['networks']['available'] = 5
  107. quota_data['subnets']['available'] = 5
  108. self._stub_net_list()
  109. quotas.tenant_quota_usages(
  110. IsA(http.HttpRequest), targets=('networks', )) \
  111. .MultipleTimes().AndReturn(quota_data)
  112. quotas.tenant_quota_usages(
  113. IsA(http.HttpRequest), targets=('subnets', )) \
  114. .MultipleTimes().AndReturn(quota_data)
  115. api.neutron.is_extension_supported(
  116. IsA(http.HttpRequest), 'network_availability_zone')\
  117. .MultipleTimes().AndReturn(True)
  118. self.mox.ReplayAll()
  119. res = self.client.get(INDEX_URL)
  120. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  121. networks = res.context['networks_table'].data
  122. self.assertItemsEqual(networks, self.networks.list())
  123. @test.create_stubs({api.neutron: ('network_list',
  124. 'is_extension_supported'),
  125. quotas: ('tenant_quota_usages',)})
  126. def test_index_network_list_exception(self):
  127. quota_data = self.neutron_quota_usages.first()
  128. api.neutron.network_list(
  129. IsA(http.HttpRequest),
  130. tenant_id=self.tenant.id,
  131. shared=False).MultipleTimes().AndRaise(self.exceptions.neutron)
  132. quotas.tenant_quota_usages(
  133. IsA(http.HttpRequest), targets=('networks', )) \
  134. .MultipleTimes().AndReturn(quota_data)
  135. api.neutron.is_extension_supported(
  136. IsA(http.HttpRequest), 'network_availability_zone')\
  137. .MultipleTimes().AndReturn(True)
  138. self.mox.ReplayAll()
  139. res = self.client.get(INDEX_URL)
  140. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  141. self.assertEqual(len(res.context['networks_table'].data), 0)
  142. self.assertMessageCount(res, error=1)
  143. @test.create_stubs({api.neutron: ('network_get',
  144. 'subnet_list',
  145. 'port_list',
  146. 'is_extension_supported',),
  147. quotas: ('tenant_quota_usages',)})
  148. def test_network_detail_subnets_tab(self):
  149. self._test_network_detail_subnets_tab()
  150. @test.create_stubs({api.neutron: ('network_get',
  151. 'subnet_list',
  152. 'port_list',
  153. 'is_extension_supported',),
  154. quotas: ('tenant_quota_usages',)})
  155. def test_network_detail_subnets_tab_with_mac_learning(self):
  156. self._test_network_detail_subnets_tab(mac_learning=True)
  157. @test.create_stubs({api.neutron: ('network_get',
  158. 'is_extension_supported'),
  159. quotas: ('tenant_quota_usages',)})
  160. def test_network_detail(self, mac_learning=False):
  161. network_id = self.networks.first().id
  162. quota_data = self.neutron_quota_usages.first()
  163. api.neutron.network_get(IsA(http.HttpRequest), network_id) \
  164. .MultipleTimes().AndReturn(self.networks.first())
  165. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  166. 'mac-learning') \
  167. .AndReturn(mac_learning)
  168. quotas.tenant_quota_usages(
  169. IsA(http.HttpRequest), targets=('subnets', )) \
  170. .MultipleTimes().AndReturn(quota_data)
  171. api.neutron.is_extension_supported(
  172. IsA(http.HttpRequest), 'network_availability_zone')\
  173. .MultipleTimes().AndReturn(True)
  174. self.mox.ReplayAll()
  175. url = urlunquote(reverse('horizon:project:networks:detail',
  176. args=[network_id]))
  177. res = self.client.get(url)
  178. network = res.context['network']
  179. self.assertEqual(self.networks.first().name_or_id, network.name_or_id)
  180. self.assertEqual(self.networks.first().status_label,
  181. network.status_label)
  182. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  183. def _test_network_detail_subnets_tab(self, mac_learning=False):
  184. quota_data = self.neutron_quota_usages.first()
  185. network_id = self.networks.first().id
  186. api.neutron.network_get(IsA(http.HttpRequest), network_id)\
  187. .AndReturn(self.networks.first())
  188. api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id)\
  189. .AndReturn([self.subnets.first()])
  190. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  191. 'mac-learning')\
  192. .AndReturn(mac_learning)
  193. quotas.tenant_quota_usages(
  194. IsA(http.HttpRequest), targets=('subnets', )) \
  195. .MultipleTimes().AndReturn(quota_data)
  196. api.neutron.is_extension_supported(
  197. IsA(http.HttpRequest), 'network_availability_zone')\
  198. .MultipleTimes().AndReturn(True)
  199. self.mox.ReplayAll()
  200. url = urlunquote(reverse('horizon:project:networks:subnets_tab',
  201. args=[network_id]))
  202. res = self.client.get(url)
  203. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  204. subnets = res.context['subnets_table'].data
  205. self.assertItemsEqual(subnets, [self.subnets.first()])
  206. @test.create_stubs({api.neutron: ('network_get',
  207. 'subnet_list',
  208. 'port_list',
  209. 'is_extension_supported',)})
  210. def test_network_detail_network_exception(self):
  211. self._test_network_detail_network_exception()
  212. @test.create_stubs({api.neutron: ('network_get',
  213. 'subnet_list',
  214. 'port_list',
  215. 'is_extension_supported',)})
  216. def test_network_detail_network_exception_with_mac_learning(self):
  217. self._test_network_detail_network_exception(mac_learning=True)
  218. def _test_network_detail_network_exception(self, mac_learning=False):
  219. network_id = self.networks.first().id
  220. api.neutron.network_get(IsA(http.HttpRequest), network_id)\
  221. .AndRaise(self.exceptions.neutron)
  222. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  223. 'mac-learning')\
  224. .AndReturn(mac_learning)
  225. self.mox.ReplayAll()
  226. url = reverse('horizon:project:networks:detail', args=[network_id])
  227. res = self.client.get(url)
  228. redir_url = INDEX_URL
  229. self.assertRedirectsNoFollow(res, redir_url)
  230. @test.create_stubs({api.neutron: ('network_get',
  231. 'subnet_list',
  232. 'port_list',
  233. 'is_extension_supported',),
  234. quotas: ('tenant_quota_usages',)})
  235. def test_subnets_tab_subnet_exception(self):
  236. self._test_subnets_tab_subnet_exception()
  237. @test.create_stubs({api.neutron: ('network_get',
  238. 'subnet_list',
  239. 'port_list',
  240. 'is_extension_supported',),
  241. quotas: ('tenant_quota_usages',)})
  242. def test_network_detail_subnet_exception_with_mac_learning(self):
  243. self._test_subnets_tab_subnet_exception(mac_learning=True)
  244. def _test_subnets_tab_subnet_exception(self, mac_learning=False):
  245. network_id = self.networks.first().id
  246. quota_data = self.neutron_quota_usages.first()
  247. quota_data['networks']['available'] = 5
  248. quota_data['subnets']['available'] = 5
  249. api.neutron.network_get(IsA(http.HttpRequest), network_id).\
  250. MultipleTimes().AndReturn(self.networks.first())
  251. api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id).\
  252. AndRaise(self.exceptions.neutron)
  253. # Called from SubnetTable
  254. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  255. 'mac-learning')\
  256. .AndReturn(mac_learning)
  257. quotas.tenant_quota_usages(
  258. IsA(http.HttpRequest), targets=('subnets', )) \
  259. .MultipleTimes().AndReturn(quota_data)
  260. api.neutron.is_extension_supported(
  261. IsA(http.HttpRequest), 'network_availability_zone')\
  262. .MultipleTimes().AndReturn(True)
  263. self.mox.ReplayAll()
  264. url = urlunquote(reverse('horizon:project:networks:subnets_tab',
  265. args=[network_id]))
  266. res = self.client.get(url)
  267. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  268. subnets = res.context['subnets_table'].data
  269. self.assertEqual(len(subnets), 0)
  270. @test.create_stubs({api.neutron: ('network_get',
  271. 'subnet_list',
  272. 'port_list',
  273. 'is_extension_supported',),
  274. quotas: ('tenant_quota_usages',)})
  275. def test_subnets_tab_port_exception(self):
  276. self._test_subnets_tab_port_exception()
  277. @test.create_stubs({api.neutron: ('network_get',
  278. 'subnet_list',
  279. 'port_list',
  280. 'is_extension_supported',),
  281. quotas: ('tenant_quota_usages',)})
  282. def test_network_detail_port_exception_with_mac_learning(self):
  283. self._test_subnets_tab_port_exception(mac_learning=True)
  284. def _test_subnets_tab_port_exception(self, mac_learning=False):
  285. network_id = self.networks.first().id
  286. quota_data = self.neutron_quota_usages.first()
  287. quota_data['subnets']['available'] = 5
  288. api.neutron.network_get(IsA(http.HttpRequest), network_id).\
  289. AndReturn(self.networks.first())
  290. api.neutron.subnet_list(IsA(http.HttpRequest), network_id=network_id).\
  291. AndReturn([self.subnets.first()])
  292. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  293. 'mac-learning')\
  294. .AndReturn(mac_learning)
  295. quotas.tenant_quota_usages(
  296. IsA(http.HttpRequest), targets=('subnets', )) \
  297. .MultipleTimes().AndReturn(quota_data)
  298. api.neutron.is_extension_supported(
  299. IsA(http.HttpRequest), 'network_availability_zone')\
  300. .MultipleTimes().AndReturn(True)
  301. self.mox.ReplayAll()
  302. url = urlunquote(reverse('horizon:project:networks:subnets_tab',
  303. args=[network_id]))
  304. res = self.client.get(url)
  305. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  306. subnets = res.context['subnets_table'].data
  307. self.assertItemsEqual(subnets, [self.subnets.first()])
  308. @test.create_stubs({api.neutron: ('is_extension_supported',
  309. 'subnetpool_list')})
  310. def test_network_create_get(self):
  311. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  312. 'network_availability_zone').\
  313. AndReturn(False)
  314. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  315. 'subnet_allocation').\
  316. AndReturn(True)
  317. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  318. AndReturn(self.subnetpools.list())
  319. self.mox.ReplayAll()
  320. url = reverse('horizon:project:networks:create')
  321. res = self.client.get(url)
  322. workflow = res.context['workflow']
  323. self.assertTemplateUsed(res, views.WorkflowView.template_name)
  324. self.assertEqual(workflow.name, workflows.CreateNetwork.name)
  325. expected_objs = ['<CreateNetworkInfo: createnetworkinfoaction>',
  326. '<CreateSubnetInfo: createsubnetinfoaction>',
  327. '<CreateSubnetDetail: createsubnetdetailaction>']
  328. self.assertQuerysetEqual(workflow.steps, expected_objs)
  329. @test.create_stubs({api.neutron: ('network_create',
  330. 'is_extension_supported',
  331. 'subnetpool_list')})
  332. def test_network_create_post(self):
  333. network = self.networks.first()
  334. params = {'name': network.name,
  335. 'admin_state_up': network.admin_state_up,
  336. 'shared': False}
  337. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  338. 'network_availability_zone').\
  339. AndReturn(False)
  340. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  341. 'subnet_allocation').\
  342. AndReturn(True)
  343. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  344. AndReturn(self.subnetpools.list())
  345. api.neutron.network_create(IsA(http.HttpRequest),
  346. **params).AndReturn(network)
  347. self.mox.ReplayAll()
  348. form_data = {'net_name': network.name,
  349. 'admin_state': network.admin_state_up,
  350. 'shared': False,
  351. # subnet
  352. 'with_subnet': False}
  353. form_data.update(form_data_no_subnet())
  354. url = reverse('horizon:project:networks:create')
  355. res = self.client.post(url, form_data)
  356. self.assertNoFormErrors(res)
  357. self.assertRedirectsNoFollow(res, INDEX_URL)
  358. @test.create_stubs({api.neutron: ('network_create',
  359. 'is_extension_supported',
  360. 'list_availability_zones',
  361. 'subnetpool_list')})
  362. def test_network_create_post_with_az(self):
  363. network = self.networks.first()
  364. params = {'name': network.name,
  365. 'admin_state_up': network.admin_state_up,
  366. 'shared': False,
  367. 'az_hints': ['nova']}
  368. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  369. 'network_availability_zone').\
  370. AndReturn(True)
  371. api.neutron.list_availability_zones(IsA(http.HttpRequest),
  372. "network", "available")\
  373. .AndReturn(self.neutron_availability_zones.list())
  374. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  375. 'subnet_allocation').\
  376. AndReturn(True)
  377. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  378. AndReturn(self.subnetpools.list())
  379. api.neutron.network_create(IsA(http.HttpRequest),
  380. **params).AndReturn(network)
  381. self.mox.ReplayAll()
  382. form_data = {'net_name': network.name,
  383. 'admin_state': network.admin_state_up,
  384. 'shared': False,
  385. 'with_subnet': False,
  386. 'availability_zone_hints': ['nova']}
  387. form_data.update(form_data_no_subnet())
  388. url = reverse('horizon:project:networks:create')
  389. res = self.client.post(url, form_data)
  390. self.assertNoFormErrors(res)
  391. self.assertRedirectsNoFollow(res, INDEX_URL)
  392. @test.create_stubs({api.neutron: ('network_create',
  393. 'is_extension_supported',
  394. 'subnetpool_list')})
  395. def test_network_create_post_with_shared(self):
  396. network = self.networks.first()
  397. params = {'name': network.name,
  398. 'admin_state_up': network.admin_state_up,
  399. 'shared': True}
  400. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  401. 'network_availability_zone').\
  402. AndReturn(False)
  403. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  404. 'subnet_allocation').\
  405. AndReturn(True)
  406. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  407. AndReturn(self.subnetpools.list())
  408. api.neutron.network_create(IsA(http.HttpRequest),
  409. **params).AndReturn(network)
  410. self.mox.ReplayAll()
  411. form_data = {'net_name': network.name,
  412. 'admin_state': network.admin_state_up,
  413. 'shared': True,
  414. # subnet
  415. 'with_subnet': False}
  416. form_data.update(form_data_no_subnet())
  417. url = reverse('horizon:project:networks:create')
  418. res = self.client.post(url, form_data)
  419. self.assertNoFormErrors(res)
  420. self.assertRedirectsNoFollow(res, INDEX_URL)
  421. @test.create_stubs({api.neutron: ('network_create',
  422. 'subnet_create',
  423. 'is_extension_supported',
  424. 'subnetpool_list')})
  425. def test_network_create_post_with_subnet(self,
  426. test_with_ipv6=True):
  427. network = self.networks.first()
  428. subnet = self.subnets.first()
  429. params = {'name': network.name,
  430. 'admin_state_up': network.admin_state_up,
  431. 'shared': False}
  432. subnet_params = {'network_id': network.id,
  433. 'name': subnet.name,
  434. 'cidr': subnet.cidr,
  435. 'ip_version': subnet.ip_version,
  436. 'gateway_ip': subnet.gateway_ip,
  437. 'enable_dhcp': subnet.enable_dhcp}
  438. if not test_with_ipv6:
  439. subnet.ip_version = 4
  440. subnet_params['ip_version'] = subnet.ip_version
  441. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  442. 'network_availability_zone').\
  443. AndReturn(False)
  444. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  445. 'subnet_allocation').\
  446. AndReturn(True)
  447. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  448. AndReturn(self.subnetpools.list())
  449. api.neutron.network_create(IsA(http.HttpRequest),
  450. **params).AndReturn(network)
  451. api.neutron.subnet_create(IsA(http.HttpRequest),
  452. **subnet_params).AndReturn(subnet)
  453. self.mox.ReplayAll()
  454. form_data = {'net_name': network.name,
  455. 'admin_state': network.admin_state_up,
  456. 'shared': False,
  457. 'with_subnet': True}
  458. form_data.update(form_data_subnet(subnet, allocation_pools=[]))
  459. url = reverse('horizon:project:networks:create')
  460. res = self.client.post(url, form_data)
  461. self.assertNoFormErrors(res)
  462. self.assertRedirectsNoFollow(res, INDEX_URL)
  463. @test.update_settings(OPENSTACK_NEUTRON_NETWORK={'enable_ipv6': False})
  464. def test_create_network_with_ipv6_disabled(self):
  465. self.test_network_create_post_with_subnet(test_with_ipv6=False)
  466. @test.create_stubs({api.neutron: ('network_create',
  467. 'is_extension_supported',
  468. 'subnetpool_list')})
  469. def test_network_create_post_network_exception(self):
  470. network = self.networks.first()
  471. params = {'name': network.name,
  472. 'shared': False,
  473. 'admin_state_up': network.admin_state_up}
  474. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  475. 'network_availability_zone').\
  476. AndReturn(False)
  477. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  478. 'subnet_allocation').\
  479. AndReturn(True)
  480. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  481. AndReturn(self.subnetpools.list())
  482. api.neutron.network_create(IsA(http.HttpRequest),
  483. **params).AndRaise(self.exceptions.neutron)
  484. self.mox.ReplayAll()
  485. form_data = {'net_name': network.name,
  486. 'admin_state': network.admin_state_up,
  487. # subnet
  488. 'shared': False,
  489. 'with_subnet': False}
  490. form_data.update(form_data_no_subnet())
  491. url = reverse('horizon:project:networks:create')
  492. res = self.client.post(url, form_data)
  493. self.assertNoFormErrors(res)
  494. self.assertRedirectsNoFollow(res, INDEX_URL)
  495. @test.create_stubs({api.neutron: ('network_create',
  496. 'is_extension_supported',
  497. 'subnetpool_list')})
  498. def test_network_create_post_with_subnet_network_exception(self):
  499. network = self.networks.first()
  500. subnet = self.subnets.first()
  501. params = {'name': network.name,
  502. 'shared': False,
  503. 'admin_state_up': network.admin_state_up}
  504. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  505. 'network_availability_zone').\
  506. AndReturn(False)
  507. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  508. 'subnet_allocation').\
  509. AndReturn(True)
  510. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  511. AndReturn(self.subnetpools.list())
  512. api.neutron.network_create(IsA(http.HttpRequest),
  513. **params).AndRaise(self.exceptions.neutron)
  514. self.mox.ReplayAll()
  515. form_data = {'net_name': network.name,
  516. 'admin_state': network.admin_state_up,
  517. 'shared': False,
  518. 'with_subnet': True}
  519. form_data.update(form_data_subnet(subnet, allocation_pools=[]))
  520. url = reverse('horizon:project:networks:create')
  521. res = self.client.post(url, form_data)
  522. self.assertNoFormErrors(res)
  523. self.assertRedirectsNoFollow(res, INDEX_URL)
  524. @test.create_stubs({api.neutron: ('network_create',
  525. 'network_delete',
  526. 'subnet_create',
  527. 'is_extension_supported',
  528. 'subnetpool_list',)})
  529. def test_network_create_post_with_subnet_subnet_exception(self):
  530. network = self.networks.first()
  531. subnet = self.subnets.first()
  532. params = {'name': network.name,
  533. 'shared': False,
  534. 'admin_state_up': network.admin_state_up}
  535. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  536. 'network_availability_zone').\
  537. AndReturn(False)
  538. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  539. 'subnet_allocation').\
  540. AndReturn(True)
  541. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  542. AndReturn(self.subnetpools.list())
  543. api.neutron.network_create(IsA(http.HttpRequest),
  544. **params).AndReturn(network)
  545. api.neutron.subnet_create(IsA(http.HttpRequest),
  546. network_id=network.id,
  547. name=subnet.name,
  548. cidr=subnet.cidr,
  549. ip_version=subnet.ip_version,
  550. gateway_ip=subnet.gateway_ip,
  551. enable_dhcp=subnet.enable_dhcp)\
  552. .AndRaise(self.exceptions.neutron)
  553. api.neutron.network_delete(IsA(http.HttpRequest),
  554. network.id)
  555. self.mox.ReplayAll()
  556. form_data = {'net_name': network.name,
  557. 'admin_state': network.admin_state_up,
  558. 'shared': False,
  559. 'with_subnet': True}
  560. form_data.update(form_data_subnet(subnet, allocation_pools=[]))
  561. url = reverse('horizon:project:networks:create')
  562. res = self.client.post(url, form_data)
  563. self.assertNoFormErrors(res)
  564. self.assertRedirectsNoFollow(res, INDEX_URL)
  565. @test.create_stubs({api.neutron: ('is_extension_supported',
  566. 'subnetpool_list',)})
  567. def test_network_create_post_with_subnet_nocidr(self,
  568. test_with_snpool=False):
  569. network = self.networks.first()
  570. subnet = self.subnets.first()
  571. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  572. 'network_availability_zone').\
  573. AndReturn(False)
  574. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  575. 'subnet_allocation').\
  576. AndReturn(True)
  577. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  578. AndReturn(self.subnetpools.list())
  579. self.mox.ReplayAll()
  580. form_data = {'net_name': network.name,
  581. 'admin_state': network.admin_state_up,
  582. 'shared': False,
  583. 'with_subnet': True}
  584. if test_with_snpool:
  585. form_data['subnetpool_id'] = ''
  586. form_data['prefixlen'] = ''
  587. form_data.update(form_data_subnet(subnet, cidr='',
  588. allocation_pools=[]))
  589. url = reverse('horizon:project:networks:create')
  590. res = self.client.post(url, form_data)
  591. self.assertContains(res, escape('Specify "Network Address" or '
  592. 'clear "Create Subnet" checkbox'
  593. ' in previous step.'))
  594. def test_network_create_post_with_subnet_nocidr_nosubnetpool(self):
  595. self.test_network_create_post_with_subnet_nocidr(
  596. test_with_snpool=True)
  597. @test.create_stubs({api.neutron: ('is_extension_supported',
  598. 'subnetpool_list',)})
  599. def test_network_create_post_with_subnet_cidr_without_mask(
  600. self,
  601. test_with_subnetpool=False,
  602. ):
  603. network = self.networks.first()
  604. subnet = self.subnets.first()
  605. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  606. 'network_availability_zone').\
  607. AndReturn(False)
  608. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  609. 'subnet_allocation').\
  610. AndReturn(True)
  611. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  612. AndReturn(self.subnetpools.list())
  613. self.mox.ReplayAll()
  614. form_data = {'net_name': network.name,
  615. 'shared': False,
  616. 'admin_state': network.admin_state_up,
  617. 'with_subnet': True}
  618. if test_with_subnetpool:
  619. subnetpool = self.subnetpools.first()
  620. form_data['subnetpool'] = subnetpool.id
  621. form_data['prefixlen'] = subnetpool.default_prefixlen
  622. form_data.update(form_data_subnet(subnet, cidr='10.0.0.0',
  623. allocation_pools=[]))
  624. url = reverse('horizon:project:networks:create')
  625. res = self.client.post(url, form_data)
  626. expected_msg = "The subnet in the Network Address is too small (/32)."
  627. self.assertContains(res, expected_msg)
  628. def test_network_create_post_with_subnet_cidr_without_mask_w_snpool(self):
  629. self.test_network_create_post_with_subnet_cidr_without_mask(
  630. test_with_subnetpool=True)
  631. @test.update_settings(
  632. ALLOWED_PRIVATE_SUBNET_CIDR={'ipv4': ['192.168.0.0/16']})
  633. @test.create_stubs({api.neutron: ('is_extension_supported',
  634. 'subnetpool_list')})
  635. def test_network_create_post_with_subnet_cidr_invalid_v4_range(
  636. self,
  637. test_with_subnetpool=False
  638. ):
  639. network = self.networks.first()
  640. subnet = self.subnets.first()
  641. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  642. 'network_availability_zone').\
  643. AndReturn(False)
  644. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  645. 'subnet_allocation').\
  646. AndReturn(True)
  647. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  648. AndReturn(self.subnetpools.list())
  649. self.mox.ReplayAll()
  650. form_data = {'net_name': network.name,
  651. 'shared': False,
  652. 'admin_state': network.admin_state_up,
  653. 'with_subnet': True}
  654. if test_with_subnetpool:
  655. subnetpool = self.subnetpools.first()
  656. form_data['subnetpool'] = subnetpool.id
  657. form_data['prefixlen'] = subnetpool.default_prefixlen
  658. form_data.update(form_data_subnet(subnet, cidr='30.30.30.0/24',
  659. allocation_pools=[]))
  660. url = reverse('horizon:project:networks:create')
  661. res = self.client.post(url, form_data)
  662. expected_msg = ("CIDRs allowed for user private ipv4 networks "
  663. "are 192.168.0.0/16.")
  664. self.assertContains(res, expected_msg)
  665. @test.update_settings(
  666. ALLOWED_PRIVATE_SUBNET_CIDR={'ipv4': ['192.168.0.0/16']})
  667. def test_network_create_post_with_subnet_cidr_invalid_v4_range_w_snpool(
  668. self):
  669. self.test_network_create_post_with_subnet_cidr_invalid_v4_range(
  670. test_with_subnetpool=True)
  671. @test.update_settings(ALLOWED_PRIVATE_SUBNET_CIDR={'ipv6': ['fc00::/9']})
  672. @test.create_stubs({api.neutron: ('is_extension_supported',
  673. 'subnetpool_list')})
  674. def test_network_create_post_with_subnet_cidr_invalid_v6_range(
  675. self,
  676. test_with_subnetpool=False
  677. ):
  678. network = self.networks.first()
  679. subnet_v6 = self.subnets.list()[4]
  680. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  681. 'network_availability_zone').\
  682. AndReturn(False)
  683. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  684. 'subnet_allocation').\
  685. AndReturn(True)
  686. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  687. AndReturn(self.subnetpools.list())
  688. self.mox.ReplayAll()
  689. form_data = {'net_name': network.name,
  690. 'shared': False,
  691. 'admin_state': network.admin_state_up,
  692. 'with_subnet': True}
  693. if test_with_subnetpool:
  694. subnetpool = self.subnetpools.first()
  695. form_data['subnetpool'] = subnetpool.id
  696. form_data['prefixlen'] = subnetpool.default_prefixlen
  697. form_data.update(form_data_subnet(subnet_v6, cidr='fc00::/7',
  698. allocation_pools=[]))
  699. url = reverse('horizon:project:networks:create')
  700. res = self.client.post(url, form_data)
  701. expected_msg = ("CIDRs allowed for user private ipv6 networks "
  702. "are fc00::/9.")
  703. self.assertContains(res, expected_msg)
  704. @test.update_settings(ALLOWED_PRIVATE_SUBNET_CIDR={'ipv6': ['fc00::/9']})
  705. def test_network_create_post_with_subnet_cidr_invalid_v6_range_w_snpool(
  706. self):
  707. self.test_network_create_post_with_subnet_cidr_invalid_v4_range(
  708. test_with_subnetpool=True)
  709. @test.create_stubs({api.neutron: ('network_create',
  710. 'subnet_create',
  711. 'is_extension_supported',
  712. 'subnetpool_list')})
  713. def test_network_create_post_with_subnet_cidr_not_restrict(self):
  714. network = self.networks.first()
  715. subnet = self.subnets.first()
  716. cidr = '30.30.30.0/24'
  717. gateway_ip = '30.30.30.1'
  718. params = {'name': network.name,
  719. 'admin_state_up': network.admin_state_up,
  720. 'shared': False}
  721. subnet_params = {'network_id': network.id,
  722. 'name': subnet.name,
  723. 'cidr': cidr,
  724. 'ip_version': subnet.ip_version,
  725. 'gateway_ip': gateway_ip,
  726. 'enable_dhcp': subnet.enable_dhcp}
  727. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  728. 'network_availability_zone').\
  729. AndReturn(False)
  730. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  731. 'subnet_allocation').\
  732. AndReturn(True)
  733. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  734. AndReturn(self.subnetpools.list())
  735. api.neutron.network_create(IsA(http.HttpRequest),
  736. **params).AndReturn(network)
  737. api.neutron.subnet_create(IsA(http.HttpRequest),
  738. **subnet_params).AndReturn(subnet)
  739. self.mox.ReplayAll()
  740. form_data = {'net_name': network.name,
  741. 'admin_state': network.admin_state_up,
  742. 'shared': False,
  743. 'with_subnet': True}
  744. form_data.update(form_data_subnet(subnet, cidr=cidr,
  745. gateway_ip=gateway_ip,
  746. allocation_pools=[]))
  747. url = reverse('horizon:project:networks:create')
  748. res = self.client.post(url, form_data)
  749. self.assertNoFormErrors(res)
  750. self.assertRedirectsNoFollow(res, INDEX_URL)
  751. @test.create_stubs({api.neutron: ('is_extension_supported',
  752. 'subnetpool_list',)})
  753. def test_network_create_post_with_subnet_cidr_inconsistent(
  754. self,
  755. test_with_subnetpool=False
  756. ):
  757. network = self.networks.first()
  758. subnet = self.subnets.first()
  759. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  760. 'network_availability_zone').\
  761. AndReturn(False)
  762. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  763. 'subnet_allocation').\
  764. AndReturn(True)
  765. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  766. AndReturn(self.subnetpools.list())
  767. self.mox.ReplayAll()
  768. # dummy IPv6 address
  769. cidr = '2001:0DB8:0:CD30:123:4567:89AB:CDEF/60'
  770. form_data = {'net_name': network.name,
  771. 'shared': False,
  772. 'admin_state': network.admin_state_up,
  773. 'with_subnet': True}
  774. if test_with_subnetpool:
  775. subnetpool = self.subnetpools.first()
  776. form_data['subnetpool'] = subnetpool.id
  777. form_data['prefixlen'] = subnetpool.default_prefixlen
  778. form_data.update(form_data_subnet(subnet, cidr=cidr,
  779. allocation_pools=[]))
  780. url = reverse('horizon:project:networks:create')
  781. res = self.client.post(url, form_data)
  782. expected_msg = 'Network Address and IP version are inconsistent.'
  783. self.assertContains(res, expected_msg)
  784. def test_network_create_post_with_subnet_cidr_inconsistent_w_snpool(self):
  785. self.test_network_create_post_with_subnet_cidr_inconsistent(
  786. test_with_subnetpool=True)
  787. @test.create_stubs({api.neutron: ('is_extension_supported',
  788. 'subnetpool_list',)})
  789. def test_network_create_post_with_subnet_gw_inconsistent(
  790. self,
  791. test_with_subnetpool=False,
  792. ):
  793. network = self.networks.first()
  794. subnet = self.subnets.first()
  795. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  796. 'network_availability_zone').\
  797. AndReturn(False)
  798. api.neutron.is_extension_supported(IsA(http.HttpRequest),
  799. 'subnet_allocation').\
  800. AndReturn(True)
  801. api.neutron.subnetpool_list(IsA(http.HttpRequest)).\
  802. AndReturn(self.subnetpools.list())
  803. self.mox.ReplayAll()
  804. # dummy IPv6 address
  805. gateway_ip = '2001:0DB8:0:CD30:123:4567:89AB:CDEF'
  806. form_data = {'net_name': network.name,
  807. 'shared': False,
  808. 'admin_state': network.admin_state_up,
  809. 'with_subnet': True}
  810. if test_with_subnetpool:
  811. subnetpool = self.subnetpools.first()
  812. form_data['subnetpool'] = subnetpool.id
  813. form_data['prefixlen'] = subnetpool.default_prefixlen
  814. form_data.update(form_data_subnet(subnet, gateway_ip=gateway_ip,
  815. allocation_pools=[]))
  816. url = reverse('horizon:project:networks:create')
  817. res = self.client.post(url, form_data)
  818. self.assertContains(res, 'Gateway IP and IP version are inconsistent.')
  819. def test_network_create_post_with_subnet_gw_inconsistent_w_snpool(self):
  820. self.test_network_create_post_with_subnet_gw_inconsistent(
  821. test_with_subnetpool=True)
  822. @test.create_stubs({api.neutron: ('network_get',)})
  823. def test_network_update_get(self):
  824. network = self.networks.first()
  825. api.neutron.network_get(IsA(http.HttpRequest), network.id,
  826. expand_subnet=False).AndReturn(network)
  827. self.mox.ReplayAll()
  828. url = reverse('horizon:project:networks:update', args=[network.id])
  829. res = self.client.get(url)
  830. self.assertTemplateUsed(res, 'project/networks/update.html')
  831. @test.create_stubs({api.neutron: ('network_get',)})
  832. def test_network_update_get_exception(self):
  833. network = self.networks.first()
  834. api.neutron.network_get(IsA(http.HttpRequest), network.id)\
  835. .AndRaise(self.exceptions.neutron)
  836. self.mox.ReplayAll()
  837. url = reverse('horizon:project:networks:update', args=[network.id])
  838. res = self.client.get(url)
  839. redir_url = INDEX_URL
  840. self.assertRedirectsNoFollow(res, redir_url)
  841. @test.create_stubs({api.neutron: ('network_update',
  842. 'network_get',)})
  843. def test_network_update_post(self):
  844. network = self.networks.first()
  845. api.neutron.network_update(IsA(http.HttpRequest), network.id,
  846. name=network.name,
  847. admin_state_up=network.admin_state_up,
  848. shared=network.shared)\
  849. .AndReturn(network)
  850. api.neutron.network_get(IsA(http.HttpRequest), network.id,
  851. expand_subnet=False).AndReturn(network)
  852. self.mox.ReplayAll()
  853. form_data = {'network_id': network.id,
  854. 'shared': False,
  855. 'name': network.name,
  856. 'admin_state': network.admin_state_up,
  857. 'tenant_id': network.tenant_id}
  858. url = reverse('horizon:project:networks:update', args=[network.id])
  859. res = self.client.post(url, form_data)
  860. self.assertRedirectsNoFollow(res, INDEX_URL)
  861. @test.create_stubs({api.neutron: ('network_update',
  862. 'network_get',)})
  863. def test_network_update_post_exception(self):
  864. network = self.networks.first()
  865. api.neutron.network_get(IsA(http.HttpRequest), network.id,
  866. expand_subnet=False).AndReturn(network)
  867. api.neutron.network_update(IsA(http.HttpRequest), network.id,
  868. name=network.name,
  869. admin_state_up=network.admin_state_up,
  870. shared=False)\
  871. .AndRaise(self.exceptions.neutron)
  872. self.mox.ReplayAll()
  873. form_data = {'network_id': network.id,
  874. 'shared': False,
  875. 'name': network.name,
  876. 'admin_state': network.admin_state_up,
  877. 'tenant_id': network.tenant_id}
  878. url = reverse('horizon:project:networks:update', args=[network.id])
  879. res = self.client.post(url, form_data)
  880. self.assertRedirectsNoFollow(res, INDEX_URL)
  881. @test.create_stubs({api.neutron: ('network_get',
  882. 'network_list',
  883. 'network_delete',
  884. 'is_extension_supported')})
  885. def test_delete_network_no_subnet(self):
  886. network = self.networks.first()
  887. network.subnets = []
  888. api.neutron.network_get(IsA(http.HttpRequest),
  889. network.id,
  890. expand_subnet=False)\
  891. .AndReturn(network)
  892. api.neutron.is_extension_supported(
  893. IsA(http.HttpRequest), 'network_availability_zone')\
  894. .MultipleTimes().AndReturn(True)
  895. self._stub_net_list()
  896. api.neutron.network_delete(IsA(http.HttpRequest), network.id)
  897. self.mox.ReplayAll()
  898. form_data = {'action': 'networks__delete__%s' % network.id}
  899. res = self.client.post(INDEX_URL, form_data)
  900. self.assertRedirectsNoFollow(res, INDEX_URL)
  901. @test.create_stubs({api.neutron: ('network_get',
  902. 'network_list',
  903. 'network_delete',
  904. 'subnet_delete',
  905. 'is_extension_supported')})
  906. def test_delete_network_with_subnet(self):
  907. network = self.networks.first()
  908. network.subnets = [subnet.id for subnet in network.subnets]
  909. subnet_id = network.subnets[0]
  910. subnetv6_id = network.subnets[1]
  911. api.neutron.network_get(IsA(http.HttpRequest),
  912. network.id,
  913. expand_subnet=False)\
  914. .AndReturn(network)
  915. api.neutron.is_extension_supported(
  916. IsA(http.HttpRequest), 'network_availability_zone')\
  917. .MultipleTimes().AndReturn(True)
  918. self._stub_net_list()
  919. api.neutron.subnet_delete(IsA(http.HttpRequest), subnet_id)
  920. api.neutron.subnet_delete(IsA(http.HttpRequest), subnetv6_id)
  921. api.neutron.network_delete(IsA(http.HttpRequest), network.id)
  922. self.mox.ReplayAll()
  923. form_data = {'action': 'networks__delete__%s' % network.id}
  924. res = self.client.post(INDEX_URL, form_data)
  925. self.assertRedirectsNoFollow(res, INDEX_URL)
  926. @test.create_stubs({api.neutron: ('network_get',
  927. 'network_list',
  928. 'network_delete',
  929. 'subnet_delete',
  930. 'is_extension_supported')})
  931. def test_delete_network_exception(self):
  932. network = self.networks.first()
  933. network.subnets = [subnet.id for subnet in network.subnets]
  934. subnet_id = network.subnets[0]
  935. subnetv6_id = network.subnets[1]
  936. api.neutron.network_get(IsA(http.HttpRequest),
  937. network.id,
  938. expand_subnet=False)\
  939. .AndReturn(network)
  940. api.neutron.is_extension_supported(
  941. IsA(http.HttpRequest), 'network_availability_zone')\
  942. .MultipleTimes().AndReturn(True)
  943. self._stub_net_list()
  944. api.neutron.subnet_delete(IsA(http.HttpRequest), subnet_id)
  945. api.neutron.subnet_delete(IsA(http.HttpRequest), subnetv6_id)
  946. api.neutron.network_delete(IsA(http.HttpRequest), network.id)\
  947. .AndRaise(self.exceptions.neutron)
  948. self.mox.ReplayAll()
  949. form_data = {'action': 'networks__delete__%s' % network.id}
  950. res = self.client.post(INDEX_URL, form_data)
  951. self.assertRedirectsNoFollow(res, INDEX_URL)
  952. class NetworkViewTests(test.TestCase, NetworkStubMixin):
  953. def _test_create_button_shown_when_quota_disabled(
  954. self,
  955. find_button_fn):
  956. # if quota_data doesnt contain a networks|subnets|routers key or
  957. # these keys are empty dicts, its disabled
  958. quota_data = self.neutron_quota_usages.first()
  959. quota_data['networks'].pop('available')
  960. quota_data['subnets'].pop('available')
  961. self._stub_net_list()
  962. quotas.tenant_quota_usages(
  963. IsA(http.HttpRequest), targets=('networks', )) \
  964. .MultipleTimes().AndReturn(quota_data)
  965. quotas.tenant_quota_usages(
  966. IsA(http.HttpRequest), targets=('subnets', )) \
  967. .MultipleTimes().AndReturn(quota_data)
  968. api.neutron.is_extension_supported(
  969. IsA(http.HttpRequest), 'network_availability_zone')\
  970. .MultipleTimes().AndReturn(True)
  971. self.mox.ReplayAll()
  972. res = self.client.get(INDEX_URL)
  973. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  974. networks = res.context['networks_table'].data
  975. self.assertItemsEqual(networks, self.networks.list())
  976. button = find_button_fn(res)
  977. self.assertFalse('disabled' in button.classes,
  978. "The create button should not be disabled")
  979. return button
  980. def _test_create_button_disabled_when_quota_exceeded(
  981. self, find_button_fn, network_quota=5, subnet_quota=5, ):
  982. quota_data = self.neutron_quota_usages.first()
  983. quota_data['networks']['available'] = network_quota
  984. quota_data['subnets']['available'] = subnet_quota
  985. self._stub_net_list()
  986. quotas.tenant_quota_usages(
  987. IsA(http.HttpRequest), targets=('networks', )) \
  988. .MultipleTimes().AndReturn(quota_data)
  989. quotas.tenant_quota_usages(
  990. IsA(http.HttpRequest), targets=('subnets', )) \
  991. .MultipleTimes().AndReturn(quota_data)
  992. api.neutron.is_extension_supported(
  993. IsA(http.HttpRequest), 'network_availability_zone')\
  994. .MultipleTimes().AndReturn(True)
  995. self.mox.ReplayAll()
  996. res = self.client.get(INDEX_URL)
  997. self.assertTemplateUsed(res, INDEX_TEMPLATE)
  998. networks = res.context['networks_table'].data
  999. self.assertItemsEqual(networks, self.networks.list())
  1000. button = find_button_fn(res)
  1001. self.assertIn('disabled', button.classes,
  1002. "The create button should be disabled")
  1003. return button
  1004. @test.create_stubs({api.neutron: ('network_list',
  1005. 'is_extension_supported'),
  1006. quotas: ('tenant_quota_usages',)})
  1007. def test_network_create_button_disabled_when_quota_exceeded_index(self):
  1008. networks_tables.CreateNetwork()
  1009. def _find_net_button(res):
  1010. return self.getAndAssertTableAction(res, 'networks', 'create')
  1011. self._test_create_button_disabled_when_quota_exceeded(_find_net_button,
  1012. network_quota=0)
  1013. @test.create_stubs({api.neutron: ('network_list',
  1014. 'is_extension_supported'),
  1015. quotas: ('tenant_quota_usages',)})
  1016. def test_subnet_create_button_disabled_when_quota_exceeded_index(self):
  1017. network_id = self.networks.first().id
  1018. networks_tables.CreateSubnet()
  1019. def _find_subnet_button(res):
  1020. return self.getAndAssertTableRowAction(res, 'networks',
  1021. 'subnet', network_id)
  1022. self._test_create_button_disabled_when_quota_exceeded(
  1023. _find_subnet_button, subnet_quota=0)
  1024. @test.create_stubs({api.neutron: ('network_list',
  1025. 'is_extension_supported'),
  1026. quotas: ('tenant_quota_usages',)})
  1027. def test_network_create_button_shown_when_quota_disabled_index(self):
  1028. # if quota_data doesnt contain a networks["available"] key its disabled
  1029. networks_tables.CreateNetwork()
  1030. self._test_create_button_shown_when_quota_disabled(
  1031. lambda res: self.getAndAssertTableAction(res, 'networks', 'create')
  1032. )
  1033. @test.create_stubs({api.neutron: ('network_list',
  1034. 'is_extension_supported'),
  1035. quotas: ('tenant_quota_usages',)})
  1036. def test_subnet_create_button_shown_when_quota_disabled_index(self):
  1037. # if quota_data doesnt contain a subnets["available"] key, its disabled
  1038. network_id = self.networks.first().id
  1039. def _find_subnet_button(res):
  1040. return self.getAndAssertTableRowAction(res, 'networks',
  1041. 'subnet', network_id)
  1042. self._test_create_button_shown_when_quota_disabled(_find_subnet_button)
  1043. @test.create_stubs({api.neutron: ('network_get',
  1044. 'subnet_list',
  1045. 'port_list',
  1046. 'is_extension_supported',),
  1047. quotas: ('tenant_quota_usages',)})
  1048. def _test_subnet_create_button(self, quota_data):
  1049. network_id = self.networks.first().id
  1050. api.neutron.network_get(
  1051. IsA(http.HttpRequest), network_id)\
  1052. .MultipleTimes().AndReturn(self.networks.first())
  1053. api.neutron.subnet_list(
  1054. IsA(http.HttpRequest), network_id=network_id)\
  1055. .AndReturn(self.subnets.list())
  1056. api.neutron.is_extension_supported(
  1057. IsA(http.HttpRequest), 'mac-learning')\
  1058. .AndReturn(False)
  1059. quotas.tenant_quota_usages(
  1060. IsA(http.HttpRequest), targets=('subnets', )) \
  1061. .MultipleTimes().AndReturn(quota_data)
  1062. api.neutron.is_extension_supported(
  1063. IsA(http.HttpRequest), 'network_availability_zone')\
  1064. .MultipleTimes().AndReturn(True)
  1065. self.mox.ReplayAll()
  1066. url = urlunquote(reverse('horizon:project:networks:subnets_tab',
  1067. args=[network_id]))
  1068. res = self.client.get(url)
  1069. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  1070. subnets = res.context['subnets_table'].data
  1071. self.assertItemsEqual(subnets, self.subnets.list())
  1072. return self.getAndAssertTableAction(res, 'subnets', 'create')
  1073. def test_subnet_create_button_disabled_when_quota_exceeded_detail(self):
  1074. quota_data = self.neutron_quota_usages.first()
  1075. quota_data['subnets']['available'] = 0
  1076. create_action = self._test_subnet_create_button(quota_data)
  1077. self.assertIn('disabled', create_action.classes,
  1078. 'The create button should be disabled')
  1079. def test_subnet_create_button_enabled_when_quota_disabled(self):
  1080. # In case of enable_quotas False, neutron related items
  1081. # are not set in a response from tenant_quota_usages.
  1082. quota_data = {}
  1083. create_action = self._test_subnet_create_button(quota_data)
  1084. self.assertNotIn('disabled', create_action.classes,
  1085. 'The create button should be enabled')
  1086. @test.create_stubs({api.neutron: ('network_list',
  1087. 'is_extension_supported'),
  1088. quotas: ('tenant_quota_usages',)})
  1089. def test_create_button_attributes(self):
  1090. create_action = self._test_create_button_shown_when_quota_disabled(
  1091. lambda res: self.getAndAssertTableAction(res, 'networks', 'create')
  1092. )
  1093. self.assertEqual(set(['ajax-modal']), set(create_action.classes))
  1094. self.assertEqual('horizon:project:networks:create', create_action.url)
  1095. self.assertEqual('Create Network',
  1096. six.text_type(create_action.verbose_name))
  1097. self.assertEqual((('network', 'create_network'),),
  1098. create_action.policy_rules)
  1099. def test_create_subnet_button_attributes(self):
  1100. quota_data = self.neutron_quota_usages.first()
  1101. quota_data['subnets']['available'] = 1
  1102. create_action = self._test_subnet_create_button(quota_data)
  1103. self.assertEqual(set(['ajax-modal']), set(create_action.classes))
  1104. self.assertEqual('horizon:project:networks:createsubnet',
  1105. create_action.url)
  1106. self.assertEqual('Create Subnet',
  1107. six.text_type(create_action.verbose_name))
  1108. self.assertEqual((('network', 'create_subnet'),),
  1109. create_action.policy_rules)
  1110. @test.create_stubs({api.neutron: ('network_get',
  1111. 'port_list',
  1112. 'is_extension_supported',),
  1113. quotas: ('tenant_quota_usages',)})
  1114. def _test_port_create_button(self, quota_data):
  1115. network_id = self.networks.first().id
  1116. api.neutron.network_get(
  1117. IsA(http.HttpRequest), network_id) \
  1118. .MultipleTimes().AndReturn(self.networks.first())
  1119. api.neutron.port_list(
  1120. IsA(http.HttpRequest), network_id=network_id) \
  1121. .AndReturn(self.ports.list())
  1122. api.neutron.is_extension_supported(
  1123. IsA(http.HttpRequest), 'mac-learning') \
  1124. .AndReturn(False)
  1125. quotas.tenant_quota_usages(
  1126. IsA(http.HttpRequest), targets=('subnets', )) \
  1127. .MultipleTimes().AndReturn(quota_data)
  1128. quotas.tenant_quota_usages(
  1129. IsA(http.HttpRequest), targets=('ports',)) \
  1130. .MultipleTimes().AndReturn(quota_data)
  1131. api.neutron.is_extension_supported(
  1132. IsA(http.HttpRequest), 'network_availability_zone')\
  1133. .MultipleTimes().AndReturn(True)
  1134. self.mox.ReplayAll()
  1135. url = urlunquote(reverse('horizon:project:networks:ports_tab',
  1136. args=[network_id]))
  1137. res = self.client.get(url)
  1138. self.assertTemplateUsed(res, 'horizon/common/_detail.html')
  1139. ports = res.context['ports_table'].data
  1140. self.assertItemsEqual(ports, self.ports.list())
  1141. return self.getAndAssertTableAction(res, 'ports', 'create')
  1142. def test_port_create_button_disabled_when_quota_exceeded(self):
  1143. quota_data = self.neutron_quota_usages.first()
  1144. quota_data['ports']['available'] = 0
  1145. create_action = self._test_port_create_button(quota_data)
  1146. self.assertIn('disabled', create_action.classes,
  1147. 'The create button should be disabled')
  1148. def test_port_create_button_enabled_when_quota_disabled(self):
  1149. # In case of enable_quotas False, neutron related items
  1150. # are not set in a response from tenant_quota_usages.
  1151. quota_data = {}
  1152. create_action = self._test_port_create_button(quota_data)
  1153. self.assertNotIn('disabled', create_action.classes,
  1154. 'The create button should be enabled')
  1155. def test_create_port_button_attributes(self):
  1156. quota_data = self.neutron_quota_usages.first()
  1157. quota_data['ports']['available'] = 1
  1158. create_action = self._test_port_create_button(quota_data)
  1159. self.assertEqual(set(['ajax-modal']), set(create_action.classes))
  1160. self.assertEqual('horizon:project:networks:addport',
  1161. create_action.url)
  1162. self.assertEqual('Create Port',
  1163. six.text_type(create_action.verbose_name))
  1164. self.assertEqual((('network', 'create_port'),),
  1165. create_action.policy_rules)