Merge the two databases and the two Django projects. Note that the TKO migrations are still present, as is the migration code for TKO in database/migrate.py. These pieces of code are preserved because they are required by this change to perform the merge. A separate change will be submitted in the future to remove those references.

WARNING: This change will move all tables in TKO into the AUTOTEST_WEB database. Custom TKO tables that Autotest does not know about will NOT be moved, and will be lost after this change. Any scripts that run directly against the TKO database will cease functioning until they are modified to run against AUTOTEST_WEB. Additionally, if you were importing any .py files from autotest_lib.new_tko.tko.*, they have been moved to autotest_lib.frontend.tko.*

Notes: You will need to manually create two files after applying this patch
touch <autotest_dir>/frontend/tko/__init__.py
touch <autotest_dir>/tko/migrations/__init__.py
Optionally, you can also remove the entire <autotest_dir>/new_tko/ directory, as it is no longer needed.

Signed-off-by: James Ren <jamesren@google.com>


git-svn-id: http://test.kernel.org/svn/autotest/trunk@4106 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/apache/conf/new-tko-directives b/apache/conf/new-tko-directives
index f684229..2081815 100644
--- a/apache/conf/new-tko-directives
+++ b/apache/conf/new-tko-directives
@@ -6,7 +6,7 @@
 <Location "/new_tko/server">
     SetHandler python-program
     PythonHandler django.core.handlers.modpython
-    SetEnv DJANGO_SETTINGS_MODULE new_tko.settings
+    SetEnv DJANGO_SETTINGS_MODULE frontend.settings
     PythonDebug On
     # Force our own site-packages to be loaded by mod_python prior
     # to mod_python's system python site-packages directory.
diff --git a/database/db_utils.py b/database/db_utils.py
index 9b6dfb3..18f5495 100644
--- a/database/db_utils.py
+++ b/database/db_utils.py
@@ -1,4 +1,5 @@
-import migrate
+TABLE_TYPE = object()
+VIEW_TYPE = object()
 
 
 def drop_views(manager, views):
@@ -11,7 +12,7 @@
     @param manager the migration manager
     @param views the views to drop
     """
-    _check_exists(manager, views, 'VIEW')
+    _check_exists(manager, views, VIEW_TYPE)
     for view in views:
         manager.execute('DROP VIEW `%s`' % view)
 
@@ -27,11 +28,38 @@
     @param mapping a dictionary of orig_name => new_name. Any table not matching
                    an entry in this dictionary will not be renamed
     """
-    _check_exists(manager, (table for table, _ in mapping.iteritems()), 'TABLE')
+    _check_exists(manager, (table for table, _ in mapping.iteritems()),
+                  TABLE_TYPE)
     for orig_name, new_name in mapping.iteritems():
         manager.execute('RENAME TABLE `%s` TO `%s`' % (orig_name, new_name))
 
 
+def move_tables(manager, src_manager, tables):
+    """
+    Moves the specified tables from another database
+
+    If a table does not exist in the source database, this method fails without
+    modification
+
+    @param manager the migration manager
+    @param src_manager a migration manager that handles the source database
+    @param tables a list of tables to move
+    """
+    _check_exists(src_manager, tables, TABLE_TYPE)
+    for table in tables:
+        manager.execute('RENAME TABLE `%(db)s`.`%(table)s` TO `%(table)s`'
+                        % dict(db=src_manager.get_db_name(), table=table))
+
+
+def drop_database(manager):
+    """
+    Drops the database that the specified manager controls
+
+    @param manager the migration manager
+    """
+    manager.execute('DROP DATABASE `%s`' % manager.get_db_name())
+
+
 def _check_exists(manager, names, type):
     """
     Checks if the tables or views exists.
@@ -42,12 +70,12 @@
     @param names the table/view names
     @param type one of 'TABLE' or 'VIEW'
     """
-    if type == 'TABLE':
+    if type == TABLE_TYPE:
         info_table = 'TABLES'
-    elif type == 'VIEW':
+    elif type == VIEW_TYPE:
         info_table = 'VIEWS'
     else:
-        raise Exception("type parameter must be either 'TABLE' or 'VIEW'")
+        raise Exception("type parameter must be either TABLE_TYPE or VIEW_TYPE")
 
     query = ('SELECT table_name FROM information_schema.%s '
              'WHERE table_schema = %%s' % info_table)
diff --git a/database/db_utils_unittest.py b/database/db_utils_unittest.py
index f0c622c..6c117da 100755
--- a/database/db_utils_unittest.py
+++ b/database/db_utils_unittest.py
@@ -28,7 +28,7 @@
     def test_check_exists(self):
         views = ('view1', 'view2')
         def _call_check_exists():
-            db_utils._check_exists(self.manager, views, 'VIEW')
+            db_utils._check_exists(self.manager, views, db_utils.VIEW_TYPE)
 
         self._setup_exists_expects(views, 'VIEWS')
         _call_check_exists()
diff --git a/database/migrate.py b/database/migrate.py
index 8c4ede7..07a286a 100755
--- a/database/migrate.py
+++ b/database/migrate.py
@@ -300,11 +300,8 @@
     parser.add_option('--debug', help='print all DB queries',
                       action='store_true')
     (options, args) = parser.parse_args()
-    database = database_connection.DatabaseConnection(options.database)
-    database.debug = options.debug
-    database.reconnect_enabled = False
-    database.connect()
-    manager = MigrationManager(database, force=options.force)
+    manager = get_migration_manager(db_name=options.database,
+                                    debug=options.debug, force=options.force)
 
     if len(args) > 0:
         if len(args) > 1:
@@ -329,5 +326,13 @@
     print USAGE
 
 
+def get_migration_manager(db_name, debug, force):
+    database = database_connection.DatabaseConnection(db_name)
+    database.debug = debug
+    database.reconnect_enabled = False
+    database.connect()
+    return MigrationManager(database, force=force)
+
+
 if __name__ == '__main__':
     main()
diff --git a/frontend/migrations/046_merge_databases.py b/frontend/migrations/046_merge_databases.py
new file mode 100644
index 0000000..a3eb878
--- /dev/null
+++ b/frontend/migrations/046_merge_databases.py
@@ -0,0 +1,35 @@
+import common
+from autotest_lib.database import db_utils, migrate
+
+TKO_MIGRATION_NAME = '031_rename_tko_tables'
+migrations_module = __import__('autotest_lib.tko.migrations', globals(),
+                               locals(), [TKO_MIGRATION_NAME])
+tko_migration = getattr(migrations_module, TKO_MIGRATION_NAME)
+
+TABLE_NAMES = tko_migration.RENAMES_UP.values()
+
+
+def migrate_up(manager):
+    tko_manager = migrate.get_migration_manager(db_name='TKO', debug=False,
+                                                force=False)
+    if (tko_manager.get_db_version() < 31):
+        raise Exception('You must update the TKO database to the latest '
+                        'version before merging')
+
+    if not manager.force:
+        response = raw_input(
+                'This migration will merge the autotest_web and tko databases. '
+                'Following the migration, the tko database will be dropped. '
+                'Any user-added tables in tko will NOT be migrated. This '
+                'migration is NOT reversible. Are you sure you want to '
+                'continue? (yes/no) ')
+        if response != 'yes':
+            raise Exception('User has chosen to abort migration')
+
+    db_utils.move_tables(manager, tko_manager, TABLE_NAMES)
+    db_utils.drop_database(tko_manager)
+    manager.execute_script(tko_migration.RECREATE_VIEWS_UP)
+
+
+def migrate_down(manager):
+    raise Exception('Migration 46 is not reversible!')
diff --git a/frontend/planner/models.py b/frontend/planner/models.py
index a7e8af7..c4d8988 100644
--- a/frontend/planner/models.py
+++ b/frontend/planner/models.py
@@ -2,7 +2,7 @@
 import common
 from autotest_lib.frontend.afe import models as afe_models
 from autotest_lib.frontend.afe import model_logic, rpc_utils
-from autotest_lib.new_tko.tko import models as tko_models
+from autotest_lib.frontend.tko import models as tko_models
 from autotest_lib.client.common_lib import enum
 
 
diff --git a/frontend/settings.py b/frontend/settings.py
index 43e5eb7..bdd849c 100644
--- a/frontend/settings.py
+++ b/frontend/settings.py
@@ -43,6 +43,7 @@
 # prefix applied to all URLs - useful if requests are coming through apache,
 # and you need this app to coexist with others
 URL_PREFIX = 'afe/server/'
+TKO_URL_PREFIX = 'new_tko/server/'
 PLANNER_URL_PREFIX = 'planner/server/'
 
 # Local time zone for this installation. Choices can be found here:
@@ -106,6 +107,7 @@
 
 INSTALLED_APPS = (
     'frontend.afe',
+    'frontend.tko',
     'frontend.planner',
     'django.contrib.admin',
     'django.contrib.auth',
diff --git a/new_tko/tko/__init__.py b/frontend/tko/__init__.py
similarity index 100%
rename from new_tko/tko/__init__.py
rename to frontend/tko/__init__.py
diff --git a/frontend/tko/common.py b/frontend/tko/common.py
new file mode 100644
index 0000000..d37136e
--- /dev/null
+++ b/frontend/tko/common.py
@@ -0,0 +1,24 @@
+import os, sys
+dirname = os.path.dirname(sys.modules[__name__].__file__)
+autotest_dir = os.path.abspath(os.path.join(dirname, '..', '..'))
+client_dir = os.path.join(autotest_dir, "client")
+sys.path.insert(0, client_dir)
+import setup_modules
+sys.path.pop(0)
+setup_modules.setup(base_path=autotest_dir, root_module_name="autotest_lib")
+import os, sys
+dirname = os.path.dirname(sys.modules[__name__].__file__)
+autotest_dir = os.path.abspath(os.path.join(dirname, '..', '..'))
+client_dir = os.path.join(autotest_dir, "client")
+sys.path.insert(0, client_dir)
+import setup_modules
+sys.path.pop(0)
+setup_modules.setup(base_path=autotest_dir, root_module_name="autotest_lib")
+import os, sys
+dirname = os.path.dirname(sys.modules[__name__].__file__)
+autotest_dir = os.path.abspath(os.path.join(dirname, '..', '..'))
+client_dir = os.path.join(autotest_dir, "client")
+sys.path.insert(0, client_dir)
+import setup_modules
+sys.path.pop(0)
+setup_modules.setup(base_path=autotest_dir, root_module_name="autotest_lib")
diff --git a/new_tko/tko/csv_encoder.py b/frontend/tko/csv_encoder.py
similarity index 100%
rename from new_tko/tko/csv_encoder.py
rename to frontend/tko/csv_encoder.py
diff --git a/new_tko/tko/csv_encoder_unittest.py b/frontend/tko/csv_encoder_unittest.py
old mode 100755
new mode 100644
similarity index 98%
rename from new_tko/tko/csv_encoder_unittest.py
rename to frontend/tko/csv_encoder_unittest.py
index a4633cd..71ca636
--- a/new_tko/tko/csv_encoder_unittest.py
+++ b/frontend/tko/csv_encoder_unittest.py
@@ -1,10 +1,10 @@
-#!/usr/bin/python
+#!/usr/bin/python2.4
 
 import unittest
 import common
 from autotest_lib.frontend import setup_django_environment
 from autotest_lib.frontend import setup_test_environment
-from autotest_lib.new_tko.tko import csv_encoder
+from autotest_lib.frontend.tko import csv_encoder
 
 class CsvEncodingTest(unittest.TestCase):
     def _make_request(self, method, columns=None):
diff --git a/new_tko/tko/graphing_utils.py b/frontend/tko/graphing_utils.py
similarity index 99%
rename from new_tko/tko/graphing_utils.py
rename to frontend/tko/graphing_utils.py
index af3cffb..aab26a5 100644
--- a/new_tko/tko/graphing_utils.py
+++ b/frontend/tko/graphing_utils.py
@@ -22,9 +22,9 @@
 import StringIO, colorsys, PIL.Image, PIL.ImageChops
 from autotest_lib.frontend.afe import readonly_connection
 from autotest_lib.frontend.afe.model_logic import ValidationError
-from simplejson import encoder
+from autotest_lib.frontend.afe.simplejson import encoder
 from autotest_lib.client.common_lib import global_config
-from autotest_lib.new_tko.tko import models, tko_rpc_utils
+from autotest_lib.frontend.tko import models, tko_rpc_utils
 
 _FIGURE_DPI = 100
 _FIGURE_WIDTH_IN = 10
@@ -810,7 +810,7 @@
 
 
 _cache_timeout = global_config.global_config.get_config_value(
-    'TKO', 'graph_cache_creation_timeout_minutes')
+    'AUTOTEST_WEB', 'graph_cache_creation_timeout_minutes')
 
 
 def handle_plot_request(id, max_age):
diff --git a/new_tko/tko/models.py b/frontend/tko/models.py
similarity index 99%
rename from new_tko/tko/models.py
rename to frontend/tko/models.py
index d975010..61b24af 100644
--- a/new_tko/tko/models.py
+++ b/frontend/tko/models.py
@@ -330,8 +330,8 @@
                 TestLabel.objects.filter(name__in=label_names)
                 .values_list('name', 'id'))
         if len(label_ids) < len(set(label_names)):
-            raise ValueError('Not all labels found: %s' %
-                             ', '.join(label_names))
+                raise ValueError('Not all labels found: %s' %
+                                 ', '.join(label_names))
         return dict(name_and_id for name_and_id in label_ids)
 
 
diff --git a/frontend/tko/preconfigs/metrics/kernel_compare b/frontend/tko/preconfigs/metrics/kernel_compare
new file mode 100644
index 0000000..43c182e
--- /dev/null
+++ b/frontend/tko/preconfigs/metrics/kernel_compare
@@ -0,0 +1,69 @@
+plot: Bar
+xAxis: test_name
+globalFilter[0][db]: test_name
+globalFilter[0][condition]: IN ('dbench', 'tbench')
+globalFilter[1][db]: iteration_key
+globalFilter[1][condition]: = 'throughput'
+globalFilter[2][db]: hostname
+globalFilter[2][condition]: = 'my_host'
+globalFilter_all: true
+name[0]: my kernel 1
+values[0]: iteration_value
+aggregation[0]: AVG
+errorBars[0]: true
+seriesFilters[0][0][db]: kernel
+seriesFilters[0][0][condition]: LIKE 'my_kernel_1.%'
+seriesFilters[0]_all: true
+name[1]: my kernel 2
+values[1]: iteration_value
+aggregation[1]: AVG
+errorBars[1]: true
+seriesFilters[1][0][db]: kernel
+seriesFilters[1][0][condition]: LIKE 'my_kernel_2.%'
+seriesFilters[1]_all: true
+plot: Bar
+xAxis: test_name
+globalFilter[0][db]: test_name
+globalFilter[0][condition]: IN ('dbench', 'tbench')
+globalFilter[1][db]: iteration_key
+globalFilter[1][condition]: = 'throughput'
+globalFilter[2][db]: hostname
+globalFilter[2][condition]: = 'my_host'
+globalFilter_all: true
+name[0]: my kernel 1
+values[0]: iteration_value
+aggregation[0]: AVG
+errorBars[0]: true
+seriesFilters[0][0][db]: kernel
+seriesFilters[0][0][condition]: LIKE 'my_kernel_1.%'
+seriesFilters[0]_all: true
+name[1]: my kernel 2
+values[1]: iteration_value
+aggregation[1]: AVG
+errorBars[1]: true
+seriesFilters[1][0][db]: kernel
+seriesFilters[1][0][condition]: LIKE 'my_kernel_2.%'
+seriesFilters[1]_all: true
+plot: Bar
+xAxis: test_name
+globalFilter[0][db]: test_name
+globalFilter[0][condition]: IN ('dbench', 'tbench')
+globalFilter[1][db]: iteration_key
+globalFilter[1][condition]: = 'throughput'
+globalFilter[2][db]: hostname
+globalFilter[2][condition]: = 'my_host'
+globalFilter_all: true
+name[0]: my kernel 1
+values[0]: iteration_value
+aggregation[0]: AVG
+errorBars[0]: true
+seriesFilters[0][0][db]: kernel
+seriesFilters[0][0][condition]: LIKE 'my_kernel_1.%'
+seriesFilters[0]_all: true
+name[1]: my kernel 2
+values[1]: iteration_value
+aggregation[1]: AVG
+errorBars[1]: true
+seriesFilters[1][0][db]: kernel
+seriesFilters[1][0][condition]: LIKE 'my_kernel_2.%'
+seriesFilters[1]_all: true
diff --git a/frontend/tko/preconfigs/metrics/perf b/frontend/tko/preconfigs/metrics/perf
new file mode 100644
index 0000000..70df08a
--- /dev/null
+++ b/frontend/tko/preconfigs/metrics/perf
@@ -0,0 +1,153 @@
+plot: Line
+xAxis: kernel
+globalFilter[0][db]: hostname
+globalFilter[0][condition]: = 'my_host'
+globalFilter_all: true
+name[0]: kernbench (elapsed)
+values[0]: iteration_value
+aggregation[0]: AVG
+errorBars[0]: true
+seriesFilters[0][0][db]: iteration_key
+seriesFilters[0][0][condition]: = 'elapsed'
+seriesFilters[0][1][db]: test_name
+seriesFilters[0][1][condition]: = 'kernbench'
+seriesFilters[0]_all: true
+name[1]: dbench (throughput)
+values[1]: iteration_value
+aggregation[1]: AVG
+errorBars[1]: true
+seriesFilters[1][0][db]: iteration_key
+seriesFilters[1][0][condition]: = 'throughput'
+seriesFilters[1][1][db]: test_name
+seriesFilters[1][1][condition]: = 'dbench'
+seriesFilters[1]_all: true
+name[2]: tbench (throughput)
+values[2]: iteration_value
+aggregation[2]: AVG
+errorBars[2]: true
+seriesFilters[2][0][db]: iteration_key
+seriesFilters[2][0][condition]: = 'throughput'
+seriesFilters[2][1][db]: test_name
+seriesFilters[2][1][condition]: = 'tbench'
+seriesFilters[2]_all: true
+name[3]: unixbench (score)
+values[3]: iteration_value
+aggregation[3]: AVG
+errorBars[3]: true
+seriesFilters[3][0][db]: iteration_key
+seriesFilters[3][0][condition]: = 'score'
+seriesFilters[3][1][db]: test_name
+seriesFilters[3][1][condition]: = 'unixbench'
+seriesFilters[3]_all: true
+name[4]: iozone (32768-4096-fwrite)
+values[4]: iteration_value
+aggregation[4]: AVG
+errorBars[4]: true
+seriesFilters[4][0][db]: iteration_key
+seriesFilters[4][0][condition]: = '32768-4096-fwrite'
+seriesFilters[4][1][db]: test_name
+seriesFilters[4][1][condition]: = 'iozone'
+seriesFilters[4]_all: true
+inverted: kernbench (elapsed)
+plot: Line
+xAxis: kernel
+globalFilter[0][db]: hostname
+globalFilter[0][condition]: = 'my_host'
+globalFilter_all: true
+name[0]: kernbench (elapsed)
+values[0]: iteration_value
+aggregation[0]: AVG
+errorBars[0]: true
+seriesFilters[0][0][db]: iteration_key
+seriesFilters[0][0][condition]: = 'elapsed'
+seriesFilters[0][1][db]: test_name
+seriesFilters[0][1][condition]: = 'kernbench'
+seriesFilters[0]_all: true
+name[1]: dbench (throughput)
+values[1]: iteration_value
+aggregation[1]: AVG
+errorBars[1]: true
+seriesFilters[1][0][db]: iteration_key
+seriesFilters[1][0][condition]: = 'throughput'
+seriesFilters[1][1][db]: test_name
+seriesFilters[1][1][condition]: = 'dbench'
+seriesFilters[1]_all: true
+name[2]: tbench (throughput)
+values[2]: iteration_value
+aggregation[2]: AVG
+errorBars[2]: true
+seriesFilters[2][0][db]: iteration_key
+seriesFilters[2][0][condition]: = 'throughput'
+seriesFilters[2][1][db]: test_name
+seriesFilters[2][1][condition]: = 'tbench'
+seriesFilters[2]_all: true
+name[3]: unixbench (score)
+values[3]: iteration_value
+aggregation[3]: AVG
+errorBars[3]: true
+seriesFilters[3][0][db]: iteration_key
+seriesFilters[3][0][condition]: = 'score'
+seriesFilters[3][1][db]: test_name
+seriesFilters[3][1][condition]: = 'unixbench'
+seriesFilters[3]_all: true
+name[4]: iozone (32768-4096-fwrite)
+values[4]: iteration_value
+aggregation[4]: AVG
+errorBars[4]: true
+seriesFilters[4][0][db]: iteration_key
+seriesFilters[4][0][condition]: = '32768-4096-fwrite'
+seriesFilters[4][1][db]: test_name
+seriesFilters[4][1][condition]: = 'iozone'
+seriesFilters[4]_all: true
+inverted: kernbench (elapsed)
+plot: Line
+xAxis: kernel
+globalFilter[0][db]: hostname
+globalFilter[0][condition]: = 'my_host'
+globalFilter_all: true
+name[0]: kernbench (elapsed)
+values[0]: iteration_value
+aggregation[0]: AVG
+errorBars[0]: true
+seriesFilters[0][0][db]: iteration_key
+seriesFilters[0][0][condition]: = 'elapsed'
+seriesFilters[0][1][db]: test_name
+seriesFilters[0][1][condition]: = 'kernbench'
+seriesFilters[0]_all: true
+name[1]: dbench (throughput)
+values[1]: iteration_value
+aggregation[1]: AVG
+errorBars[1]: true
+seriesFilters[1][0][db]: iteration_key
+seriesFilters[1][0][condition]: = 'throughput'
+seriesFilters[1][1][db]: test_name
+seriesFilters[1][1][condition]: = 'dbench'
+seriesFilters[1]_all: true
+name[2]: tbench (throughput)
+values[2]: iteration_value
+aggregation[2]: AVG
+errorBars[2]: true
+seriesFilters[2][0][db]: iteration_key
+seriesFilters[2][0][condition]: = 'throughput'
+seriesFilters[2][1][db]: test_name
+seriesFilters[2][1][condition]: = 'tbench'
+seriesFilters[2]_all: true
+name[3]: unixbench (score)
+values[3]: iteration_value
+aggregation[3]: AVG
+errorBars[3]: true
+seriesFilters[3][0][db]: iteration_key
+seriesFilters[3][0][condition]: = 'score'
+seriesFilters[3][1][db]: test_name
+seriesFilters[3][1][condition]: = 'unixbench'
+seriesFilters[3]_all: true
+name[4]: iozone (32768-4096-fwrite)
+values[4]: iteration_value
+aggregation[4]: AVG
+errorBars[4]: true
+seriesFilters[4][0][db]: iteration_key
+seriesFilters[4][0][condition]: = '32768-4096-fwrite'
+seriesFilters[4][1][db]: test_name
+seriesFilters[4][1][condition]: = 'iozone'
+seriesFilters[4]_all: true
+inverted: kernbench (elapsed)
diff --git a/frontend/tko/preconfigs/qual/pre b/frontend/tko/preconfigs/qual/pre
new file mode 100644
index 0000000..b3b5ea3
--- /dev/null
+++ b/frontend/tko/preconfigs/qual/pre
@@ -0,0 +1,27 @@
+globalFilter[0][db]: hostname
+globalFilter[0][condition]: LIKE 'my_host%'
+globalFilter[1][db]: hostname
+globalFilter[1][condition]: LIKE 'my_other_host%'
+globalFilter_all: false
+testFilter[0][db]: test_name
+testFilter[0][condition]: = 'my_test_name'
+testFilter_all: true
+interval: 10
+globalFilter[0][db]: hostname
+globalFilter[0][condition]: LIKE 'my_host%'
+globalFilter[1][db]: hostname
+globalFilter[1][condition]: LIKE 'my_other_host%'
+globalFilter_all: false
+testFilter[0][db]: test_name
+testFilter[0][condition]: = 'my_test_name'
+testFilter_all: true
+interval: 10
+globalFilter[0][db]: hostname
+globalFilter[0][condition]: LIKE 'my_host%'
+globalFilter[1][db]: hostname
+globalFilter[1][condition]: LIKE 'my_other_host%'
+globalFilter_all: false
+testFilter[0][db]: test_name
+testFilter[0][condition]: = 'my_test_name'
+testFilter_all: true
+interval: 10
diff --git a/new_tko/tko/rpc_interface.py b/frontend/tko/rpc_interface.py
similarity index 98%
rename from new_tko/tko/rpc_interface.py
rename to frontend/tko/rpc_interface.py
index 719e9773..c7194ac 100644
--- a/new_tko/tko/rpc_interface.py
+++ b/frontend/tko/rpc_interface.py
@@ -3,8 +3,8 @@
 from autotest_lib.frontend import thread_local
 from autotest_lib.frontend.afe import rpc_utils, model_logic
 from autotest_lib.frontend.afe import readonly_connection
-from autotest_lib.new_tko.tko import models, tko_rpc_utils, graphing_utils
-from autotest_lib.new_tko.tko import preconfigs
+from autotest_lib.frontend.tko import models, tko_rpc_utils, graphing_utils
+from autotest_lib.frontend.tko import preconfigs
 
 # table/spreadsheet view support
 
@@ -415,7 +415,8 @@
     result['group_fields'] = sorted(group_fields)
     result['all_fields'] = sorted(model_fields + extra_fields)
     result['test_labels'] = get_test_labels(sort_by=['name'])
-    result['current_user'] = {'login' : thread_local.get_user()}
+    result['current_user'] = rpc_utils.prepare_for_serialization(
+            thread_local.get_user().get_object_dict())
     result['benchmark_key'] = benchmark_key
     result['tko_perf_view'] = tko_perf_view
     result['tko_test_view'] = model_fields
diff --git a/new_tko/tko/rpc_interface_unittest.py b/frontend/tko/rpc_interface_unittest.py
old mode 100755
new mode 100644
similarity index 99%
rename from new_tko/tko/rpc_interface_unittest.py
rename to frontend/tko/rpc_interface_unittest.py
index 104b608..4cc2eb1
--- a/new_tko/tko/rpc_interface_unittest.py
+++ b/frontend/tko/rpc_interface_unittest.py
@@ -2,11 +2,11 @@
 
 import re, unittest
 import common
-from autotest_lib.new_tko import setup_django_environment
+from autotest_lib.frontend import setup_django_environment
 from autotest_lib.frontend import setup_test_environment
 from autotest_lib.client.common_lib.test_utils import mock
 from django.db import connection
-from autotest_lib.new_tko.tko import models, rpc_interface
+from autotest_lib.frontend.tko import models, rpc_interface
 
 # this will need to be updated when the view changes for the test to be
 # consistent with reality
diff --git a/new_tko/tko/tko_rpc_utils.py b/frontend/tko/tko_rpc_utils.py
similarity index 98%
rename from new_tko/tko/tko_rpc_utils.py
rename to frontend/tko/tko_rpc_utils.py
index 063e28a..b702c63 100644
--- a/new_tko/tko/tko_rpc_utils.py
+++ b/frontend/tko/tko_rpc_utils.py
@@ -1,6 +1,6 @@
 from autotest_lib.frontend.afe import rpc_utils
 from autotest_lib.client.common_lib import kernel_versions
-from autotest_lib.new_tko.tko import models
+from autotest_lib.frontend.tko import models
 
 class TooManyRowsError(Exception):
     """
diff --git a/frontend/tko/urls.py b/frontend/tko/urls.py
new file mode 100644
index 0000000..3eba01e
--- /dev/null
+++ b/frontend/tko/urls.py
@@ -0,0 +1,15 @@
+from django.conf.urls import defaults
+import common
+from autotest_lib.frontend import settings, urls_common
+
+urlpatterns, debug_patterns = (
+        urls_common.generate_patterns('frontend.tko', 'TkoClient'))
+
+urlpatterns += defaults.patterns(
+        '',
+        (r'^(?:|noauth/)jsonp_rpc/', 'frontend.tko.views.handle_jsonp_rpc'),
+        (r'^(?:|noauth/)csv/', 'frontend.tko.views.handle_csv'),
+        (r'^(?:|noauth/)plot/', 'frontend.tko.views.handle_plot'))
+
+if settings.DEBUG:
+    urlpatterns += debug_patterns
diff --git a/new_tko/tko/views.py b/frontend/tko/views.py
similarity index 89%
rename from new_tko/tko/views.py
rename to frontend/tko/views.py
index 2499242..2b29c9f 100644
--- a/new_tko/tko/views.py
+++ b/frontend/tko/views.py
@@ -1,5 +1,6 @@
 import django.http
-from autotest_lib.new_tko.tko import rpc_interface, graphing_utils, csv_encoder
+from autotest_lib.frontend.tko import rpc_interface, graphing_utils
+from autotest_lib.frontend.tko import csv_encoder
 from autotest_lib.frontend.afe import rpc_handler, rpc_utils
 
 rpc_handler_obj = rpc_handler.RpcHandler((rpc_interface,),
diff --git a/frontend/urls.py b/frontend/urls.py
index 189a515..14c1dfc 100644
--- a/frontend/urls.py
+++ b/frontend/urls.py
@@ -6,6 +6,7 @@
 admin.autodiscover()
 
 RE_PREFIX = '^' + settings.URL_PREFIX
+TKO_RE_PREFIX = '^' + settings.TKO_URL_PREFIX
 PLANNER_RE_PREFIX = '^' + settings.PLANNER_URL_PREFIX
 
 handler500 = 'frontend.afe.views.handler500'
@@ -14,6 +15,7 @@
         '',
         (RE_PREFIX + r'admin/(.*)', admin.site.root),
         (RE_PREFIX, defaults.include('frontend.afe.urls')),
+        (TKO_RE_PREFIX, defaults.include('frontend.tko.urls')),
         (PLANNER_RE_PREFIX, defaults.include('frontend.planner.urls')),
     )
 
diff --git a/global_config.ini b/global_config.ini
index c559412..6948fff 100644
--- a/global_config.ini
+++ b/global_config.ini
@@ -1,17 +1,3 @@
-[TKO]
-host: localhost
-database: tko
-db_type: mysql
-user: autotest
-password: please_set_this_password
-readonly_host: localhost
-readonly_user: nobody
-readonly_password:
-query_timeout: 3600
-min_retry_delay: 20
-max_retry_delay: 60
-graph_cache_creation_timeout_minutes: 10
-
 [AUTOTEST_WEB]
 host: localhost
 database: autotest_web
@@ -23,6 +9,20 @@
 parse_failed_repair_default: 0
 # Only set this if your server is not 'http://[SERVER] hostname/afe/'
 #base_url: http://your_autotest_server/afe/
+readonly_host: localhost
+readonly_user: nobody
+readonly_password:
+query_timeout: 3600
+min_retry_delay: 20
+max_retry_delay: 60
+graph_cache_creation_timeout_minutes: 10
+
+[TKO]
+host: localhost
+database: tko
+db_type: mysql
+user: autotest
+password: please_set_this_password
 
 [AUTOSERV]
 # Autotest potential install paths
diff --git a/new_tko/common.py b/new_tko/common.py
deleted file mode 100644
index 9941b19..0000000
--- a/new_tko/common.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import os, sys
-dirname = os.path.dirname(sys.modules[__name__].__file__)
-autotest_dir = os.path.abspath(os.path.join(dirname, ".."))
-client_dir = os.path.join(autotest_dir, "client")
-sys.path.insert(0, client_dir)
-import setup_modules
-sys.path.pop(0)
-setup_modules.setup(base_path=autotest_dir, root_module_name="autotest_lib")
diff --git a/new_tko/manage.py b/new_tko/manage.py
deleted file mode 100755
index faf9daf..0000000
--- a/new_tko/manage.py
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env python
-import common
-from django.core.management import execute_manager
-try:
-    import settings # Assumed to be in the same directory.
-except ImportError:
-    import sys
-    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
-    sys.exit(1)
-
-if __name__ == "__main__":
-    execute_manager(settings)
diff --git a/new_tko/settings.py b/new_tko/settings.py
deleted file mode 100644
index 7289653..0000000
--- a/new_tko/settings.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# Django settings for new_tko project.
-
-import common
-from autotest_lib.client.common_lib import global_config
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
-    # ('Your Name', 'your_email@domain.com'),
-)
-
-MANAGERS = ADMINS
-
-DATABASE_ENGINE = 'mysql'      # 'postgresql_psycopg2', 'postgresql',
-                               # 'mysql', 'sqlite3' or 'ado_mssql'.
-DATABASE_PORT = ''             # Set to empty string for default.
-                               # Not used with sqlite3.
-
-c = global_config.global_config
-_section = 'TKO'
-DATABASE_HOST = c.get_config_value(_section, "host")
-# Or path to database file if using sqlite3.
-DATABASE_NAME = c.get_config_value(_section, "database")
-# The following not used with sqlite3.
-DATABASE_USER = c.get_config_value(_section, "user")
-DATABASE_PASSWORD = c.get_config_value(_section, "password", default='')
-
-DATABASE_READONLY_HOST = c.get_config_value(_section, "readonly_host",
-                                            default=DATABASE_HOST)
-DATABASE_READONLY_USER = c.get_config_value(_section, "readonly_user",
-                                            default=DATABASE_USER)
-if DATABASE_READONLY_USER != DATABASE_USER:
-    DATABASE_READONLY_PASSWORD = c.get_config_value(_section,
-                                                    "readonly_password",
-                                                    default='')
-else:
-    DATABASE_READONLY_PASSWORD = DATABASE_PASSWORD
-
-# prefix applied to all URLs - useful if requests are coming through apache,
-# and you need this app to coexist with others
-URL_PREFIX = 'new_tko/server/'
-
-# Local time zone for this installation. Choices can be found here:
-# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
-# although not all variations may be possible on all operating systems.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-TIME_ZONE = 'America/Los_Angeles'
-
-# Language code for this installation. All choices can be found here:
-# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
-# http://blogs.law.harvard.edu/tech/stories/storyReader$15
-LANGUAGE_CODE = 'en-us'
-
-SITE_ID = 1
-
-# If you set this to False, Django will make some optimizations so as not
-# to load the internationalization machinery.
-USE_I18N = True
-
-# Absolute path to the directory that holds media.
-# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = ''
-
-# URL that handles the media served from MEDIA_ROOT.
-# Example: "http://media.lawrence.com"
-MEDIA_URL = ''
-
-# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
-# trailing slash.
-# Examples: "http://foo.com/media/", "/media/".
-ADMIN_MEDIA_PREFIX = '/media/'
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = 't5h^o%emw-1#ga4tvcb82)8irk^w5kyy348osow#$=&3c%60@r'
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.load_template_source',
-    'django.template.loaders.app_directories.load_template_source',
-#     'django.template.loaders.eggs.load_template_source',
-)
-
-MIDDLEWARE_CLASSES = (
-    'django.middleware.common.CommonMiddleware',
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'autotest_lib.frontend.apache_auth.GetApacheUserMiddleware',
-    'django.middleware.doc.XViewMiddleware',
-)
-
-ROOT_URLCONF = 'new_tko.urls'
-
-TEMPLATE_DIRS = (
-    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
-    # Always use forward slashes, even on Windows.
-    # Don't forget to use absolute paths, not relative paths.
-)
-
-INSTALLED_APPS = (
-    'new_tko.tko',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.sites',
-)
diff --git a/new_tko/tko/common.py b/new_tko/tko/common.py
deleted file mode 100644
index 1edf302..0000000
--- a/new_tko/tko/common.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import os, sys
-dirname = os.path.dirname(sys.modules[__name__].__file__)
-autotest_dir = os.path.abspath(os.path.join(dirname, '..', '..'))
-client_dir = os.path.join(autotest_dir, "client")
-sys.path.insert(0, client_dir)
-import setup_modules
-sys.path.pop(0)
-setup_modules.setup(base_path=autotest_dir, root_module_name="autotest_lib")
diff --git a/new_tko/tko/preconfigs.py b/new_tko/tko/preconfigs.py
deleted file mode 100644
index 7bb4660..0000000
--- a/new_tko/tko/preconfigs.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import os
-
-class PreconfigManager(object):
-    _preconfigs = {}
-    _is_init = False
-
-    def _get_preconfig_path(self, suffix):
-        """\
-        Get the absolute path to a prefix directory or file.
-
-        suffix: list of suffixes after the 'preconfigs' directory to navigate to
-            E.g., ['metrics', 'abc'] gives the path to
-            <tko>/preconfigs/metrics/abc
-        """
-        rel_path = os.path.join(os.path.dirname(__file__), 'preconfigs',
-                                *suffix)
-        return os.path.abspath(rel_path)
-
-
-    def _init_preconfigs(self):
-        """\
-        Read the names of all the preconfigs from disk and store them in the
-        _preconfigs dictionary.
-        """
-        if not self._is_init:
-            # Read the data
-            self._preconfigs['metrics'] = dict.fromkeys(
-                os.listdir(self._get_preconfig_path(['metrics'])))
-            self._preconfigs['qual'] = dict.fromkeys(
-                os.listdir(self._get_preconfig_path(['qual'])))
-            self._is_init = True
-
-    def _read_preconfig(self, name, type):
-        """\
-        Populate the _preconfigs dictionary entry for the preconfig described
-        by the given parameters.  If the preconfig has already been loaded,
-        do nothing.
-
-        name: specific name of the preconfig
-        type: 'metrics' or 'qual'
-        """
-        if self._preconfigs[type][name] is not None:
-            return
-
-        self._preconfigs[type][name] = {}
-        path = self._get_preconfig_path([type, name])
-        config = open(path)
-        try:
-            for line in config:
-                parts = line.split(':')
-                self._preconfigs[type][name][parts[0]] = parts[1].strip()
-        finally:
-            config.close()
-
-
-    def get_preconfig(self, name, type):
-        self._init_preconfigs()
-        self._read_preconfig(name, type)
-        return self._preconfigs[type][name]
-
-
-    def all_preconfigs(self):
-        self._init_preconfigs()
-        return dict(self._preconfigs)
-
-
-manager = PreconfigManager()
diff --git a/new_tko/tko/preconfigs/metrics/kernel_compare b/new_tko/tko/preconfigs/metrics/kernel_compare
deleted file mode 100644
index 7035abb..0000000
--- a/new_tko/tko/preconfigs/metrics/kernel_compare
+++ /dev/null
@@ -1,23 +0,0 @@
-plot: Bar
-xAxis: test_name
-globalFilter[0][db]: test_name
-globalFilter[0][condition]: IN ('dbench', 'tbench')
-globalFilter[1][db]: iteration_key
-globalFilter[1][condition]: = 'throughput'
-globalFilter[2][db]: hostname
-globalFilter[2][condition]: = 'my_host'
-globalFilter_all: true
-name[0]: my kernel 1
-values[0]: iteration_value
-aggregation[0]: AVG
-errorBars[0]: true
-seriesFilters[0][0][db]: kernel
-seriesFilters[0][0][condition]: LIKE 'my_kernel_1.%'
-seriesFilters[0]_all: true
-name[1]: my kernel 2
-values[1]: iteration_value
-aggregation[1]: AVG
-errorBars[1]: true
-seriesFilters[1][0][db]: kernel
-seriesFilters[1][0][condition]: LIKE 'my_kernel_2.%'
-seriesFilters[1]_all: true
diff --git a/new_tko/tko/preconfigs/metrics/perf b/new_tko/tko/preconfigs/metrics/perf
deleted file mode 100644
index afbae87..0000000
--- a/new_tko/tko/preconfigs/metrics/perf
+++ /dev/null
@@ -1,51 +0,0 @@
-plot: Line
-xAxis: kernel
-globalFilter[0][db]: hostname
-globalFilter[0][condition]: = 'my_host'
-globalFilter_all: true
-name[0]: kernbench (elapsed)
-values[0]: iteration_value
-aggregation[0]: AVG
-errorBars[0]: true
-seriesFilters[0][0][db]: iteration_key
-seriesFilters[0][0][condition]: = 'elapsed'
-seriesFilters[0][1][db]: test_name
-seriesFilters[0][1][condition]: = 'kernbench'
-seriesFilters[0]_all: true
-name[1]: dbench (throughput)
-values[1]: iteration_value
-aggregation[1]: AVG
-errorBars[1]: true
-seriesFilters[1][0][db]: iteration_key
-seriesFilters[1][0][condition]: = 'throughput'
-seriesFilters[1][1][db]: test_name
-seriesFilters[1][1][condition]: = 'dbench'
-seriesFilters[1]_all: true
-name[2]: tbench (throughput)
-values[2]: iteration_value
-aggregation[2]: AVG
-errorBars[2]: true
-seriesFilters[2][0][db]: iteration_key
-seriesFilters[2][0][condition]: = 'throughput'
-seriesFilters[2][1][db]: test_name
-seriesFilters[2][1][condition]: = 'tbench'
-seriesFilters[2]_all: true
-name[3]: unixbench (score)
-values[3]: iteration_value
-aggregation[3]: AVG
-errorBars[3]: true
-seriesFilters[3][0][db]: iteration_key
-seriesFilters[3][0][condition]: = 'score'
-seriesFilters[3][1][db]: test_name
-seriesFilters[3][1][condition]: = 'unixbench'
-seriesFilters[3]_all: true
-name[4]: iozone (32768-4096-fwrite)
-values[4]: iteration_value
-aggregation[4]: AVG
-errorBars[4]: true
-seriesFilters[4][0][db]: iteration_key
-seriesFilters[4][0][condition]: = '32768-4096-fwrite'
-seriesFilters[4][1][db]: test_name
-seriesFilters[4][1][condition]: = 'iozone'
-seriesFilters[4]_all: true
-inverted: kernbench (elapsed)
diff --git a/new_tko/tko/preconfigs/qual/pre b/new_tko/tko/preconfigs/qual/pre
deleted file mode 100644
index d302346..0000000
--- a/new_tko/tko/preconfigs/qual/pre
+++ /dev/null
@@ -1,9 +0,0 @@
-globalFilter[0][db]: hostname
-globalFilter[0][condition]: LIKE 'my_host%'
-globalFilter[1][db]: hostname
-globalFilter[1][condition]: LIKE 'my_other_host%'
-globalFilter_all: false
-testFilter[0][db]: test_name
-testFilter[0][condition]: = 'my_test_name'
-testFilter_all: true
-interval: 10
diff --git a/new_tko/tko/urls.py b/new_tko/tko/urls.py
deleted file mode 100644
index e36ffa8..0000000
--- a/new_tko/tko/urls.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from django.conf.urls.defaults import *
-import common
-from autotest_lib.frontend import settings, urls_common
-
-pattern_list, debug_pattern_list = (
-        urls_common.generate_patterns(django_name='new_tko.tko',
-                                           gwt_name='TkoClient'))
-
-pattern_list += [(r'^(?:|noauth/)jsonp_rpc/',
-                  'new_tko.tko.views.handle_jsonp_rpc'),
-                 (r'^(?:|noauth/)csv/', 'new_tko.tko.views.handle_csv'),
-                 (r'^(?:|noauth/)plot/', 'new_tko.tko.views.handle_plot')]
-
-if settings.DEBUG:
-    pattern_list += debug_pattern_list
-
-urlpatterns = patterns('', *pattern_list)
diff --git a/new_tko/urls.py b/new_tko/urls.py
deleted file mode 100644
index 2b2e8df..0000000
--- a/new_tko/urls.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import os
-from django.conf.urls.defaults import *
-from django.conf import settings
-# The next two lines enable the admin and load each admin.py file:
-from django.contrib import admin
-admin.autodiscover()
-
-RE_PREFIX = '^' + settings.URL_PREFIX
-
-pattern_list = (
-    (RE_PREFIX + r'admin/(.*)', admin.site.root),
-    (RE_PREFIX, include('new_tko.tko.urls')),
-)
-
-if os.path.exists(os.path.join(os.path.dirname(__file__),
-                               'tko', 'site_urls.py')):
-    pattern_list += ((RE_PREFIX, include('new_tko.tko.site_urls')),)
-
-debug_pattern_list = (
-    # redirect /tko and /results to local apache server
-    (r'^(?P<path>(tko|results)/.*)$',
-     'frontend.afe.views.redirect_with_extra_data',
-     {'url': 'http://%(server_name)s/%(path)s?%(getdata)s'}),
-)
-
-if settings.DEBUG:
-    pattern_list += debug_pattern_list
-
-urlpatterns = patterns('', *pattern_list)
diff --git a/tko/db.py b/tko/db.py
index 536e3cc..c170fe9 100644
--- a/tko/db.py
+++ b/tko/db.py
@@ -43,28 +43,28 @@
         if host:
             self.host = host
         else:
-            self.host = get_value("TKO", "host")
+            self.host = get_value("AUTOTEST_WEB", "host")
         if database:
             self.database = database
         else:
-            self.database = get_value("TKO", "database")
+            self.database = get_value("AUTOTEST_WEB", "database")
 
         # grab the user and password
         if user:
             self.user = user
         else:
-            self.user = get_value("TKO", "user")
+            self.user = get_value("AUTOTEST_WEB", "user")
         if password is not None:
             self.password = password
         else:
-            self.password = get_value("TKO", "password")
+            self.password = get_value("AUTOTEST_WEB", "password")
 
         # grab the timeout configuration
-        self.query_timeout = get_value("TKO", "query_timeout",
+        self.query_timeout = get_value("AUTOTEST_WEB", "query_timeout",
                                        type=int, default=3600)
-        self.min_delay = get_value("TKO", "min_retry_delay", type=int,
+        self.min_delay = get_value("AUTOTEST_WEB", "min_retry_delay", type=int,
                                    default=20)
-        self.max_delay = get_value("TKO", "max_retry_delay", type=int,
+        self.max_delay = get_value("AUTOTEST_WEB", "max_retry_delay", type=int,
                                    default=60)
 
 
@@ -515,7 +515,7 @@
 def _get_db_type():
     """Get the database type name to use from the global config."""
     get_value = global_config.global_config.get_config_value
-    return "db_" + get_value("TKO", "db_type", default="mysql")
+    return "db_" + get_value("AUTOTEST_WEB", "db_type", default="mysql")
 
 
 def _get_error_class(class_name):
diff --git a/new_tko/__init__.py b/tko/migrations/__init__.py
similarity index 100%
rename from new_tko/__init__.py
rename to tko/migrations/__init__.py
diff --git a/utils/modelviz/generate_schema_diagrams.py b/utils/modelviz/generate_schema_diagrams.py
index 8a4551a..42b644b 100755
--- a/utils/modelviz/generate_schema_diagrams.py
+++ b/utils/modelviz/generate_schema_diagrams.py
@@ -11,7 +11,7 @@
 
 ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
 PROJECTS = (
-        ('new_tko', 'tko'),
+        ('frontend', 'tko'),
         ('frontend', 'afe'),
     )