Merge lp:~gz/juju-ci-tools/substrate_maas2 into lp:juju-ci-tools
- substrate_maas2
- Merge into trunk
Proposed by
Martin Packman
Status: | Merged |
---|---|
Approved by: | Martin Packman |
Approved revision: | 1385 |
Merged at revision: | 1385 |
Proposed branch: | lp:~gz/juju-ci-tools/substrate_maas2 |
Merge into: | lp:juju-ci-tools |
Prerequisite: | lp:~gz/juju-ci-tools/substrate_logging |
Diff against target: |
386 lines (+208/-52) 2 files modified
substrate.py (+45/-18) tests/test_substrate.py (+163/-34) |
To merge this branch: | bzr merge lp:~gz/juju-ci-tools/substrate_maas2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | code | Approve | |
Review via email: mp+293175@code.launchpad.net |
Commit message
Description of the change
Add MAAS 2.0 support in substrate
There are lots of different ways of doing this, the most elegant seemed a subclass for the 'old' api like we do with juju itself. The version selection is unfortunately a little ugly as I can't find a better way of doing it than trying login and swallowing the (unhelpful) failure and trying the other version.
This means I've broken the common pattern of having a usable manager_from_config classmethod, but MAAS was already special cased in practice and the alternatives seemed worse.
To post a comment you must log in.
- 1385. By Martin Packman
-
Correct check on MAASAccount types in test
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'substrate.py' | |||
2 | --- substrate.py 2016-04-27 19:59:40 +0000 | |||
3 | +++ substrate.py 2016-04-27 21:59:07 +0000 | |||
4 | @@ -54,7 +54,7 @@ | |||
5 | 54 | environ.update(translate_to_env(env.config)) | 54 | environ.update(translate_to_env(env.config)) |
6 | 55 | command_args = ['nova', 'delete'] + instance_ids | 55 | command_args = ['nova', 'delete'] + instance_ids |
7 | 56 | elif provider_type == 'maas': | 56 | elif provider_type == 'maas': |
9 | 57 | with MAASAccount.manager_from_config(env.config) as substrate: | 57 | with maas_account_from_config(env.config) as substrate: |
10 | 58 | substrate.terminate_instances(instance_ids) | 58 | substrate.terminate_instances(instance_ids) |
11 | 59 | return | 59 | return |
12 | 60 | elif provider_type == 'lxd': | 60 | elif provider_type == 'lxd': |
13 | @@ -362,23 +362,15 @@ | |||
14 | 362 | 362 | ||
15 | 363 | 363 | ||
16 | 364 | class MAASAccount: | 364 | class MAASAccount: |
18 | 365 | """Represent a Mass account.""" | 365 | """Represent a MAAS 2.0 account.""" |
19 | 366 | |||
20 | 367 | _API_PATH = 'api/2.0/' | ||
21 | 366 | 368 | ||
22 | 367 | def __init__(self, profile, url, oauth): | 369 | def __init__(self, profile, url, oauth): |
23 | 368 | self.profile = profile | 370 | self.profile = profile |
25 | 369 | self.url = urlparse.urljoin(url, 'api/1.0/') | 371 | self.url = urlparse.urljoin(url, self._API_PATH) |
26 | 370 | self.oauth = oauth | 372 | self.oauth = oauth |
27 | 371 | 373 | ||
28 | 372 | @classmethod | ||
29 | 373 | @contextmanager | ||
30 | 374 | def manager_from_config(cls, config): | ||
31 | 375 | """Create a ContextManager for a MaasAccount.""" | ||
32 | 376 | manager = cls( | ||
33 | 377 | config['name'], config['maas-server'], config['maas-oauth']) | ||
34 | 378 | manager.login() | ||
35 | 379 | yield manager | ||
36 | 380 | manager.logout() | ||
37 | 381 | |||
38 | 382 | def login(self): | 374 | def login(self): |
39 | 383 | """Login with the maas cli.""" | 375 | """Login with the maas cli.""" |
40 | 384 | subprocess.check_call( | 376 | subprocess.check_call( |
41 | @@ -389,18 +381,22 @@ | |||
42 | 389 | subprocess.check_call( | 381 | subprocess.check_call( |
43 | 390 | ['maas', 'logout', self.profile]) | 382 | ['maas', 'logout', self.profile]) |
44 | 391 | 383 | ||
45 | 384 | def _machine_release_args(self, machine_id): | ||
46 | 385 | return ['maas', self.profile, 'machine', 'release', machine_id] | ||
47 | 386 | |||
48 | 392 | def terminate_instances(self, instance_ids): | 387 | def terminate_instances(self, instance_ids): |
49 | 393 | """Terminate the specified instances.""" | 388 | """Terminate the specified instances.""" |
50 | 394 | for instance in instance_ids: | 389 | for instance in instance_ids: |
51 | 395 | maas_system_id = instance.split('/')[5] | 390 | maas_system_id = instance.split('/')[5] |
52 | 396 | log.info('Deleting %s.' % instance) | 391 | log.info('Deleting %s.' % instance) |
55 | 397 | subprocess.check_call( | 392 | subprocess.check_call(self._machine_release_args(maas_system_id)) |
56 | 398 | ['maas', self.profile, 'node', 'release', maas_system_id]) | 393 | |
57 | 394 | def _list_allocated_args(self): | ||
58 | 395 | return ['maas', self.profile, 'machines', 'list-allocated'] | ||
59 | 399 | 396 | ||
60 | 400 | def get_allocated_nodes(self): | 397 | def get_allocated_nodes(self): |
61 | 401 | """Return a dict of allocated nodes with the hostname as keys.""" | 398 | """Return a dict of allocated nodes with the hostname as keys.""" |
64 | 402 | data = subprocess.check_output( | 399 | data = subprocess.check_output(self._list_allocated_args()) |
63 | 403 | ['maas', self.profile, 'nodes', 'list-allocated']) | ||
65 | 404 | nodes = json.loads(data) | 400 | nodes = json.loads(data) |
66 | 405 | allocated = {node['hostname']: node for node in nodes} | 401 | allocated = {node['hostname']: node for node in nodes} |
67 | 406 | return allocated | 402 | return allocated |
68 | @@ -417,6 +413,37 @@ | |||
69 | 417 | return ips | 413 | return ips |
70 | 418 | 414 | ||
71 | 419 | 415 | ||
72 | 416 | class MAAS1Account(MAASAccount): | ||
73 | 417 | """Represent a MAAS 1.X account.""" | ||
74 | 418 | |||
75 | 419 | _API_PATH = 'api/1.0/' | ||
76 | 420 | |||
77 | 421 | def _list_allocated_args(self): | ||
78 | 422 | return ['maas', self.profile, 'nodes', 'list-allocated'] | ||
79 | 423 | |||
80 | 424 | def _machine_release_args(self, machine_id): | ||
81 | 425 | return ['maas', self.profile, 'node', 'release', machine_id] | ||
82 | 426 | |||
83 | 427 | |||
84 | 428 | @contextmanager | ||
85 | 429 | def maas_account_from_config(config): | ||
86 | 430 | """Create a ContextManager for either a MAASAccount or a MAAS1Account. | ||
87 | 431 | |||
88 | 432 | As it's not possible to tell from the maas config which version of the api | ||
89 | 433 | to use, try 2.0 and if that fails on login fallback to 1.0 instead. | ||
90 | 434 | """ | ||
91 | 435 | args = (config['name'], config['maas-server'], config['maas-oauth']) | ||
92 | 436 | manager = MAASAccount(*args) | ||
93 | 437 | try: | ||
94 | 438 | manager.login() | ||
95 | 439 | except subprocess.CalledProcessError: | ||
96 | 440 | log.info("Could not login with MAAS 2.0 API, trying 1.0") | ||
97 | 441 | manager = MAAS1Account(*args) | ||
98 | 442 | manager.login() | ||
99 | 443 | yield manager | ||
100 | 444 | manager.logout() | ||
101 | 445 | |||
102 | 446 | |||
103 | 420 | class LXDAccount: | 447 | class LXDAccount: |
104 | 421 | """Represent a LXD account.""" | 448 | """Represent a LXD account.""" |
105 | 422 | 449 | ||
106 | @@ -602,7 +629,7 @@ | |||
107 | 602 | # Only MAAS requires special handling at prsent. | 629 | # Only MAAS requires special handling at prsent. |
108 | 603 | return | 630 | return |
109 | 604 | # MAAS hostnames are not resolvable, but we can adapt them to IPs. | 631 | # MAAS hostnames are not resolvable, but we can adapt them to IPs. |
111 | 605 | with MAASAccount.manager_from_config(env.config) as account: | 632 | with maas_account_from_config(env.config) as account: |
112 | 606 | allocated_ips = account.get_allocated_ips() | 633 | allocated_ips = account.get_allocated_ips() |
113 | 607 | for remote in remote_machines: | 634 | for remote in remote_machines: |
114 | 608 | if remote.get_address() in allocated_ips: | 635 | if remote.get_address() in allocated_ips: |
115 | 609 | 636 | ||
116 | === modified file 'tests/test_substrate.py' | |||
117 | --- tests/test_substrate.py 2016-04-27 19:59:40 +0000 | |||
118 | +++ tests/test_substrate.py 2016-04-27 21:59:07 +0000 | |||
119 | @@ -31,6 +31,8 @@ | |||
120 | 31 | LXDAccount, | 31 | LXDAccount, |
121 | 32 | make_substrate_manager, | 32 | make_substrate_manager, |
122 | 33 | MAASAccount, | 33 | MAASAccount, |
123 | 34 | MAAS1Account, | ||
124 | 35 | maas_account_from_config, | ||
125 | 34 | OpenStackAccount, | 36 | OpenStackAccount, |
126 | 35 | parse_euca, | 37 | parse_euca, |
127 | 36 | run_instances, | 38 | run_instances, |
128 | @@ -159,11 +161,11 @@ | |||
129 | 159 | with patch('subprocess.check_call') as cc_mock: | 161 | with patch('subprocess.check_call') as cc_mock: |
130 | 160 | terminate_instances(env, ['/A/B/C/D/node-3d/']) | 162 | terminate_instances(env, ['/A/B/C/D/node-3d/']) |
131 | 161 | expected = ( | 163 | expected = ( |
133 | 162 | ['maas', 'login', 'mas', 'http://10.0.10.10/MAAS/api/1.0/', | 164 | ['maas', 'login', 'mas', 'http://10.0.10.10/MAAS/api/2.0/', |
134 | 163 | 'a:password:string'], | 165 | 'a:password:string'], |
135 | 164 | ) | 166 | ) |
136 | 165 | self.assertEqual(expected, cc_mock.call_args_list[0][0]) | 167 | self.assertEqual(expected, cc_mock.call_args_list[0][0]) |
138 | 166 | expected = (['maas', 'mas', 'node', 'release', 'node-3d'],) | 168 | expected = (['maas', 'mas', 'machine', 'release', 'node-3d'],) |
139 | 167 | self.assertEqual(expected, cc_mock.call_args_list[1][0]) | 169 | self.assertEqual(expected, cc_mock.call_args_list[1][0]) |
140 | 168 | expected = (['maas', 'logout', 'mas'],) | 170 | expected = (['maas', 'logout', 'mas'],) |
141 | 169 | self.assertEqual(expected, cc_mock.call_args_list[2][0]) | 171 | self.assertEqual(expected, cc_mock.call_args_list[2][0]) |
142 | @@ -674,25 +676,85 @@ | |||
143 | 674 | client.delete_hosted_service.assert_called_once_with('foo') | 676 | client.delete_hosted_service.assert_called_once_with('foo') |
144 | 675 | 677 | ||
145 | 676 | 678 | ||
165 | 677 | class TestMAASAcount(TestCase): | 679 | class TestMAASAccount(TestCase): |
166 | 678 | 680 | ||
167 | 679 | @patch.object(MAASAccount, 'logout', autospec=True) | 681 | @patch('subprocess.check_call', autospec=True) |
168 | 680 | @patch.object(MAASAccount, 'login', autospec=True) | 682 | def test_login(self, cc_mock): |
169 | 681 | def test_manager_from_config(self, li_mock, lo_mock): | 683 | config = get_maas_env().config |
170 | 682 | config = get_maas_env().config | 684 | account = MAASAccount( |
171 | 683 | with MAASAccount.manager_from_config(config) as account: | 685 | config['name'], config['maas-server'], config['maas-oauth']) |
172 | 684 | self.assertEqual(account.profile, 'mas') | 686 | account.login() |
173 | 685 | self.assertEqual(account.url, 'http://10.0.10.10/MAAS/api/1.0/') | 687 | cc_mock.assert_called_once_with([ |
174 | 686 | self.assertEqual(account.oauth, 'a:password:string') | 688 | 'maas', 'login', 'mas', 'http://10.0.10.10/MAAS/api/2.0/', |
175 | 687 | # As the class object is patched, the mocked methods | 689 | 'a:password:string']) |
176 | 688 | # show that self is passed. | 690 | |
177 | 689 | li_mock.assert_called_once_with(account) | 691 | @patch('subprocess.check_call', autospec=True) |
178 | 690 | lo_mock.assert_called_once_with(account) | 692 | def test_logout(self, cc_mock): |
179 | 691 | 693 | config = get_maas_env().config | |
180 | 692 | @patch('subprocess.check_call', autospec=True) | 694 | account = MAASAccount( |
181 | 693 | def test_login(self, cc_mock): | 695 | config['name'], config['maas-server'], config['maas-oauth']) |
182 | 694 | config = get_maas_env().config | 696 | account.logout() |
183 | 695 | account = MAASAccount( | 697 | cc_mock.assert_called_once_with(['maas', 'logout', 'mas']) |
184 | 698 | |||
185 | 699 | @patch('subprocess.check_call', autospec=True) | ||
186 | 700 | def test_terminate_instances(self, cc_mock): | ||
187 | 701 | config = get_maas_env().config | ||
188 | 702 | account = MAASAccount( | ||
189 | 703 | config['name'], config['maas-server'], config['maas-oauth']) | ||
190 | 704 | instance_ids = ['/A/B/C/D/node-1d/', '/A/B/C/D/node-2d/'] | ||
191 | 705 | account.terminate_instances(instance_ids) | ||
192 | 706 | cc_mock.assert_any_call( | ||
193 | 707 | ['maas', 'mas', 'machine', 'release', 'node-1d']) | ||
194 | 708 | cc_mock.assert_called_with( | ||
195 | 709 | ['maas', 'mas', 'machine', 'release', 'node-2d']) | ||
196 | 710 | |||
197 | 711 | def test_get_allocated_nodes(self): | ||
198 | 712 | config = get_maas_env().config | ||
199 | 713 | account = MAASAccount( | ||
200 | 714 | config['name'], config['maas-server'], config['maas-oauth']) | ||
201 | 715 | node = make_maas_node('maas-node-1.maas') | ||
202 | 716 | allocated_nodes_string = '[%s]' % json.dumps(node) | ||
203 | 717 | with patch('subprocess.check_output', autospec=True, | ||
204 | 718 | return_value=allocated_nodes_string) as co_mock: | ||
205 | 719 | allocated = account.get_allocated_nodes() | ||
206 | 720 | co_mock.assert_called_once_with( | ||
207 | 721 | ['maas', 'mas', 'machines', 'list-allocated']) | ||
208 | 722 | self.assertEqual(node, allocated['maas-node-1.maas']) | ||
209 | 723 | |||
210 | 724 | def test_get_allocated_ips(self): | ||
211 | 725 | config = get_maas_env().config | ||
212 | 726 | account = MAASAccount( | ||
213 | 727 | config['name'], config['maas-server'], config['maas-oauth']) | ||
214 | 728 | node = make_maas_node('maas-node-1.maas') | ||
215 | 729 | allocated_nodes_string = '[%s]' % json.dumps(node) | ||
216 | 730 | with patch('subprocess.check_output', autospec=True, | ||
217 | 731 | return_value=allocated_nodes_string) as co_mock: | ||
218 | 732 | ips = account.get_allocated_ips() | ||
219 | 733 | co_mock.assert_called_once_with( | ||
220 | 734 | ['maas', 'mas', 'machines', 'list-allocated']) | ||
221 | 735 | self.assertEqual('10.0.30.165', ips['maas-node-1.maas']) | ||
222 | 736 | |||
223 | 737 | def test_get_allocated_ips_empty(self): | ||
224 | 738 | config = get_maas_env().config | ||
225 | 739 | account = MAASAccount( | ||
226 | 740 | config['name'], config['maas-server'], config['maas-oauth']) | ||
227 | 741 | node = make_maas_node('maas-node-1.maas') | ||
228 | 742 | node['ip_addresses'] = [] | ||
229 | 743 | allocated_nodes_string = '[%s]' % json.dumps(node) | ||
230 | 744 | with patch('subprocess.check_output', autospec=True, | ||
231 | 745 | return_value=allocated_nodes_string) as co_mock: | ||
232 | 746 | ips = account.get_allocated_ips() | ||
233 | 747 | co_mock.assert_called_once_with( | ||
234 | 748 | ['maas', 'mas', 'machines', 'list-allocated']) | ||
235 | 749 | self.assertEqual({}, ips) | ||
236 | 750 | |||
237 | 751 | |||
238 | 752 | class TestMAAS1Account(TestCase): | ||
239 | 753 | |||
240 | 754 | @patch('subprocess.check_call', autospec=True) | ||
241 | 755 | def test_login(self, cc_mock): | ||
242 | 756 | config = get_maas_env().config | ||
243 | 757 | account = MAAS1Account( | ||
244 | 696 | config['name'], config['maas-server'], config['maas-oauth']) | 758 | config['name'], config['maas-server'], config['maas-oauth']) |
245 | 697 | account.login() | 759 | account.login() |
246 | 698 | cc_mock.assert_called_once_with([ | 760 | cc_mock.assert_called_once_with([ |
247 | @@ -702,7 +764,7 @@ | |||
248 | 702 | @patch('subprocess.check_call', autospec=True) | 764 | @patch('subprocess.check_call', autospec=True) |
249 | 703 | def test_logout(self, cc_mock): | 765 | def test_logout(self, cc_mock): |
250 | 704 | config = get_maas_env().config | 766 | config = get_maas_env().config |
252 | 705 | account = MAASAccount( | 767 | account = MAAS1Account( |
253 | 706 | config['name'], config['maas-server'], config['maas-oauth']) | 768 | config['name'], config['maas-server'], config['maas-oauth']) |
254 | 707 | account.logout() | 769 | account.logout() |
255 | 708 | cc_mock.assert_called_once_with(['maas', 'logout', 'mas']) | 770 | cc_mock.assert_called_once_with(['maas', 'logout', 'mas']) |
256 | @@ -710,7 +772,7 @@ | |||
257 | 710 | @patch('subprocess.check_call', autospec=True) | 772 | @patch('subprocess.check_call', autospec=True) |
258 | 711 | def test_terminate_instances(self, cc_mock): | 773 | def test_terminate_instances(self, cc_mock): |
259 | 712 | config = get_maas_env().config | 774 | config = get_maas_env().config |
261 | 713 | account = MAASAccount( | 775 | account = MAAS1Account( |
262 | 714 | config['name'], config['maas-server'], config['maas-oauth']) | 776 | config['name'], config['maas-server'], config['maas-oauth']) |
263 | 715 | instance_ids = ['/A/B/C/D/node-1d/', '/A/B/C/D/node-2d/'] | 777 | instance_ids = ['/A/B/C/D/node-1d/', '/A/B/C/D/node-2d/'] |
264 | 716 | account.terminate_instances(instance_ids) | 778 | account.terminate_instances(instance_ids) |
265 | @@ -719,10 +781,9 @@ | |||
266 | 719 | cc_mock.assert_called_with( | 781 | cc_mock.assert_called_with( |
267 | 720 | ['maas', 'mas', 'node', 'release', 'node-2d']) | 782 | ['maas', 'mas', 'node', 'release', 'node-2d']) |
268 | 721 | 783 | ||
271 | 722 | @patch('subprocess.check_call', autospec=True) | 784 | def test_get_allocated_nodes(self): |
270 | 723 | def test_get_allocated_nodes(self, cc_mock): | ||
272 | 724 | config = get_maas_env().config | 785 | config = get_maas_env().config |
274 | 725 | account = MAASAccount( | 786 | account = MAAS1Account( |
275 | 726 | config['name'], config['maas-server'], config['maas-oauth']) | 787 | config['name'], config['maas-server'], config['maas-oauth']) |
276 | 727 | node = make_maas_node('maas-node-1.maas') | 788 | node = make_maas_node('maas-node-1.maas') |
277 | 728 | allocated_nodes_string = '[%s]' % json.dumps(node) | 789 | allocated_nodes_string = '[%s]' % json.dumps(node) |
278 | @@ -733,32 +794,100 @@ | |||
279 | 733 | ['maas', 'mas', 'nodes', 'list-allocated']) | 794 | ['maas', 'mas', 'nodes', 'list-allocated']) |
280 | 734 | self.assertEqual(node, allocated['maas-node-1.maas']) | 795 | self.assertEqual(node, allocated['maas-node-1.maas']) |
281 | 735 | 796 | ||
284 | 736 | @patch('subprocess.check_call', autospec=True) | 797 | def test_get_allocated_ips(self): |
283 | 737 | def test_get_allocated_ips(self, cc_mock): | ||
285 | 738 | config = get_maas_env().config | 798 | config = get_maas_env().config |
287 | 739 | account = MAASAccount( | 799 | account = MAAS1Account( |
288 | 740 | config['name'], config['maas-server'], config['maas-oauth']) | 800 | config['name'], config['maas-server'], config['maas-oauth']) |
289 | 741 | node = make_maas_node('maas-node-1.maas') | 801 | node = make_maas_node('maas-node-1.maas') |
290 | 742 | allocated_nodes_string = '[%s]' % json.dumps(node) | 802 | allocated_nodes_string = '[%s]' % json.dumps(node) |
291 | 743 | with patch('subprocess.check_output', autospec=True, | 803 | with patch('subprocess.check_output', autospec=True, |
293 | 744 | return_value=allocated_nodes_string): | 804 | return_value=allocated_nodes_string) as co_mock: |
294 | 745 | ips = account.get_allocated_ips() | 805 | ips = account.get_allocated_ips() |
295 | 806 | co_mock.assert_called_once_with( | ||
296 | 807 | ['maas', 'mas', 'nodes', 'list-allocated']) | ||
297 | 746 | self.assertEqual('10.0.30.165', ips['maas-node-1.maas']) | 808 | self.assertEqual('10.0.30.165', ips['maas-node-1.maas']) |
298 | 747 | 809 | ||
301 | 748 | @patch('subprocess.check_call', autospec=True) | 810 | def test_get_allocated_ips_empty(self): |
300 | 749 | def test_get_allocated_ips_empty(self, cc_mock): | ||
302 | 750 | config = get_maas_env().config | 811 | config = get_maas_env().config |
304 | 751 | account = MAASAccount( | 812 | account = MAAS1Account( |
305 | 752 | config['name'], config['maas-server'], config['maas-oauth']) | 813 | config['name'], config['maas-server'], config['maas-oauth']) |
306 | 753 | node = make_maas_node('maas-node-1.maas') | 814 | node = make_maas_node('maas-node-1.maas') |
307 | 754 | node['ip_addresses'] = [] | 815 | node['ip_addresses'] = [] |
308 | 755 | allocated_nodes_string = '[%s]' % json.dumps(node) | 816 | allocated_nodes_string = '[%s]' % json.dumps(node) |
309 | 756 | with patch('subprocess.check_output', autospec=True, | 817 | with patch('subprocess.check_output', autospec=True, |
311 | 757 | return_value=allocated_nodes_string): | 818 | return_value=allocated_nodes_string) as co_mock: |
312 | 758 | ips = account.get_allocated_ips() | 819 | ips = account.get_allocated_ips() |
313 | 820 | co_mock.assert_called_once_with( | ||
314 | 821 | ['maas', 'mas', 'nodes', 'list-allocated']) | ||
315 | 759 | self.assertEqual({}, ips) | 822 | self.assertEqual({}, ips) |
316 | 760 | 823 | ||
317 | 761 | 824 | ||
318 | 825 | class TestMAASAccountFromConfig(TestCase): | ||
319 | 826 | |||
320 | 827 | def test_login_succeeds(self): | ||
321 | 828 | config = get_maas_env().config | ||
322 | 829 | with patch('subprocess.check_call', autospec=True) as cc_mock: | ||
323 | 830 | with maas_account_from_config(config) as maas: | ||
324 | 831 | self.assertIs(type(maas), MAASAccount) | ||
325 | 832 | self.assertEqual(maas.profile, 'mas') | ||
326 | 833 | self.assertEqual(maas.url, 'http://10.0.10.10/MAAS/api/2.0/') | ||
327 | 834 | self.assertEqual(maas.oauth, 'a:password:string') | ||
328 | 835 | # The login call has happened on context manager enter, reset | ||
329 | 836 | # the mock after to verify the logout call. | ||
330 | 837 | cc_mock.assert_called_once_with([ | ||
331 | 838 | 'maas', 'login', 'mas', 'http://10.0.10.10/MAAS/api/2.0/', | ||
332 | 839 | 'a:password:string']) | ||
333 | 840 | cc_mock.reset_mock() | ||
334 | 841 | cc_mock.assert_called_once_with(['maas', 'logout', 'mas']) | ||
335 | 842 | |||
336 | 843 | def test_login_fallback(self): | ||
337 | 844 | config = get_maas_env().config | ||
338 | 845 | login_error = CalledProcessError(1, ['maas', 'login']) | ||
339 | 846 | with patch('subprocess.check_call', autospec=True, | ||
340 | 847 | side_effect=[login_error, None, None]) as cc_mock: | ||
341 | 848 | with maas_account_from_config(config) as maas: | ||
342 | 849 | self.assertIs(type(maas), MAAS1Account) | ||
343 | 850 | self.assertEqual(maas.profile, 'mas') | ||
344 | 851 | self.assertEqual(maas.url, 'http://10.0.10.10/MAAS/api/1.0/') | ||
345 | 852 | self.assertEqual(maas.oauth, 'a:password:string') | ||
346 | 853 | # The first login attempt was with the 2.0 api, after which | ||
347 | 854 | # a 1.0 login succeeded. | ||
348 | 855 | self.assertEquals(cc_mock.call_args_list, [ | ||
349 | 856 | call(['maas', 'login', 'mas', | ||
350 | 857 | 'http://10.0.10.10/MAAS/api/2.0/', | ||
351 | 858 | 'a:password:string']), | ||
352 | 859 | call(['maas', 'login', 'mas', | ||
353 | 860 | 'http://10.0.10.10/MAAS/api/1.0/', | ||
354 | 861 | 'a:password:string']), | ||
355 | 862 | ]) | ||
356 | 863 | cc_mock.reset_mock() | ||
357 | 864 | cc_mock.assert_called_once_with(['maas', 'logout', 'mas']) | ||
358 | 865 | self.assertEqual( | ||
359 | 866 | self.log_stream.getvalue(), | ||
360 | 867 | 'INFO Could not login with MAAS 2.0 API, trying 1.0\n') | ||
361 | 868 | |||
362 | 869 | def test_login_both_fail(self): | ||
363 | 870 | config = get_maas_env().config | ||
364 | 871 | login_error = CalledProcessError(1, ['maas', 'login']) | ||
365 | 872 | with patch('subprocess.check_call', autospec=True, | ||
366 | 873 | side_effect=login_error) as cc_mock: | ||
367 | 874 | with self.assertRaises(CalledProcessError) as ctx: | ||
368 | 875 | with maas_account_from_config(config): | ||
369 | 876 | self.fail('Should never get manager with failed login') | ||
370 | 877 | self.assertIs(ctx.exception, login_error) | ||
371 | 878 | self.assertEquals(cc_mock.call_args_list, [ | ||
372 | 879 | call(['maas', 'login', 'mas', | ||
373 | 880 | 'http://10.0.10.10/MAAS/api/2.0/', | ||
374 | 881 | 'a:password:string']), | ||
375 | 882 | call(['maas', 'login', 'mas', | ||
376 | 883 | 'http://10.0.10.10/MAAS/api/1.0/', | ||
377 | 884 | 'a:password:string']), | ||
378 | 885 | ]) | ||
379 | 886 | self.assertEqual( | ||
380 | 887 | self.log_stream.getvalue(), | ||
381 | 888 | 'INFO Could not login with MAAS 2.0 API, trying 1.0\n') | ||
382 | 889 | |||
383 | 890 | |||
384 | 762 | class TestMakeSubstrateManager(TestCase): | 891 | class TestMakeSubstrateManager(TestCase): |
385 | 763 | 892 | ||
386 | 764 | def test_make_substrate_manager_aws(self): | 893 | def test_make_substrate_manager_aws(self): |
Thank you.