~andreserl/maas/qa-lab-tests-bionic

« back to all changes in this revision

Viewing changes to common.py

  • Committer: Brendan Donegan
  • Date: 2016-11-03 22:30:53 UTC
  • mto: This revision was merged to the branch mainline in revision 461.
  • Revision ID: brendan.donegan@canonical.com-20161103223053-m2k2bxxaqcg3xg6i
Whole tonne of flake8 and pylint fixes

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