Mirror is a Python-based task scheduler application designed for open source mirror sites (e.g., mirror.bjtu.edu.cn) to synchronize files from upstream sources. It uses rsync internally and works similarly to cron but with additional features like priority-based scheduling, load balancing, and timeout handling.
Key Features:
- Cron-like scheduling with priority-based task execution
- System load and HTTP connection-aware scheduling
- Two-stage syncing support (for Ubuntu, Debian)
- Plugin system for extensibility
- Task timeout and auto-retry mechanisms
- Internationalization (i18n) support
Version: 0.8.4 License: GPLv3 Python Support: 2.7, 3.5, 3.6, 3.7, 3.8
mirror/ # Main source code
├── __init__.py # Package initialization (namespace package)
├── main.py # Entry point for daemon (mirrord)
├── daemon.py # MirrorDaemon class - main daemon orchestration
├── scheduler.py # Task scheduler with priority queue
├── task.py # Task classes (Task, SimpleTask, SystemTask)
├── queue.py # Priority queue implementation for tasks
├── component.py # Component registry for dependency management
├── event.py # Event definitions (MirrorEvent hierarchy)
├── eventmanager.py # Event emission and handling
├── pluginmanager.py # Plugin loading and management
├── pluginbase.py # Base class for plugins
├── pluginthread.py # Thread for plugin execution
├── config.py # Configuration file parsing (INI format)
├── configmanager.py # Configuration manager singleton
├── common.py # Utility functions (version, i18n, cron parsing)
├── log.py # Logging setup and configuration
├── error.py # Custom exception classes
├── handler.py # Signal handlers (SIGTERM, SIGCHLD, SIGHUP)
├── console.py # CLI commands (-t for task list, -s for signals)
├── sysinfo.py # System info (load avg, TCP connections)
├── color.py # Terminal color utilities
├── bus.py # D-Bus integration (optional)
├── plugins/ # Built-in plugins
│ ├── Notifier/ # Notification plugin
│ ├── SlateFish/ # Web status plugin
│ ├── SystemTask/ # System maintenance tasks
│ └── TaskStatus/ # Task status export plugin
└── i18n/ # Internationalization files
├── mirror.pot # Translation template
└── zh_CN.po # Chinese translation
config/ # Configuration examples
├── mirror.ini # Main configuration example
├── bjtu.ini # Production config for BJTU mirror
└── plugin.ini # Plugin configuration
test/ # Unit tests
├── test_task.py # Task scheduling tests
└── test_taskqueue.py # Priority queue tests
docs/man/ # Man page
└── mirrord.1 # Manual page for mirrord
completion/ # Shell completions
├── bash/mirrord # Bash completion
└── zsh/_mirrord # Zsh completion
util/systemd/ # SystemD service files
└── system/mirrord@.service
- Language: Python (2.7, 3.5+)
- Build System: setuptools
- Dependencies:
setuptools- Package managementchardet- Character encoding detection
- Optional Dependencies:
xdg- XDG Base Directory specification support
sudo pip install mirrorpython setup.py build
sudo python setup.py install# Build translations (.po to .mo)
python setup.py build_trans
# Build plugins into .egg files
python setup.py build_plugins
# Run tests
python setup.py test
# Clean build artifacts
python setup.py cleansudo mkdir -p /etc/mirror /var/log/mirrord /var/log/rsync
sudo cp config/mirror.ini /etc/mirror/
sudo chown mirror:mirror /var/log/mirrord /var/log/rsyncLocated at /etc/mirror/mirror.ini or ~/.config/mirror/mirror.ini:
[general]
emails = admin@example.com
loadlimit = 2.0 # Max system load before throttling
httpconn = 1200 # Max HTTP connections before throttling
logdir = /var/log/rsync/
maxtasks = 10 # Max concurrent tasks
[archlinux]
upstream[] = mirror.aarnet.edu.au
command = rsync
exclude = --exclude .~tmp~/
time = * */2 * * * # Cron-like schedule (every 2 hours)
rsyncdir = archlinux/
localdir = /home/mirror/archlinux
args = --links --hard-links --times --verbose --delete --recursive
twostage = 0 # Two-stage sync (0=disabled, 1=enabled)
timeout = 12h # Task timeout (0=no timeout)
priority = 10 # 1=highest, 10=lowest
autoretry = 1m # Auto-retry on failure| Option | Description | Default |
|---|---|---|
upstream[] |
Upstream rsync server (array syntax for multiple) | Required |
command |
Command to execute | rsync |
time |
Cron-like schedule expression | Required |
rsyncdir |
Remote rsync module path | Required |
localdir |
Local destination path | Required |
args |
Command arguments | --links --hard-links... |
exclude |
Exclusion patterns | None |
twostage |
Enable two-stage sync | 0 |
firststage |
First stage path (if twostage=1) | None |
timeout |
Task timeout (seconds or XhYm) | 0 |
priority |
Task priority (1-10) | 10 |
autoretry |
Retry interval on failure | 0 |
type |
Task type (simple, or omit for rsync) | rsync |
* */2 * * *- Every 2 hours0 2,14 * * *- At 2:00 and 14:00*/30 * * * *- Every 30 minutes0-20/2 * * * *- Every 2 minutes from 0-20
1200- 1200 seconds12h- 12 hours12h4m- 12 hours and 4 minutes
# Start as daemon (default)
mirrord
# Start in foreground (debug mode)
mirrord -D
# Start with custom config
mirrord -c /path/to/config/dir
# Start with custom log file
mirrord -l /var/log/mirrord/mirrord.log
# Start with specific user/group (after starting as root)
mirrord -u mirror -g mirror# List current task queue
mirrord -t
# Stop daemon
mirrord -s stop
# Reload configuration
mirrord -s reloadSIGTERM,SIGQUIT,SIGINT- Graceful shutdownSIGHUP- Reload configurationSIGCHLD- Child process (task) finished
-
MirrorDaemon (
daemon.py)- Main orchestrator
- Initializes EventManager, PluginManager, Scheduler
-
Scheduler (
scheduler.py)- Priority-based task scheduling
- System load-aware execution decisions
- Queue management with
TaskInfoobjects
-
Task Types (
task.py)Task- Standard rsync-based mirror taskSimpleTask- Generic command executionSystemTask- Internal maintenance tasks
-
Event System
MirrorEvent- Base event classTaskStartEvent,TaskStopEvent,TaskEnqueueEvent- Plugin hooks via EventManager
-
Plugin System (
pluginmanager.py,pluginbase.py)- Entry point based:
mirror.plugin.mirrorplugin - Plugins as setuptools eggs in
mirror/plugins/ - Enable/disable lifecycle hooks
- Entry point based:
- Tasks are stored in a priority queue sorted by (time, priority)
- Scheduler sleeps until next task's scheduled time
- Before running, scheduler checks:
- System load <
loadlimit - HTTP connections <
httpconn - Running tasks <
maxtasks(unless priority ≤ 2)
- System load <
- If conditions not met, task is delayed by 30 minutes
- Two-stage tasks run first stage, then immediately second stage
mirrord (main process)
├── Scheduler (main loop)
│ └── fork() → rsync tasks
├── EventManager
│ └── PluginThread (for async plugin execution)
└── Signal handlers
# Run all tests
python setup.py test
# Run with verbose output
python setup.py test --args="-v"test/test_task.py- Tests Task class scheduling logictest/test_taskqueue.py- Tests priority queue operations
GitHub Actions workflow (.github/workflows/mirror-test.yml):
- Tests on Python 2.7, 3.5, 3.6, 3.7, 3.8
- Runs on Ubuntu
- Command:
python setup.py test
- Line Length: 127 characters (per CI config)
- Indentation: 4 spaces
- Encoding: UTF-8
- Documentation: Docstrings for public methods
- Copyright: All files include GPLv3 header
- Classes:
CamelCase(e.g.,MirrorDaemon,TaskInfo) - Functions/Variables:
snake_case(e.g.,get_schedule_time) - Constants:
UPPER_CASE(e.g.,PRIORITY_MIN,REGULAR_TASK) - Private:
_prefix(e.g.,_component_registry)
mirror/plugins/MyPlugin/
├── setup.py
└── mirror/
└── plugins/
└── myplugin/
├── __init__.py
└── myplugin.py
# mirror/plugins/myplugin/myplugin.py
from mirror.pluginbase import PluginBase
class MyPlugin(PluginBase):
enabled = True # Set to False to disable
def enable(self):
# Register event handlers
pass
def disable(self):
# Cleanup
passfrom setuptools import setup, find_packages
setup(
name="MyPlugin",
version="0.1.0",
packages=find_packages(),
namespace_packages=["mirror", "mirror.plugins"],
entry_points="""
[mirror.plugin.mirrorplugin]
MyPlugin = mirror.plugins.myplugin:MyPlugin
"""
)-
User Privileges
- Daemon can start as root and switch to unprivileged user via
-u/-g - Recommended: Create dedicated
mirroruser
- Daemon can start as root and switch to unprivileged user via
-
File Permissions
- Log directories need write access for mirror user
- Config directory should be readable only by mirror user
-
Task Isolation
- Each task runs in separate forked process
- Tasks cannot interfere with scheduler directly
-
Path Validation
- Local directories are created if missing
- Command paths are validated against PATH
mirrord -D -L debugmirrord --profile
# Generates mirrord.profile in config directorymirrord -t# Task-specific logs
tail -f /var/log/rsync/archlinux.log.$(date +%Y-%m-%d)
# Daemon logs
tail -f /var/log/mirrord/mirrord.log-
"Config dir does not exist"
- Create
/etc/mirror/or~/.config/mirror/ - Copy
config/mirror.iniexample
- Create
-
"There was an error writing logs"
- Ensure
/var/log/mirrord/and/var/log/rsync/exist - Check ownership:
chown mirror:mirror /var/log/mirrord /var/log/rsync
- Ensure
-
Tasks not running
- Check priority vs. current system load
- Verify time format is correct
- Check
enabledstatus in config
- Shang Yuanchun ('ideal') idealities@gmail.com
- Bob Gao gaobo@bjtu.edu.cn
- Chestnut chestnut@bjtu.edu.cn