blob: 2b2f10d71c1569789f51f143a461c240800a0d50 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001import unittest
2import re
3from oeqa.oetest import oeRuntimeTest, skipModule
4from oeqa.utils.decorators import *
5
6def setUpModule():
7 if not oeRuntimeTest.hasFeature("systemd"):
8 skipModule("target doesn't have systemd in DISTRO_FEATURES")
9 if "systemd" != oeRuntimeTest.tc.d.getVar("VIRTUAL-RUNTIME_init_manager", True):
10 skipModule("systemd is not the init manager for this image")
11
12
13class SystemdTest(oeRuntimeTest):
14
15 def systemctl(self, action = '', target = '', expected = 0, verbose = False):
16 command = 'systemctl %s %s' % (action, target)
17 status, output = self.target.run(command)
18 message = '\n'.join([command, output])
19 if status != expected and verbose:
20 message += self.target.run('systemctl status --full %s' % target)[1]
21 self.assertEqual(status, expected, message)
22 return output
23
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050024 #TODO: use pyjournalctl instead
25 def journalctl(self, args='',l_match_units=[]):
26 """
27 Request for the journalctl output to the current target system
28
29 Arguments:
30 -args, an optional argument pass through argument
31 -l_match_units, an optional list of units to filter the output
32 Returns:
33 -string output of the journalctl command
34 Raises:
35 -AssertionError, on remote commands that fail
36 -ValueError, on a journalctl call with filtering by l_match_units that
37 returned no entries
38 """
39 query_units=""
40 if len(l_match_units):
41 query_units = ['_SYSTEMD_UNIT='+unit for unit in l_match_units]
42 query_units = " ".join(query_units)
43 command = 'journalctl %s %s' %(args, query_units)
44 status, output = self.target.run(command)
45 if status:
46 raise AssertionError("Command '%s' returned non-zero exit \
47 code %d:\n%s" % (command, status, output))
48 if len(output) == 1 and "-- No entries --" in output:
49 raise ValueError("List of units to match: %s, returned no entries"
50 % l_match_units)
51 return output
Patrick Williamsc124f4f2015-09-15 14:41:29 -050052
53class SystemdBasicTests(SystemdTest):
54
55 @skipUnlessPassed('test_ssh')
56 def test_systemd_basic(self):
57 self.systemctl('--version')
58
59 @testcase(551)
60 @skipUnlessPassed('test_system_basic')
61 def test_systemd_list(self):
62 self.systemctl('list-unit-files')
63
64 def settle(self):
65 """
66 Block until systemd has finished activating any units being activated,
67 or until two minutes has elapsed.
68
69 Returns a tuple, either (True, '') if all units have finished
70 activating, or (False, message string) if there are still units
71 activating (generally, failing units that restart).
72 """
73 import time
74 endtime = time.time() + (60 * 2)
75 while True:
76 status, output = self.target.run('systemctl --state=activating')
77 if "0 loaded units listed" in output:
78 return (True, '')
79 if time.time() >= endtime:
80 return (False, output)
81 time.sleep(10)
82
83 @testcase(550)
84 @skipUnlessPassed('test_systemd_basic')
85 def test_systemd_failed(self):
86 settled, output = self.settle()
87 self.assertTrue(settled, msg="Timed out waiting for systemd to settle:\n" + output)
88
89 output = self.systemctl('list-units', '--failed')
90 match = re.search("0 loaded units listed", output)
91 if not match:
92 output += self.systemctl('status --full --failed')
93 self.assertTrue(match, msg="Some systemd units failed:\n%s" % output)
94
95
96class SystemdServiceTests(SystemdTest):
97
98 def check_for_avahi(self):
99 if not self.hasPackage('avahi-daemon'):
100 raise unittest.SkipTest("Testcase dependency not met: need avahi-daemon installed on target")
101
102 @skipUnlessPassed('test_systemd_basic')
103 def test_systemd_status(self):
104 self.check_for_avahi()
105 self.systemctl('status --full', 'avahi-daemon.service')
106
107 @testcase(695)
108 @skipUnlessPassed('test_systemd_status')
109 def test_systemd_stop_start(self):
110 self.check_for_avahi()
111 self.systemctl('stop', 'avahi-daemon.service')
112 self.systemctl('is-active', 'avahi-daemon.service', expected=3, verbose=True)
113 self.systemctl('start','avahi-daemon.service')
114 self.systemctl('is-active', 'avahi-daemon.service', verbose=True)
115
116 @testcase(696)
117 @skipUnlessPassed('test_systemd_basic')
118 def test_systemd_disable_enable(self):
119 self.check_for_avahi()
120 self.systemctl('disable', 'avahi-daemon.service')
121 self.systemctl('is-enabled', 'avahi-daemon.service', expected=1)
122 self.systemctl('enable', 'avahi-daemon.service')
123 self.systemctl('is-enabled', 'avahi-daemon.service')
124
125class SystemdJournalTests(SystemdTest):
126 @skipUnlessPassed('test_ssh')
127 def test_systemd_journal(self):
128 (status, output) = self.target.run('journalctl')
129 self.assertEqual(status, 0, output)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500130
131 @skipUnlessPassed('test_systemd_basic')
132 def test_systemd_boot_time(self, systemd_TimeoutStartSec=90):
133 """
134 Get the target boot time from journalctl and log it
135
136 Arguments:
137 -systemd_TimeoutStartSec, an optional argument containing systemd's
138 unit start timeout to compare against
139 """
140
141 # the expression chain that uniquely identifies the time boot message
142 expr_items=["Startup finished","kernel", "userspace","\.$"]
143 try:
144 output = self.journalctl(args="-o cat --reverse")
145 except AssertionError:
146 self.fail("Error occurred while calling journalctl")
147 if not len(output):
148 self.fail("Error, unable to get startup time from systemd journal")
149
150 # check for the regular expression items that match the startup time
151 for line in output.split('\n'):
152 check_match = "".join(re.findall(".*".join(expr_items), line))
153 if check_match: break
154 # put the startup time in the test log
155 if check_match:
156 print "%s" % check_match
157 else:
158 self.skipTest("Error at obtaining the boot time from journalctl")
159 boot_time_sec = 0
160
161 # get the numeric values from the string and convert them to seconds
162 # same data will be placed in list and string for manipulation
163 l_boot_time = check_match.split(" ")[-2:]
164 s_boot_time = " ".join(l_boot_time)
165 try:
166 # Obtain the minutes it took to boot
167 if l_boot_time[0].endswith('min') and l_boot_time[0][0].isdigit():
168 boot_time_min = s_boot_time.split("min")[0]
169 # convert to seconds and accumulate it
170 boot_time_sec += int(boot_time_min) * 60
171 # Obtain the seconds it took to boot and accumulate
172 boot_time_sec += float(l_boot_time[1].split("s")[0])
173 except ValueError:
174 self.skipTest("Error when parsing time from boot string")
175 #Assert the target boot time against systemd's unit start timeout
176 if boot_time_sec > systemd_TimeoutStartSec:
177 print "Target boot time %s exceeds systemd's TimeoutStartSec %s"\
178 %(boot_time_sec, systemd_TimeoutStartSec)