diff --git a/common.py b/common.py index 8b1e179..c93380f 100644 --- a/common.py +++ b/common.py @@ -11,7 +11,13 @@ import socket import uuid -CONFIG_FILE = os.path.expanduser("~/pc-autobackup.cfg") +CONFIG_FILE = os.path.expanduser("~/pc_autobackup.cfg") +LOG_DATE_FMT = '[%m/%d/%Y %I:%M %p]' +LOG_FMT = '%(asctime)s[%(name)s] %(levelname)s:%(message)s' + +LOG_DEFAULTS = {'level': logging.WARN, + 'format': LOG_FMT, + 'datefmt': LOG_DATE_FMT} def LoadOrCreateConfig(): diff --git a/mediaserver.py b/mediaserver.py index a3b8dc3..1cd823b 100644 --- a/mediaserver.py +++ b/mediaserver.py @@ -85,6 +85,7 @@ backup_objects = {} def __init__(self): + self.logger = logging.getLogger('MediaServer.Backup') self.config = common.LoadOrCreateConfig() def _GenerateObjectID(self, obj_date, length=10): @@ -96,8 +97,8 @@ def CreateObject(self, obj_name, obj_date, obj_type, obj_size): (parent_id, obj_id) = self._GenerateObjectID(obj_date) - logging.debug('Creating Backup Object for %s (type:%s size:%s)', obj_name, - obj_type, obj_size) + self.logger.debug('Creating Backup Object for %s (type:%s size:%s)', + obj_name, obj_type, obj_size) self.backup_objects[obj_id] = {'obj_name': obj_name, 'obj_date': obj_date, 'obj_type': obj_type, @@ -121,59 +122,67 @@ obj_details['obj_date']) if not os.path.isdir(obj_dir): - logging.info('Creating output dir %s', obj_dir) + self.logger.info('Creating output dir %s', obj_dir) os.makedirs(obj_dir) obj_file = os.path.join(obj_dir, obj_details['obj_name']) - logging.info('Saving %s', obj_file) + self.logger.info('Saving %s to %s', obj_details['obj_name'], obj_dir) with open(obj_file, 'wb') as f: f.write(data) + self.logger.info('%s saved successfully', obj_details['obj_name']) del(self.backup_objects[obj_id]) class MediaServer(Resource): + clients = {} isLeaf = True def __init__(self): + self.logger = logging.getLogger('MediaServer') self.config = common.LoadOrCreateConfig() def render_GET(self, request): - logging.debug('Request Headers: %s', request.getAllHeaders()) + self.logger.debug('Request Headers: %s', request.getAllHeaders()) if request.path == '/DMS/SamsungDmsDesc.xml': - logging.info('New connection from %s', request.getHeader('user-agent')) + self.logger.info('New connection from %s (%s)', + request.getClientIP(), + request.getHeader('user-agent')) + self.clients[request.getClientIP()] = request.getHeader('user-agent') response = self.GetDMSDescriptionResponse() else: - logging.error('Unhandled GET request: %s', request.path) + self.logger.error('Unhandled GET request: %s', request.path) return NoResource() - logging.debug('Response: %s', response) + self.logger.debug('Response: %s', response) return response def render_POST(self, request): - logging.debug('Request args: %s', request.args) - logging.debug('Request headers: %s', request.getAllHeaders()) + self.logger.debug('Request args: %s', request.args) + self.logger.debug('Request headers: %s', request.getAllHeaders()) if request.path == '/cd/content': response = self.ReceiveUpload(request) elif request.path == '/upnp/control/ContentDirectory1': response = self.GetContentDirectoryResponse(request) else: - logging.error('Unhandled POST request: %s', request.path) + self.logger.error('Unhandled POST request: %s', request.path) return NoResource() - logging.debug('Response: %s', response) + self.logger.debug('Response: %s', response) return response def GetContentDirectoryResponse(self, request): - logging.debug('Request content: %s', request.content.read()) + self.logger.debug('Request content: %s', request.content.read()) request.content.seek(0) soapaction = request.getHeader('soapaction') if soapaction == X_BACKUP_START: + self.logger.info('Starting backup for %s (%s)', request.getClientIP(), + self.clients[request.getClientIP()]) response = X_BACKUP_RESPONSE % 'START' elif soapaction == CREATE_OBJ: soap_xml = request.content.read() @@ -196,9 +205,11 @@ 'obj_size': obj_size, 'parent_id': obj_details['parent_id']} elif soapaction == X_BACKUP_DONE: + self.logger.info('Backup complete for %s (%s)', request.getClientIP(), + self.clients[request.getClientIP()]) response = X_BACKUP_RESPONSE % 'DONE' else: - logging.error('Unhandled soapaction: %s', soapaction) + self.logger.error('Unhandled soapaction: %s', soapaction) return NoResource() return response @@ -231,7 +242,10 @@ def main(): - logging.basicConfig(filename='mediaserver.log', level=logging.DEBUG) + logging_options = common.LOG_DEFAULTS + logging_options['filename'] = 'mediaserver.log' + logging_options['level'] = logging.DEBUG + logging.basicConfig(**logging_options) StartMediaServer() diff --git a/pc-autobackup.py b/pc-autobackup.py deleted file mode 100644 index 4d9afb7..0000000 --- a/pc-autobackup.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2013 Jeff Rebeiro (jeff@rebeiro.net) All rights reserved -# Main runnable for PC Autobackup - -__author__ = 'jeff@rebeiro.net (Jeff Rebeiro)' - -import logging -import optparse - -from twisted.internet import reactor -from twisted.web.server import Site - -import common -import ssdp -import mediaserver - - -def main(): - parser = optparse.OptionParser() - parser.add_option('-b', '--bind', dest='bind', - help='bind the server to a specific IP', - metavar='IP') - parser.add_option('-d', '--debug', dest='debug', action='store_true', - default=False, help='debug output') - parser.add_option('--log_file', dest='log_file', - help='output log to file', metavar='FILE') - parser.add_option('-o', '--output_dir', dest='output_dir', - help='output directory for files', metavar='DIR') - parser.add_option('-v', '--verbose', dest='verbose', action='store_true', - default=False, help='verbose output') - (options, args) = parser.parse_args() - - logging_options = {'level': logging.WARN} - - if options.verbose: - logging_options['level'] = logging.INFO - if options.debug: - logging_options['level'] = logging.DEBUG - if options.log_file: - logging_options['filename'] = options.log_file - - logging.basicConfig(**logging_options) - - logging.info('pc-autobackup started') - - config = common.LoadOrCreateConfig() - if options.bind: - config.set('AUTOBACKUP', 'default_interface', options.bind) - if options.output_dir: - config.set('AUTOBACKUP', 'backup_dir', options.output_dir) - - resource = mediaserver.MediaServer() - factory = Site(resource) - reactor.listenMulticast(1900, ssdp.SSDPServer()) - logging.info('SSDPServer started') - reactor.listenTCP(52235, factory) - logging.info('MediaServer started') - reactor.run() - - -if __name__ == '__main__': - main() diff --git a/pc_autobackup.py b/pc_autobackup.py new file mode 100644 index 0000000..1e22d58 --- /dev/null +++ b/pc_autobackup.py @@ -0,0 +1,91 @@ +#!/usr/bin/python +# +# Copyright 2013 Jeff Rebeiro (jeff@rebeiro.net) All rights reserved +# Main runnable for PC Autobackup + +__author__ = 'jeff@rebeiro.net (Jeff Rebeiro)' + +import logging +import optparse +import platform +import socket + +from twisted.internet import reactor +from twisted.web.server import Site + +import common +import ssdp +import mediaserver + + +def GetSystemInfo(): + logger = logging.getLogger('PCAutoBackup') + logger.debug('Python Version: %s', platform.python_version()) + logger.debug('System Information (platform): %s', platform.platform()) + logger.debug('System Information (uname): %s', ' '.join(platform.uname())) + logger.debug('System Information (node): %s', platform.node()) + logger.debug('System Information (hostname): %s', socket.gethostname()) + + config = common.LoadOrCreateConfig() + for section in config.sections(): + for option in config.options(section): + logger.debug('Config (%s): %s = %s', section, option, + config.get(section, option)) + + +def main(): + parser = optparse.OptionParser() + parser.add_option('-b', '--bind', dest='bind', + help='bind the server to a specific IP', + metavar='IP') + parser.add_option('-d', '--debug', dest='debug', action='store_true', + default=False, help='debug output') + parser.add_option('--log_file', dest='log_file', default='backup.log', + help='output log to file', metavar='FILE') + parser.add_option('-o', '--output_dir', dest='output_dir', + help='output directory for files', metavar='DIR') + parser.add_option('-v', '--verbose', dest='verbose', action='store_true', + default=False, help='verbose output') + (options, args) = parser.parse_args() + + logging_options = common.LOG_DEFAULTS + + if options.verbose: + logging_options['level'] = logging.INFO + if options.debug: + logging_options['level'] = logging.DEBUG + + logging_options['filename'] = options.log_file + + logging.basicConfig(**logging_options) + + console = logging.StreamHandler() + console.setLevel(logging_options['level']) + formatter = logging.Formatter('%(asctime)s %(message)s', common.LOG_DATE_FMT) + console.setFormatter(formatter) + logging.getLogger('').addHandler(console) + + config = common.LoadOrCreateConfig() + if options.bind: + config.set('AUTOBACKUP', 'default_interface', options.bind) + if options.output_dir: + config.set('AUTOBACKUP', 'backup_dir', options.output_dir) + + logger = logging.getLogger('PCAutoBackup') + logger.info('PCAutoBackup started on %s', config.get('AUTOBACKUP', + 'default_interface')) + + if options.debug: + GetSystemInfo() + + resource = mediaserver.MediaServer() + factory = Site(resource) + reactor.listenMulticast(1900, ssdp.SSDPServer()) + logger.info('SSDPServer started') + reactor.listenTCP(52235, factory) + logger.info('MediaServer started') + reactor.run() + + +if __name__ == '__main__': + main() diff --git a/ssdp.py b/ssdp.py index 0327979..4a0009d 100644 --- a/ssdp.py +++ b/ssdp.py @@ -29,6 +29,7 @@ class SSDPServer(DatagramProtocol): def __init__(self): + self.logger = logging.getLogger('SSDPServer') self.config = common.LoadOrCreateConfig() def startProtocol(self): @@ -40,8 +41,10 @@ if m: # TODO(jrebeiro): Verify that MediaServer is the only discovery request # PCAutoBackup responds to. - logging.debug('Received M-SEARCH for %s from %r', m.group(3), address) + self.logger.debug('Received M-SEARCH for %s from %s', m.group(3), + address[0]) if m.group(3) == 'MediaServer': + self.logger.info('Received discovery request from %s', address[0]) self.SendSSDPResponse(address) def SendSSDPResponse(self, address): @@ -54,7 +57,7 @@ 'default_interface'), self.config.get('AUTOBACKUP', 'uuid')) self.transport.write(response, address) - logging.debug('Response: %s', response) + self.logger.debug('Response: %s', response) def StartSSDPServer(): @@ -64,7 +67,10 @@ def main(): - logging.basicConfig(filename='ssdp.log', level=logging.DEBUG) + logging_options = common.LOG_DEFAULTS + logging_options['filename'] = 'ssdpserver.log' + logging_options['level'] = logging.DEBUG + logging.basicConfig(**logging_options) StartSSDPServer()