OpenSecurity/bin/ui/configure_dialog.py
author BarthaM@N3SIM1218.D03.arc.local
Fri, 05 Sep 2014 12:28:30 +0100
changeset 221 853af9cfab6a
parent 220 f5805ee62d80
child 240 d7ef04254e9c
permissions -rwxr-xr-x
Integrated import script (rewritten in python) into opensecurity/vmmanager.py
Improoved user feedback upon import and update as well as logging.
Reduced system shutdown times and ui response times
Improoved the decoupling between UI and OSec subsystem.
Various other fixes
oliver@100
     1
#!/bin/env python
oliver@100
     2
# -*- coding: utf-8 -*-
oliver@100
     3
oliver@100
     4
# ------------------------------------------------------------
oliver@100
     5
# configure_dialog.pyw
oliver@100
     6
# 
oliver@100
     7
# let the user configure the opensecurity system
oliver@100
     8
#
oliver@100
     9
# Autor: Oliver Maurhart, <oliver.maurhart@ait.ac.at>
oliver@100
    10
#
oliver@100
    11
# Copyright (C) 2014 AIT Austrian Institute of Technology
oliver@100
    12
# AIT Austrian Institute of Technology GmbH
oliver@100
    13
# Donau-City-Strasse 1 | 1220 Vienna | Austria
oliver@100
    14
# http://www.ait.ac.at
oliver@100
    15
#
oliver@100
    16
# This program is free software; you can redistribute it and/or
oliver@100
    17
# modify it under the terms of the GNU General Public License
oliver@100
    18
# as published by the Free Software Foundation version 2.
oliver@100
    19
# 
oliver@100
    20
# This program is distributed in the hope that it will be useful,
oliver@100
    21
# but WITHOUT ANY WARRANTY; without even the implied warranty of
oliver@100
    22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
oliver@100
    23
# GNU General Public License for more details.
oliver@100
    24
# 
oliver@100
    25
# You should have received a copy of the GNU General Public License
oliver@100
    26
# along with this program; if not, write to the Free Software
oliver@100
    27
# Foundation, Inc., 51 Franklin Street, Fifth Floor, 
oliver@100
    28
# Boston, MA  02110-1301, USA.
oliver@100
    29
# ------------------------------------------------------------
oliver@100
    30
oliver@100
    31
oliver@100
    32
# ------------------------------------------------------------
oliver@116
    33
# for the record:
oliver@116
    34
#
oliver@116
    35
#   This script uses a QTimer (in a busy loop) for checking
oliver@116
    36
#   changes to the log file watched.
oliver@116
    37
#
oliver@116
    38
#   Yes, this is sure f--g ugly.
oliver@116
    39
#
oliver@116
    40
#   Thing is: there is no real good cross-platform in
oliver@116
    41
#   doing this nicely. And here we solve this problem
oliver@116
    42
#   neither. See doc in update_progress method.
oliver@116
    43
#
oliver@116
    44
#   There is no python native but external packages
oliver@116
    45
#   using pip with different qualities (and struggling).
oliver@116
    46
#
oliver@116
    47
#   Qt and as such PyQt offer the QFileSystemWatcher class
oliver@116
    48
#   which *could* solve this isse but
oliver@116
    49
#
oliver@116
    50
#       a) this class is somehow buggy and unstable
oliver@116
    51
#       b) deprecated as of Qt 5.1
oliver@116
    52
#
oliver@116
    53
#   ... and this ain't a beauty contest either, is it?
oliver@116
    54
# ------------------------------------------------------------
oliver@116
    55
oliver@116
    56
oliver@116
    57
# ------------------------------------------------------------
oliver@100
    58
# imports
oliver@100
    59
oliver@107
    60
import json
oliver@116
    61
import os
oliver@199
    62
import subprocess
oliver@100
    63
import sys
oliver@107
    64
import urllib2
oliver@100
    65
oliver@100
    66
from PyQt4 import QtCore
oliver@100
    67
from PyQt4 import QtGui
oliver@100
    68
oliver@100
    69
from ui_ConfigureDialog import Ui_ConfigureDialog 
oliver@100
    70
from about_dialog import AboutDialog
oliver@100
    71
oliver@100
    72
oliver@100
    73
# ------------------------------------------------------------
oliver@100
    74
# code
oliver@100
    75
oliver@100
    76
oliver@100
    77
class ConfigureDialog(QtGui.QDialog):
oliver@100
    78
oliver@100
    79
    """A dialog which lets the user configure the OpenSecurity Subsystem"""
oliver@100
    80
oliver@100
    81
    def __init__(self):
oliver@100
    82
oliver@100
    83
        QtGui.QDialog.__init__(self)
oliver@100
    84
oliver@100
    85
        # setup the user interface
oliver@100
    86
        self.ui = Ui_ConfigureDialog()
oliver@100
    87
        self.ui.setupUi(self)
oliver@116
    88
oliver@116
    89
        # monospace font fix for windows systems
oliver@116
    90
        font = QtGui.QFont("Monospace [Courier]")
oliver@191
    91
        font.setPointSize(8);
oliver@116
    92
        font.setStyleHint(QtGui.QFont.TypeWriter)
oliver@116
    93
        font.setStyleStrategy(QtGui.QFont.PreferAntialias)
oliver@116
    94
        self.ui.edtProgress.setFont(font)
oliver@191
    95
        self.ui.edtLog.setFont(font)
oliver@100
    96
    
oliver@106
    97
        self.ui.edtStatus.setText('<not evaluated yet>')
oliver@106
    98
        self.ui.edtVersion.setText('<not evaluated yet>')
oliver@106
    99
        self.ui.edtStatus.setEnabled(False);
oliver@106
   100
        self.ui.edtVersion.setEnabled(False);
oliver@106
   101
oliver@100
   102
        # local members
oliver@100
   103
        self._about_dialog = AboutDialog()
oliver@116
   104
        self._file_watched_name = ''
oliver@116
   105
        self._file_watched_size = 0
oliver@116
   106
        self._file_watcher = QtCore.QTimer(self)
oliver@199
   107
        self._service_log_file = ''
oliver@188
   108
        self._service_log_size = 0
oliver@100
   109
oliver@100
   110
        # connectors
oliver@100
   111
        self.ui.btnAbout.clicked.connect(self.clicked_about)
oliver@100
   112
        self.ui.btnClose.clicked.connect(self.accept)
oliver@100
   113
        self.ui.btnDownload.clicked.connect(self.clicked_download)
oliver@199
   114
        self.ui.btnExplorer.clicked.connect(self.clicked_explorer)
oliver@100
   115
        self.ui.btnImport.clicked.connect(self.clicked_import)
BarthaM@221
   116
        self.ui.btnUpdate.clicked.connect(self.clicked_update)
oliver@100
   117
        self.ui.btnRefresh.clicked.connect(self.clicked_refresh)
oliver@116
   118
        self._file_watcher.timeout.connect(self.update_progress)
oliver@100
   119
oliver@107
   120
        # call first refresh immediately
oliver@107
   121
        QtCore.QTimer.singleShot(0, self.clicked_refresh)
oliver@116
   122
        self._file_watcher.start(100)
oliver@107
   123
oliver@100
   124
oliver@100
   125
    def clicked_about(self):
oliver@100
   126
oliver@100
   127
        """About button has been clicked."""
oliver@106
   128
        self._about_dialog.exec_()
oliver@100
   129
oliver@100
   130
oliver@100
   131
    def clicked_download(self):
oliver@100
   132
oliver@100
   133
        """Download button has been clicked."""
oliver@115
   134
oliver@116
   135
        # remove old stuff
oliver@116
   136
        self._file_watched_name = ''
oliver@116
   137
        self._file_watched_size = 0
oliver@115
   138
        self.ui.edtProgress.clear()
oliver@115
   139
oliver@115
   140
        try:
oliver@116
   141
oliver@116
   142
            # get general server info
oliver@116
   143
            j = json.load(urllib2.urlopen('http://127.0.0.1:8080/fetch_initial_image'))
oliver@116
   144
            self._file_watched_name = str(j['fetch_log'])
oliver@115
   145
oliver@115
   146
        except:
oliver@115
   147
            pass
oliver@115
   148
oliver@100
   149
oliver@199
   150
    def clicked_explorer(self):
oliver@199
   151
oliver@199
   152
        """Explorer button has been clicked."""
oliver@199
   153
        if sys.platform == 'win32' or sys.platform == 'cygwin':
oliver@199
   154
            subprocess.Popen([os.path.join(os.environ['WINDIR'], 'explorer.exe'), self._service_log_path])
oliver@199
   155
oliver@199
   156
oliver@100
   157
    def clicked_import(self):
oliver@100
   158
oliver@100
   159
        """Import button has been clicked."""
oliver@119
   160
oliver@119
   161
        # remove old stuff
oliver@119
   162
        self._file_watched_name = ''
oliver@119
   163
        self._file_watched_size = 0
oliver@119
   164
        self.ui.edtProgress.clear()
oliver@119
   165
oliver@119
   166
        try:
oliver@119
   167
            j = json.load(urllib2.urlopen('http://127.0.0.1:8080/init'))
oliver@119
   168
            self._file_watched_name = str(j['init_log'])
oliver@119
   169
        except:
oliver@119
   170
            pass
BarthaM@220
   171
    
BarthaM@221
   172
    
BarthaM@221
   173
    def clicked_update(self):
BarthaM@220
   174
BarthaM@221
   175
        """Update button has been clicked."""
BarthaM@220
   176
BarthaM@220
   177
        # remove old stuff
BarthaM@220
   178
        self._file_watched_name = ''
BarthaM@220
   179
        self._file_watched_size = 0
BarthaM@220
   180
        self.ui.edtProgress.clear()
BarthaM@220
   181
BarthaM@220
   182
        try:
BarthaM@221
   183
            j = json.load(urllib2.urlopen('http://127.0.0.1:8080/update_template'))
BarthaM@221
   184
            self._file_watched_name = str(j['init_log'])
BarthaM@220
   185
        except:
BarthaM@220
   186
            pass
oliver@100
   187
oliver@100
   188
oliver@100
   189
    def clicked_refresh(self):
oliver@100
   190
oliver@100
   191
        """Refresh button has been clicked."""
oliver@107
   192
        self.ui.edtStatus.setText('<not evaluated yet>')
oliver@107
   193
        self.ui.edtVersion.setText('<not evaluated yet>')
oliver@107
   194
        self.ui.edtStatus.setEnabled(False);
oliver@107
   195
        self.ui.edtVersion.setEnabled(False);
oliver@112
   196
        self.ui.tvTemplate.clear()
oliver@107
   197
oliver@107
   198
        try:
oliver@112
   199
oliver@112
   200
            # get general server info
oliver@107
   201
            j = json.load(urllib2.urlopen('http://127.0.0.1:8080'))
oliver@132
   202
            self.ui.edtStatus.setText(j['os_server']['status message'])
oliver@107
   203
            self.ui.edtStatus.setEnabled(True)
oliver@107
   204
            self.ui.edtVersion.setText(j['os_server']['version'])
oliver@107
   205
            self.ui.edtVersion.setEnabled(True)
oliver@199
   206
            self._service_log_path = j['os_server']['current log folder']
oliver@199
   207
            self._service_log_file = os.path.join(self._service_log_path, 'OpenSecurity.log')
oliver@188
   208
            self._service_log_size = 0
oliver@112
   209
oliver@112
   210
            # get initial template info
oliver@112
   211
            j = json.load(urllib2.urlopen('http://127.0.0.1:8080/initial_image'))
oliver@112
   212
            i = QtGui.QTreeWidgetItem(self.ui.tvTemplate)
oliver@112
   213
            i.setText(0, j['initial_template']['name'])
oliver@112
   214
            i.setText(1, QtCore.QDateTime.fromTime_t(int(j['initial_template']['date'])).toString(QtCore.Qt.SystemLocaleShortDate))
oliver@112
   215
            i.setText(2, str(j['initial_template']['size']))
oliver@112
   216
            i.setText(3, j['initial_template']['path'])
oliver@107
   217
            
oliver@107
   218
        except:
oliver@107
   219
            pass
oliver@100
   220
oliver@203
   221
        # trigger machine update
oliver@203
   222
        self.update_machines()
oliver@203
   223
oliver@203
   224
oliver@203
   225
    def update_machines(self):
oliver@203
   226
oliver@203
   227
        """Update the machines view"""
oliver@203
   228
        self.ui.tvMachines.clear()
oliver@203
   229
        self._icon_machine = QtGui.QIcon()
oliver@203
   230
        self._icon_machine.addPixmap(QtGui.QPixmap(":/opensecurity/gfx/cpu.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
oliver@203
   231
        self._icon_property = QtGui.QIcon()
oliver@203
   232
        self._icon_property.addPixmap(QtGui.QPixmap(":/opensecurity/gfx/configure.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
oliver@203
   233
        self._icon_network = QtGui.QIcon()
oliver@203
   234
        self._icon_network.addPixmap(QtGui.QPixmap(":/opensecurity/gfx/network-workgroup.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
oliver@203
   235
oliver@207
   236
        try:
oliver@203
   237
oliver@207
   238
            # get machines
oliver@207
   239
            machines = json.load(urllib2.urlopen('http://127.0.0.1:8080/sdvms'))
oliver@207
   240
            for m in machines:
oliver@207
   241
                i = QtGui.QTreeWidgetItem(self.ui.tvMachines)
oliver@207
   242
                i.setIcon(0, self._icon_machine)
oliver@207
   243
                i.setText(0, m)
oliver@207
   244
                i.setIcon(1, self._icon_network)
oliver@207
   245
                i.setText(1, machines[m])
oliver@203
   246
oliver@204
   247
oliver@207
   248
                properties = json.load(urllib2.urlopen('http://127.0.0.1:8080/sdvms/' + m))
oliver@207
   249
                for p in properties:
oliver@207
   250
                    j = QtGui.QTreeWidgetItem(i)
oliver@207
   251
                    j.setIcon(0, self._icon_property)
oliver@207
   252
                    j.setText(0, p)
oliver@207
   253
                    j.setText(1, properties[p])
oliver@203
   254
oliver@207
   255
                j.setIcon(0, self._icon_network)
oliver@207
   256
                j.setText(0, 'ip')
oliver@207
   257
                j.setText(1, machines[m])
oliver@203
   258
oliver@203
   259
oliver@207
   260
        except:
oliver@207
   261
            pass
oliver@203
   262
oliver@203
   263
oliver@116
   264
oliver@116
   265
    def update_progress(self):
oliver@116
   266
        
oliver@116
   267
        """Our log file has changed, update ui"""
oliver@116
   268
oliver@116
   269
        # we could use os.stat(PATH).st_mtime ...
oliver@116
   270
        # 
oliver@116
   271
        # ... but Windows does not update it frequently. -.- 
oliver@116
   272
        #
oliver@116
   273
        # Running a Cygwin Bash Shell script in the background
oliver@116
   274
        # does not update mtime of the redirected stderr file.
oliver@116
   275
        #
oliver@116
   276
        # This is terrible. 
oliver@116
   277
oliver@188
   278
        # first the service log
oliver@199
   279
        if self._service_log_file != '':
oliver@199
   280
            file_size = os.stat(self._service_log_file).st_size
oliver@188
   281
            if file_size > self._service_log_size:
oliver@199
   282
                f = open(self._service_log_file, 'r')
oliver@191
   283
                f.seek(self._service_log_size)
oliver@191
   284
                self.ui.edtLog.appendPlainText(f.read())
oliver@188
   285
                self._service_log_size = file_size
oliver@188
   286
        
oliver@188
   287
        # now any processing log (download or import)
oliver@188
   288
        if self._file_watched_name == '':
oliver@188
   289
            return
oliver@188
   290
oliver@116
   291
        file_size = os.stat(self._file_watched_name).st_size
oliver@116
   292
        if self._file_watched_size >= file_size:
oliver@116
   293
            return
oliver@116
   294
oliver@191
   295
        f = open(self._file_watched_name, 'r')
oliver@199
   296
        self.ui.edtProgress.setPlainText(f.read())
oliver@199
   297
        self.ui.edtProgress.verticalScrollBar().setValue(self.ui.edtProgress.verticalScrollBar().maximum())
oliver@116
   298
        self._file_watched_size = file_size
oliver@116
   299
oliver@116
   300
oliver@100
   301
if __name__ == "__main__":
oliver@113
   302
    a = QtGui.QApplication(sys.argv)
oliver@113
   303
    d = ConfigureDialog()
oliver@113
   304
    d.show()
oliver@113
   305
    sys.exit(a.exec_())     
oliver@100
   306