import textwrap from django.utils.translation import gettext_lazy as _ from orchestra.contrib.orchestration import ServiceController, replace from orchestra.contrib.resources import ServiceMonitor from . import settings class MySQLController(ServiceController): """ Simple backend for creating MySQL databases using CREATE DATABASE statement. """ verbose_name = "MySQL database" model = 'databases.Database' default_route_match = "database.type == 'mysql'" doc_settings = (settings, ('DATABASES_DEFAULT_HOST',) ) def save(self, database): if database.type != database.MYSQL: return context = self.get_context(database) # Not available on delete() context['owner'] = database.owner self.append(textwrap.dedent(""" # Create database and re-set permissions mysql -e 'CREATE DATABASE `%(database)s`;' || true mysql mysql -e 'DELETE FROM db WHERE db = "%(database)s";'\ """) % context ) for user in database.users.all(): context.update({ 'username': user.username, 'grant': 'WITH GRANT OPTION' if user == context['owner'] else '' }) if user.permision == "ro": self.append(textwrap.dedent("""\ mysql -e 'GRANT SELECT ON `%(database)s`.* TO "%(username)s"@"%(host)s" %(grant)s;'\ """) % context ) else: self.append(textwrap.dedent("""\ mysql -e 'GRANT ALL PRIVILEGES ON `%(database)s`.* TO "%(username)s"@"%(host)s" %(grant)s;'\ """) % context ) def delete(self, database): if database.type != database.MYSQL: return context = self.get_context(database) self.append(textwrap.dedent(""" # Remove database %(database)s mysql -e 'DROP DATABASE `%(database)s`;' || exit_code=$? mysql mysql -e 'DELETE FROM db WHERE db = "%(database)s";'\ """) % context ) def commit(self): self.append(textwrap.dedent(""" # Apply permissions mysql -e 'FLUSH PRIVILEGES;'\ """) ) super(MySQLController, self).commit() def get_context(self, database): context = { 'database': database.name, 'host': settings.DATABASES_DEFAULT_HOST, } return replace(replace(context, "'", '"'), ';', '') class MySQLUserController(ServiceController): """ Simple backend for creating MySQL users using CREATE USER statement. """ verbose_name = "MySQL user" model = 'databases.DatabaseUser' default_route_match = "databaseuser.type == 'mysql'" doc_settings = (settings, ('DATABASES_DEFAULT_HOST',) ) def save(self, user): if user.type != user.MYSQL: return context = self.get_context(user) self.append(textwrap.dedent("""\ # Create user %(username)s mysql -e 'CREATE USER IF NOT EXISTS "%(username)s"@"%(host)s";' mysql -e 'ALTER USER IF EXISTS "%(username)s"@"%(host)s" IDENTIFIED BY PASSWORD "%(password)s";'\ """) % context ) def delete(self, user): if user.type != user.MYSQL: return context = self.get_context(user) self.append(textwrap.dedent(""" # Delete user %(username)s mysql -e 'DROP USER "%(username)s"@"%(host)s";' || exit_code=$? \ """) % context ) def commit(self): self.append("# Apply permissions") self.append("mysql -e 'FLUSH PRIVILEGES;'") def get_context(self, user): context = { 'username': user.username, 'password': user.password, 'host': settings.DATABASES_DEFAULT_HOST, } return replace(replace(context, "'", '"'), ';', '') class MysqlDisk(ServiceMonitor): """ du -bs <database_path> Implements triggers for resource limit exceeded and recovery, disabling insert and create privileges. """ model = 'databases.Database' verbose_name = _("MySQL disk") delete_old_equal_values = True doc_settings = (settings, ('DATABASES_MYSQL_DB_DIR',) ) mysql_db_dir = settings.DATABASES_MYSQL_DB_DIR def exceeded(self, db): if db.type != db.MYSQL: return context = self.get_context(db) self.append(textwrap.dedent("""\ mysql -e 'UPDATE db SET Insert_priv="N", Create_priv="N" WHERE Db="%(db_name)s";'\ """) % context ) def recovery(self, db): if db.type != db.MYSQL: return context = self.get_context(db) self.append(textwrap.dedent("""\ mysql -e 'UPDATE db SET Insert_priv="Y", Create_priv="Y" WHERE Db="%(db_name)s";'\ """) % context ) def prepare(self): super().prepare() context = { 'mysql_db_dir': self.mysql_db_dir, } self.append(textwrap.dedent("""\ function monitor_mysql () { { SIZE=$(du -bs "%(mysql_db_dir)s/$1") && echo $SIZE || echo 0; } | awk {'print $1'} }""") % context) # Slower way #self.append(textwrap.dedent("""\ # function monitor () { # mysql -B -e " # SELECT IFNULL(sum(data_length + index_length), 0) 'Size' # FROM information_schema.TABLES # WHERE table_schema = '$1'; # " | tail -n 1 # }""")) def monitor(self, db): if db.type != db.MYSQL: return context = self.get_context(db) self.append('echo %(db_id)s $(monitor_%(db_type)s "%(db_dirname)s")' % context) def get_context(self, db): context = { 'db_name': db.name, 'db_dirname': db.name.replace('-', '@002d').replace('.', '@002e'), 'db_id': db.pk, 'db_type': db.type, } return replace(replace(context, "'", '"'), ';', '')