3
from landscape.monitor.mountinfo import MountInfo
4
from landscape.tests.test_hal import MockHALManager, MockRealHALDevice
5
from landscape.tests.helpers import (LandscapeTest, MakePathHelper,
6
mock_counter, MonitorHelper)
7
from landscape.tests.mocker import ANY
10
mb = lambda x: x * 1024 * 1024
13
class MountInfoTest(LandscapeTest):
14
"""Tests for mount-info plugin."""
16
helpers = [MonitorHelper, MakePathHelper]
19
LandscapeTest.setUp(self)
20
self.mstore.set_accepted_types(["mount-info", "mount-activity",
23
def get_mount_info(self, *args, **kwargs):
24
hal_devices = kwargs.pop("hal_devices", [])
25
kwargs["hal_manager"] = MockHALManager(hal_devices)
26
if "statvfs" not in kwargs:
27
kwargs["statvfs"] = lambda path: (0,)*10
28
return MountInfo(*args, **kwargs)
30
def test_read_proc_mounts(self):
32
When the mount info plugin runs it reads data from
33
/proc/mounts to discover mounts and calls os.statvfs() to
34
retrieve current data for each mount point. This test makes
35
sure that os.statvfs() is called without failing, that
36
/proc/mounts is readable, and that messages with the expected
37
datatypes are generated.
39
plugin = self.get_mount_info(create_time=self.reactor.time)
40
self.monitor.add(plugin)
42
self.reactor.advance(self.monitor.step_size)
44
message = plugin.create_mount_info_message()
45
self.assertTrue(message)
46
self.assertEquals(message["type"], "mount-info")
47
self.assertTrue("mount-info" in message)
48
self.assertTrue(len(message["mount-info"]) > 0)
50
keys = set(["filesystem", "total-space", "device", "mount-point"])
51
for now, mount_info in message["mount-info"]:
52
self.assertEquals(set(mount_info.keys()), keys)
53
self.assertTrue(isinstance(mount_info["filesystem"], basestring))
54
self.assertTrue(isinstance(mount_info["device"], basestring))
55
self.assertTrue(isinstance(mount_info["total-space"], (int, long)))
56
self.assertTrue(isinstance(mount_info["mount-point"], basestring))
58
def test_read_sample_data(self):
60
Sample data is used to ensure that the free space included in
61
the message is calculated correctly.
65
return (4096, 0, mb(1000L), mb(100L), 0L, 0L, 0L, 0, 0)
67
return (4096, 0, mb(10000L), mb(1000L), 0L, 0L, 0L, 0, 0)
69
filename = self.make_path("""\
70
rootfs / rootfs rw 0 0
71
none /dev ramfs rw 0 0
72
/dev/hda1 / ext3 rw 0 0
73
/dev/hda1 /dev/.static/dev ext3 rw 0 0
74
proc /proc proc rw,nodiratime 0 0
75
sysfs /sys sysfs rw 0 0
76
usbfs /proc/bus/usb usbfs rw 0 0
77
devpts /dev/pts devpts rw 0 0
78
tmpfs /dev/shm tmpfs rw 0 0
79
tmpfs /lib/modules/2.6.12-10-386/volatile tmpfs rw 0 0
80
/dev/hde1 /mnt/hde1 reiserfs rw 0 0
81
/dev/hde1 /mnt/bind reiserfs rw 0 0
82
/dev/sdb2 /media/Boot\\040OSX hfsplus nls=utf8 0 0
85
mtab_filename = self.make_path("""\
86
rootfs / rootfs rw 0 0
87
none /dev ramfs rw 0 0
88
/dev/hda1 / ext3 rw 0 0
89
/dev/hda1 /dev/.static/dev ext3 rw 0 0
90
proc /proc proc rw,nodiratime 0 0
91
sysfs /sys sysfs rw 0 0
92
usbfs /proc/bus/usb usbfs rw 0 0
93
devpts /dev/pts devpts rw 0 0
94
tmpfs /dev/shm tmpfs rw 0 0
95
tmpfs /lib/modules/2.6.12-10-386/volatile tmpfs rw 0 0
96
/dev/hde1 /mnt/hde1 reiserfs rw 0 0
97
/dev/hde1 /mnt/bind none rw,bind 0 0
98
/dev/sdb2 /media/Boot\\040OSX hfsplus rw 0 0
101
plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
102
create_time=self.reactor.time,
103
mtab_file=mtab_filename)
104
self.monitor.add(plugin)
105
self.reactor.advance(self.monitor.step_size)
107
message = plugin.create_mount_info_message()
108
self.assertTrue(message)
109
self.assertEquals(message["type"], "mount-info")
111
mount_info = message.get("mount-info", ())
113
self.assertEquals(len(mount_info), 3)
115
self.assertEquals(mount_info[0][1],
116
{"device": "/dev/hda1", "mount-point": "/",
117
"filesystem": "ext3", "total-space": 4096000})
119
self.assertEquals(mount_info[1][1],
120
{"device": "/dev/hde1", "mount-point": "/mnt/hde1",
121
"filesystem": "reiserfs", "total-space": 40960000})
123
self.assertEquals(mount_info[2][1],
124
{"device": "/dev/sdb2", "mount-point": "/media/Boot OSX",
125
"filesystem": "hfsplus", "total-space": 40960000})
127
def test_read_changing_total_space(self):
129
Total space measurements are only sent when (a) none have ever
130
been sent, or (b) the value has changed since the last time
131
data was collected. The test sets the mount info plugin
132
interval to the same value as the step size and advances the
133
reactor such that the plugin will be run twice. Each time it
134
runs it gets a different value from our sample statvfs()
135
function which should cause it to queue new messages.
137
def statvfs(path, multiplier=mock_counter(1).next):
138
return (4096, 0, mb(multiplier() * 1000), mb(100), 0, 0, 0, 0, 0)
140
filename = self.make_path("""\
141
/dev/hda1 / ext3 rw 0 0
143
plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
144
create_time=self.reactor.time,
145
interval=self.monitor.step_size,
147
self.monitor.add(plugin)
149
self.reactor.advance(self.monitor.step_size * 2)
151
message = plugin.create_mount_info_message()
152
mount_info = message["mount-info"]
153
self.assertEquals(len(mount_info), 2)
155
for i, total_space in enumerate([4096000, 8192000]):
156
self.assertEquals(mount_info[i][0], (i+1) * self.monitor.step_size)
157
self.assertEquals(mount_info[i][1],
158
{"device": "/dev/hda1", "filesystem": "ext3",
159
"mount-point": "/", "total-space": total_space})
161
def test_read_disjointed_changing_total_space(self):
163
Total space measurements are only sent when (a) none have ever
164
been sent, or (b) the value has changed since the last time
165
data was collected. This test ensures that the (b) criteria
166
is checked per-mount point. The sample statvfs() function
167
only provides changing total space for /; therefore, new
168
messages should only be queued for / after the first message
171
def statvfs(path, multiplier=mock_counter(1).next):
173
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
174
return (4096, 0, mb(multiplier() * 1000), mb(100), 0, 0, 0, 0, 0)
176
filename = self.make_path("""\
177
/dev/hda1 / ext3 rw 0 0
178
/dev/hde1 /mnt/hde1 ext3 rw 0 0
180
plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
181
create_time=self.reactor.time,
182
interval=self.monitor.step_size,
184
self.monitor.add(plugin)
186
self.reactor.advance(self.monitor.step_size * 2)
188
message = plugin.create_mount_info_message()
189
self.assertTrue(message)
191
mount_info = message.get("mount-info", ())
192
self.assertEquals(len(mount_info), 3)
194
self.assertEquals(mount_info[0][0], self.monitor.step_size)
195
self.assertEquals(mount_info[0][1],
196
{"device": "/dev/hda1", "mount-point": "/",
197
"filesystem": "ext3", "total-space": 4096000})
199
self.assertEquals(mount_info[1][0], self.monitor.step_size)
200
self.assertEquals(mount_info[1][1],
201
{"device": "/dev/hde1", "mount-point": "/mnt/hde1",
202
"filesystem": "ext3", "total-space": 4096000})
204
self.assertEquals(mount_info[2][0], self.monitor.step_size * 2)
205
self.assertEquals(mount_info[2][1],
206
{"device": "/dev/hde1", "mount-point": "/mnt/hde1",
207
"filesystem": "ext3", "total-space": 8192000})
209
def test_exchange_messages(self):
211
The mount_info plugin queues message when manager.exchange()
212
is called. Each message should be aligned to a step boundary;
213
messages collected bewteen exchange periods should be
214
delivered in a single message.
217
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
219
filename = self.make_path("""\
220
/dev/hda1 / ext3 rw 0 0
222
plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
223
create_time=self.reactor.time,
225
step_size = self.monitor.step_size
226
self.monitor.add(plugin)
228
self.reactor.advance(step_size * 2)
229
self.monitor.exchange()
231
messages = self.mstore.get_pending_messages()
232
self.assertEquals(len(messages), 3)
234
message = [d for d in messages if d["type"] == "free-space"][0]
235
free_space = message["free-space"]
236
for i in range(1, 1):
237
self.assertEquals(free_space[i][0], i * step_size)
238
self.assertEquals(free_space[i][1], "/")
239
self.assertEquals(free_space[i][2], 409600)
241
def test_messaging_flushes(self):
243
Duplicate message should never be created. If no data is
244
available, None will be returned when messages are created.
247
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
249
filename = self.make_path("""\
250
/dev/hda1 / ext3 rw 0 0
252
plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
253
create_time=self.reactor.time,
255
self.monitor.add(plugin)
257
self.reactor.advance(self.monitor.step_size)
259
messages = plugin.create_messages()
260
self.assertEquals(len(messages), 3)
262
messages = plugin.create_messages()
263
self.assertEquals(len(messages), 0)
265
def test_read_multi_bound_mounts(self):
267
The mount info plugin should handle multi-bound mount points
268
by reporting them only once. In practice, this test doesn't
269
really test anything since the current behaviour is to ignore
270
any mount point for which the device doesn't start with /dev.
273
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
275
filename = self.make_path("""\
276
/dev/hdc4 /mm xfs rw 0 0
277
/mm/ubuntu-mirror /home/dchroot/warty/mirror none bind 0 0
278
/mm/ubuntu-mirror /home/dchroot/hoary/mirror none bind 0 0
279
/mm/ubuntu-mirror /home/dchroot/breezy/mirror none bind 0 0
281
plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
282
create_time=self.reactor.time,
284
step_size = self.monitor.step_size
285
self.monitor.add(plugin)
287
self.reactor.advance(step_size)
289
message = plugin.create_mount_info_message()
290
self.assertTrue(message)
292
mount_info = message.get("mount-info", ())
293
self.assertEquals(len(mount_info), 1)
295
self.assertEquals(mount_info[0][0], step_size)
296
self.assertEquals(mount_info[0][1],
297
{"device": "/dev/hdc4", "mount-point": "/mm",
298
"filesystem": "xfs", "total-space": 4096000})
300
def test_ignore_nfs_mounts(self):
302
The mount info plugin should only report data about local
306
filename = self.make_path("""\
307
ennui:/data /data nfs rw,v3,rsize=32768,wsize=32768,hard,lock,proto=udp,addr=ennui 0 0
309
plugin = self.get_mount_info(mounts_file=filename, mtab_file=filename)
310
self.monitor.add(plugin)
313
message = plugin.create_mount_info_message()
314
self.assertEquals(message, None)
316
def test_ignore_removable_partitions(self):
318
Partitions on removable devices don't directly report
319
storage.removable : True, but they do point to their parent and the
320
parent will be marked removable if appropriate.
322
devices = [MockRealHALDevice({"info.udi": "wubble",
323
"block.device": "/dev/scd",
324
"storage.removable": True}),
325
MockRealHALDevice({"info.udi": "wubble0",
326
"block.device": "/dev/scd0",
327
"info.parent": "wubble"}),]
329
filename = self.make_path("""\
330
/dev/scd0 /media/Xerox_M750 iso9660 ro,nosuid,nodev,uid=1000,utf8 0 0
332
plugin = self.get_mount_info(mounts_file=filename, hal_devices=devices,
334
self.monitor.add(plugin)
337
message = plugin.create_mount_info_message()
338
self.assertEquals(message, None)
340
def test_ignore_removable_devices(self):
342
The mount info plugin should only report data about
343
non-removable devices.
345
devices = [MockRealHALDevice({"info.udi": "wubble",
346
"block.device": "/dev/scd0",
347
"storage.removable": True}),]
348
filename = self.make_path("""\
349
/dev/scd0 /media/Xerox_M750 iso9660 ro,nosuid,nodev,uid=1000,utf8 0 0
351
plugin = self.get_mount_info(mounts_file=filename, hal_devices=devices,
353
self.monitor.add(plugin)
356
message = plugin.create_mount_info_message()
357
self.assertEquals(message, None)
359
def test_ignore_multiparented_removable_devices(self):
361
Some removable devices might be the grand-children of a device that is
362
marked as "storage.removable".
364
devices = [MockRealHALDevice({"info.udi": "wubble",
365
"block.device": "/dev/scd",
366
"storage.removable": True}),
367
MockRealHALDevice({"info.udi": "wubble0",
368
"block.device": "/dev/scd0",
369
"info.parent": "wubble"}),
370
MockRealHALDevice({"info.udi": "wubble0a",
371
"block.device": "/dev/scd0a",
372
"info.parent": "wubble0"}),
373
MockRealHALDevice({"info.udi": "wubble0b",
374
"block.device": "/dev/scd0b",
375
"info.parent": "wubble0"}),]
378
filename = self.make_path("""\
379
/dev/scd0a /media/Xerox_M750 iso9660 ro,nosuid,nodev,uid=1000,utf8 0 0
381
plugin = self.get_mount_info(mounts_file=filename, hal_devices=devices,
383
self.monitor.add(plugin)
386
message = plugin.create_mount_info_message()
387
self.assertEquals(message, None)
389
def test_sample_free_space(self):
390
"""Test collecting information about free space."""
391
def statvfs(path, multiplier=mock_counter(1).next):
392
return (4096, 0, mb(1000), mb(multiplier() * 100), 0, 0, 0, 0, 0)
394
filename = self.make_path("""\
395
/dev/hda2 / xfs rw 0 0
397
plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
398
create_time=self.reactor.time,
400
step_size = self.monitor.step_size
401
self.monitor.add(plugin)
403
self.reactor.advance(step_size)
405
message = plugin.create_free_space_message()
406
self.assertTrue(message)
407
self.assertEquals(message.get("type"), "free-space")
408
free_space = message.get("free-space", ())
409
self.assertEquals(len(free_space), 1)
410
self.assertEquals(free_space[0], (step_size, "/", 409600))
412
def test_never_exchange_empty_messages(self):
414
When the plugin has no data, it's various create_X_message()
415
methods will return None. Empty or null messages should never
418
self.mstore.set_accepted_types(["load-average"])
420
filename = self.make_path("")
421
plugin = self.get_mount_info(mounts_file=filename, mtab_file=filename)
422
self.monitor.add(plugin)
423
self.monitor.exchange()
424
self.assertEquals(len(self.mstore.get_pending_messages()), 0)
426
def test_messages(self):
428
Test ensures all expected messages are created and contain the
432
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
434
filename = self.make_path("""\
435
/dev/hda2 / xfs rw 0 0
437
plugin = self.get_mount_info(mounts_file=filename, statvfs=statvfs,
438
create_time=self.reactor.time,
440
step_size = self.monitor.step_size
441
self.monitor.add(plugin)
443
self.reactor.advance(step_size)
444
self.monitor.exchange()
446
messages = self.mstore.get_pending_messages()
447
self.assertEquals(len(messages), 3)
448
self.assertEquals(messages[0].get("mount-info"),
450
{"device": "/dev/hda2", "mount-point": "/",
451
"filesystem": "xfs", "total-space": 4096000})])
452
self.assertEquals(messages[1].get("free-space"),
453
[(step_size, "/", 409600)])
454
self.assertTrue(isinstance(messages[1]["free-space"][0][2],
456
self.assertEquals(messages[2].get("activities"),
457
[(step_size, "/", True)])
459
def test_first_mount_activity_message(self):
461
Mount activity is only reported when a change from the
462
previous known state is detected. If mount activity has never
463
been reported, it should be.
465
filename = self.make_path("""\
466
/dev/hda2 / xfs rw 0 0
468
plugin = self.get_mount_info(mounts_file=filename,
469
create_time=self.reactor.time,
471
step_size = self.monitor.step_size
472
self.monitor.add(plugin)
474
self.reactor.advance(step_size)
475
message = plugin.create_mount_activity_message()
476
self.assertEquals(message.get("type"), "mount-activity")
477
self.assertEquals(message.get("activities"), [(300, "/", True)])
479
self.reactor.advance(step_size)
480
self.assertEquals(plugin.create_mount_activity_message(), None)
482
def test_wb_umount_activity(self):
483
"""Test ensures the plugin reports new umounts."""
484
filename = self.make_path("""\
485
/dev/hda2 / xfs rw 0 0
487
plugin = self.get_mount_info(mounts_file=filename,
488
create_time=self.reactor.time,
490
step_size = self.monitor.step_size
491
self.monitor.add(plugin)
493
self.reactor.advance(step_size)
494
message = plugin.create_mount_activity_message()
495
self.assertEquals(message.get("type"), "mount-activity")
496
self.assertEquals(message.get("activities"), [(step_size, "/", True)])
498
plugin._mounts_file = self.make_path("""\
500
self.reactor.advance(step_size)
501
message = plugin.create_mount_activity_message()
502
self.assertEquals(message.get("type"), "mount-activity")
503
self.assertEquals(message.get("activities"),
504
[(step_size * 2, "/", False)])
506
def test_wb_mount_activity(self):
507
"""Test ensures the plugin reports new mounts."""
508
filename = self.make_path("""\
509
/dev/hda2 / xfs rw 0 0
511
plugin = self.get_mount_info(mounts_file=filename,
512
create_time=self.reactor.time,
514
step_size = self.monitor.step_size
515
self.monitor.add(plugin)
517
self.reactor.advance(step_size)
518
message = plugin.create_mount_activity_message()
519
self.assertEquals(message.get("type"), "mount-activity")
520
self.assertEquals(message.get("activities"), [(step_size, "/", True)])
522
mount_dir = self.make_dir()
523
plugin._mounts_file = self.make_path("""\
524
/dev/hda2 / xfs rw 0 0
525
/dev/hdb5 %s xfs rw 0 0
527
self.reactor.advance(step_size)
528
message = plugin.create_mount_activity_message()
529
self.assertEquals(message.get("type"), "mount-activity")
530
self.assertEquals(message.get("activities"),
531
[(step_size * 2, mount_dir, True)])
534
def test_resynchronize(self):
536
On the reactor "resynchronize" event, new mount-info messages
540
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
541
filename = self.make_path("""\
542
/dev/hda1 / ext3 rw 0 0
544
plugin = self.get_mount_info(mounts_file=filename,
545
create_time=self.reactor.time,
546
statvfs=statvfs, mtab_file=filename)
547
self.monitor.add(plugin)
551
self.reactor.fire("resynchronize")
554
messages = self.mstore.get_pending_messages()
555
messages = [message for message in messages
556
if message["type"] == "mount-info"]
558
'type': 'mount-info',
559
'mount-info': [(0, {'device': '/dev/hda1', 'mount-point': '/',
560
'total-space': 4096000, 'filesystem': 'ext3'})]}
561
self.assertMessages(messages, [expected_message, expected_message])
563
def test_bind_mounts(self):
565
Mounted devices that are mounted using Linux's "--bind" option
566
shouldn't be listed, as they have the same free space/used space as the
567
device they're bound to.
570
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
572
# From this test data, we expect only two mount points to be returned,
573
# and the other two to be ignored (the rebound /dev/hda2 -> /mnt mounting)
574
filename = self.make_path("""\
575
/dev/devices/by-uuid/12345567 / ext3 rw 0 0
576
/dev/hda2 /usr ext3 rw 0 0
577
/dev/devices/by-uuid/12345567 /mnt ext3 rw 0 0
578
/dev/devices/by-uuid/12345567 /media/Boot\\040OSX hfsplus rw 0 0
581
mtab_filename = self.make_path("""\
582
/dev/hda1 / ext3 rw 0 0
583
/dev/hda2 /usr ext3 rw 0 0
584
/opt /mnt none rw,bind 0 0
585
/opt /media/Boot\\040OSX none rw,bind 0 0
587
plugin = MountInfo(mounts_file=filename, create_time=self.reactor.time,
588
statvfs=statvfs, mtab_file=mtab_filename)
589
self.monitor.add(plugin)
591
message = plugin.create_mount_info_message()
592
self.assertEquals(message.get("mount-info"),
593
[(0, {"device": "/dev/devices/by-uuid/12345567",
594
"mount-point": "/", "total-space": 4096000L,
595
"filesystem": "ext3"}),
596
(0 ,{"device": "/dev/hda2",
597
"mount-point": "/usr",
598
"total-space": 4096000L,
599
"filesystem": "ext3"}),
602
def test_no_mtab_file(self):
604
If there's no mtab file available, then we can make no guesses about
605
bind mounted directories, so any filesystems in /proc/mounts will be
609
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
611
# In this test, we expect all mount points to be returned, as we can't
612
# identify any as bind mounts.
613
filename = self.make_path("""\
614
/dev/devices/by-uuid/12345567 / ext3 rw 0 0
615
/dev/hda2 /usr ext3 rw 0 0
616
/dev/devices/by-uuid/12345567 /mnt ext3 rw 0 0
618
# mktemp isn't normally secure, due to race conditions, but in this
619
# case, we don't actually create the file at all.
620
mtab_filename = tempfile.mktemp()
621
plugin = MountInfo(mounts_file=filename, create_time=self.reactor.time,
622
statvfs=statvfs, mtab_file=mtab_filename)
623
self.monitor.add(plugin)
625
message = plugin.create_mount_info_message()
626
self.assertEquals(message.get("mount-info"),
627
[(0, {"device": "/dev/devices/by-uuid/12345567",
628
"mount-point": "/", "total-space": 4096000L,
629
"filesystem": "ext3"}),
630
(0,{"device": "/dev/hda2",
631
"mount-point": "/usr",
632
"total-space": 4096000L,
633
"filesystem": "ext3"}),
634
(0,{"device": "/dev/devices/by-uuid/12345567",
635
"mount-point": "/mnt",
636
"total-space": 4096000L,
637
"filesystem": "ext3"}),])
639
def test_no_message_if_not_accepted(self):
641
Don't add any messages at all if the broker isn't currently
642
accepting their type.
644
self.mstore.set_accepted_types([])
646
return (4096, 0, mb(1000), mb(100), 0, 0, 0, 0, 0)
648
# From this test data, we expect only two mount points to be returned,
649
# and the third to be ignored (the rebound /dev/hda2 -> /mnt mounting)
650
filename = self.make_path("""\
651
/dev/devices/by-uuid/12345567 / ext3 rw 0 0
652
/dev/hda2 /usr ext3 rw 0 0
653
/dev/devices/by-uuid/12345567 /mnt ext3 rw 0 0
656
mtab_filename = self.make_path("""\
657
/dev/hda1 / ext3 rw 0 0
658
/dev/hda2 /usr ext3 rw 0 0
659
/opt /mnt none rw,bind 0 0
661
plugin = MountInfo(mounts_file=filename, create_time=self.reactor.time,
662
statvfs=statvfs, mtab_file=mtab_filename)
663
self.monitor.add(plugin)
664
self.reactor.advance(self.monitor.step_size * 2)
665
self.monitor.exchange()
667
self.mstore.set_accepted_types(["mount-info"])
668
self.assertMessages(list(self.mstore.get_pending_messages()), [])
670
def test_call_on_accepted(self):
671
plugin = self.get_mount_info(create_time=self.reactor.time)
672
self.monitor.add(plugin)
674
self.reactor.advance(plugin.run_interval)
676
remote_broker_mock = self.mocker.replace(self.remote)
677
remote_broker_mock.send_message(ANY, urgent=True)
681
self.reactor.fire(("message-type-acceptance-changed", "mount-info"),