first push message
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
import psycopg2
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
import secrets
|
||||
import string
|
||||
import subprocess
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DatabaseInstance(models.Model):
|
||||
_name = 'database.instance'
|
||||
_description = 'Database Instance'
|
||||
_rec_name = 'database_name'
|
||||
|
||||
subscription_id = fields.Many2one('user.subscription', string='Subscription',
|
||||
required=True, ondelete='cascade')
|
||||
database_name = fields.Char(string='Database Name', required=True, index=True)
|
||||
subdomain = fields.Char(string='Subdomain', required=True)
|
||||
full_domain = fields.Char(string='Full Domain', compute='_compute_full_domain')
|
||||
|
||||
# Database status
|
||||
db_state = fields.Selection([
|
||||
('pending', 'Pending'),
|
||||
('creating', 'Creating'),
|
||||
('installing', 'Installing Apps'),
|
||||
('ready', 'Ready'),
|
||||
('failed', 'Failed'),
|
||||
], string='Status', default='pending', tracking=True)
|
||||
|
||||
# PostgreSQL connection
|
||||
pg_host = fields.Char(string='PostgreSQL Host', default='localhost')
|
||||
pg_port = fields.Integer(string='PostgreSQL Port', default=5432)
|
||||
pg_user = fields.Char(string='Database User')
|
||||
pg_password = fields.Char(string='Database Password')
|
||||
|
||||
# Instance configuration
|
||||
workers = fields.Integer(string='Workers', default=2)
|
||||
timeout = fields.Integer(string='Timeout (seconds)', default=300)
|
||||
memory_limit_mb = fields.Integer(string='Memory Limit (MB)', default=512)
|
||||
max_connections = fields.Integer(string='Max Connections', default=20)
|
||||
|
||||
# Timestamps
|
||||
created_at = fields.Datetime(string='Created At', default=fields.Datetime.now)
|
||||
ready_at = fields.Datetime(string='Ready At')
|
||||
last_accessed = fields.Datetime(string='Last Accessed')
|
||||
|
||||
# Admin credentials
|
||||
admin_login = fields.Char(string='Admin Login')
|
||||
admin_password = fields.Char(string='Admin Password')
|
||||
admin_email = fields.Char(string='Admin Email')
|
||||
|
||||
installed_apps = fields.Text(string='Installed Apps')
|
||||
error_message = fields.Text(string='Error Message')
|
||||
|
||||
@api.depends('subdomain')
|
||||
def _compute_full_domain(self):
|
||||
base_domain = self.env['ir.config_parameter'].sudo().get_param('web.base.domain', 'localhost')
|
||||
for record in self:
|
||||
if record.subdomain:
|
||||
record.full_domain = f"{record.subdomain}.{base_domain}"
|
||||
else:
|
||||
record.full_domain = False
|
||||
|
||||
def generate_unique_subdomain(self, company_name):
|
||||
"""Generate unique subdomain from company name"""
|
||||
import re
|
||||
# Convert to lowercase and remove special characters
|
||||
base = re.sub(r'[^a-z0-9]+', '-', company_name.lower()).strip('-')
|
||||
# Add random string
|
||||
random_str = ''.join(secrets.choice(string.digits) for _ in range(5))
|
||||
subdomain = f"{base}-{random_str}"
|
||||
|
||||
# Ensure uniqueness
|
||||
counter = 1
|
||||
original_subdomain = subdomain
|
||||
while self.search_count([('subdomain', '=', subdomain)]) > 0:
|
||||
subdomain = f"{original_subdomain}-{counter}"
|
||||
counter += 1
|
||||
|
||||
return subdomain
|
||||
|
||||
def generate_password(self, length=12):
|
||||
"""Generate secure random password"""
|
||||
chars = string.ascii_letters + string.digits + "!@#$%^&*"
|
||||
return ''.join(secrets.choice(chars) for _ in range(length))
|
||||
|
||||
def create_database(self):
|
||||
"""Create PostgreSQL database for tenant"""
|
||||
self.ensure_one()
|
||||
|
||||
try:
|
||||
self.db_state = 'creating'
|
||||
self._cr.commit()
|
||||
|
||||
# Generate credentials
|
||||
self.pg_user = f"user_{self.database_name.replace('-', '_')}"
|
||||
self.pg_password = self.generate_password()
|
||||
self.admin_login = 'admin'
|
||||
self.admin_password = self.generate_password()
|
||||
|
||||
# Connect to PostgreSQL
|
||||
conn = psycopg2.connect(
|
||||
host=self.pg_host,
|
||||
port=self.pg_port,
|
||||
database='postgres',
|
||||
user='odoo', # Master PostgreSQL user
|
||||
password='odoo_password' # Should be in config
|
||||
)
|
||||
conn.autocommit = True
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Create database user
|
||||
try:
|
||||
cursor.execute(f"DROP ROLE IF EXISTS {self.pg_user}")
|
||||
cursor.execute(
|
||||
f"CREATE ROLE {self.pg_user} WITH LOGIN PASSWORD %s",
|
||||
(self.pg_password,)
|
||||
)
|
||||
except Exception as e:
|
||||
_logger.error(f"Error creating user: {e}")
|
||||
|
||||
# Create database
|
||||
cursor.execute(f"DROP DATABASE IF EXISTS {self.database_name}")
|
||||
cursor.execute(
|
||||
f"CREATE DATABASE {self.database_name} "
|
||||
f"OWNER {self.pg_user} "
|
||||
f"ENCODING 'UTF8' "
|
||||
f"TEMPLATE template0"
|
||||
)
|
||||
|
||||
# Set permissions
|
||||
cursor.execute(
|
||||
f"GRANT ALL PRIVILEGES ON DATABASE {self.database_name} TO {self.pg_user}"
|
||||
)
|
||||
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
_logger.info(f"Database {self.database_name} created successfully")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.db_state = 'failed'
|
||||
self.error_message = str(e)
|
||||
_logger.error(f"Failed to create database: {e}")
|
||||
return False
|
||||
|
||||
def install_apps(self, app_list):
|
||||
"""Install selected Odoo apps in the database"""
|
||||
self.ensure_one()
|
||||
|
||||
try:
|
||||
self.db_state = 'installing'
|
||||
self.installed_apps = ', '.join(app_list)
|
||||
self._cr.commit()
|
||||
|
||||
# This would typically use Odoo's database provisioning API
|
||||
# For now, we'll simulate it
|
||||
import odoo
|
||||
from odoo import api, SUPERUSER_ID
|
||||
|
||||
# Initialize new database
|
||||
registry = odoo.modules.registry.Registry.new(
|
||||
self.database_name,
|
||||
force_demo=False,
|
||||
signal=False,
|
||||
update_module=True
|
||||
)
|
||||
|
||||
with api.Environment.manage(), registry.cursor() as cr:
|
||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||
|
||||
# Install selected modules
|
||||
for module_name in app_list:
|
||||
try:
|
||||
module = env['ir.module.module'].search([('name', '=', module_name)])
|
||||
if module:
|
||||
module.button_immediate_install()
|
||||
_logger.info(f"Installed module: {module_name}")
|
||||
except Exception as e:
|
||||
_logger.warning(f"Failed to install {module_name}: {e}")
|
||||
|
||||
# Create admin user
|
||||
env['res.users'].create({
|
||||
'name': 'Administrator',
|
||||
'login': self.admin_login,
|
||||
'password': self.admin_password,
|
||||
'email': self.admin_email,
|
||||
'groups_id': [(6, 0, [env.ref('base.group_system').id])],
|
||||
})
|
||||
|
||||
self.db_state = 'ready'
|
||||
self.ready_at = fields.Datetime.now()
|
||||
_logger.info(f"Database {self.database_name} is ready")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.db_state = 'failed'
|
||||
self.error_message = str(e)
|
||||
_logger.error(f"Failed to install apps: {e}")
|
||||
return False
|
||||
|
||||
def configure_instance(self):
|
||||
"""Configure system instance parameters"""
|
||||
self.ensure_one()
|
||||
|
||||
# Update odoo.conf or use environment variables
|
||||
config_params = {
|
||||
'workers': self.workers,
|
||||
'timeout': self.timeout,
|
||||
'limit_memory_hard': self.memory_limit_mb * 1024 * 1024,
|
||||
'max_cron_threads': 1,
|
||||
}
|
||||
|
||||
# These would be applied at the Odoo server level
|
||||
_logger.info(f"Configuring instance {self.database_name}: {config_params}")
|
||||
return True
|
||||
Reference in New Issue
Block a user