Merge lp:~jtv/maas-test/main-breakup into lp:maas-test

Proposed by Jeroen T. Vermeulen
Status: Merged
Approved by: Jeroen T. Vermeulen
Approved revision: 117
Merged at revision: 117
Proposed branch: lp:~jtv/maas-test/main-breakup
Merge into: lp:maas-test
Diff against target: 137 lines (+84/-24)
1 file modified
maastest/main.py (+84/-24)
To merge this branch: bzr merge lp:~jtv/maas-test/main-breakup
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Review via email: mp+201148@code.launchpad.net

Commit message

Break up main() a bit, make it testable, use exceptions sensibly.

Description of the change

This was getting to a size where carrying on Go-style was just not feasible any more.

I introduced a dedicated exception for "this is a mode of failure that we know can happen, and which should terminate the program in a known, managed, helpful way." I hope that was not presumptuous of me. If we wanted to get fancy, we might have exception classes for this instead of a return-codes enum, but that's not furthering the goal of this branch. The goal of this branch is to let me add yet another startup check without everything getting out of hand, and without unleashing sheer patching hell just to be able to exercise my new code in tests.

Jeroen

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'maastest/main.py'
2--- maastest/main.py 2014-01-09 14:21:17 +0000
3+++ maastest/main.py 2014-01-10 09:34:51 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2013 Canonical Ltd. This software is licensed under the
6+# Copyright 2014 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Test whether or not a node is compatible with MAAS."""
10@@ -33,28 +33,55 @@
11 UNEXPECTED_DHCP_SERVER = 4
12
13
14-def main(argv=None):
15- from maastest.parser import prepare_parser
16- args = prepare_parser().parse_args(argv)
17-
18- from maastest import utils
19- import logging
20- # We tie everything onto one output stream so that we can capture
21- # things for reporting.
22- output_stream = utils.CachingOutputStream(sys.stdout)
23- logging.basicConfig(
24- level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s',
25- stream=output_stream)
26-
27+class ProgramFailure(Exception):
28+ """A known kind of program failure.
29+
30+ These are fatal, program-level failures that we know how to present to the
31+ user, including which error code to return.
32+
33+ :ivar return_code: Return code, from `RETURN_CODES`, which the program
34+ should return to the shell.
35+ """
36+ def __init__(self, return_code, message):
37+ super(ProgramFailure, self).__init__(message)
38+ self.return_code = return_code
39+
40+
41+def check_for_root():
42+ """Verify that the program is being run as root.
43+
44+ :raise ProgramFailure: if the program is not running as root.
45+ """
46+ # Importing locally for responsiveness.
47 import os
48 if os.geteuid() != 0:
49- logging.error("This test must be run as root. Use sudo.")
50- return RETURN_CODES.NOT_ROOT
51-
52+ raise ProgramFailure(
53+ RETURN_CODES.NOT_ROOT, "This test must be run as root. Use sudo.")
54+
55+
56+def check_for_virtualisation_support():
57+ """Verify that the system supports efficient virtual machines.
58+
59+ :raise ProgramFailure: if the CPU lacks virtualisation support, or support
60+ has been disabled in firmware.
61+ """
62+ # Importing locally for responsiveness.
63 from maastest import utils
64 if not utils.check_kvm_ok():
65- logging.error("Unable to continue without KVM extensions.")
66- return RETURN_CODES.NO_KVM_EXTENSIONS
67+ raise ProgramFailure(
68+ RETURN_CODES.NO_KVM_EXTENSIONS,
69+ "Unable to continue without KVM extensions.")
70+
71+
72+def check_against_virtual_host():
73+ """Warn if the program is running in a virtual machine.
74+
75+ For acceptable performance, maas-test should be run on a physical system.
76+ This function warns, but does not fail, if the system is virtual.
77+ """
78+ # Importing locally for responsiveness.
79+ import logging
80+ from maastest import utils
81 virt_type = utils.virtualization_type()
82 if virt_type is not None:
83 logging.info(
84@@ -63,16 +90,49 @@
85 "Running maas-test on this machine will result in significantly "
86 "poorer performance than on physical hardware.")
87
88+
89+def check_against_dhcp_servers(interface):
90+ """Make sure there are no unexpected DHCP servers on the testing network.
91+
92+ :param interface: Network interface to the testing network. This will
93+ send out a DHCP discovery request on that interface.
94+ :raise ProgramFailure: if any DHCP servers were detected.
95+ """
96+ # Importing locally for responsiveness.
97 from maastest.detect_dhcp import probe_dhcp
98- dhcp_servers = probe_dhcp(args.interface)
99+ dhcp_servers = probe_dhcp(interface)
100 if len(dhcp_servers) > 0:
101- logging.error(
102+ raise ProgramFailure(
103+ RETURN_CODES.UNEXPECTED_DHCP_SERVER,
104 "DHCP server(s) detected on %s: %s. "
105 "Pass an interface that is connected to the testing network. "
106 "Ensure that the testing network connects only to the node and "
107- "its BMC, and has no DHCP service running.",
108- args.interface, ', '.join(sorted(dhcp_servers)))
109- return RETURN_CODES.UNEXPECTED_DHCP_SERVER
110+ "its BMC, and has no DHCP service running."
111+ % (interface, ', '.join(sorted(dhcp_servers))))
112+
113+
114+def main(argv=None):
115+ from maastest.parser import prepare_parser
116+ args = prepare_parser().parse_args(argv)
117+
118+ # Importing locally for responsiveness.
119+ from maastest import utils
120+ import logging
121+ # We tie everything onto one output stream so that we can capture
122+ # things for reporting.
123+ output_stream = utils.CachingOutputStream(sys.stdout)
124+ logging.basicConfig(
125+ level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s',
126+ stream=output_stream)
127+
128+ try:
129+ check_for_root()
130+ check_for_virtualisation_support()
131+ check_against_virtual_host()
132+ check_against_dhcp_servers(args.interface)
133+ except ProgramFailure as e:
134+ logging.error(e)
135+ return e.return_code
136
137 from maastest.cases import TestOneNode
138

Subscribers

People subscribed via source and target branches