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

test_attributes.py 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2013 Mirantis, Inc.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. from oslo_serialization import jsonutils
  16. import six
  17. from nailgun import consts
  18. from nailgun.db.sqlalchemy.models import Release
  19. from nailgun import objects
  20. from nailgun.settings import settings
  21. from nailgun.test.base import BaseIntegrationTest
  22. from nailgun.utils import reverse
  23. class TestClusterAttributes(BaseIntegrationTest):
  24. ATTRIBUTES_WITH_RESTRICTIONS = {
  25. 'editable': {
  26. 'test': {
  27. 'comp1': {
  28. 'description': 'desc',
  29. 'label': 'Comp 1',
  30. 'type': 'checkbox',
  31. 'value': False,
  32. 'weight': 10,
  33. },
  34. 'comp2': {
  35. 'description': 'desc',
  36. 'label': 'Comp 2',
  37. 'type': 'checkbox',
  38. 'value': False,
  39. 'weight': 20,
  40. 'restrictions': ["settings:test.comp1.value == true"],
  41. },
  42. 'comp3': {
  43. 'description': 'desc',
  44. 'label': 'Comp 3',
  45. 'type': 'text',
  46. 'value': '',
  47. 'weight': 30,
  48. 'restrictions': [
  49. {
  50. 'condition': "settings:test.comp1.value == true",
  51. 'action': "disable"
  52. }
  53. ],
  54. 'regex': {
  55. 'source': '^[a-zA-Z\d][a-zA-Z\d_\-.]+(:[0-9]+)?$',
  56. 'error': "Wrong Comp 3 value"
  57. }
  58. }
  59. }
  60. }
  61. }
  62. def test_attributes_creation(self):
  63. cluster = self.env.create_cluster(api=True)
  64. resp = self.app.get(
  65. reverse(
  66. 'ClusterAttributesHandler',
  67. kwargs={'cluster_id': cluster['id']}),
  68. headers=self.default_headers
  69. )
  70. release = objects.Release.get_by_uid(cluster['release_id'])
  71. self.assertEqual(200, resp.status_code)
  72. self._compare_editable(
  73. release.attributes_metadata['editable'],
  74. resp.json_body['editable'],
  75. cluster
  76. )
  77. attrs = objects.Cluster.get_attributes(cluster)
  78. self._compare_generated(
  79. release.attributes_metadata['generated'],
  80. attrs['generated'],
  81. cluster
  82. )
  83. def test_500_if_no_attributes(self):
  84. cluster = self.env.create_cluster(api=False)
  85. self.db.delete(cluster.attributes)
  86. self.db.commit()
  87. resp = self.app.put(
  88. reverse(
  89. 'ClusterAttributesHandler',
  90. kwargs={'cluster_id': cluster.id}),
  91. params=jsonutils.dumps({
  92. 'editable': {
  93. "foo": "bar"
  94. },
  95. }),
  96. headers=self.default_headers,
  97. expect_errors=True
  98. )
  99. self.assertEqual(500, resp.status_code)
  100. def test_attributes_update_put(self):
  101. cluster = self.env.create_cluster(api=True)
  102. cluster_id = cluster['id']
  103. resp = self.app.get(
  104. reverse(
  105. 'ClusterAttributesHandler',
  106. kwargs={'cluster_id': cluster_id}),
  107. headers=self.default_headers
  108. )
  109. self.assertEqual(200, resp.status_code)
  110. resp = self.app.put(
  111. reverse(
  112. 'ClusterAttributesHandler',
  113. kwargs={'cluster_id': cluster_id}),
  114. params=jsonutils.dumps({
  115. 'editable': {
  116. 'foo': {'bar': None}
  117. },
  118. }),
  119. headers=self.default_headers
  120. )
  121. self.assertEqual(200, resp.status_code)
  122. attrs = objects.Cluster.get_editable_attributes(cluster)
  123. self.assertEqual({'bar': None}, attrs["foo"])
  124. attrs.pop('foo')
  125. # 400 on generated update
  126. resp = self.app.put(
  127. reverse(
  128. 'ClusterAttributesHandler',
  129. kwargs={'cluster_id': cluster_id}),
  130. params=jsonutils.dumps({
  131. 'generated': {
  132. "foo": "bar"
  133. },
  134. }),
  135. headers=self.default_headers,
  136. expect_errors=True
  137. )
  138. self.assertEqual(400, resp.status_code)
  139. # 400 if editable is not dict
  140. resp = self.app.put(
  141. reverse(
  142. 'ClusterAttributesHandler',
  143. kwargs={'cluster_id': cluster_id}),
  144. params=jsonutils.dumps({
  145. 'editable': ["foo", "bar"],
  146. }),
  147. headers=self.default_headers,
  148. expect_errors=True
  149. )
  150. self.assertEqual(400, resp.status_code)
  151. def test_attributes_update_patch(self):
  152. cluster = self.env.create_cluster(api=True)
  153. resp = self.app.get(
  154. reverse(
  155. 'ClusterAttributesHandler',
  156. kwargs={'cluster_id': cluster['id']}),
  157. headers=self.default_headers
  158. )
  159. self.assertEqual(200, resp.status_code)
  160. resp = self.app.patch(
  161. reverse(
  162. 'ClusterAttributesHandler',
  163. kwargs={'cluster_id': cluster['id']}),
  164. params=jsonutils.dumps({
  165. 'editable': {
  166. 'foo': {'bar': None}
  167. },
  168. }),
  169. headers=self.default_headers
  170. )
  171. self.assertEqual(200, resp.status_code)
  172. attrs = objects.Cluster.get_editable_attributes(cluster)
  173. self.assertEqual({'bar': None}, attrs["foo"])
  174. attrs.pop('foo')
  175. self.assertNotEqual(attrs, {})
  176. def test_failing_attributes_put(self):
  177. cluster_id = self.env.create_cluster(api=True)['id']
  178. resp = self.app.get(
  179. reverse(
  180. 'ClusterAttributesHandler',
  181. kwargs={'cluster_id': cluster_id}),
  182. headers=self.default_headers
  183. )
  184. self.assertEqual(200, resp.status_code)
  185. resp = self.app.patch(
  186. reverse(
  187. 'ClusterAttributesHandler',
  188. kwargs={'cluster_id': cluster_id}),
  189. params=jsonutils.dumps({
  190. 'editable': {
  191. 'storage': {
  192. 'osd_pool_size': {
  193. 'description': 'desc',
  194. 'label': 'OSD Pool Size',
  195. 'type': 'text',
  196. 'value': True,
  197. 'weight': 80,
  198. },
  199. },
  200. },
  201. }),
  202. headers=self.default_headers,
  203. expect_errors=True
  204. )
  205. self.assertEqual(400, resp.status_code)
  206. def test_failing_attributes_with_restrictions(self):
  207. cluster = self.env.create_cluster(api=False)
  208. objects.Cluster.patch_attributes(
  209. cluster, self.ATTRIBUTES_WITH_RESTRICTIONS)
  210. resp = self.app.patch(
  211. reverse(
  212. 'ClusterAttributesHandler',
  213. kwargs={'cluster_id': cluster.id}),
  214. params=jsonutils.dumps({
  215. 'editable': {
  216. 'test': {
  217. 'comp1': {
  218. 'value': True
  219. },
  220. 'comp2': {
  221. 'value': True
  222. }
  223. }
  224. }
  225. }),
  226. headers=self.default_headers,
  227. expect_errors=True
  228. )
  229. self.assertEqual(400, resp.status_code)
  230. extended_restr = {
  231. 'condition': "settings:test.comp1.value == true",
  232. 'action': 'disable',
  233. }
  234. self.assertIn(
  235. "Validation failed for attribute 'Comp 2': restriction with action"
  236. "='{}' and condition='{}' failed due to attribute value='True'"
  237. .format(extended_restr['action'], extended_restr['condition']),
  238. resp.json_body['message'])
  239. def test_disabled_attributes_with_restrictions_not_fail(self):
  240. cluster = self.env.create_cluster(api=False)
  241. objects.Cluster.patch_attributes(
  242. cluster, self.ATTRIBUTES_WITH_RESTRICTIONS)
  243. resp = self.app.patch(
  244. reverse(
  245. 'ClusterAttributesHandler',
  246. kwargs={'cluster_id': cluster.id}),
  247. params=jsonutils.dumps({
  248. 'editable': {
  249. 'test': {
  250. 'comp1': {
  251. 'value': True
  252. },
  253. 'comp3': {
  254. 'value': ''
  255. }
  256. }
  257. }
  258. }),
  259. headers=self.default_headers
  260. )
  261. self.assertEqual(200, resp.status_code)
  262. def test_enabled_attributes_raise_regex_exception(self):
  263. cluster = self.env.create_cluster(api=False)
  264. objects.Cluster.patch_attributes(
  265. cluster, self.ATTRIBUTES_WITH_RESTRICTIONS)
  266. resp = self.app.patch(
  267. reverse(
  268. 'ClusterAttributesHandler',
  269. kwargs={'cluster_id': cluster.id}),
  270. params=jsonutils.dumps({
  271. 'editable': {
  272. 'test': {
  273. 'comp1': {
  274. 'value': False
  275. },
  276. 'comp3': {
  277. 'value': ''
  278. }
  279. }
  280. }
  281. }),
  282. headers=self.default_headers,
  283. expect_errors=True
  284. )
  285. self.assertEqual(400, resp.status_code)
  286. self.assertEqual(
  287. "Some restrictions didn't pass verification: "
  288. "['Wrong Comp 3 value']",
  289. resp.json_body['message']
  290. )
  291. def test_get_default_attributes(self):
  292. cluster = self.env.create_cluster(api=True)
  293. release = self.db.query(Release).get(
  294. cluster['release_id']
  295. )
  296. resp = self.app.put(
  297. reverse(
  298. 'ClusterAttributesDefaultsHandler',
  299. kwargs={'cluster_id': cluster['id']}),
  300. headers=self.default_headers
  301. )
  302. self.assertEqual(200, resp.status_code)
  303. self._compare_editable(
  304. release.attributes_metadata['editable'],
  305. resp.json_body['editable'],
  306. cluster
  307. )
  308. def test_get_last_deployed_attributes(self):
  309. cluster = self.env.create_cluster(api=True)
  310. cluster_attrs = objects.Cluster.get_editable_attributes(
  311. self.env.clusters[-1]
  312. )
  313. transaction = objects.Transaction.create({
  314. 'cluster_id': cluster.id,
  315. 'status': consts.TASK_STATUSES.ready,
  316. 'name': consts.TASK_NAMES.deployment
  317. })
  318. objects.Transaction.attach_cluster_settings(
  319. transaction, {'editable': cluster_attrs}
  320. )
  321. self.assertIsNotNone(
  322. objects.TransactionCollection.get_last_succeed_run(cluster)
  323. )
  324. resp = self.app.get(
  325. reverse(
  326. 'ClusterAttributesDeployedHandler',
  327. kwargs={'cluster_id': cluster.id}),
  328. headers=self.default_headers
  329. )
  330. self.assertEqual(200, resp.status_code)
  331. self.datadiff(cluster_attrs, resp.json_body['editable'])
  332. def test_get_deployed_attributes_fails_if_no_attrs(self):
  333. cluster = self.env.create_cluster(api=True)
  334. resp = self.app.get(
  335. reverse(
  336. 'ClusterAttributesDeployedHandler',
  337. kwargs={'cluster_id': cluster['id']}),
  338. headers=self.default_headers,
  339. expect_errors=True,
  340. )
  341. self.assertEqual(404, resp.status_code)
  342. def test_attributes_set_defaults(self):
  343. cluster = self.env.create_cluster(api=True)
  344. # Change editable attributes.
  345. resp = self.app.put(
  346. reverse(
  347. 'ClusterAttributesHandler',
  348. kwargs={'cluster_id': cluster['id']}),
  349. params=jsonutils.dumps({
  350. 'editable': {
  351. 'foo': {'bar': None}
  352. },
  353. }),
  354. headers=self.default_headers,
  355. expect_errors=True
  356. )
  357. self.assertEqual(200, resp.status_code, resp.body)
  358. attrs = objects.Cluster.get_editable_attributes(cluster)
  359. self.assertEqual({'bar': None}, attrs['foo'])
  360. # Set attributes to defaults.
  361. resp = self.app.put(
  362. reverse(
  363. 'ClusterAttributesDefaultsHandler',
  364. kwargs={'cluster_id': cluster['id']}),
  365. headers=self.default_headers
  366. )
  367. self.assertEqual(200, resp.status_code)
  368. release = self.db.query(Release).get(
  369. cluster['release_id']
  370. )
  371. self._compare_editable(
  372. release.attributes_metadata['editable'],
  373. resp.json_body['editable'],
  374. cluster
  375. )
  376. def test_attributes_merged_values(self):
  377. cluster = self.env.create_cluster(api=True)
  378. cluster_db = objects.Cluster.get_by_uid(cluster['id'])
  379. orig_attrs = objects.Attributes.merged_attrs(cluster_db.attributes)
  380. attrs = objects.Attributes.merged_attrs_values(cluster_db.attributes)
  381. for group, group_attrs in six.iteritems(orig_attrs):
  382. for attr, orig_value in six.iteritems(group_attrs):
  383. if group == 'common':
  384. value = attrs[attr]
  385. elif group == 'additional_components':
  386. for c, val in six.iteritems(group_attrs):
  387. self.assertIn(c, attrs)
  388. if 'value' in val:
  389. self.assertEqual(val["value"],
  390. attrs[c]["enabled"])
  391. continue
  392. else:
  393. value = attrs[group][attr]
  394. if isinstance(orig_value, dict) and 'value' in orig_value:
  395. self.assertEqual(orig_value['value'], value)
  396. else:
  397. self.assertEqual(orig_value, value)
  398. def _compare_generated(self, d1, d2, cluster):
  399. if isinstance(d1, dict) and isinstance(d2, dict):
  400. for s_field, s_value in six.iteritems(d1):
  401. if s_field not in d2:
  402. raise KeyError()
  403. self._compare_generated(s_value, d2[s_field], cluster)
  404. elif isinstance(d1, six.string_types):
  405. if d1 in [u"", ""]:
  406. self.assertEqual(len(d2), 8)
  407. else:
  408. self.assertEqual(
  409. d1.format(settings=settings, cluster=cluster),
  410. d2.format(settings=settings, cluster=cluster))
  411. def _compare_editable(self, r_attrs, c_attrs, cluster=None):
  412. """Compare editable attributes omitting the check of generated values
  413. :param r_attrs: attributes from release
  414. :param c_attrs: attributes from cluster
  415. """
  416. # TODO(ikalnitsky):
  417. # This code should be rewritten completely. We have to use one
  418. # function for comparing both generated and editable attributes.
  419. # Moreover, I'm not sure we have keep that code, since it duplicated
  420. # traverse function in many cases.
  421. if isinstance(r_attrs, dict) and isinstance(c_attrs, dict):
  422. for s_field, s_value in six.iteritems(r_attrs):
  423. if s_field not in c_attrs:
  424. self.fail("'{0}' not found in '{1}'".format(s_field,
  425. c_attrs))
  426. if s_field != 'regex':
  427. self._compare_editable(s_value, c_attrs[s_field], cluster)
  428. else:
  429. self.assertEqual(s_value, c_attrs[s_field])
  430. elif isinstance(r_attrs, (list, tuple)) and \
  431. isinstance(c_attrs, (list, tuple)):
  432. if len(r_attrs) != len(c_attrs):
  433. self.fail('Different number of elements: {0} vs {1}'.format(
  434. c_attrs, r_attrs))
  435. for index in range(0, len(r_attrs)):
  436. self._compare_editable(r_attrs[index], c_attrs[index], cluster)
  437. elif isinstance(c_attrs, six.string_types + (list, tuple)) and \
  438. isinstance(r_attrs, dict):
  439. self.assertIn("generator", r_attrs)
  440. elif isinstance(c_attrs, six.string_types) and \
  441. isinstance(r_attrs, six.string_types):
  442. self.assertEqual(
  443. c_attrs, r_attrs.format(settings=settings, cluster=cluster))
  444. else:
  445. self.assertEqual(c_attrs, r_attrs)
  446. def test_compare_editable(self):
  447. r_attrs = {
  448. 'section1': {
  449. 'value': 'string1'
  450. },
  451. 'section2': {
  452. 'subsection1': {
  453. 'value': 'string2'
  454. }
  455. }
  456. }
  457. c_attrs = {
  458. 'section1': {
  459. 'value': 'string1'
  460. },
  461. 'section2': {
  462. 'subsection1': {
  463. 'value': 'string2'
  464. }
  465. }
  466. }
  467. self._compare_editable(r_attrs, c_attrs)
  468. r_attrs['section1']['value'] = {
  469. 'generator': 'generator1'
  470. }
  471. self._compare_editable(r_attrs, c_attrs)
  472. r_attrs['section2']['subsection1']['value'] = {
  473. 'generator': 'generator2'
  474. }
  475. self._compare_editable(r_attrs, c_attrs)
  476. r_attrs['section1']['value'] = 'zzzzzzz'
  477. self.assertRaises(
  478. AssertionError, self._compare_editable, r_attrs, c_attrs)
  479. def test_editable_attributes_generators(self):
  480. cluster = self.env.create_cluster(api=True)
  481. editable = objects.Cluster.get_editable_attributes(cluster)
  482. self.assertEqual(
  483. editable["external_dns"]["dns_list"]["value"],
  484. settings.DNS_UPSTREAM
  485. )
  486. self.assertEqual(
  487. editable["external_ntp"]["ntp_list"]["value"],
  488. settings.NTP_UPSTREAM
  489. )
  490. def test_workloads_collector_attributes(self):
  491. cluster = self.env.create_cluster(api=True)
  492. editable = objects.Cluster.get_editable_attributes(cluster)
  493. self.assertEqual(
  494. editable["workloads_collector"]["enabled"]["value"],
  495. True
  496. )
  497. self.assertEqual(
  498. editable["workloads_collector"]["user"]["value"],
  499. "fuel_stats_user"
  500. )
  501. self.assertEqual(
  502. editable["workloads_collector"]["tenant"]["value"],
  503. "services"
  504. )
  505. self.assertEqual(
  506. len(editable["workloads_collector"]["password"]["value"]),
  507. 24
  508. )
  509. self.assertEqual(
  510. set(editable["workloads_collector"]["metadata"].keys()),
  511. set(["label", "weight", "restrictions", "group"])
  512. )
  513. class TestAlwaysEditable(BaseIntegrationTest):
  514. _reposetup = {
  515. 'repo_setup': {
  516. 'metadata': {
  517. 'label': 'Repositories',
  518. 'weight': 50,
  519. },
  520. 'repos': {
  521. 'type': 'custom_repo_configuration',
  522. 'extra_priority': 15,
  523. 'value': [
  524. {
  525. 'type': 'rpm',
  526. 'name': 'mos',
  527. 'uri': 'http://127.0.0.1:8080/myrepo'
  528. }
  529. ]
  530. }
  531. }}
  532. def setUp(self):
  533. super(TestAlwaysEditable, self).setUp()
  534. self.cluster = self.env.create(
  535. release_kwargs={
  536. 'version': 'liberty-8.0',
  537. 'operating_system': consts.RELEASE_OS.centos})
  538. def _put(self, data, expect_code=200):
  539. resp = self.app.put(
  540. reverse(
  541. 'ClusterAttributesHandler',
  542. kwargs={'cluster_id': self.cluster['id']}),
  543. params=jsonutils.dumps(data),
  544. expect_errors=True,
  545. headers=self.default_headers)
  546. self.assertEqual(expect_code, resp.status_code)
  547. return resp.json_body
  548. def test_can_change_repos_on_operational_cluster(self):
  549. self.cluster.status = consts.CLUSTER_STATUSES.operational
  550. self.db.flush()
  551. self.assertFalse(self.cluster.is_locked)
  552. data = {'editable': {}}
  553. data['editable'].update(self._reposetup)
  554. self._put(data, expect_code=200)
  555. attrs = self.cluster.attributes.editable
  556. self.assertEqual(attrs['repo_setup']['repos']['value'], [{
  557. 'type': 'rpm',
  558. 'name': 'mos',
  559. 'uri': 'http://127.0.0.1:8080/myrepo',
  560. }])
  561. class TestAttributesWithPlugins(BaseIntegrationTest):
  562. def setUp(self):
  563. super(TestAttributesWithPlugins, self).setUp()
  564. self.cluster = self.env.create(
  565. release_kwargs={
  566. 'operating_system': consts.RELEASE_OS.ubuntu,
  567. 'version': '2015.1.0-7.0',
  568. },
  569. cluster_kwargs={
  570. 'mode': consts.CLUSTER_MODES.ha_compact,
  571. 'net_provider': consts.CLUSTER_NET_PROVIDERS.neutron,
  572. 'net_segment_type': consts.NEUTRON_SEGMENT_TYPES.vlan,
  573. },
  574. nodes_kwargs=[
  575. {'roles': ['controller'], 'pending_addition': True},
  576. {'roles': ['compute'], 'pending_addition': True},
  577. ]
  578. )
  579. self.plugin_data = {
  580. 'releases': [
  581. {
  582. 'repository_path': 'repositories/ubuntu',
  583. 'version': self.cluster.release.version,
  584. 'os': self.cluster.release.operating_system.lower(),
  585. 'mode': [self.cluster.mode],
  586. }
  587. ]
  588. }
  589. def test_cluster_contains_plugins_attributes(self):
  590. self.env.create_plugin(cluster=self.cluster, **self.plugin_data)
  591. resp = self.app.get(
  592. reverse(
  593. 'ClusterAttributesHandler',
  594. kwargs={'cluster_id': self.cluster['id']}),
  595. headers=self.default_headers
  596. )
  597. self.assertEqual(200, resp.status_code)
  598. self.assertIn('testing_plugin', resp.json_body['editable'])
  599. def test_change_plugins_attributes(self):
  600. plugin = self.env.create_plugin(cluster=self.cluster,
  601. **self.plugin_data)
  602. def _modify_plugin(enabled=True):
  603. return self.app.put(
  604. reverse(
  605. 'ClusterAttributesHandler',
  606. kwargs={'cluster_id': self.cluster['id']}),
  607. params=jsonutils.dumps({
  608. 'editable': {
  609. plugin.name: {
  610. 'metadata': {
  611. 'class': 'plugin',
  612. 'label': 'Test plugin',
  613. 'toggleable': True,
  614. 'weight': 70,
  615. 'enabled': enabled,
  616. 'chosen_id': plugin.id,
  617. 'versions': [{
  618. 'metadata': {
  619. 'plugin_id': plugin.id,
  620. 'plugin_version': plugin.version
  621. },
  622. 'attr': {
  623. 'type': 'text',
  624. 'description': 'description',
  625. 'label': 'label',
  626. 'value': '1',
  627. 'weight': 25,
  628. 'restrictions': [{
  629. 'condition': 'true',
  630. 'action': 'hide'}]
  631. }
  632. }]
  633. },
  634. }
  635. }
  636. }),
  637. headers=self.default_headers
  638. )
  639. resp = _modify_plugin(enabled=True)
  640. self.assertEqual(200, resp.status_code)
  641. editable = objects.Cluster.get_editable_attributes(self.cluster)
  642. self.assertIn(plugin.name, editable)
  643. self.assertTrue(editable[plugin.name]['metadata']['enabled'])
  644. self.assertEqual('1', editable[plugin.name]['attr']['value'])
  645. resp = _modify_plugin(enabled=False)
  646. self.assertEqual(200, resp.status_code)
  647. editable = objects.Cluster.get_editable_attributes(self.cluster)
  648. self.assertNotIn(plugin.name, editable)
  649. def _modify_plugin(self, plugin, enabled):
  650. return self.app.put(
  651. reverse(
  652. 'ClusterAttributesHandler',
  653. kwargs={'cluster_id': self.cluster.id}
  654. ),
  655. params=jsonutils.dumps({
  656. 'editable': {
  657. plugin.name: {
  658. 'metadata': {
  659. 'class': 'plugin',
  660. 'enabled': enabled,
  661. 'chosen_id': plugin.id,
  662. 'versions': [{
  663. 'metadata': {
  664. 'plugin_id': plugin.id,
  665. 'plugin_version': plugin.version
  666. }
  667. }]
  668. }
  669. }
  670. }
  671. }),
  672. headers=self.default_headers,
  673. expect_errors=True
  674. )
  675. def test_install_plugins_after_deployment(self):
  676. self.cluster.status = consts.CLUSTER_STATUSES.operational
  677. self.assertFalse(self.cluster.is_locked)
  678. runtime_plugin = self.env.create_plugin(
  679. cluster=self.cluster,
  680. is_hotpluggable=True,
  681. version='1.0.1',
  682. enabled=False,
  683. **self.plugin_data
  684. )
  685. resp = self._modify_plugin(runtime_plugin, True)
  686. self.assertEqual(200, resp.status_code, resp.body)
  687. editable = objects.Cluster.get_editable_attributes(self.cluster)
  688. self.assertIn(runtime_plugin.name, editable)
  689. self.assertTrue(editable[runtime_plugin.name]['metadata']['enabled'])
  690. def test_enable_plugin_is_idempotent(self):
  691. plugin = self.env.create_plugin(
  692. cluster=self.cluster,
  693. version='1.0.1',
  694. is_hotpluggable=True,
  695. enabled=True,
  696. **self.plugin_data
  697. )
  698. self.cluster.status = consts.CLUSTER_STATUSES.operational
  699. self.assertFalse(self.cluster.is_locked)
  700. resp = self._modify_plugin(plugin, True)
  701. self.assertEqual(200, resp.status_code, resp.body)