diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | README.md | 28 | ||||
| -rw-r--r-- | main.py | 5 | ||||
| -rw-r--r-- | opendc/api/v1/users/endpoint.py | 2 | ||||
| -rw-r--r-- | opendc/models/machine.py | 8 | ||||
| -rw-r--r-- | opendc/models/machine_state.py | 6 | ||||
| -rw-r--r-- | opendc/models/model.py | 11 | ||||
| -rw-r--r-- | opendc/models/rack_state.py | 6 | ||||
| -rw-r--r-- | opendc/models/room_state.py | 6 | ||||
| -rw-r--r-- | opendc/models/task_duration.py | 2 | ||||
| -rw-r--r-- | opendc/models/task_state.py | 2 | ||||
| -rw-r--r-- | opendc/models/user.py | 4 | ||||
| -rw-r--r-- | opendc/util/database.py | 22 | ||||
| -rw-r--r-- | setup.py | 41 |
14 files changed, 97 insertions, 47 deletions
@@ -12,3 +12,4 @@ _mailinglist .cache/ .idea/ config.json +test.json @@ -46,12 +46,11 @@ The following steps will guide you through setting up the OpenDC web server loca Make sure you have Python 2.7 installed (if not, get it [here](https://www.python.org/)), as well as pip (if not, get it [here](https://pip.pypa.io/en/stable/installing/)). Then run the following to install the requirements. ```bash -pip install flask -pip install flask_socketio -pip install oauth2client -pip install eventlet +python setup.py install ``` +The web server also requires MariaDB >= 10.1. Instructions to install MariaDB can be found [here](https://mariadb.com/kb/en/mariadb/getting-installing-and-upgrading-mariadb/). The Docker image can be found [here](https://hub.docker.com/_/mariadb/). + #### Get the code Clone both this repository and the main OpenDC repository, from the same base directory. @@ -63,12 +62,7 @@ git clone https://github.com/atlarge-research/opendc.git #### Set up the database -Set up the database, replacing `PATH_TO_DATABASE` with where you'd like to create the SQLite database. (This will replace any file named `opendc.db` at the location `PATH_TO_DATABASE`.) - -```bash -cd opendc/database -python rebuild-database.py "PATH_TO_DATABASE" -``` +The database can be rebuilt by using the `schema.sql` file from main opendc repository. #### Configure OpenDC @@ -76,18 +70,22 @@ Create a file `config.json` in `opendc-web-server`, containing: ```json { - "ROOT_DIR": "BASE_DIRECTORY", - "OAUTH_CLIENT_ID": "OAUTH_CLIENT_ID", - "DATABASE_LOCATION": "PATH_TO_DATABASE\\opendc.db", - "FLASK_SECRET": "FLASK_SECRET" + "ROOT_DIR": "BASE_DIRECTORY", + "OAUTH_CLIENT_ID": "OAUTH_CLIENT_ID", + "FLASK_SECRET": "FLASK_SECRET", + "MYSQL_DATABASE": "opendc", + "MYSQL_USER": "opendc", + "MYSQL_PASSWORD": "opendcpassword", + "MYSQL_HOST": "127.0.0.1", + "MYSQL_PORT": 3306 } ``` Make the following replacements: * Replace `BASE_DIRECTORY` with the base directory in which you cloned `opendc` and `opendc-web-server`. * Replace `OAUTH_CLIENT_ID` with your OAuth client ID (see the [OpenDC README](https://github.com/atlarge-research/opendc#preamble)). -* Replace `PATH_TO_DATABASE` with where you created the database. * Replace `FLASK_SECRET`, come up with some string. +* Replace the `MYSQL_*` variables with the correct settings for accessing the MariaDB database that was just created. In `opendc-web-server/static/index.html`, add your own `OAUTH_CLIENT_ID` in `content=` on line `2`. @@ -10,7 +10,7 @@ import flask_socketio from oauth2client import client, crypt from opendc.models.user import User -from opendc.util import exceptions, rest, path_parser +from opendc.util import exceptions, rest, path_parser, database if len(sys.argv) < 2: print "config file path not given as argument" @@ -22,6 +22,9 @@ with open(sys.argv[1]) as file: STATIC_ROOT = os.path.join(KEYS['ROOT_DIR'], 'opendc-frontend', 'build') +database.init_connection_pool(user=KEYS['MYSQL_USER'], password=KEYS['MYSQL_PASSWORD'], \ + database=KEYS['MYSQL_DATABASE'], host=KEYS['MYSQL_HOST'], port=KEYS['MYSQL_PORT']) + FLASK_CORE_APP = Flask(__name__, static_url_path='') FLASK_CORE_APP.config['SECREY_KEY'] = KEYS['FLASK_SECRET'] diff --git a/opendc/api/v1/users/endpoint.py b/opendc/api/v1/users/endpoint.py index 7e381ed5..1c971b56 100644 --- a/opendc/api/v1/users/endpoint.py +++ b/opendc/api/v1/users/endpoint.py @@ -52,7 +52,7 @@ def POST(request): return Response(400, e.message) # Instantiate a User - + request.params_body['user']['googleId'] = request.google_id user = User.from_JSON(request.params_body['user']) diff --git a/opendc/models/machine.py b/opendc/models/machine.py index 54838ffd..90945ef1 100644 --- a/opendc/models/machine.py +++ b/opendc/models/machine.py @@ -44,14 +44,14 @@ class Machine(Model): # First, delete current machine-device links - statement = 'DELETE FROM machine_{} WHERE machine_id = ?'.format(device_table) + statement = 'DELETE FROM machine_{} WHERE machine_id = %s'.format(device_table) database.execute(statement, (before_insert.id,)) # Then, add current ones for device_id in getattr(before_insert, before_insert.device_table_to_attribute[device_table]): - statement = 'INSERT INTO machine_{} (machine_id, {}) VALUES (?, ?)'.format( + statement = 'INSERT INTO machine_{} (machine_id, {}) VALUES (%s, %s)'.format( device_table, before_insert.device_table_to_attribute[device_table][:-1] ) @@ -68,7 +68,7 @@ class Machine(Model): return cls(id = -1) try: - statement = 'SELECT id FROM machines WHERE rack_id = ? AND position = ?' + statement = 'SELECT id FROM machines WHERE rack_id = %s AND position = %s' machine_id = database.fetchone(statement, (rack.id, position))[0] except: return cls(id = -1) @@ -106,7 +106,7 @@ class Machine(Model): for device_table in self.device_table_to_attribute.keys(): - statement = 'SELECT * FROM machine_{} WHERE machine_id = ?'.format(device_table) + statement = 'SELECT * FROM machine_{} WHERE machine_id = %s'.format(device_table) results = database.fetchall(statement, (self.id,)) device_ids = [] diff --git a/opendc/models/machine_state.py b/opendc/models/machine_state.py index 44bc6746..693b57d2 100644 --- a/opendc/models/machine_state.py +++ b/opendc/models/machine_state.py @@ -38,7 +38,7 @@ class MachineState(Model): machine_states = [] - statement = 'SELECT * FROM machine_states WHERE experiment_id = ?' + statement = 'SELECT * FROM machine_states WHERE experiment_id = %s' results = database.fetchall(statement, (experiment_id,)) for row in results: @@ -52,7 +52,7 @@ class MachineState(Model): machine_states = [] - statement = 'SELECT * FROM machine_states WHERE experiment_id = ? AND machine_states.tick = ?' + statement = 'SELECT * FROM machine_states WHERE experiment_id = %s AND machine_states.tick = %s' results = database.fetchall(statement, (experiment_id, tick)) for row in results: @@ -65,7 +65,7 @@ class MachineState(Model): super(MachineState, self).read() - statement = 'SELECT tick FROM task_states WHERE id = ?' + statement = 'SELECT tick FROM task_states WHERE id = %s' result = database.fetchone(statement, (self.task_state_id,)) self.tick = result[0] diff --git a/opendc/models/model.py b/opendc/models/model.py index e8a14e76..2507a287 100644 --- a/opendc/models/model.py +++ b/opendc/models/model.py @@ -109,20 +109,20 @@ class Model(object): def _generate_insert_placeholders_string(cls): """Generate a SQLite insertion placeholders string for this Model.""" - return ', '.join(['?'] * len(cls.COLUMNS)) + return ', '.join(['%s'] * len(cls.COLUMNS)) @classmethod def _generate_primary_key_string(cls): """Generate the SQLite primary key string for this Model.""" - return ' AND '.join(['{} = ?'.format(x) for x in cls.COLUMNS_PRIMARY_KEY]) + return ' AND '.join(['{} = %s'.format(x) for x in cls.COLUMNS_PRIMARY_KEY]) @classmethod def _generate_update_columns_string(cls): """Generate a SQLite updatable columns string for this Model.""" return ', '.join( - ['{} = ?'.format(x) for x in cls.COLUMNS if not x in cls.COLUMNS_PRIMARY_KEY] + ['{} = %s'.format(x) for x in cls.COLUMNS if not x in cls.COLUMNS_PRIMARY_KEY] ) # SQL TUPLE GENERATION METHODS @@ -207,7 +207,7 @@ class Model(object): """Return all instances of the Model in the database where column_name = value.""" if column_name is not None and value is not None: - statement = 'SELECT * FROM {} WHERE {} = ?'.format(cls.TABLE_NAME, column_name) + statement = 'SELECT * FROM {} WHERE {} = %s'.format(cls.TABLE_NAME, column_name) database_models = database.fetchall(statement, (value,)) else: @@ -264,7 +264,7 @@ class Model(object): else: query = query.format( self.TABLE_NAME, - '{} = ?'.format(column) + '{} = %s'.format(column) ) values = (getattr(self, column),) @@ -292,6 +292,7 @@ class Model(object): try: last_row_id = database.execute(statement, values) except Exception as e: + print e raise exceptions.ForeignKeyError(e.message) if 'id' in self.COLUMNS_PRIMARY_KEY: diff --git a/opendc/models/rack_state.py b/opendc/models/rack_state.py index 77cafd95..e43dc940 100644 --- a/opendc/models/rack_state.py +++ b/opendc/models/rack_state.py @@ -32,7 +32,7 @@ class RackState(Model): FROM racks JOIN machines ON racks.id = machines.rack_id JOIN machine_states ON machines.id = machine_states.machine_id - WHERE machine_states.experiment_id = ? + WHERE machine_states.experiment_id = %s GROUP BY machine_states.tick, racks.id ''' results = database.fetchall(statement, (experiment_id,)) @@ -53,8 +53,8 @@ class RackState(Model): FROM racks JOIN machines ON racks.id = machines.rack_id JOIN machine_states ON machines.id = machine_states.machine_id - WHERE machine_states.experiment_id = ? - AND machine_states.tick = ? + WHERE machine_states.experiment_id = %s + AND machine_states.tick = %s GROUP BY machine_states.tick, racks.id ''' results = database.fetchall(statement, (experiment_id, tick)) diff --git a/opendc/models/room_state.py b/opendc/models/room_state.py index 7dd151e6..169aaa55 100644 --- a/opendc/models/room_state.py +++ b/opendc/models/room_state.py @@ -36,7 +36,7 @@ class RoomState(Model): JOIN machines ON racks.id = machines.rack_id JOIN machine_states ON machines.id = machine_states.machine_id WHERE objects.type = "RACK" - AND machine_states.experiment_id = ? + AND machine_states.experiment_id = %s GROUP BY machine_states.tick, rooms.id ''' results = database.fetchall(statement, (experiment_id,)) @@ -61,8 +61,8 @@ class RoomState(Model): JOIN machines ON racks.id = machines.rack_id JOIN machine_states ON machines.id = machine_states.machine_id WHERE objects.type = "RACK" - AND machine_states.experiment_id = ? - AND machine_states.tick = ? + AND machine_states.experiment_id = %s + AND machine_states.tick = %s GROUP BY rooms.id ''' results = database.fetchall(statement, (experiment_id, tick)) diff --git a/opendc/models/task_duration.py b/opendc/models/task_duration.py index 31334040..0d3432e3 100644 --- a/opendc/models/task_duration.py +++ b/opendc/models/task_duration.py @@ -27,7 +27,7 @@ class TaskDuration(Model): statement = ''' SELECT task_id, MAX(tick) - MIN(tick) as duration FROM task_states - WHERE experiment_id = ? + WHERE experiment_id = %s GROUP BY task_id ''' diff --git a/opendc/models/task_state.py b/opendc/models/task_state.py index e9b5b63f..7d216aa0 100644 --- a/opendc/models/task_state.py +++ b/opendc/models/task_state.py @@ -25,7 +25,7 @@ class TaskState(Model): task_states = [] - statement = 'SELECT * FROM task_states WHERE experiment_id = ? AND tick = ?' + statement = 'SELECT * FROM task_states WHERE experiment_id = %s AND tick = %s' results = database.fetchall(statement, (experiment_id, tick)) for row in results: diff --git a/opendc/models/user.py b/opendc/models/user.py index 9c716470..885170d0 100644 --- a/opendc/models/user.py +++ b/opendc/models/user.py @@ -22,7 +22,7 @@ class User(Model): def from_google_id(cls, google_id): """Initialize a User by fetching them by their google id.""" - user = cls._from_database('SELECT * FROM users WHERE google_id = ?', (google_id,)) + user = cls._from_database('SELECT * FROM users WHERE google_id = %s', (google_id,)) if user is not None: return user @@ -33,7 +33,7 @@ class User(Model): def from_email(cls, email): """Initialize a User by fetching them by their email.""" - user = cls._from_database('SELECT * FROM users WHERE email = ?', (email,)) + user = cls._from_database('SELECT * FROM users WHERE email = %s', (email,)) if user is not None: return user diff --git a/opendc/util/database.py b/opendc/util/database.py index 2ef8b982..337f8fc7 100644 --- a/opendc/util/database.py +++ b/opendc/util/database.py @@ -3,40 +3,46 @@ import json import sqlite3 import sys +from mysql.connector.pooling import MySQLConnectionPool + # Get keys from config file with open(sys.argv[1]) as file: KEYS = json.load(file) DATETIME_STRING_FORMAT = '%Y-%m-%dT%H:%M:%S' +CONNECTION_POOL = None + +def init_connection_pool(user, password, database, host, port): + global CONNECTION_POOL + CONNECTION_POOL = MySQLConnectionPool(pool_name = "opendcpool", pool_size = 5, \ + user=user, password=password, database=database, host=host, port=port) def execute(statement, t): """Open a database connection and execute the statement.""" # Connect to the database - connection = sqlite3.connect(KEYS['DATABASE_LOCATION']) + connection = CONNECTION_POOL.get_connection() cursor = connection.cursor() - - # Turn on foreign key checks - cursor.execute('pragma foreign_keys=ON') # Execute the statement cursor.execute(statement, t) # Get the id - database_id = cursor.execute('SELECT last_insert_rowid()').fetchone()[0] + cursor.execute('SELECT last_insert_id();') + row_id = cursor.fetchone()[0] # Disconnect from the database connection.commit() connection.close() # Return the id - return database_id + return row_id def fetchone(statement, t=None): """Open a database connection and return the first row matched by the SELECT statement.""" # Connect to the database - connection = sqlite3.connect(KEYS['DATABASE_LOCATION']) + connection = CONNECTION_POOL.get_connection() cursor = connection.cursor() # Execute the SELECT statement @@ -56,7 +62,7 @@ def fetchall(statement, t=None): """Open a database connection and return all rows matched by the SELECT statement.""" # Connect to the database - connection = sqlite3.connect(KEYS['DATABASE_LOCATION']) + connection = CONNECTION_POOL.get_connection() cursor = connection.cursor() # Execute the SELECT statement diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..1dd81d5a --- /dev/null +++ b/setup.py @@ -0,0 +1,41 @@ +from setuptools import setup +from codecs import open +from os import path + +# Get the long description from the README file +here = path.abspath(path.dirname(__file__)) +with open(path.join(here, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='opendc-web-server', + version='0.1.0', + + description='Python web server for the OpenDC project', + long_description=long_description, + + url='http://opendc.org', + + author='The OpenDC team', + author_email='opendc@atlarge-research.com', + + license='MIT', + + classifiers=[ + 'License :: OSI Approved :: MIT License', + + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + ], + + keywords='opendc datacenter simulation web-server', + + packages=['opendc'], + + install_requires=[ + 'flask', + 'flask_socketio', + 'oauth2client', + 'eventlet', + ], +) |
