Source code for stoq.gui.config

# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4

##
## Copyright (C) 2006-2011 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation; either version 2 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 Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
## Author(s): Stoq Team <stoq-devel@async.com.br>
##
##

"""First time installation wizard for Stoq

Stoq Configuration dialogs

Current flow of the database steps:

  * :obj:`DatabaseLocationStep`

    * :obj:`DatabaseSettingsStep` (if network database)

      * :obj:`PostgresAdminPasswordStep`
      * :obj:`FinishInstallationStep` (if an installed database was found)

  * :obj:`InstallationModeStep`
  * :obj:`CreateDatabaseStep`
  * :obj:`LinkStep`
  * :obj:`FinishInstallationStep`
"""

import imp
import logging
import platform
import subprocess
import sys

import glib
import gtk
from kiwi.component import provide_utility, get_utility
from kiwi.datatypes import ValidationError
from kiwi.python import Settable
from kiwi.ui.delegates import GladeSlaveDelegate

from stoqlib.api import api
from stoqlib.exceptions import DatabaseInconsistency
from stoqlib.database.admin import (USER_ADMIN_DEFAULT_NAME,
                                    create_default_profile_settings)
from stoqlib.database.interfaces import (ICurrentBranchStation,
                                         ICurrentBranch)
from stoqlib.database.migration import needs_schema_update
from stoqlib.database.settings import (DatabaseSettings, test_local_database,
                                       validate_database_name,
                                       check_extensions, get_database_version)
from stoqlib.domain.person import LoginUser
from stoqlib.domain.station import BranchStation
from stoqlib.domain.system import SystemTable
from stoqlib.exceptions import DatabaseError
from stoqlib.gui.base.dialogs import run_dialog
from stoqlib.gui.base.wizards import (BaseWizard, WizardEditorStep,
                                      WizardStep)
from stoqlib.gui.slaves.userslave import PasswordEditorSlave
from stoqlib.gui.utils.openbrowser import open_browser
from stoqlib.gui.widgets.processview import ProcessView
from stoqlib.lib.configparser import StoqConfig
from stoqlib.lib.interfaces import ICookieFile
from stoqlib.lib.kiwilibrary import library
from stoqlib.lib.message import error, warning, yesno
from stoqlib.lib.osutils import read_registry_key
from stoqlib.lib.pgpass import write_pg_pass
from stoqlib.lib.threadutils import schedule_in_main_thread
from stoqlib.lib.validators import validate_email, validate_phone_number
from stoqlib.lib.webservice import WebService
from stoqlib.lib.translation import stoqlib_gettext as _
from stoqlib.net.socketutils import get_hostname

from stoq.gui.update import SchemaUpdateWizard
from stoq.lib.options import get_option_parser
from stoq.lib.startup import setup

logger = logging.getLogger(__name__)

is_windows = platform.system() == 'Windows'

WINDOWS_DEFAULT_DBNAME = 'stoq'
WINDOWS_DEFAULT_USER = 'postgres'
WINDOWS_DEFAULT_PASSWORD = 'postgres'

#
# Wizard Steps
#

(TRUST_AUTHENTICATION,
 PASSWORD_AUTHENTICATION) = range(2)


[docs]class BaseWizardStep(WizardStep, GladeSlaveDelegate): """A wizard step base class definition""" domain = 'stoq' gladefile = None def __init__(self, wizard, previous=None): logger.info('Entering step: %s' % self.__class__.__name__) self.wizard = wizard WizardStep.__init__(self, previous) GladeSlaveDelegate.__init__(self, gladefile=self.gladefile)
[docs]class DatabaseLocationStep(BaseWizardStep): gladefile = 'DatabaseLocationStep'
[docs] def post_init(self): self.radio_local.grab_focus()
[docs] def next_step(self): self.wizard.db_is_local = self.radio_local.get_active() # If we're not connecting to a local, ask for the # connection settings if not self.wizard.db_is_local: return DatabaseSettingsStep(self.wizard, self) settings = self.wizard.settings settings.dbname = "stoq" if is_windows: settings.address = "localhost" settings.username = WINDOWS_DEFAULT_USER settings.port = self.wizard.get_win32_postgres_port() return PostgresAdminPasswordStep(self.wizard, self) else: settings.address = "" # Unix socket really return self.wizard.connect_for_settings(self)
[docs] def on_radio_local__activate(self, radio): self.wizard.go_to_next()
[docs] def on_radio_network__activate(self, radio): self.wizard.go_to_next()
[docs]class DatabaseSettingsStep(WizardEditorStep): gladefile = 'DatabaseSettingsStep' model_type = DatabaseSettings proxy_widgets = ('address', 'port', 'username', 'password', 'dbname') def __init__(self, wizard, previous, focus_dbname=True): self.focus_dbname = focus_dbname WizardEditorStep.__init__(self, None, wizard, wizard.settings, previous) self._update_widgets() def _update_widgets(self): selected = self.authentication_type.get_selected_data() need_password = selected == PASSWORD_AUTHENTICATION self.password.set_sensitive(need_password) self.passwd_label.set_sensitive(need_password) # # WizardStep hooks #
[docs] def post_init(self): self.register_validate_function(self.wizard.refresh_next) self.force_validation() if self.focus_dbname: self.dbname.grab_focus() else: self.address.grab_focus()
[docs] def validate_step(self): if not self.model.check_database_address(): msg = _("The database address '%s' is invalid. " "Please fix it and try again") % self.model.address warning(_(u'Invalid database address'), msg) # '' is not strictly invalid, since it's an alias for # unix socket, so don't tell that to the user, make him # belive that he still uses "localhost" if self.model.address != "": self.address.set_invalid(_("Invalid database address")) self.force_validation() return False self.wizard.write_pgpass() settings = self.wizard.settings # If we configured setting to localhost, try connecting # with address == '', eg unix socket first before trying # to connect to localhost. This is done because the default # postgres configuration doesn't allow you to connect via localhost, # only unix socket. if settings.address == 'localhost': if not self.wizard.try_connect(settings, warn=False): settings.address = '' if not self.wizard.try_connect(settings): # Restore it settings.address = 'localhost' return False if settings.address == '': # Reload settings as they changed self.wizard.config.load_settings(settings) self.wizard.auth_type = self.authentication_type.get_selected() return True
[docs] def setup_proxies(self): self.authentication_type.prefill([ (_("Needs Password"), PASSWORD_AUTHENTICATION), (_("Trust"), TRUST_AUTHENTICATION)]) self.add_proxy(self.model, DatabaseSettingsStep.proxy_widgets) # Show localhost instead of empty for unix socket, not strictly # correct but better than showing nothing. if not self.model.address: self.address.set_text("localhost") self.model.stoq_user_data = Settable(password='') self.add_proxy(self.model.stoq_user_data)
[docs] def next_step(self): if self.wizard.has_installed_db: return FinishInstallationStep(self.wizard) elif self.wizard.check_incomplete_database(): self.dbname.grab_focus() return self else: return InstallationModeStep(self.wizard, self)
# # Callbacks #
[docs] def on_authentication_type__content_changed(self, *args): self._update_widgets()
[docs] def on_dbname__validate(self, widget, value): if not validate_database_name(value): return ValidationError(_('%s is not a valid database name') % value)
[docs]class InstallationModeStep(BaseWizardStep): gladefile = "InstallationModeStep" model_type = object
[docs] def post_init(self): self.empty_database_radio.grab_focus()
[docs] def next_step(self): self.wizard.create_examples = not self.empty_database_radio.get_active() if self.wizard.db_is_local and not test_local_database(): return InstallPostgresStep(self.wizard, self) else: return CreateDatabaseStep(self.wizard, self)
[docs] def on_empty_database_radio__activate(self, radio): self.wizard.go_to_next()
[docs] def on_example_database_radio__activate(self, radio): self.wizard.go_to_next()
[docs]class LinkStep(WizardEditorStep): """Stoq link registration step""" gladefile = 'LinkStep' model_type = Settable proxy_widgets = ['name', 'email', 'phone', 'register_now'] def __init__(self, wizard, previous): model = Settable(name='', email='', phone='', register_now=True) WizardEditorStep.__init__(self, None, wizard, model, previous) self._setup_widgets() # # Private API # def _setup_widgets(self): if is_windows: # Make it mandatory to register on windows. self.register_now.hide() self.wizard.disable_cancel() self.wizard.disable_back() self.image.set_from_file( library.get_resource_filename('stoq', 'pixmaps', 'link_step.png')) self.image_eventbox.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.POINTER_MOTION_MASK) self.send_progress.hide() self.send_error_label.hide() def _pulse(self): self.send_progress.pulse() if self.wizard.link_request_done: self.send_progress.set_fraction(1.0) self.send_progress.set_text(_("Done!")) return False return True def _cancel_request(self): if not self.wizard.link_request_done: self._show_error() return False def _show_error(self): self.wizard.link_request_done = True self.send_progress.hide() self.send_error_label.show() self.wizard.next_button.set_sensitive(True) def _update_widgets(self): for widget in [self.email, self.name]: widget.set_property('mandatory', self.model.register_now) self.force_validation() def _inside_button(self, event): x, y = event.get_coords() return 115 < x < 280 and 290 < y < 315 # # WizardStep #
[docs] def post_init(self): self.register_validate_function(self.wizard.refresh_next) self.name.grab_focus() self._update_widgets()
[docs] def setup_proxies(self): self.add_proxy(self.model, self.proxy_widgets)
[docs] def next_step(self): # We already sent the details, but may still be on the same step. # Also, if the user didn't choose to "register now", respect his # decision if not self.model.register_now or self.wizard.link_request_done: return FinishInstallationStep(self.wizard) webapi = WebService() webapi.link_registration( self.model.name, self.model.email, self.model.phone, callback=lambda r: schedule_in_main_thread(self._on_response_done, r), errback=lambda e: schedule_in_main_thread(self._on_response_error, e)) self.send_progress.show() self.send_progress.set_text(_('Sending...')) self.send_progress.set_pulse_step(0.05) self.wizard.next_button.set_sensitive(False) glib.timeout_add(50, self._pulse) # Cancel the request after 30 seconds without a reply glib.timeout_add(30000, self._cancel_request) # Stay on the same step while sending the details return self
# # Callbacks #
[docs] def on_image_eventbox__motion_notify_event(self, widget, event): w = widget.get_window() if self._inside_button(event): cursor = gtk.gdk.Cursor(gtk.gdk.HAND2) else: cursor = w.get_parent().get_property('cursor') w.set_cursor(cursor)
[docs] def on_image_eventbox__button_press_event(self, widget, event): if self._inside_button(event): url = 'https://stoq.link?source=stoqwizard&hash={}' user_hash = api.sysparam.get_string('USER_HASH') open_browser(url.format(user_hash), self.wizard)
[docs] def on_register_now__toggled(self, widget): self.wizard.enable_online_services = widget.get_active() self._update_widgets()
[docs] def on_email__validate(self, widget, value): if not value: return if not validate_email(value): return ValidationError(_('%s is not a valid email') % value)
[docs] def on_phone__validate(self, widget, value): if not value: return if not validate_phone_number(value): return ValidationError(_('%s is not a valid phone') % value)
[docs] def on_phone__activate(self, widget): if self.wizard.next_button.get_sensitive(): self.wizard.go_to_next()
def _on_response_done(self, response): if response.status_code != 200: self._show_error() return details = response.json() if details['message'] != 'client_instance_created': self._show_error() return if not self.wizard.link_request_done: self.wizard.link_request_done = True self.wizard.next_button.set_sensitive(True) def _on_response_error(self, err): self._show_error()
[docs]class PasswordStep(BaseWizardStep): gladefile = 'AdminPasswordStep' def __init__(self, wizard, previous): BaseWizardStep.__init__(self, wizard, previous) self.description_label.set_markup( self.get_description_label()) self.title_label.set_markup(self.get_title_label()) self.setup_slaves()
[docs] def get_slave(self): return PasswordEditorSlave(None)
# # WizardStep hooks #
[docs] def setup_slaves(self): self.password_slave = self.get_slave() self.attach_slave("password_holder", self.password_slave)
[docs] def post_init(self): self.register_validate_function(self.wizard.refresh_next) self.force_validation() self.password_slave.password.grab_focus()
[docs]class PostgresAdminPasswordStep(PasswordStep): """ Ask a password for posgres administration user. """ title_label = _("PostgresSQL 'postgres' password")
[docs] def get_title_label(self): return '<b>%s</b>' % _("PostgreSQL password")
[docs] def get_description_label(self): return _("To be able to create a new Stoq database you need to enter " "the password of the <b>postgres</b> user for the PostgreSQL " "database we're installing to.\n\n" "This is the same password you were asked when you installed " "PostgreSQL on this computer.")
[docs] def get_slave(self): return PasswordEditorSlave(None, confirm_password=False)
# # WizardStep hooks #
[docs] def next_step(self): self.wizard.settings.password = str(self.password_slave.model.new_password) self.wizard.auth_type = PASSWORD_AUTHENTICATION self.wizard.write_pgpass() return self.wizard.connect_for_settings(self)
[docs]class InstallPostgresStep(BaseWizardStep): """Since we are going to sell the TEF funcionality, we cant enable the plugin right away. Just ask for some user information and we will contact. """ gladefile = 'InstallPostgresStep' def __init__(self, wizard, previous): self.done = False BaseWizardStep.__init__(self, wizard, previous) forward_label = '<b>%s</b>' % (_("Forward"), ) if self._can_install(): self.description.props.label += ( "\n\n" + _("The installation guide will now install the packages for you " "using apt, it may ask you for your password to continue.")) # Translators: %s is the string "Forward" label = _("Click %s to begin installing the " "PostgreSQL server.") % ( forward_label, ) else: # Translators: %s is the string "Forward" label = _("Click %s to continue when you have installed " "PostgreSQL server on this computer.") % ( forward_label, ) self.label.set_markup(label) # # WizardStep #
[docs] def next_step(self): if self.done or test_local_database(): return CreateDatabaseStep(self.wizard, self) if self._can_install(): self._install_postgres() else: warning(_("You need to install PostgreSQL before moving forward")) return self
# # Private # def _can_install(self): # We cannot import gtk3widgets here as gtk3 will raise an error if # gtk2 is already imported on the system try: aptdaemon = imp.find_module('aptdaemon') imp.find_module('gtk3widgets', [aptdaemon[1]]) except ImportError: return False return True def _install_postgres(self): self.wizard.disable_back() self.wizard.disable_next() self.label.set_markup( _("Please wait while the package installation is completing.")) packageinstaller = library.get_resource_filename( 'stoq', 'scripts', 'packageinstaller.py') p = subprocess.Popen( [sys.executable, packageinstaller, 'postgresql', 'postgresql-contrib', 'stoq-server'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() self.wizard.enable_back() if p.returncode == 0: self.done = True self.wizard.enable_next() self.label.set_markup( _("Postgresql installation succeeded. You may now proceed " "to the next step by clicking the <b>Forward</b> button")) elif p.returncode == 11: self.wizard.enable_next() self.label.set_markup( _("Authorization failed, try again or connect to " "another database")) else: warning(_("Something went wrong while trying to install " "the PostgreSQL server.")) self.label.set_markup( _("Sorry, something went wrong while installing PostgreSQL, " "try again manually or go back and configure Stoq to connect " "to another."))
[docs]class CreateDatabaseStep(BaseWizardStep): gladefile = 'CreateDatabaseStep'
[docs] def post_init(self): self.n_patches = 0 self.process_view = ProcessView() self.process_view.listen_stderr = True self.process_view.connect('read-line', self._on_processview__readline) self.process_view.connect('finished', self._on_processview__finished) self.expander.add(self.process_view) self.expander.grab_focus() self._maybe_create_database()
[docs] def next_step(self): return LinkStep(self.wizard, self)
def _maybe_create_database(self): logger.info('_maybe_create_database (db_is_local=%s, enable_production=%s)' % (self.wizard.db_is_local, self.wizard.enable_production)) if self.wizard.db_is_local: self._launch_stoqdbadmin() return elif self.wizard.enable_production: self._launch_stoqdbadmin() return self.wizard.write_pgpass() settings = self.wizard.settings self.wizard.config.load_settings(settings) store = settings.create_super_store() version = get_database_version(store) if version < (9, 1): store.close() error(_("Stoq requires PostgresSQL 9.1 or later, but %s found") % ( ".".join(map(str, version)))) try: check_extensions(store=store) except ValueError: store.close() error(_("Missing PostgreSQL extension on the server, " "please install postgresql-contrib on it")) store.close() self.process_view.feed("** Creating database\r\n") self._launch_stoqdbadmin() def _launch_stoqdbadmin(self): logger.info('_launch_stoqdbadmin') self.wizard.disable_back() self.wizard.disable_next() if sys.argv[0].endswith('.egg'): args = [sys.executable, sys.argv[0]] elif platform.system() == 'Windows': if library.uninstalled: args = ['stoq.bat'] else: args = ['stoq-cmd.exe'] else: args = ['stoq'] args.extend([ 'dbadmin', 'init', '--no-load-config', '--no-register-station', '-v']) if self.wizard.create_examples: args.append('--demo') elif self.wizard.enable_production: args.append('--force') if self.wizard.db_is_local: args.append('--create-dbuser') dbargs = self.wizard.settings.get_command_line_arguments() args.extend(dbargs) self.label.set_label( _("Creating a new database for Stoq, depending on the speed of " "your computer and the server it may take a couple of " "minutes to finish.")) self.progressbar.set_text(_("Creating database...")) self.progressbar.set_fraction(0.05) logger.info(' '.join(args)) self.process_view.execute_command(args) self.done_label.set_markup( _("Please wait while the database is being created.")) def _parse_process_line(self, line): LOG_CATEGORY = 'stoqlib.database.create' log_pos = line.find(LOG_CATEGORY) if log_pos == -1: return line = line[log_pos + len(LOG_CATEGORY) + 1:] if line == 'SCHEMA': value = 0.1 text = _("Creating base schema...") elif line.startswith('PATCHES:'): value = 0.35 self.n_patches = int(line.split(':', 1)[1]) text = _("Creating schema, applying patches...") elif line.startswith('PATCH:'): # 0.4 - 0.7 patches patch = float(line.split(':', 1)[1]) value = 0.4 + (patch / self.n_patches) * 0.3 text = _("Creating schema, applying patch %d ...") % (patch + 1, ) elif line == 'INIT START': text = _("Creating additional database objects ...") value = 0.8 elif line == 'INIT DONE' and self.wizard.create_examples: text = _("Creating examples ...") value = 0.85 elif line.startswith('PLUGIN'): text = _("Activating plugins ...") if 'nfe' in self.wizard.plugins: text += ' ' + _('This may take some time.') value = 0.95 else: return self.progressbar.set_fraction(value) self.progressbar.set_text(text) def _finish(self, returncode): logger.info('CreateDatabaseStep._finish (returncode=%s)' % returncode) if returncode: self.wizard.enable_back() # Failed to execute/create database if returncode == 30: # This probably happened because the user either; # - pressed cancel in the authentication popup # - user erred the password 3 times # Allow him to try again if yesno(_("Something went wrong while trying to create " "the database. Try again?"), gtk.RESPONSE_NO, _("Change settings"), _("Try again")): return self._launch_stoqdbadmin() return elif returncode == 31: # Missing postgresql-contrib package self.expander.set_expanded(True) warning(_("Your database is missing the postgresql-contrib " "package. Install it and try again")) else: # Unknown error, just inform user that something went wrong. self.expander.set_expanded(True) warning(_("Something went wrong while trying to create " "the Stoq database")) return self.label.set_text("") self.wizard.load_config_and_call_setup() create_default_profile_settings() self.progressbar.set_text(_("Done.")) self.progressbar.set_fraction(1.0) self.wizard.enable_next() self.done_label.set_markup( _("Installation successful, click <b>Forward</b> to continue.")) # Callbacks def _on_processview__readline(self, view, line): self._parse_process_line(line) def _on_processview__finished(self, view, returncode): self._finish(returncode)
[docs]class FinishInstallationStep(BaseWizardStep): gladefile = 'FinishInstallationStep'
[docs] def has_next_step(self): return False
[docs] def post_init(self): # replaces the cancel button with a quit button self.wizard.cancel_button.set_label(gtk.STOCK_QUIT) # self._cancel will be a callback for the quit button self.wizard.cancel = self._cancel self.wizard.next_button.set_label(_(u'Run Stoq')) self.wizard.next_button.grab_focus()
def _cancel(self): # This is the last step, so we will finish the installation # before we quit self.wizard.finish(run=False)
# # Main wizard #
[docs]class FirstTimeConfigWizard(BaseWizard): title = _("Stoq - Installation") size = (711, 400) # 16:9 proportion def __init__(self, options, config=None): if not config: config = StoqConfig() self.settings = config.get_settings() if is_windows: self.settings.username = WINDOWS_DEFAULT_USER self.settings.password = WINDOWS_DEFAULT_PASSWORD self.settings.dbname = WINDOWS_DEFAULT_DBNAME self.settings.address = "localhost" self.settings.port = 5432 self.link_request_done = False self.create_examples = False self.config = config self.enable_production = False self.has_installed_db = False self.options = options self.db_is_local = False self.enable_online_services = True self.auth_type = TRUST_AUTHENTICATION if config.get('Database', 'enable_production') == 'True': self.enable_production = True if self.enable_production: first_step = CreateDatabaseStep(self) elif is_windows and self.try_connect(self.settings, warn=False): self.auth_type = PASSWORD_AUTHENTICATION self.write_pgpass() first_step = self.connect_for_settings() else: first_step = DatabaseLocationStep(self) BaseWizard.__init__(self, None, first_step, title=self.title) self.get_toplevel().set_deletable(False) self.next_button.grab_focus() def _create_station(self, store): # FIXME: This is fishy, we can probably simplify this significantly by # allowing users to connect to the initial database without # having a branch station nor branch registered. # The whole BranchStation/Branch creation is weird, it should # be done at the same place. logger.info('_create_station') if self.create_examples: branch = api.sysparam.get_object(store, 'MAIN_COMPANY') assert branch provide_utility(ICurrentBranch, branch) else: branch = None station_name = get_hostname() if store.find(BranchStation, branch=branch, name=station_name).one(): return station = BranchStation(store=store, is_active=True, branch=branch, name=station_name) provide_utility(ICurrentBranchStation, station) def _set_admin_password(self, store): logger.info('_set_admin_password') adminuser = store.find(LoginUser, username=USER_ADMIN_DEFAULT_NAME).one() if adminuser is None: raise DatabaseInconsistency( ("You should have a user with username: %s" % USER_ADMIN_DEFAULT_NAME)) # Lets create a user without password and set a cookie so that it # auto login adminuser.set_password(u'') get_utility(ICookieFile).store('admin', '') def _set_online_services(self, store): logger.info('_set_online_services (%s)' % self.enable_online_services) api.sysparam.set_bool(store, 'ONLINE_SERVICES', self.enable_online_services) # Public API
[docs] def get_win32_postgres_port(self): for v in ['9.2', '9.1', '9.0', '8.4']: key = read_registry_key( 'HKLM', r'Software\PostgreSQL\Services\postgresql-%s' % (v, ), 'Port') if key: return key # Default port return 5432
[docs] def try_connect(self, settings, warn=True): logger.info('try_connect (warn=%s)' % (warn)) logger.info('settings: address=%s username=%s, dbname=%s' % ( settings.address, settings.username, settings.dbname)) self.config.load_settings(settings) try: if settings.has_database(): store = settings.create_store() self.has_installed_db = SystemTable.is_available(store) store.close() except DatabaseError as e: if warn: warning(e.short, str(e.msg)) logger.info('Failed to connect') return False logger.info('Connected') return True
[docs] def check_incomplete_database(self): logger.info('check_incomplete_database (db_is_local=%s)' % (self.db_is_local, )) # If we don't have postgres installed we cannot have # an incomplete database if self.db_is_local and not test_local_database(): return False # a database which doesn't exist isn't incomplete try: if not self.settings.has_database(): return False except DatabaseError as e: # If we're install stoq locally and hasn't created a database # user yet, we'll receive an authentiction error, there's no # way to reliably check for this, so assume all errors are # authentication errors # first install on 9.1: FATAL: role "stoq" does not exist. if self.db_is_local: return False msg = (_('It was not possible to connect to the database.') + '\n' + _('Check the server configuration and try again.')) warning(msg, str(e)) return True # If we have the SystemTable we are pretty much there, # could verify a few more tables in the future, including # row content of the tables. store = self.settings.create_store() if SystemTable.is_available(store): store.close() return False store.close() # okay, we have a database which exists and doesn't have # the "SystemTable" SQL table present, means that we cannot use # it and should warn the user # Not 100% correct, should perhaps say "unix socket" address = self.settings.address or "localhost" msg = _("Database {dbname} at {address}:{port} is not " "a Stoq database.").format( dbname=self.settings.dbname, address=address, port=self.settings.port) description = _( "Stoq was able to succesfully connect to the database " "{dbname} at the database server {address}, however it " "is not a Stoq database or it was corrupted, please select " "another one.").format(dbname=self.settings.dbname, address=self.settings.address or "localhost") warning(msg, description) return True
[docs] def load_config_and_call_setup(self): dbargs = self.settings.get_command_line_arguments() parser = get_option_parser() db_options, unused_args = parser.parse_args(dbargs) self.config.set_from_options(db_options) if needs_schema_update(): retval = run_dialog(SchemaUpdateWizard, None) if not retval: raise SystemExit() try: setup(self.config, options=self.options, check_schema=True, register_station=False, load_plugins=False) except DatabaseInconsistency as err: error(_('The database version differs from your installed ' 'version.'), str(err))
[docs] def connect_for_settings(self, step=None): # Try to connect, we don't care if we can connect, # we just want to know if it's properly installed self.try_connect(self.settings, warn=False) # Corrupted or a non-Stoq database if self.check_incomplete_database(): self.settings.dbname = "" return DatabaseSettingsStep(self, step, focus_dbname=True) if self.has_installed_db: return FinishInstallationStep(self) return InstallationModeStep(self, step)
[docs] def write_pgpass(self): # Only save password if using password authentication if self.auth_type != PASSWORD_AUTHENTICATION: return write_pg_pass(self.settings.dbname, self.settings.address, self.settings.port, self.settings.username, self.settings.password)
# # WizardStep hooks #
[docs] def cancel(self, *args): raise SystemExit
[docs] def finish(self, run=True): if self.has_installed_db: self.load_config_and_call_setup() else: # Commit data created during the wizard, such as stations store = api.new_store() self._set_admin_password(store) self._create_station(store) self._set_online_services(store) store.commit() # Write configuration to disk if self.enable_production: self.config.remove('Database', 'enable_production') self.config.flush() self.close()