nova/plugins/xenserver/xenapi/etc/xapi.d/plugins/_bittorrent_seeder.py

130 lines
3.9 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright (c) 2012 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# NOTE: XenServer still only supports Python 2.4 in it's dom0 userspace
# which means the Nova xenapi plugins must use only Python 2.4 features
# TODO(sfinucan): Resolve all 'noqa' items once the above is no longer true
"""Seed a bittorent image. This file should not be executed directly, rather it
should be kicked off by the `bittorent` dom0 plugin."""
import os
import sys
import time
import libtorrent
import pluginlib_nova
pluginlib_nova.configure_logging('_bittorrent_seeder')
logging = pluginlib_nova.logging
def _daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
"""Daemonize the current process.
Do the UNIX double-fork magic, see Stevens' "Advanced Programming
in the UNIX Environment" for details (ISBN 0201563177).
Source: http://www.jejik.com/articles/2007/02/
a_simple_unix_linux_daemon_in_python/
"""
# 1st fork
try:
pid = os.fork()
if pid > 0:
# first parent returns
return False
except OSError, e: # noqa
logging.error("fork #1 failed: %d (%s)" % (
e.errno, e.strerror))
return
# decouple from parent environment
os.chdir("/")
os.setsid()
os.umask(0)
# 2nd fork
try:
pid = os.fork()
if pid > 0:
# second parent exits
sys.exit(0)
except OSError, e: # noqa
logging.error("fork #2 failed: %d (%s)" % (
e.errno, e.strerror))
return
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
si = open(stdin, 'r')
so = open(stdout, 'a+')
se = open(stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
return True
def main(torrent_path, seed_cache_path, torrent_seed_duration,
torrent_listen_port_start, torrent_listen_port_end):
seed_time = time.time() + torrent_seed_duration
logging.debug("Seeding '%s' for %d secs" % (
torrent_path, torrent_seed_duration))
child = _daemonize()
if not child:
return
# At this point we're the daemonized child...
session = libtorrent.session()
session.listen_on(torrent_listen_port_start, torrent_listen_port_end)
torrent_file = open(torrent_path, 'rb')
try:
torrent_data = torrent_file.read()
finally:
torrent_file.close()
decoded_data = libtorrent.bdecode(torrent_data)
info = libtorrent.torrent_info(decoded_data)
torrent = session.add_torrent(
info, seed_cache_path,
storage_mode=libtorrent.storage_mode_t.storage_mode_sparse)
try:
while time.time() < seed_time:
time.sleep(5)
finally:
session.remove_torrent(torrent)
logging.debug("Seeding of '%s' finished" % torrent_path)
if __name__ == "__main__":
(torrent_path, seed_cache_path, torrent_seed_duration,
torrent_listen_port_start, torrent_listen_port_end) = sys.argv[1:]
torrent_seed_duration = int(torrent_seed_duration)
torrent_listen_port_start = int(torrent_listen_port_start)
torrent_listen_port_end = int(torrent_listen_port_end)
main(torrent_path, seed_cache_path, torrent_seed_duration,
torrent_listen_port_start, torrent_listen_port_end)