Use Django models to store variables

- Remove dependency on withsqlite and use Django models.
  This avoids depending on a module that is not available in PyPi.
  Withsqlite does not have Python3 support. It does not work when
  we choose a different database backend. Atleast partly duplicates
  what Django models are meant for.

- Check and update database schema on every run so that
  newly added modules can add tables and old ones can update.
This commit is contained in:
Sunil Mohan Adapa 2014-09-10 12:28:09 +05:30
parent 2bbaa11c96
commit 42d05bfe1f
8 changed files with 119 additions and 67 deletions

View File

@ -13,7 +13,7 @@ actions_dir = /usr/share/plinth/actions
doc_dir = /usr/share/doc/plinth
# file locations
store_file = %(data_dir)s/store.sqlite3
store_file = %(data_dir)s/plinth.sqlite3
status_log_file = %(log_dir)s/status.log
access_log_file = %(log_dir)s/access.log
pidfile = %(pid_dir)s/plinth.pid

View File

@ -13,7 +13,7 @@ actions_dir = %(file_root)s/actions
doc_dir = %(file_root)s/doc
# file locations
store_file = %(data_dir)s/store.sqlite3
store_file = %(data_dir)s/plinth.sqlite3
status_log_file = %(log_dir)s/status.log
access_log_file = %(log_dir)s/access.log
pidfile = %(pid_dir)s/plinth.pid

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/python
import argparse
import django.conf
@ -160,8 +160,6 @@ def configure_django():
}
}
data_file = os.path.join(cfg.data_dir, 'plinth.sqlite3')
template_directories = module_loader.get_template_directories()
sessions_directory = os.path.join(cfg.data_dir, 'sessions')
django.conf.settings.configure(
@ -170,12 +168,13 @@ def configure_django():
{'BACKEND': 'django.core.cache.backends.dummy.DummyCache'}},
DATABASES={'default':
{'ENGINE': 'django.db.backends.sqlite3',
'NAME': data_file}},
'NAME': cfg.store_file}},
DEBUG=cfg.debug,
INSTALLED_APPS=['bootstrapform',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.messages'],
'django.contrib.messages',
'plinth'],
LOGGING=logging_configuration,
LOGIN_URL='lib:login',
LOGIN_REDIRECT_URL='apps:index',
@ -199,10 +198,9 @@ def configure_django():
LOGGER.info('Configured Django')
LOGGER.info('Template directories - %s', template_directories)
if not os.path.isfile(data_file):
LOGGER.info('Creating and initializing data file')
django.core.management.call_command('syncdb', interactive=False)
os.chmod(data_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
LOGGER.info('Creating or adding new tables to data file')
django.core.management.call_command('syncdb', interactive=False)
os.chmod(cfg.store_file, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
def main():

View File

@ -70,7 +70,3 @@ def read():
global port # pylint: disable-msg=W0603
port = int(port)
global store_file # pylint: disable-msg=W0603
if store_file.endswith(".sqlite3"):
store_file = os.path.splitext(store_file)[0]

42
plinth/kvstore.py Normal file
View File

@ -0,0 +1,42 @@
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Simple key/value store using Django models
"""
from plinth.models import KVStore
def get(key):
"""Return the value of a key"""
# pylint: disable-msg=E1101
return KVStore.objects.get(pk=key).value
def get_default(key, default_value):
"""Return the value of the key if key exists else return default_value"""
try:
return get(key)
except Exception:
return default_value
def set(key, value): # pylint: disable-msg=W0622
"""Store the value of a key"""
store = KVStore(key=key, value=value)
store.save()

39
plinth/models.py Normal file
View File

@ -0,0 +1,39 @@
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
"""
Django models for the main application
"""
from django.db import models
import json
class KVStore(models.Model):
"""Model to store retrieve key/value configuration"""
key = models.TextField(primary_key=True)
value_json = models.TextField()
@property
def value(self):
"""Return the JSON decoded value of the key/value pair"""
return json.loads(self.value_json)
@value.setter
def value(self, val):
"""Store the value of the key/value pair by JSON encoding it"""
self.value_json = json.dumps(val)

View File

@ -26,10 +26,9 @@ from django.http.response import HttpResponseRedirect
from django.template.response import TemplateResponse
from gettext import gettext as _
from plinth import cfg
from plinth.modules.config import config
from plinth.modules.lib.auth import add_user
from withsqlite.withsqlite import sqlite_db
from plinth import kvstore
## TODO: flesh out these tests values
@ -94,7 +93,7 @@ def state0(request):
user. It's a good place to put error messages.
"""
try:
if _read_state() >= 5:
if kvstore.get_default('firstboot_state', 0) >= 5:
return HttpResponseRedirect(reverse('index'))
except KeyError:
pass
@ -112,7 +111,7 @@ def state0(request):
if success:
# Everything is good, permanently mark and move to page 2
_write_state(1)
kvstore.set('firstboot_state', 1)
return HttpResponseRedirect(reverse('first_boot:state1'))
else:
form = State0Form(initial=status, prefix='firstboot')
@ -124,35 +123,29 @@ def state0(request):
def get_state0():
"""Return the state for form state 0"""
with sqlite_db(cfg.store_file, table="thisbox", autocommit=True) as \
database:
return {'hostname': config.get_hostname(),
'box_key': database.get('box_key', None)}
return {'hostname': config.get_hostname(),
'box_key': kvstore.get_default('box_key', None)}
def _apply_state0(request, old_state, new_state):
"""Apply changes in state 0 form"""
success = True
with sqlite_db(cfg.store_file, table="thisbox", autocommit=True) as \
database:
database['about'] = 'Information about this FreedomBox'
if new_state['box_key']:
database['box_key'] = new_state['box_key']
elif not old_state['box_key']:
database['box_key'] = generate_box_key()
if new_state['box_key']:
kvstore.set('box_key', new_state['box_key'])
elif not old_state['box_key']:
kvstore.set('box_key', generate_box_key())
if old_state['hostname'] != new_state['hostname']:
config.set_hostname(new_state['hostname'])
if old_state['hostname'] != new_state['hostname']:
config.set_hostname(new_state['hostname'])
error = add_user(new_state['username'], new_state['password'],
'First user, please change', '', True)
if error:
messages.error(
request, _('User account creation failed: %s') % error)
success = False
else:
messages.success(request, _('User account created'))
error = add_user(new_state['username'], new_state['password'],
'First user, please change', '', True)
if error:
messages.error(request, _('User account creation failed: %s') % error)
success = False
else:
messages.success(request, _('User account created'))
return success
@ -168,21 +161,7 @@ def state1(request):
"""
# TODO complete first_boot handling
# Make sure the user is not stuck on a dead end for now.
_write_state(5)
kvstore.set('firstboot_state', 5)
return TemplateResponse(request, 'firstboot_state1.html',
{'title': _('Installing the Certificate')})
def _read_state():
"""Read the current state from database"""
with sqlite_db(cfg.store_file, table='firstboot',
autocommit=True) as database:
return database['state']
def _write_state(state):
"""Write state to database"""
with sqlite_db(cfg.store_file, table='firstboot',
autocommit=True) as database:
database['state'] = state

View File

@ -24,8 +24,7 @@ from django.core.urlresolvers import reverse
from django.http.response import HttpResponseRedirect
import logging
from plinth import cfg
from withsqlite.withsqlite import sqlite_db
from plinth import kvstore
LOGGER = logging.getLogger(__name__)
@ -42,14 +41,13 @@ class FirstBootMiddleware(object):
if request.path.startswith(reverse('first_boot:index')):
return
with sqlite_db(cfg.store_file, table='firstboot') as database:
if 'state' not in database:
# Permanent redirect causes the browser to cache the redirect,
# preventing the user from navigating to /plinth until the
# browser is restarted.
return HttpResponseRedirect(reverse('first_boot:index'))
state = kvstore.get_default('firstboot_state', 0)
if not state:
# Permanent redirect causes the browser to cache the redirect,
# preventing the user from navigating to /plinth until the
# browser is restarted.
return HttpResponseRedirect(reverse('first_boot:index'))
if database['state'] < 5:
LOGGER.info('First boot state - %d', database['state'])
return HttpResponseRedirect(reverse('first_boot:state%d' %
database['state']))
if state < 5:
LOGGER.info('First boot state - %d', state)
return HttpResponseRedirect(reverse('first_boot:state%d' % state))