OpenSecurity/bin/opensecurity_client_restful_server.py
author Oliver Maurhart <oliver.maurhart@ait.ac.at>
Mon, 19 May 2014 13:13:30 +0200
changeset 154 651bf8fd169e
parent 151 d0f24f265331
child 155 bcb24068c63e
child 156 caf3dd2bf081
permissions -rwxr-xr-x
threaded log file capture and store & forward
om@13
     1
#!/bin/env python
om@13
     2
# -*- coding: utf-8 -*-
om@13
     3
om@13
     4
# ------------------------------------------------------------
om@13
     5
# opensecurity_client_restful_server
om@13
     6
# 
om@13
     7
# the OpenSecurity client RESTful server
om@13
     8
#
om@13
     9
# Autor: Oliver Maurhart, <oliver.maurhart@ait.ac.at>
om@13
    10
#
om@13
    11
# Copyright (C) 2013 AIT Austrian Institute of Technology
om@13
    12
# AIT Austrian Institute of Technology GmbH
om@13
    13
# Donau-City-Strasse 1 | 1220 Vienna | Austria
om@13
    14
# http://www.ait.ac.at
om@13
    15
#
om@13
    16
# This program is free software; you can redistribute it and/or
om@13
    17
# modify it under the terms of the GNU General Public License
om@13
    18
# as published by the Free Software Foundation version 2.
om@13
    19
# 
om@13
    20
# This program is distributed in the hope that it will be useful,
om@13
    21
# but WITHOUT ANY WARRANTY; without even the implied warranty of
om@13
    22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
om@13
    23
# GNU General Public License for more details.
om@13
    24
# 
om@13
    25
# You should have received a copy of the GNU General Public License
om@13
    26
# along with this program; if not, write to the Free Software
om@13
    27
# Foundation, Inc., 51 Franklin Street, Fifth Floor, 
om@13
    28
# Boston, MA  02110-1301, USA.
om@13
    29
# ------------------------------------------------------------
om@13
    30
om@13
    31
om@13
    32
# ------------------------------------------------------------
om@13
    33
# imports
om@13
    34
oliver@142
    35
import getpass
oliver@154
    36
import glob
oliver@134
    37
import json
om@13
    38
import os
om@13
    39
import os.path
oliver@154
    40
import pickle
oliver@142
    41
import platform
oliver@136
    42
import socket
om@13
    43
import subprocess
om@13
    44
import sys
oliver@154
    45
import threading
oliver@154
    46
import time
om@29
    47
import urllib
om@29
    48
import urllib2
om@13
    49
import web
BarthaM@151
    50
BarthaM@151
    51
from opensecurity_util import logger, setupLogger, OpenSecurityException
BarthaM@151
    52
if sys.platform == 'win32' or sys.platform == 'cygwin':
BarthaM@151
    53
    from cygwin import Cygwin
om@13
    54
om@13
    55
# local
oliver@144
    56
import __init__ as opensecurity
oliver@154
    57
from environment import Environment
om@13
    58
om@13
    59
om@13
    60
# ------------------------------------------------------------
om@13
    61
# const
om@13
    62
om@13
    63
om@13
    64
"""All the URLs we know mapping to class handler"""
om@13
    65
opensecurity_urls = (
om@13
    66
    '/credentials',             'os_credentials',
oliver@134
    67
    '/keyfile',                 'os_keyfile',
oliver@142
    68
    '/log',                     'os_log',
om@29
    69
    '/notification',            'os_notification',
om@13
    70
    '/password',                'os_password',
BarthaM@151
    71
    '/netmount',                'os_netmount',
BarthaM@151
    72
    '/netumount',               'os_netumount',
om@13
    73
    '/',                        'os_root'
om@13
    74
)
om@13
    75
om@13
    76
om@13
    77
# ------------------------------------------------------------
oliver@136
    78
# vars
oliver@136
    79
oliver@136
    80
oliver@154
    81
"""lock for read/write log file"""
oliver@154
    82
log_file_lock = threading.Lock()
oliver@154
    83
oliver@154
    84
"""timer for the log file bouncer"""
oliver@154
    85
log_file_bouncer = None
oliver@154
    86
oliver@154
    87
oliver@136
    88
"""The REST server object"""
oliver@136
    89
server = None
oliver@136
    90
oliver@136
    91
oliver@136
    92
# ------------------------------------------------------------
om@13
    93
# code
om@13
    94
om@13
    95
oliver@134
    96
class os_credentials:
om@31
    97
om@13
    98
    """OpenSecurity '/credentials' handler.
om@13
    99
    
om@13
   100
    This is called on GET /credentials?text=TEXT.
om@13
   101
    Ideally this should pop up a user dialog to insert his
om@13
   102
    credentials based the given TEXT.
om@13
   103
    """
om@13
   104
    
om@13
   105
    def GET(self):
om@13
   106
        
om@13
   107
        # pick the arguments
om@13
   108
        args = web.input()
om@13
   109
        
om@29
   110
        # we _need_ a text
om@13
   111
        if not "text" in args:
om@29
   112
            raise web.badrequest('no text given')
om@13
   113
        
oliver@134
   114
        # remember remote ip
oliver@134
   115
        remote_ip = web.ctx.environ['REMOTE_ADDR']
oliver@134
   116
oliver@134
   117
        # create the process which queries the user
oliver@134
   118
        dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
om@29
   119
        process_command = [sys.executable, dlg_image, 'credentials', args.text]
oliver@134
   120
        process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
om@13
   121
        
oliver@134
   122
        # run process result handling in seprate thread (not to block main one)
oliver@136
   123
        bouncer = ProcessResultBouncer(process, remote_ip, '/credentials')
oliver@134
   124
        bouncer.start()
oliver@134
   125
         
oliver@134
   126
        return 'user queried for credentials'
oliver@134
   127
oliver@134
   128
oliver@134
   129
class os_keyfile:
oliver@134
   130
oliver@134
   131
    """OpenSecurity '/keyfile' handler.
oliver@134
   132
    
oliver@134
   133
    This is called on GET /keyfile?text=TEXT.
oliver@134
   134
    Ideally this should pop up a user dialog to insert his
oliver@134
   135
    password along with a keyfile.
oliver@134
   136
    """
oliver@134
   137
    
oliver@134
   138
    def GET(self):
oliver@134
   139
        
oliver@134
   140
        # pick the arguments
oliver@134
   141
        args = web.input()
oliver@134
   142
        
oliver@134
   143
        # we _need_ a text
oliver@134
   144
        if not "text" in args:
oliver@134
   145
            raise web.badrequest('no text given')
oliver@134
   146
            
oliver@134
   147
        # remember remote ip
oliver@134
   148
        remote_ip = web.ctx.environ['REMOTE_ADDR']
oliver@134
   149
        
oliver@134
   150
        # create the process which queries the user
oliver@134
   151
        dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
oliver@134
   152
        process_command = [sys.executable, dlg_image, 'keyfile', args.text]
oliver@134
   153
        process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
oliver@134
   154
        
oliver@134
   155
        # run process result handling in seprate thread (not to block main one)
oliver@136
   156
        bouncer = ProcessResultBouncer(process, remote_ip, '/keyfile')
oliver@134
   157
        bouncer.start()
oliver@134
   158
         
oliver@134
   159
        return 'user queried for password and keyfile'
om@13
   160
om@13
   161
oliver@142
   162
class os_log:
oliver@142
   163
oliver@142
   164
    """OpenSecurity '/log' handler.
oliver@142
   165
    
oliver@142
   166
    This is called on GET or POST on the log function /log
oliver@142
   167
    """
oliver@142
   168
    
oliver@142
   169
    def GET(self):
oliver@142
   170
        
oliver@142
   171
        # pick the arguments
oliver@142
   172
        self.POST()
oliver@142
   173
oliver@142
   174
oliver@142
   175
    def POST(self):
oliver@142
   176
        
oliver@142
   177
        # pick the arguments
oliver@142
   178
        args = web.input()
oliver@142
   179
        args['user'] = getpass.getuser()
oliver@142
   180
        args['system'] = platform.node() + " " + platform.system() + " " + platform.release()
oliver@142
   181
oliver@154
   182
        # add these to new data to log
oliver@154
   183
        global log_file_lock
oliver@154
   184
        log_file_name = os.path.join(Environment('OpenSecurity').log_path, 'vm_new.log')
oliver@154
   185
        log_file_lock.acquire()
oliver@154
   186
        pickle.dump(args,  open(log_file_name, 'ab'))
oliver@154
   187
        log_file_lock.release()
oliver@142
   188
oliver@142
   189
        return "Ok"
oliver@142
   190
oliver@142
   191
om@29
   192
class os_notification:
oliver@134
   193
om@29
   194
    """OpenSecurity '/notification' handler.
om@29
   195
    
om@29
   196
    This is called on GET /notification?msgtype=TYPE&text=TEXT.
om@29
   197
    This will pop up an OpenSecurity notifcation window
om@29
   198
    """
om@29
   199
    
om@29
   200
    def GET(self):
om@29
   201
        
om@29
   202
        # pick the arguments
om@29
   203
        args = web.input()
om@29
   204
        
om@29
   205
        # we _need_ a type
om@29
   206
        if not "msgtype" in args:
om@29
   207
            raise web.badrequest('no msgtype given')
om@29
   208
            
oliver@134
   209
        if not args.msgtype in ['information', 'warning', 'critical']:
om@29
   210
            raise web.badrequest('Unknown value for msgtype')
om@29
   211
            
om@29
   212
        # we _need_ a text
om@29
   213
        if not "text" in args:
om@29
   214
            raise web.badrequest('no text given')
om@29
   215
            
om@29
   216
        # invoke the user dialog as a subprocess
om@29
   217
        dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.py')
om@29
   218
        process_command = [sys.executable, dlg_image, 'notification-' + args.msgtype, args.text]
om@29
   219
        process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)
oliver@134
   220
om@29
   221
        return "Ok"
om@29
   222
om@29
   223
om@13
   224
class os_password:
oliver@134
   225
om@13
   226
    """OpenSecurity '/password' handler.
om@13
   227
    
om@13
   228
    This is called on GET /password?text=TEXT.
om@13
   229
    Ideally this should pop up a user dialog to insert his
om@13
   230
    password based device name.
om@13
   231
    """
om@13
   232
    
om@13
   233
    def GET(self):
om@13
   234
        
om@13
   235
        # pick the arguments
om@13
   236
        args = web.input()
om@13
   237
        
om@29
   238
        # we _need_ a text
om@13
   239
        if not "text" in args:
om@29
   240
            raise web.badrequest('no text given')
om@13
   241
            
om@29
   242
        # remember remote ip
om@29
   243
        remote_ip = web.ctx.environ['REMOTE_ADDR']
om@29
   244
        
oliver@134
   245
        # create the process which queries the user
oliver@134
   246
        dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
oliver@134
   247
        process_command = [sys.executable, dlg_image, 'password', args.text]
oliver@134
   248
        process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)        
om@29
   249
        
oliver@134
   250
        # run process result handling in seprate thread (not to block main one)
oliver@136
   251
        bouncer = ProcessResultBouncer(process, remote_ip, '/password')
oliver@134
   252
        bouncer.start()
oliver@134
   253
        
oliver@134
   254
        return 'user queried for password'
om@13
   255
BarthaM@151
   256
# handles netumount request                    
BarthaM@151
   257
class MountNetworkDriveHandler(threading.Thread): 
BarthaM@151
   258
    drive = None
BarthaM@151
   259
    resource = None
BarthaM@151
   260
    
BarthaM@151
   261
    def __init__(self, drv, net_path):
BarthaM@151
   262
        threading.Thread.__init__(self)
BarthaM@151
   263
        self.drive = drv
BarthaM@151
   264
        self.networkPath = net_path
BarthaM@151
   265
    
BarthaM@151
   266
    def run(self):
BarthaM@151
   267
        #Check for drive availability
BarthaM@151
   268
        if os.path.exists(self.drive):
BarthaM@151
   269
            logger.error("Drive letter is already in use: " + self.drive)
BarthaM@151
   270
            return 1
BarthaM@151
   271
        
BarthaM@151
   272
        #Check for network resource availability
BarthaM@151
   273
        retry = 5
BarthaM@151
   274
        while not os.path.exists(self.networkPath):
BarthaM@151
   275
            time.sleep(1)
BarthaM@151
   276
            if retry == 0:
BarthaM@151
   277
                return 1
BarthaM@151
   278
            logger.info("Path not accessible: " + self.networkPath + " retrying")
BarthaM@151
   279
            retry-=1
BarthaM@151
   280
    
BarthaM@151
   281
        command = 'USE ' + self.drive + ' ' + self.networkPath + ' /PERSISTENT:NO'
BarthaM@151
   282
    
BarthaM@151
   283
        result = Cygwin.checkResult(Cygwin.execute('C:\\Windows\\system32\\NET', command))
BarthaM@151
   284
        if string.find(result[1], 'successfully',) == -1:
BarthaM@151
   285
            logger.error("Failed: NET " + command)
BarthaM@151
   286
            return 1
BarthaM@151
   287
        return 0
BarthaM@151
   288
BarthaM@151
   289
class os_netmount:
BarthaM@151
   290
    
BarthaM@151
   291
    """OpenSecurity '/netmount' handler"""
BarthaM@151
   292
    
BarthaM@151
   293
    def GET(self):
BarthaM@151
   294
        # pick the arguments
BarthaM@151
   295
        args = web.input()
BarthaM@151
   296
        
BarthaM@151
   297
        # we _need_ a net_resource
BarthaM@151
   298
        if not "net_resource" in args:
BarthaM@151
   299
            raise web.badrequest('no net_resource given')
BarthaM@151
   300
        
BarthaM@151
   301
        # we _need_ a drive_letter
BarthaM@151
   302
        if not "drive_letter" in args:
BarthaM@151
   303
            raise web.badrequest('no drive_letter given')
BarthaM@151
   304
BarthaM@151
   305
        driveHandler = MountNetworkDriveHandler(args['drive_letter'], args['net_resource'])
BarthaM@151
   306
        driveHandler.start()
BarthaM@151
   307
        return 'Ok'
BarthaM@151
   308
BarthaM@151
   309
         
BarthaM@151
   310
        
BarthaM@151
   311
# handles netumount request                    
BarthaM@151
   312
class UmountNetworkDriveHandler(threading.Thread): 
BarthaM@151
   313
    drive = None
BarthaM@151
   314
    running = True
BarthaM@151
   315
    
BarthaM@151
   316
    def __init__(self, drv):
BarthaM@151
   317
        threading.Thread.__init__(self)
BarthaM@151
   318
        self.drive = drv
BarthaM@151
   319
BarthaM@151
   320
    def run(self):
BarthaM@151
   321
        while self.running:
BarthaM@151
   322
            result = Cygwin.checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
BarthaM@151
   323
            mappedDrives = list()
BarthaM@151
   324
            for line in result[1].splitlines():
BarthaM@151
   325
                if 'USB' in line or 'Download' in line:
BarthaM@151
   326
                    parts = line.split()
BarthaM@151
   327
                    mappedDrives.append(parts[1])
BarthaM@151
   328
            
BarthaM@151
   329
            logger.info(mappedDrives)
BarthaM@151
   330
            logger.info(self.drive)
BarthaM@151
   331
            if self.drive not in mappedDrives:
BarthaM@151
   332
                self.running = False
BarthaM@151
   333
            else:
BarthaM@151
   334
                command = 'USE ' + self.drive + ' /DELETE /YES' 
BarthaM@151
   335
                result = Cygwin.checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', command)) 
BarthaM@151
   336
                if string.find(str(result[1]), 'successfully',) == -1:
BarthaM@151
   337
                    logger.error(result[2])
BarthaM@151
   338
                    continue
BarthaM@151
   339
                        
BarthaM@151
   340
BarthaM@151
   341
class os_netumount:
BarthaM@151
   342
    
BarthaM@151
   343
    """OpenSecurity '/netumount' handler"""
BarthaM@151
   344
    
BarthaM@151
   345
    def GET(self):
BarthaM@151
   346
        # pick the arguments
BarthaM@151
   347
        args = web.input()
BarthaM@151
   348
        
BarthaM@151
   349
        # we _need_ a drive_letter
BarthaM@151
   350
        if not "drive_letter" in args:
BarthaM@151
   351
            raise web.badrequest('no drive_letter given')
BarthaM@151
   352
        
BarthaM@151
   353
        driveHandler = UmountNetworkDriveHandler(args['drive_letter'])
BarthaM@151
   354
        driveHandler.start()
BarthaM@151
   355
        return 'Ok'
BarthaM@151
   356
    
om@13
   357
om@13
   358
class os_root:
oliver@134
   359
om@13
   360
    """OpenSecurity '/' handler"""
om@13
   361
    
om@13
   362
    def GET(self):
om@13
   363
    
oliver@144
   364
        res = "OpenSecurity-Client RESTFul Server { \"version\": \"%s\" }" % opensecurity.__version__
om@13
   365
        
om@13
   366
        # add some sample links
om@13
   367
        res = res + """
om@13
   368
        
om@13
   369
USAGE EXAMPLES:
om@13
   370
        
om@13
   371
Request a password: 
om@13
   372
    (copy paste this into your browser's address field after the host:port)
om@13
   373
    
om@13
   374
    /password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0)
om@13
   375
    
om@13
   376
    (eg.: http://127.0.0.1:8090/password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0))
om@13
   377
    NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
om@13
   378
    
om@13
   379
    
om@13
   380
Request a combination of user and password:
om@13
   381
    (copy paste this into your browser's address field after the host:port)
om@13
   382
    
om@13
   383
    /credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.
om@13
   384
    
om@13
   385
    (eg.: http://127.0.0.1:8090/credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.)
om@13
   386
    NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
om@13
   387
    
om@13
   388
oliver@134
   389
Request a combination of password and keyfile:
oliver@134
   390
    (copy paste this into your browser's address field after the host:port)
oliver@134
   391
    
oliver@134
   392
    /keyfile?text=Your%20private%20RSA%20Keyfile%3A
oliver@134
   393
    
oliver@134
   394
    (eg.: http://127.0.0.1:8090//keyfile?text=Your%20private%20RSA%20Keyfile%3A)
oliver@134
   395
    NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
oliver@134
   396
    
oliver@134
   397
om@13
   398
Start a Browser:
om@13
   399
    (copy paste this into your browser's address field after the host:port)
om@13
   400
om@13
   401
    /application?vm=Debian+7&app=Browser
om@13
   402
om@13
   403
    (e.g. http://127.0.0.1:8090/application?vm=Debian+7&app=Browser)
om@13
   404
        """
om@13
   405
    
om@13
   406
        return res
om@13
   407
om@13
   408
oliver@136
   409
class ProcessResultBouncer(threading.Thread):
oliver@134
   410
oliver@136
   411
    """A class to post the result of a given process - assuming it to be in JSON - to a REST Api."""
oliver@134
   412
oliver@134
   413
    def __init__(self, process, remote_ip, resource): 
oliver@134
   414
oliver@134
   415
        """ctor"""
oliver@134
   416
oliver@134
   417
        threading.Thread.__init__(self)
oliver@134
   418
        self._process = process
oliver@134
   419
        self._remote_ip = remote_ip
oliver@134
   420
        self._resource = resource
oliver@134
   421
 
oliver@134
   422
    
oliver@134
   423
    def stop(self):
oliver@134
   424
oliver@134
   425
        """stop thread"""
oliver@134
   426
        self.running = False
oliver@134
   427
        
oliver@134
   428
    
oliver@134
   429
    def run(self):
oliver@134
   430
oliver@134
   431
        """run the thread"""
oliver@134
   432
oliver@134
   433
        # invoke the user dialog as a subprocess
oliver@134
   434
        result = self._process.communicate()[0]
oliver@134
   435
        if self._process.returncode != 0:
oliver@134
   436
            print 'user request has been aborted.'
oliver@134
   437
            return
oliver@134
   438
        
oliver@134
   439
        # all ok, tell send request back appropriate destination
oliver@134
   440
        try:
oliver@134
   441
            j = json.loads(result)
oliver@134
   442
        except:
oliver@134
   443
            print 'error in password parsing'
oliver@134
   444
            return
oliver@134
   445
        
oliver@134
   446
        # TODO: it would be WAY easier and secure if we just 
oliver@134
   447
        #       add the result json to a HTTP-POST here.
oliver@134
   448
        url_addr = 'http://' + self._remote_ip + ':58080' + self._resource
oliver@142
   449
oliver@142
   450
        # by provided a 'data' we turn this into a POST statement
oliver@142
   451
        req = urllib2.Request(url_addr, urllib.urlencode(j))
oliver@134
   452
        try:
oliver@134
   453
            res = urllib2.urlopen(req)
oliver@134
   454
        except:
oliver@134
   455
            print 'failed to contact: ' + url_addr
oliver@134
   456
            return 
oliver@134
   457
oliver@134
   458
oliver@136
   459
class RESTServerThread(threading.Thread):
oliver@136
   460
oliver@136
   461
    """Thread for serving the REST API."""
oliver@136
   462
oliver@136
   463
    def __init__(self, port): 
oliver@136
   464
oliver@136
   465
        """ctor"""
oliver@136
   466
        threading.Thread.__init__(self)
oliver@136
   467
        self._port = port 
oliver@136
   468
    
oliver@136
   469
    def stop(self):
oliver@136
   470
oliver@136
   471
        """stop thread"""
oliver@136
   472
        self.running = False
oliver@136
   473
        
oliver@136
   474
    
oliver@136
   475
    def run(self):
oliver@136
   476
oliver@136
   477
        """run the thread"""
oliver@136
   478
        _serve(self._port)
oliver@136
   479
oliver@136
   480
oliver@136
   481
oliver@136
   482
def is_already_running(port = 8090):
oliver@136
   483
oliver@136
   484
    """check if this is started twice"""
oliver@136
   485
oliver@136
   486
    try:
oliver@136
   487
        s = socket.create_connection(('127.0.0.1', port), 0.5)
oliver@136
   488
    except:
oliver@136
   489
        return False
oliver@136
   490
oliver@136
   491
    return True
oliver@136
   492
oliver@136
   493
oliver@154
   494
def _bounce_vm_logs():
oliver@154
   495
oliver@154
   496
    """grab all logs from the VMs and push them to the log servers"""
oliver@154
   497
oliver@154
   498
    global log_file_lock
oliver@154
   499
oliver@154
   500
    # pick the highest current number
oliver@154
   501
    cur = 0
oliver@154
   502
    for f in glob.iglob(os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.*')):
oliver@154
   503
        try:
oliver@154
   504
            n = f.split('.')[-1:][0]
oliver@154
   505
            if cur < int(n):
oliver@154
   506
                cur = int(n)
oliver@154
   507
        except:
oliver@154
   508
            pass
oliver@154
   509
oliver@154
   510
    cur = cur + 1
oliver@154
   511
oliver@154
   512
    # first add new vm logs to our existing one: rename the log file
oliver@154
   513
    log_file_name_new = os.path.join(Environment('OpenSecurity').log_path, 'vm_new.log')
oliver@154
   514
    log_file_name_cur = os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.' + str(cur))
oliver@154
   515
    log_file_lock.acquire()
oliver@154
   516
    try:
oliver@154
   517
        os.rename(log_file_name_new, log_file_name_cur)
oliver@154
   518
        print('new log file: ' + log_file_name_cur)
oliver@154
   519
    except:
oliver@154
   520
        pass
oliver@154
   521
    log_file_lock.release()
oliver@154
   522
oliver@154
   523
    # now we have a list of next log files to dump
oliver@154
   524
    log_files = glob.glob(os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.*'))
oliver@154
   525
    log_files.sort()
oliver@154
   526
    for log_file in log_files:
oliver@154
   527
oliver@154
   528
        try:
oliver@154
   529
            f = open(log_file, 'rb')
oliver@154
   530
            while True:
oliver@154
   531
                l = pickle.load(f)
oliver@154
   532
                _push_log(l)
oliver@154
   533
oliver@154
   534
        except EOFError:
oliver@154
   535
oliver@154
   536
            try:
oliver@154
   537
                os.remove(log_file)
oliver@154
   538
            except:
oliver@154
   539
                logger.warning('tried to delete log file (pushed to EOF) "' + log_file + '" but failed')
oliver@154
   540
oliver@154
   541
        except:
oliver@154
   542
            logger.warning('encountered error while pushing log file "' + log_file + '"')
oliver@154
   543
oliver@154
   544
    # start bouncer again ...
oliver@154
   545
    global log_file_bouncer
oliver@154
   546
    log_file_bouncer = threading.Timer(5.0, _bounce_vm_logs)
oliver@154
   547
    log_file_bouncer.start()
oliver@154
   548
oliver@154
   549
oliver@154
   550
def _push_log(log):
oliver@154
   551
    """POST a single log to log server
oliver@154
   552
oliver@154
   553
    @param  log     the log POST param
oliver@154
   554
    """
oliver@154
   555
oliver@154
   556
    url_addr = 'http://GIMME-SERVER-TO-LOG-TO/log'
oliver@154
   557
oliver@154
   558
    # by provided a 'data' we turn this into a POST statement
oliver@154
   559
    d = urllib.urlencode(log)
oliver@154
   560
    req = urllib2.Request(url_addr, d)
oliver@154
   561
    urllib2.urlopen(req)
oliver@154
   562
    logger.debug('pushed log to server: ' + str(log))
oliver@154
   563
oliver@154
   564
oliver@136
   565
def _serve(port):
oliver@136
   566
oliver@136
   567
    """Start the REST server"""
oliver@136
   568
oliver@136
   569
    global server
oliver@136
   570
oliver@154
   571
    # start the VM-log bouncer timer
oliver@154
   572
    global log_file_bouncer
oliver@154
   573
    log_file_bouncer = threading.Timer(5.0, _bounce_vm_logs)
oliver@154
   574
    log_file_bouncer.start()
oliver@154
   575
oliver@136
   576
    # trick the web.py server 
oliver@136
   577
    sys.argv = [__file__, str(port)]
om@13
   578
    server = web.application(opensecurity_urls, globals())
om@13
   579
    server.run()
oliver@134
   580
oliver@136
   581
oliver@136
   582
def serve(port = 8090, background = False):
oliver@136
   583
oliver@136
   584
    """Start serving the REST Api
oliver@136
   585
    port ... port number to listen on
oliver@136
   586
    background ... cease into background (spawn thread) and return immediately"""
oliver@136
   587
oliver@136
   588
    # start threaded or direct version
oliver@136
   589
    if background == True:
oliver@136
   590
        t = RESTServerThread(port)
oliver@136
   591
        t.start()
oliver@136
   592
    else:
oliver@136
   593
        _serve(port)
oliver@136
   594
oliver@136
   595
def stop():
oliver@136
   596
oliver@136
   597
    """Stop serving the REST Api"""
oliver@136
   598
oliver@136
   599
    global server
oliver@136
   600
    if server is None:
oliver@136
   601
        return
oliver@136
   602
oliver@154
   603
    global log_file_bouncer
oliver@154
   604
    if log_file_bouncer is not None:
oliver@154
   605
        log_file_bouncer.cancel()
oliver@154
   606
oliver@136
   607
    server.stop()
oliver@136
   608
oliver@136
   609
# start
oliver@136
   610
if __name__ == "__main__":
oliver@136
   611
    serve()
oliver@136
   612