~brendan-donegan/maas/qa-lab-tests_ipmi_config_silent_fail

« back to all changes in this revision

Viewing changes to common.py

  • Committer: MAAS Lander
  • Author(s): Brendan Donegan
  • Date: 2016-11-09 14:16:18 UTC
  • mfrom: (459.2.6 flake8_cleanup)
  • Revision ID: maas_lander-20161109141618-c3qthypbq8qc835c
[r=allenap][bug=][author=brendan-donegan] Fix a whole bunch of flake8 and pylint issues

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from __future__ import (
2
 
    absolute_import,
3
 
    print_function,
4
 
    unicode_literals,
5
 
    )
6
 
 
7
 
__metaclass__ = type
8
 
__all__ = []
9
 
 
10
1
import os
11
 
import pipes
12
 
import platform
13
 
import pwd
14
 
import re
15
2
import sys
16
3
import tempfile
17
4
from contextlib import closing
22
9
from unittest import (
23
10
    skipIf,
24
11
    skipUnless,
25
 
    SkipTest,
26
12
)
27
13
try:
28
14
    from urllib2 import (
38
24
except ImportError:
39
25
    from urlparse import urlparse
40
26
 
 
27
from apiclient.creds import convert_tuple_to_string
 
28
 
41
29
from netaddr import (
42
30
    IPAddress,
43
31
    IPRange,
51
39
)
52
40
from testtools.matchers import (
53
41
    Contains,
54
 
    ContainsAll,
55
42
    Equals,
56
43
    HasLength,
57
44
    MatchesRegex,
61
48
    MatchesListwise,
62
49
)
63
50
from timeout import timeout
64
 
# Import `pause_until_released` if you need to block execution at a given state
65
 
# so you can examine the machines in the testing pool.
66
 
from utils import (
67
 
    assertCommandReturnCode,
68
 
    assertStartedUpstartService,
69
 
    change_logs_permissions,
70
 
    filter_dict,
71
 
    get_maas_revision,
72
 
    get_maas_version,
73
 
    get_ubuntu_version,
74
 
    retries,
75
 
    run_command,
76
 
    pause_until_released  # If this is not used below, linters will complain.
77
 
)
78
 
import yaml
79
 
import zmq
80
 
 
81
 
 
82
 
# Needed by the imports below - linters will complain.
83
 
sys.path.insert(0, "/usr/share/maas")
84
 
os.environ['DJANGO_SETTINGS_MODULE'] = 'maas.settings'
85
 
 
86
 
import django
87
 
django.setup()
88
 
 
89
 
from django.core.exceptions import AppRegistryNotReady
90
 
from django.core.management import call_command
91
 
from django.contrib.auth.models import User
92
 
 
93
 
from maasserver.enum import NODE_STATUS
94
 
from maasserver.models.user import get_creds_tuple
95
 
from maasserver.utils import get_local_cluster_UUID
96
 
from apiclient.creds import convert_tuple_to_string
97
 
 
98
51
from config import (
99
52
    ADMIN_USER,
100
53
    ARM_LAB,
101
54
    BMC_START_IP,
102
55
    BMC_END_IP,
103
 
    CLUSTER_CONTROLLER_IP,
104
56
    DO_NOT_USE_ARM_NODES,
105
57
    TEST_JUJU,
106
58
    TEST_CUSTOM_IMAGES,
109
61
    LENOVO_LAB,
110
62
    MAAS_URL,
111
63
    MAIN_ARCHIVE,
112
 
    NODE_SERIES,
113
64
    PASSWORD,
114
65
    PORTS_ARCHIVE,
115
66
    POWER_PASS,
121
72
    SQUID_DEB_PROXY_URL,
122
73
    USER_DATA_URL,
123
74
    USE_ARM_NODES,
124
 
    USE_CC_NODES,
125
75
    USE_PPC_NODES,
126
76
)
 
77
from utils import (
 
78
    assertCommandReturnCode,
 
79
    get_maas_revision,
 
80
    get_maas_version,
 
81
    get_ubuntu_version,
 
82
    retries,
 
83
    run_command,
 
84
)
 
85
import yaml
 
86
 
 
87
 
 
88
# Needed by the imports below - linters will complain.
 
89
sys.path.insert(0, "/usr/share/maas")
 
90
os.environ['DJANGO_SETTINGS_MODULE'] = 'maas.settings'
 
91
 
 
92
# TODO: I think all this code can be removed - 2.0 allows the admin user to be
 
93
# created on the command line without any trickery - Brendan Donegan
 
94
import django  # noqa
 
95
django.setup()
 
96
 
 
97
from django.core.management import call_command  # noqa
 
98
from django.contrib.auth.models import User  # noqa
 
99
 
 
100
from maasserver.enum import NODE_STATUS  # noqa
 
101
from maasserver.models.user import get_creds_tuple  # noqa
127
102
 
128
103
 
129
104
def setup_juju_cloud(server, cloud_yaml):
145
120
            maas-oauth: {oauth}""".format(oauth=oauth))
146
121
    credentials_yaml.flush()
147
122
 
 
123
 
148
124
def get_token_str():
149
125
    admin = User.objects.get(username=ADMIN_USER)
150
126
    token = admin.tokens.all()[0]
211
187
 
212
188
    def get_node_count(self):
213
189
        """The number of available nodes."""
214
 
        count = len(LENOVO_LAB)
 
190
        node_count = len(LENOVO_LAB)
215
191
        if USE_ARM_NODES:
216
 
            count += len(ARM_LAB)
 
192
            node_count += len(ARM_LAB)
217
193
        if USE_PPC_NODES:
218
 
            count += len(PPC_SYSTEMS)
219
 
        return count
 
194
            node_count += len(PPC_SYSTEMS)
 
195
        return node_count
220
196
 
221
197
    def _wait_maas_running(self):
222
 
        for tries in range(10):
 
198
        for _ in range(10):
223
199
            try:
224
200
                urlopen(MAAS_URL).code
225
 
            except HTTPError as e:
 
201
            except HTTPError:
226
202
                sleep(30)
227
203
                continue
228
204
            return True
256
232
        assertCommandReturnCode(self, cmd, expected_output)
257
233
 
258
234
    def _get_rack_interface_link(self, primary_rack_system_id, ip):
259
 
        rack, err = self._run_maas_cli(
 
235
        rack, _ = self._run_maas_cli(
260
236
            ['rack-controller', 'read', primary_rack_system_id])
261
237
        rack_controller = loads(rack)
262
238
 
277
253
        rack_vlan = rack_interface_link['subnet']['vlan']['name']
278
254
 
279
255
        maas_dhcp_cmd = [
280
 
             'vlan', 'update', rack_fabric, rack_vlan, 'dhcp_on=True',
281
 
             'primary_rack=%s' % primary_rack_system_id]
282
 
 
283
 
        output, err = self._run_maas_cli(maas_dhcp_cmd)
284
 
 
285
 
        try:
286
 
            vlan_result = loads(output)
287
 
        except:
288
 
            pass
289
 
            #from maas.provisioningserver.service_monitor import get_service_state
290
 
            pause_until_released(ng_list.__repr__())
291
 
        # The JSON object returned by MAAS doesn't include the router_ip
292
 
        # address, so let's remove it from the dhcp_config before comparing.
 
256
            'vlan', 'update', rack_fabric, rack_vlan, 'dhcp_on=True',
 
257
            'primary_rack=%s' % primary_rack_system_id
 
258
        ]
 
259
        output, _ = self._run_maas_cli(maas_dhcp_cmd)
 
260
        vlan_result = loads(output)
293
261
        self.assertEqual(True, vlan_result['dhcp_on'])
294
262
 
295
263
    def _boot_nodes(self):
299
267
            self.power_on(ipmi_address, user=POWER_USER, password=POWER_PASS)
300
268
        if USE_PPC_NODES:
301
269
            for ipmi_address in PPC_SYSTEMS.values():
302
 
                self.power_off(ipmi_address, password='admin', driver='LAN_2_0')
303
 
                self.power_on(ipmi_address, password='admin', driver='LAN_2_0')
 
270
                self.power_off(
 
271
                    ipmi_address, password='admin', driver='LAN_2_0'
 
272
                )
 
273
                self.power_on(
 
274
                    ipmi_address, password='admin', driver='LAN_2_0'
 
275
                )
304
276
        if USE_ARM_NODES:
305
277
            for ipmi_address in ARM_LAB.values():
306
278
                self.power_off(ipmi_address, user='admin', password='admin')
335
307
        # Start by listing all nodes.  The output will be shown on failure, so
336
308
        # even if we can filter by status, doing it client-side may provide
337
309
        # more helpful debug output.
338
 
        output, err = self._run_maas_cli(["machines", "read"])
 
310
        output, _ = self._run_maas_cli(["machines", "read"])
339
311
        machine_list = loads(output)
340
 
        # In 1.9 we used to look at the substatus for new status's. However 2.0+
341
 
        # we simply look at the status
 
312
        # In 1.9 we used to look at the substatus for new status's.
 
313
        # However 2.0+ we simply look at the status
342
314
        machines = [
343
315
            machine for machine in machine_list
344
316
            if status == machine['status']
365
337
        command = ["juju", "--debug"]
366
338
        command.extend(args)
367
339
        retcode, output, err = run_command(command, env=env)
368
 
        command_name = " ".join(map(pipes.quote, command))
 
340
        command_name = " ".join(command)
369
341
        self.addDetail(command_name, text_content(output))
370
342
        self.addDetail(command_name, text_content(err))
371
343
        return retcode, output, err
372
344
 
373
345
    def get_juju_status(self):
374
346
        # Juju2 status defaults to tabular so we need to force YAML
375
 
        _, status_output, _ = self._run_juju_command(["status", "--format=yaml"])
 
347
        _, status_output, _ = self._run_juju_command(
 
348
            ["status", "--format=yaml"]
 
349
        )
376
350
        status = yaml.safe_load(status_output)
377
351
        return status
378
352
 
383
357
            if status is not None:
384
358
                machines = status['machines'].values()
385
359
                running_machines = [
386
 
                    machine for machine in machines
387
 
                    if machine.get('machine-status', {}).get('current', '') in [
388
 
                        'running', 'started']]
 
360
                    m for m in machines if
 
361
                    m.get('machine-status', {}).get('current', '') in ['running', 'started']  # noqa
 
362
                ]
389
363
                if len(running_machines) >= nb_machines:
390
364
                    break
391
365
            sleep(20)
400
374
                units = []
401
375
            started_units = [
402
376
                unit for unit in units
403
 
                if unit.get('workload-status', {}).get('current', '') == 'active']
 
377
                if unit.get('workload-status', {}).get('current', '') == 'active'  # noqa
 
378
            ]
404
379
            if len(started_units) >= nb_units:
405
380
                break
406
381
            sleep(20)
438
413
        assertCommandReturnCode(self, cmd, "")
439
414
        # Restart apache2.
440
415
        cmd = ["systemctl", "restart", "maas-regiond"]
441
 
        for retry in retries(delay=10, timeout=60):
442
 
            retcode, output, err = run_command(cmd)
 
416
        for _ in retries(delay=10, timeout=60):
 
417
            retcode, _, _ = run_command(cmd)
443
418
            if retcode == 0:
444
419
                break
445
420
        else:
453
428
        assertCommandReturnCode(self, cmd, "")
454
429
        # Restart apache2.
455
430
        cmd = ["systemctl", "restart", "maas-rackd"]
456
 
        for retry in retries(delay=10, timeout=60):
457
 
            retcode, output, err = run_command(cmd)
 
431
        for _ in retries(delay=10, timeout=60):
 
432
            retcode, _, _ = run_command(cmd)
458
433
            if retcode == 0:
459
434
                break
460
435
        else:
477
452
        self.addDetail('url', text_content(url))
478
453
        errors = []
479
454
        attempts = count(1)
480
 
        for elapsed, remaining in retries(timeout=300, delay=10):
 
455
        for elapsed, _ in retries(timeout=300, delay=10):
481
456
            attempt = next(attempts)
482
457
            try:
483
458
                with closing(urlopen(url)) as response:
515
490
        userdata_fd.close()
516
491
 
517
492
    def test_login_api(self):
518
 
        assert(self._wait_maas_running())
 
493
        self.assertTrue(self._wait_maas_running())
519
494
        token_str = get_token_str()
520
495
        api_url = MAAS_URL + "/api/2.0/"
521
496
        cmd = [maascli, "login", "maas", api_url, token_str]
536
511
        place to put a pause."""
537
512
        # pause_until_released('test_maas_logged_in')
538
513
        cmd = ["maas", "list"]
539
 
        retcode, output, err = run_command(cmd)
 
514
        retcode, _, err = run_command(cmd)
540
515
        self.assertEqual(0, retcode)
541
516
        self.assertEqual('', err)
542
517
 
543
518
    def test_set_main_archive(self):
544
 
        output, err = self._run_maas_cli([
 
519
        output, _ = self._run_maas_cli([
545
520
            "maas", "set-config", "name=main_archive",
546
521
            "value=%s" % MAIN_ARCHIVE])
547
522
        self.assertThat(output, Contains("OK"))
548
 
        output, err = self._run_maas_cli([
 
523
        output, _ = self._run_maas_cli([
549
524
            "maas", "get-config", "name=main_archive"])
550
525
        self.assertThat(output, Contains(MAIN_ARCHIVE))
551
526
 
615
590
        }))
616
591
 
617
592
    def test_set_http_proxy(self):
618
 
        output, err = self._run_maas_cli([
 
593
        output, _ = self._run_maas_cli([
619
594
            "maas", "set-config", "name=http_proxy",
620
595
            "value=%s" % HTTP_PROXY])
621
596
        self.assertThat(output, Contains("OK"))
622
 
        output, err = self._run_maas_cli([
 
597
        output, _ = self._run_maas_cli([
623
598
            "maas", "get-config", "name=http_proxy"])
624
599
        self.assertThat(output, Contains(HTTP_PROXY))
625
600
 
629
604
            "apt_proxy: %s" % HTTP_PROXY))
630
605
 
631
606
    def get_master_rack(self):
632
 
        output, err = self._run_maas_cli(["rack-controllers", "read"])
 
607
        output, _ = self._run_maas_cli(["rack-controllers", "read"])
633
608
        rack_controllers = loads(output)
634
609
        if len(rack_controllers) > 0:
635
610
            return rack_controllers[0]
636
611
        return None
637
612
 
638
613
    def get_rack_service_status(self, rack_id, service_name):
639
 
        output, err = self._run_maas_cli(["rack-controller", "read", rack_id])
 
614
        output, _ = self._run_maas_cli(["rack-controller", "read", rack_id])
640
615
        rack_info = loads(output)
641
616
        for service in rack_info['service_set']:
642
617
            if service['name'] == service_name:
654
629
            sleep(10)
655
630
 
656
631
    def test_create_dynamic_range(self):
657
 
        output, err = self._run_maas_cli(
 
632
        output, _ = self._run_maas_cli(
658
633
            ['ipranges', 'create', 'type=dynamic',
659
634
             'start_ip=%s' % REGION_DHCP_CONFIG['ip_range_low'],
660
635
             'end_ip=%s' % REGION_DHCP_CONFIG['ip_range_high']])
661
 
        # If the range was configured correct, it should return the subnet where
662
 
        # the range belogs to. As such, we check the output to ensure it is there.
663
 
        self.assertThat(output, Contains('"cidr": "%s"' % REGION_DHCP_CONFIG['cidr']))
664
 
        # TODO: We need to read all IP ranges for the subnet and check that the range
665
 
        # has actually been created regardless whether we checked the output above.
666
 
        output, err = self._run_maas_cli(
 
636
        # If the range was configured correct, it should return the subnet
 
637
        # where the range belogs to. As such, we check the output to ensure
 
638
        # it is there.
 
639
        self.assertThat(
 
640
            output, Contains('"cidr": "%s"' % REGION_DHCP_CONFIG['cidr'])
 
641
        )
 
642
        # TODO: We need to read all IP ranges for the subnet and check that
 
643
        # the range has actually been created regardless whether we checked
 
644
        # the output above.
 
645
        _, _ = self._run_maas_cli(
667
646
            ['ipranges', 'read'])
668
647
 
669
648
    def _get_rack_systemid_on_region(self):
670
 
        output, err = self._run_maas_cli(["rack-controllers", "read"])
 
649
        output, _ = self._run_maas_cli(["rack-controllers", "read"])
671
650
        rack_controllers = loads(output)
672
651
        region_rack = 4
673
652
        for rack in rack_controllers:
749
728
    @timeout(2 * 60)
750
729
    def test_check_dhcp_service_systemctl(self):
751
730
        cmd = ["systemctl", "status", "maas-dhcpd"]
752
 
        # systemd will return 3 if a 'condition failed. This typically means that
753
 
        # /var/lib/maas/dhcpd.conf is not there yet, and we should wait for a bit
754
 
        # to see if the config is written and maas-dhcp is brought up by the rack.
 
731
        # systemd will return 3 if a 'condition failed. This typically means
 
732
        # that /var/lib/maas/dhcpd.conf is not there yet, and we should wait
 
733
        # for a bit to see if the config is written and maas-dhcp is brought up
 
734
        # by the rack.
755
735
        retcode, output, err = run_command(cmd)
756
736
        while retcode != 0:
757
737
            # query systemd every 3 seconds to see if maas-dhcpd us running
758
738
            sleep(3)
759
739
            retcode, output, err = run_command(cmd)
760
 
        #pause_until_released((retcode, output, err).__repr__())
761
740
        self.assertEqual(0, retcode)
762
741
        self.assertIn('Active: active (running)', output)
763
742
        self.assertEqual('', err)
781
760
        "Zone feature only available after 1.5")
782
761
    def test_add_new_zones(self):
783
762
        # Create 2 new zones.
784
 
        output, err = self._run_maas_cli(
 
763
        output, _ = self._run_maas_cli(
785
764
            ["zones", "create", "name=test-zone", "description='A test zone'"])
786
765
        zone = loads(output)
787
766
        self.assertEqual('test-zone', zone['name'])
788
 
        output, err = self._run_maas_cli(
 
767
        output, _ = self._run_maas_cli(
789
768
            ["zones", "create", "name=delete-zone",
790
769
             "description='A test zone to be deleted'"])
791
770
        zone = loads(output)
795
774
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.5'),
796
775
        "Zone feature only available after 1.5")
797
776
    def test_list_zones(self):
798
 
        output, err = self._run_maas_cli(["zones", "read"])
 
777
        output, _ = self._run_maas_cli(["zones", "read"])
799
778
        zones = loads(output)
800
779
        expected = [u'default', u'delete-zone', u'test-zone']
801
780
        self.assertEqual(expected, sorted([zone['name'] for zone in zones]))
804
783
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.5'),
805
784
        "Zone feature only available after 1.5")
806
785
    def test_delete_zone(self):
807
 
        _, err = self._run_maas_cli(["zone", "delete", "delete-zone"])
 
786
        self._run_maas_cli(["zone", "delete", "delete-zone"])
808
787
        # List the remaining zones after the delete command.
809
 
        output, err = self._run_maas_cli(["zones", "read"])
 
788
        output, _ = self._run_maas_cli(["zones", "read"])
810
789
        zones = loads(output)
811
790
        self.assertNotIn(
812
791
            'delete-zone', sorted([zone['name'] for zone in zones]))
817
796
        'Space feature only available after 1.9')
818
797
    def test_add_new_spaces(self):
819
798
        # Create 2 new spaces.
820
 
        output, err = self._run_maas_cli(
 
799
        output, _ = self._run_maas_cli(
821
800
            ['spaces', 'create', 'name=test-space'])
822
801
        out_dict = loads(output)
823
802
        self.assertEqual('test-space', out_dict['name'])
824
 
        output, err = self._run_maas_cli(
825
 
            ['spaces', 'create', 'name=delete-space'])
826
 
        out_dict = loads(output)
827
 
        self.assertEqual('delete-space', out_dict['name'])
 
803
        output, _ = self._run_maas_cli(
 
804
            ['spaces', 'create', 'name=delete-space']
 
805
        )
 
806
        delete_space = loads(output)
 
807
        self.assertEqual('delete-space', delete_space['name'])
828
808
 
829
809
    @skipIf(
830
810
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
831
811
        'Subnet feature only available after 1.9')
832
812
    def test_add_subnet_to_space(self):
833
 
        output, err = self._run_maas_cli(
 
813
        output, _ = self._run_maas_cli(
834
814
            ['subnets', 'create', 'space=0', 'name=test-subnet',
835
815
             'cidr=192.168.200.0/24'])
836
816
        out_dict = loads(output)
842
822
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
843
823
        'Space feature only available after 1.9')
844
824
    def test_list_spaces(self):
845
 
        output, err = self._run_maas_cli(['spaces', 'read'])
 
825
        output, _ = self._run_maas_cli(['spaces', 'read'])
846
826
        out_dict = loads(output)
847
827
        expected = ['delete-space', 'space-0', 'test-space']
848
828
        self.assertItemsEqual(
852
832
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
853
833
        'Subnet feature only available after 1.9')
854
834
    def test_list_subnets(self):
855
 
        output, err = self._run_maas_cli(['subnets', 'read'])
 
835
        output, _ = self._run_maas_cli(['subnets', 'read'])
856
836
        out_dict = loads(output)
857
837
        self.assertEqual(2, len(out_dict), 'space-0 should now have 2 subnets')
858
838
 
860
840
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
861
841
        'Subnet feature only available after 1.9')
862
842
    def test_delete_subnet(self):
863
 
        _, err = self._run_maas_cli(['subnet', 'delete', 'test-subnet'])
864
 
        output, err = self._run_maas_cli(['subnets', 'read'])
865
 
        out = loads(output)
866
 
        self.assertEqual(1, len(out), 'space-0 should now have 1 subnet')
867
 
        self.assertNotIn('test-subnet', [n['name'] for n in out])
 
843
        self._run_maas_cli(['subnet', 'delete', 'test-subnet'])
 
844
        output, _ = self._run_maas_cli(['subnets', 'read'])
 
845
        subnets = loads(output)
 
846
        self.assertEqual(1, len(subnets), 'space-0 should now have 1 subnet')
 
847
        self.assertNotIn('test-subnet', [subnet['name'] for subnet in subnets])
868
848
        self.assertEqual(
869
 
            out[0]['name'], out[0]['cidr'],
 
849
            subnets[0]['name'], subnets[0]['cidr'],
870
850
            'Name and CIDR should be equal for the default subnet.')
871
851
 
872
852
    @skipIf(
873
853
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
874
854
        'Space feature only available after 1.9')
875
855
    def test_delete_space(self):
876
 
        _, err = self._run_maas_cli(['space', 'delete', 'delete-space'])
 
856
        self._run_maas_cli(['space', 'delete', 'delete-space'])
877
857
        # List the remaining zones after the delete command.
878
 
        output, err = self._run_maas_cli(['spaces', 'read'])
 
858
        output, _ = self._run_maas_cli(['spaces', 'read'])
879
859
        out_dict = loads(output)
880
860
        self.assertNotIn(
881
861
            'delete-space', [item['name'] for item in out_dict])
886
866
        'Fabric feature only available after 1.9')
887
867
    def test_add_new_fabrics(self):
888
868
        # Create 2 new fabrics.
889
 
        output, err = self._run_maas_cli(
 
869
        output, _ = self._run_maas_cli(
890
870
            ['fabrics', 'create', 'name=test-fabric'])
891
 
        out_dict = loads(output)
892
 
        self.assertEqual('test-fabric', out_dict['name'])
893
 
        output, err = self._run_maas_cli(
 
871
        test_fabric = loads(output)
 
872
        self.assertEqual('test-fabric', test_fabric['name'])
 
873
        output, _ = self._run_maas_cli(
894
874
            ['fabrics', 'create', 'name=delete-fabric'])
895
 
        out_dict = loads(output)
896
 
        self.assertEqual('delete-fabric', out_dict['name'])
 
875
        delete_fabric = loads(output)
 
876
        self.assertEqual('delete-fabric', delete_fabric['name'])
897
877
 
898
878
    @skipIf(
899
879
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
900
880
        'VLAN feature only available after 1.9')
901
881
    def test_add_vlan_to_fabric(self):
902
 
        output, err = self._run_maas_cli(
 
882
        output, _ = self._run_maas_cli(
903
883
            ['vlans', 'create', 'test-fabric', 'name=test-vlan', 'vid=2'])
904
 
        out_dict = loads(output)
905
 
        self.assertEqual('test-vlan', out_dict['name'])
906
 
        self.assertEqual('test-fabric', out_dict['fabric'])
907
 
        self.assertEqual(2, out_dict['vid'])
 
884
        test_vlan = loads(output)
 
885
        self.assertEqual('test-vlan', test_vlan['name'])
 
886
        self.assertEqual('test-fabric', test_vlan['fabric'])
 
887
        self.assertEqual(2, test_vlan['vid'])
908
888
 
909
889
    @skipIf(
910
890
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
911
891
        'Fabric feature only available after 1.9')
912
892
    def test_list_fabrics(self):
913
 
        output, err = self._run_maas_cli(['fabrics', 'read'])
914
 
        out_dict = loads(output)
915
 
        fabric_names = [item['name'] for item in out_dict]
 
893
        output, _ = self._run_maas_cli(['fabrics', 'read'])
 
894
        fabrics = loads(output)
 
895
        fabric_names = [fabric['name'] for fabric in fabrics]
916
896
        self.assertThat(fabric_names, Contains('test-fabric'))
917
897
        fabric_names.remove('test-fabric')
918
898
        self.assertThat(fabric_names, Contains('delete-fabric'))
919
899
        fabric_names.remove('delete-fabric')
920
900
        for fabric_name in fabric_names:
921
 
            self.assertThat(fabric_name, MatchesRegex('fabric-\d$'))
 
901
            self.assertThat(fabric_name, MatchesRegex(r'fabric-\d$'))
922
902
        self.assertIn(
923
903
            'test-vlan',
924
904
            [[v['name'] for v in f['vlans']]
925
 
             for f in out_dict if f['name'] == 'test-fabric'][0])
 
905
             for f in fabrics if f['name'] == 'test-fabric'][0])
926
906
 
927
907
    @skipIf(
928
908
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
929
909
        'VLAN feature only available after 1.9')
930
910
    def test_list_vlans(self):
931
 
        output, err = self._run_maas_cli(['vlans', 'read', 'test-fabric'])
932
 
        out_dict = loads(output)
 
911
        output, _ = self._run_maas_cli(['vlans', 'read', 'test-fabric'])
 
912
        vlans = loads(output)
933
913
        expected = ['untagged', 'test-vlan']
934
 
        self.assertItemsEqual(expected, [v['name'] for v in out_dict])
 
914
        self.assertItemsEqual(expected, [v['name'] for v in vlans])
935
915
 
936
916
    @skipIf(
937
917
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
938
918
        'VLAN feature only available after 1.9')
939
919
    def test_delete_vlan(self):
940
 
        _, err = self._run_maas_cli(
941
 
            ['vlan', 'delete', 'test-fabric', 'test-vlan'])
942
 
        output, err = self._run_maas_cli(['vlans', 'read', 'test-fabric'])
943
 
        out = loads(output)
944
 
        self.assertNotIn('test-vlan', [v['name'] for v in out])
945
 
        self.assertEqual(1, len(out), 'Fabric should have only one VLAN now.')
 
920
        self._run_maas_cli(['vlan', 'delete', 'test-fabric', 'test-vlan'])
 
921
        output, _ = self._run_maas_cli(['vlans', 'read', 'test-fabric'])
 
922
        vlans = loads(output)
 
923
        self.assertNotIn('test-vlan', [v['name'] for v in vlans])
 
924
        self.assertEqual(
 
925
            1, len(vlans), 'Fabric should have only one VLAN now.'
 
926
        )
946
927
 
947
928
    def test_reserve_bmc_range(self):
948
 
        _, err = self._run_maas_cli(
949
 
            ['ipranges', 'create', 'type=reserved',
950
 
             'start_ip=' + BMC_START_IP, 'end_ip=' + BMC_END_IP,
951
 
             'comment=BMCs']
952
 
        )
953
 
        output, err = self._run_maas_cli(
 
929
        self._run_maas_cli([
 
930
            'ipranges', 'create', 'type=reserved',
 
931
            'start_ip=' + BMC_START_IP, 'end_ip=' + BMC_END_IP,
 
932
            'comment=BMCs'
 
933
        ])
 
934
        output, _ = self._run_maas_cli(
954
935
            ['ipranges', 'read']
955
936
        )
956
 
        out = loads(output)
 
937
        ipranges = loads(output)
957
938
        # TODO: assert that newly created range is there
958
939
        bmc_range = None
959
 
        for range in out:
960
 
            if range['comment'] == 'BMCs':
961
 
                bmc_range = range
 
940
        for iprange in ipranges:
 
941
            if iprange['comment'] == 'BMCs':
 
942
                bmc_range = iprange
962
943
        self.assertIsNotNone(bmc_range, 'BMC range not found')
963
944
        self.assertThat(bmc_range['start_ip'], Equals(BMC_START_IP))
964
945
        self.assertThat(bmc_range['end_ip'], Equals(BMC_END_IP))
968
949
        StrictVersion(get_maas_version()[:3]) < StrictVersion('1.9'),
969
950
        "Fabric feature only available after 1.9")
970
951
    def test_delete_fabric(self):
971
 
        _, err = self._run_maas_cli(["fabric", "delete", "delete-fabric"])
 
952
        self._run_maas_cli(["fabric", "delete", "delete-fabric"])
972
953
        # List the remaining zones after the delete command.
973
 
        output, err = self._run_maas_cli(["fabrics", "read"])
974
 
        out_dict = loads(output)
 
954
        output, _ = self._run_maas_cli(["fabrics", "read"])
 
955
        fabrics = loads(output)
975
956
        self.assertNotIn(
976
 
            'delete-fabric', sorted([item['name'] for item in out_dict]))
 
957
            'delete-fabric', sorted([fabric['name'] for fabric in fabrics]))
977
958
 
978
959
    @skipUnless(TEST_CUSTOM_IMAGES, "Not testing custom images")
979
960
    def test_set_custom_boot_source(self):
986
967
        self.assertThat(boot_source['url'], Equals(boot_source_url))
987
968
 
988
969
    def test_stop_image_import(self):
989
 
        output, err = self._run_maas_cli(
 
970
        output, _ = self._run_maas_cli(
990
971
            ["boot-resources", "stop-import"])
991
972
        self.assertThat(
992
973
            output, Contains("Import of boot resources is being stopped")
995
976
    @skipUnless(USE_PPC_NODES, "Not testing PPC systems")
996
977
    def test_add_boot_source_selection_ppc64el(self):
997
978
        # Add the ppc64el boot source selection to all boot sources
998
 
        output, err = self._run_maas_cli(["boot-sources", "read"])
 
979
        output, _ = self._run_maas_cli(["boot-sources", "read"])
999
980
        boot_sources = loads(output)
1000
981
        for source in boot_sources:
1001
 
            output, err = self._run_maas_cli(
 
982
            output, _ = self._run_maas_cli(
1002
983
                ["boot-source-selections", "read", str(source["id"])]
1003
984
            )
1004
 
            selections = loads(output)
1005
985
            # To add a new arch we need to specify the arches= parameter
1006
986
            # multiple times, e.g. arches=amd64 arches=ppc64el
1007
 
            for selection in selections:
 
987
            for selection in loads(output):
1008
988
                new_arches = selection['arches'] + ['ppc64el']
1009
989
                selection_update_params = [
1010
990
                    'arches={arch}'.format(arch=arch) for arch in new_arches
1011
991
                ]
1012
 
                output, err = self._run_maas_cli([
 
992
                output, _ = self._run_maas_cli([
1013
993
                    "boot-source-selection",
1014
994
                    "update",
1015
995
                    str(source["id"]),
1016
996
                    str(selection["id"]),
1017
997
                ] + selection_update_params)
1018
998
                updated_selection = loads(output)
1019
 
                self.assertThat(updated_selection["arches"], Equals(new_arches))
 
999
                self.assertThat(
 
1000
                    updated_selection["arches"], Equals(new_arches)
 
1001
                )
1020
1002
 
1021
1003
    def test_start_image_import(self):
1022
 
        output, err = self._run_maas_cli(
1023
 
            ["boot-resources", "import"])
1024
 
        self.assertThat(
1025
 
            output, Contains("Import of boot resources started")
1026
 
        )
 
1004
        output, _ = self._run_maas_cli(["boot-resources", "import"])
 
1005
        self.assertThat(output, Contains("Import of boot resources started"))
1027
1006
 
1028
1007
    @timeout(60 * 60)  # Allow for up to one hour
1029
1008
    def test_region_imported_images(self):
1051
1030
 
1052
1031
        complete_resources = set()
1053
1032
        while not expected_resources.issubset(complete_resources):
1054
 
            resources_output, err = self._run_maas_cli(
 
1033
            resources_output, _ = self._run_maas_cli(
1055
1034
                ["boot-resources", "read"])
1056
1035
            resources = loads(resources_output)
1057
1036
            for resource in resources:
1058
1037
                resource_id = resource['id']
1059
1038
                resource_name = resource['name']
1060
1039
                resource_arch = resource['architecture']
1061
 
                output, err = self._run_maas_cli(
 
1040
                output, _ = self._run_maas_cli(
1062
1041
                    ['boot-resource', 'read', '%s' % resource_id])
1063
1042
                resource_data = loads(output)
1064
1043
                for _, resource_set in resource_data['sets'].items():
1073
1052
    def test_rack_imported_images(self):
1074
1053
        rack_system_id = self._get_rack_systemid_on_region()
1075
1054
        while True:
1076
 
            output, err = self._run_maas_cli(
 
1055
            output, _ = self._run_maas_cli(
1077
1056
                ["rack-controller", "list-boot-images", rack_system_id])
1078
1057
            rack_images = loads(output)
1079
1058
            if rack_images['status'] == 'synced':
1090
1069
        ssh_dir = os.path.join(home_dir, '.ssh')
1091
1070
        ssh_key = open(
1092
1071
            os.path.join(ssh_dir, 'id_rsa.pub')).read()
1093
 
        out, err = self._run_maas_cli(["sshkeys", "create", "key=%s" % ssh_key])
1094
 
        out, err = self._run_maas_cli(["sshkeys", "read"])
 
1072
        self._run_maas_cli(["sshkeys", "create", "key=%s" % ssh_key])
 
1073
        out, _ = self._run_maas_cli(["sshkeys", "read"])
1095
1074
        keys = loads(out)
1096
1075
        self.assertEqual(ssh_key, keys[0]['key'])
1097
1076
 
1107
1086
        "Zone feature only available after 1.5")
1108
1087
    def test_assign_machines_to_test_zone(self):
1109
1088
        # Set two of the declared nodes to the test-zone created earlier.
1110
 
        output, err = self._run_maas_cli(["machines", "read"])
 
1089
        output, _ = self._run_maas_cli(["machines", "read"])
1111
1090
        machines = loads(output)
1112
1091
        for machine in machines[:2]:
1113
1092
            self._run_maas_cli(
1114
1093
                ["machine", "update", machine['system_id'], "zone=test-zone"])
1115
1094
        # Check nodes are in the test-zone
1116
 
        output, err = self._run_maas_cli(["machines", "read", "zone=test-zone"])
 
1095
        output, _ = self._run_maas_cli(["machines", "read", "zone=test-zone"])
1117
1096
        machines = loads(output)
1118
1097
        self.assertEqual(2, len(machines))
1119
1098
 
1125
1104
        all_machines.update(PPC_SYSTEMS)
1126
1105
        for mac in all_machines.keys():
1127
1106
            # run maas-cli command to search node by mac and return system_id
1128
 
            out, err = self._run_maas_cli(
 
1107
            out, _ = self._run_maas_cli(
1129
1108
                ["machines", "read", "mac_address=%s" % mac])
1130
 
            machine_list = loads(out)
1131
 
            for machine in machine_list:
 
1109
            for machine in loads(out):
1132
1110
                power_driver = ''
1133
1111
                if mac in ARM_LAB:
1134
1112
                    power_user = power_pass = 'admin'
1140
1118
                    power_user = POWER_USER
1141
1119
                    power_pass = POWER_PASS
1142
1120
                self._run_maas_cli([
1143
 
                    "machine", "update", machine['system_id'], "power_type=ipmi",
 
1121
                    "machine", "update", machine['system_id'],
 
1122
                    "power_type=ipmi",
1144
1123
                    "power_parameters_power_address=" + all_machines[mac],
1145
1124
                    "power_parameters_power_user=" + power_user,
1146
1125
                    "power_parameters_power_pass=" + power_pass,
1149
1128
 
1150
1129
    def test_start_commissioning_machines(self):
1151
1130
        # Use maas-cli to accept all nodes.
1152
 
        output, err = self._run_maas_cli(["machines", "accept-all"])
 
1131
        output, _ = self._run_maas_cli(["machines", "accept-all"])
1153
1132
        for node in loads(output):
1154
1133
            self.assertEqual(node['status'], 1)
1155
1134
 
1159
1138
 
1160
1139
    def test_apply_tag_to_all_machines(self):
1161
1140
        # Use maas-cli to set a tag on all nodes.
1162
 
        output, err = self._run_maas_cli(
 
1141
        output, _ = self._run_maas_cli(
1163
1142
            ["tags", "create", "name=all", "definition=true()",
1164
1143
             "comment=A tag present on all nodes"])
1165
1144
        tag = loads(output)
1170
1149
        # Wait for all nodes to have new tag applied.
1171
1150
        expected_node_count = self.get_node_count()
1172
1151
        while True:
1173
 
            output, err = self._run_maas_cli(["tag", "machines", "all"])
 
1152
            output, _ = self._run_maas_cli(["tag", "machines", "all"])
1174
1153
            nodes = loads(output)
1175
1154
            if len(nodes) == expected_node_count:
1176
1155
                break
1199
1178
        self.assertThat(retcode, Equals(0))
1200
1179
        self.assertThat(list_clouds_output, Contains("testmaas"))
1201
1180
        # Setup credentials
1202
 
        with open(os.path.expanduser(
1203
 
            '~/.local/share/juju/credentials.yaml'), mode='w', encoding='utf8') as credentials:
1204
 
            setup_juju_authentication(oauth=token_str, credentials_yaml=credentials)
 
1181
        credentials_path = os.path.expanduser(
 
1182
            '~/.local/share/juju/credentials.yaml'
 
1183
        )
 
1184
        with open(credentials_path, mode='w', encoding='utf8') as credentials:
 
1185
            setup_juju_authentication(
 
1186
                oauth=token_str, credentials_yaml=credentials
 
1187
            )
1205
1188
        retcode, list_creds_output, _ = self._run_juju_command(
1206
1189
            ['list-credentials']
1207
1190
        )
1236
1219
        env = os.environ.copy()
1237
1220
        env['http_proxy'] = HTTP_PROXY
1238
1221
        env['https_proxy'] = HTTP_PROXY
1239
 
        retcode, _, _ = self._run_juju_command(["deploy", "postgresql"], env=env)
 
1222
        retcode, _, _ = self._run_juju_command(
 
1223
            ["deploy", "postgresql"], env=env
 
1224
        )
1240
1225
        self.assertThat(retcode, Equals(0))
1241
1226
        self._wait_machines_running(1)
1242
1227
        self._wait_units_started('postgresql', 1)
1322
1307
                'ssh', 'ubuntu@{ip}'.format(ip=ip),
1323
1308
                'echo', 'test', '>', 'test;', 'cat', 'test'
1324
1309
            ]
1325
 
            ret, stdout, stderr = run_command(ssh_command)
 
1310
            _, stdout, _ = run_command(ssh_command)
1326
1311
            self.assertThat(stdout.strip(), Equals('test'))
1327
1312
 
1328
1313
    def test_ssh_add_apt_repository_and_install(self):
1411
1396
    @classmethod
1412
1397
    def dump_database(cls):
1413
1398
        """Dump the Django DB to /var/log/maas."""
1414
 
        ret, stdout, stderr = run_command([
 
1399
        _, stdout, stderr = run_command([
1415
1400
            "sudo", maas_region_admin, "dumpdata",
1416
1401
            "--indent", "4",
1417
1402
            "--exclude", "maasserver.candidatename",
1427
1412
    @classmethod
1428
1413
    def copy_juju_log(cls):
1429
1414
        # Use timeout because juju debug-log never returns (known juju bug).
1430
 
        ret, stdout, stderr = run_command(
1431
 
            ["timeout", "10", "juju", "debug-log", "--replay", "-l", "TRACE"])
 
1415
        _, stdout, _ = run_command(
 
1416
            ["timeout", "10", "juju", "debug-log", "--replay", "-l", "TRACE"]
 
1417
        )
1432
1418
        log_dir = "/var/log/maas"
1433
1419
        stdout_path = os.path.join(log_dir, 'juju-log-replay.stdout')
1434
1420
        with open(stdout_path, 'wb') as w_file:
1435
1421
            w_file.write(stdout.encode('utf-8'))
1436
 
        ret, stdout, stderr = run_command([
1437
 
            "juju", "scp", "0:/var/log/juju/*", log_dir])
 
1422
        run_command(
 
1423
            ["juju", "scp", "0:/var/log/juju/*", log_dir]
 
1424
        )
1438
1425
 
1439
1426
    @classmethod
1440
1427
    def tearDownClass(cls):