|
@@ -1,10 +1,24 @@
|
|
|
# -*- coding: utf-8 -*-
|
|
|
from openerp import models, fields, api
|
|
|
+from openerp.tools import email_send
|
|
|
from openerp.exceptions import Warning, ValidationError
|
|
|
from jinja2 import Environment as JinjaEnv, PackageLoader as JinjaLoader
|
|
|
-from ..utils.docker_api import get_all_external_ports
|
|
|
-from ..utils.command import execute
|
|
|
+from ..utils.docker_api import (
|
|
|
+ get_all_external_ports,
|
|
|
+ run_command,
|
|
|
+ copy_in,
|
|
|
+ run_container,
|
|
|
+ get_internal_ip,
|
|
|
+ get_status,
|
|
|
+ stop_container,
|
|
|
+ start_container,
|
|
|
+ restart_container
|
|
|
+)
|
|
|
+from ..utils.command import execute, list_files_and_folders
|
|
|
from random import randint
|
|
|
+from git.util import join_path
|
|
|
+from git.repo.base import Repo
|
|
|
+from git.exc import GitCommandError
|
|
|
import unicodedata
|
|
|
import stringcase
|
|
|
import time
|
|
@@ -50,27 +64,28 @@ class OdooInstance(models.Model):
|
|
|
if len(name) == 0:
|
|
|
return False
|
|
|
|
|
|
- root_path = self.env['ir.values'].get_default(self._name, 'odoo_root_path')
|
|
|
+ root_path = self.env['odoo.management.config'].get_default_settings([]).get('odoo_root_path')
|
|
|
|
|
|
if len(root_path) == 0:
|
|
|
raise Warning('La ruta por defecto para los sistemas no está definido')
|
|
|
|
|
|
full_path = os.path.join(root_path, name)
|
|
|
|
|
|
- return os.path.exists(full_path)
|
|
|
+ return not os.path.exists(full_path)
|
|
|
|
|
|
def _randomize_port(self):
|
|
|
- ports = self.env['ir.values'].get_default(self._name, 'odoo_ports_range')
|
|
|
+ ports = self.env['odoo.management.config'].get_default_settings([]).get('odoo_ports_range')
|
|
|
ports = filter(lambda x: x != '', ports.split(','))
|
|
|
|
|
|
if len(ports) == 0:
|
|
|
- raise Warning('El rango de puertos para los sistemas no está definido')
|
|
|
+ ports = [10000, 20000]
|
|
|
+ # raise Warning('El rango de puertos para los sistemas no está definido')
|
|
|
|
|
|
port = 0
|
|
|
|
|
|
while not self._check_port_availability(port):
|
|
|
port = randint(ports[0], ports[1])
|
|
|
- time.sleep(1)
|
|
|
+ time.sleep(0.05)
|
|
|
|
|
|
return port
|
|
|
|
|
@@ -80,7 +95,11 @@ class OdooInstance(models.Model):
|
|
|
for d in self.DEFAULT_DIRS:
|
|
|
full_path = os.path.join(root_path, name, d.strip())
|
|
|
os.makedirs(full_path)
|
|
|
- time.sleep(1)
|
|
|
+
|
|
|
+ def _delete_dirs(self, name):
|
|
|
+ root_path = self.env['ir.values'].get_default(self._name, 'odoo_root_path')
|
|
|
+ full_path = os.path.join(root_path, name)
|
|
|
+ execute(['rm', '-Rf', full_path])
|
|
|
|
|
|
def _make_config_file(self, name):
|
|
|
root_path = self.env['ir.values'].get_default(self._name, 'odoo_root_path')
|
|
@@ -106,9 +125,159 @@ class OdooInstance(models.Model):
|
|
|
def _create_database(self, name):
|
|
|
odoo_db = self.env['ir.values'].get_default(self._name, 'odoo_db_link')
|
|
|
cmd = 'createdb -U %s %s' % ('odoo', name)
|
|
|
+ output = run_command(odoo_db, cmd)
|
|
|
+
|
|
|
+ return output
|
|
|
+
|
|
|
+ def _drop_database(self, name):
|
|
|
+ odoo_db = self.env['ir.values'].get_default(self._name, 'odoo_db_link')
|
|
|
+
|
|
|
+ terminate_cmd = "psql -U %s select pg_terminate_backend(pid) from pg_stats where datname = '%s'" % ('odoo', name)
|
|
|
+ run_command(odoo_db, terminate_cmd)
|
|
|
+
|
|
|
+ drop_cmd = 'drop database %s' % name
|
|
|
+ run_command(odoo_db, drop_cmd)
|
|
|
+
|
|
|
+ def _copy_database_seed(self):
|
|
|
+ odoo_db = self.env['ir.values'].get_default(self._name, 'odoo_db_link')
|
|
|
+ backup_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'files', 'odoo.tar')
|
|
|
+ output = copy_in(odoo_db, '/tmp', backup_path)
|
|
|
+
|
|
|
+ return output
|
|
|
+
|
|
|
+ def _restore_database(self, name):
|
|
|
+ odoo_db = self.env['ir.values'].get_default(self._name, 'odoo_db_link')
|
|
|
+ cmd = 'psql -U %s -d %s -f %s' % (odoo_db, name, '/tmp/odoo.sql')
|
|
|
+ output = run_command(odoo_db, cmd)
|
|
|
+
|
|
|
+ return output
|
|
|
+
|
|
|
+ def _remove_database_seed(self):
|
|
|
+ odoo_db = self.env['ir.values'].get_default(self._name, 'odoo_db_link')
|
|
|
+ cmd = 'rm -f %s' % '/tmp/odoo.sql'
|
|
|
+ output = run_command(odoo_db, cmd)
|
|
|
+
|
|
|
+ return output
|
|
|
+
|
|
|
+ def _create_container(self, name, ports=[]):
|
|
|
+ ir_values = self.env['ir.values']
|
|
|
+
|
|
|
+ root_path = ir_values.get_default(self._name, 'odoo_root_path')
|
|
|
+ odoo_docker_image = ir_values.get_default(self._name, 'odoo_docker_image')
|
|
|
+ odoo_network = ir_values.get_default(self._name, 'odoo_network')
|
|
|
+
|
|
|
+ ports = dict(map(lambda p: ('%d/tcp' % 8069, p), ports))
|
|
|
+ volumes = dict()
|
|
|
+
|
|
|
+ run_container(
|
|
|
+ odoo_docker_image,
|
|
|
+ name,
|
|
|
+ ports,
|
|
|
+ volumes,
|
|
|
+ odoo_network,
|
|
|
+ '150m',
|
|
|
+ '150m'
|
|
|
+ )
|
|
|
+
|
|
|
+ time.sleep(3)
|
|
|
+
|
|
|
+ return True
|
|
|
+
|
|
|
+ def _apply_permissions(self, name):
|
|
|
+ root_path = self.env['ir.values'].get_default(self._name, 'odoo_root_path')
|
|
|
+ full_path = os.path.join(root_path, name)
|
|
|
+ execute(['chmod', '-Rf', '777'], full_path)
|
|
|
+
|
|
|
+ return True
|
|
|
+
|
|
|
+ def _get_internal_ip(self, name):
|
|
|
+ return get_internal_ip(name)
|
|
|
+
|
|
|
+ def _clone_repositories(self, names=[], path=None, branch='master'):
|
|
|
+ if len(names) == 0 or not path:
|
|
|
+ return False
|
|
|
+
|
|
|
+ git_path = self.env['ir.values'].get_default(self._name, 'modules_git_path')
|
|
|
+ paths = list_files_and_folders(git_path)
|
|
|
+
|
|
|
+ if len(git_path) == 0:
|
|
|
+ raise Warning('La ruta por defecto del repositorio no está definido')
|
|
|
+
|
|
|
+ for path in paths:
|
|
|
+ for name in names:
|
|
|
+ repo_path = '%s.git' % join_path(git_path, path, name)
|
|
|
+
|
|
|
+ if not os.path.exists(repo_path):
|
|
|
+ continue
|
|
|
+
|
|
|
+ try:
|
|
|
+ repo = Repo(repo_path)
|
|
|
+ repo.clone(join_path(path, name), branch=branch)
|
|
|
+ repo.close()
|
|
|
+ except GitCommandError:
|
|
|
+ pass
|
|
|
+
|
|
|
+ return True
|
|
|
+
|
|
|
+ def _clone_repository(self, name, path):
|
|
|
+ result = self._clone_repositories([name], path)
|
|
|
+
|
|
|
+ return result
|
|
|
+
|
|
|
+ def _copy_modules(self, name, modules=[]):
|
|
|
+ root_path = self.env['ir.values'].get_default(self._name, 'odoo_root_path')
|
|
|
+
|
|
|
+ if not os.path.exists(root_path):
|
|
|
+ raise Warning('El directorio del sistema no existe')
|
|
|
+
|
|
|
+ container_is_running = get_status(name)
|
|
|
+
|
|
|
+ if container_is_running:
|
|
|
+ stopped = stop_container(name)
|
|
|
+
|
|
|
+ if not stopped:
|
|
|
+ raise Warning('No se pudo parar el contenedor')
|
|
|
+
|
|
|
+ for module in modules:
|
|
|
+ module_path = os.path.join(root_path, 'custom-addons', module)
|
|
|
+
|
|
|
+ if os.path.exists(module_path):
|
|
|
+ execute(['rm', '-Rf', module_path])
|
|
|
+
|
|
|
+ addons_path = os.path.join(root_path, 'custom-addons')
|
|
|
+ self._clone_repository(module, addons_path)
|
|
|
|
|
|
- return execute(cmd)
|
|
|
+ git_data_path = os.path.join(module, '.git')
|
|
|
|
|
|
+ if os.path.exists(git_data_path):
|
|
|
+ execute(['rm', '-Rf', git_data_path])
|
|
|
+
|
|
|
+ try:
|
|
|
+ execute(['chmod', '-Rf', '777', module_path])
|
|
|
+ except Exception:
|
|
|
+ pass
|
|
|
+
|
|
|
+ if container_is_running:
|
|
|
+ started = start_container(name)
|
|
|
+
|
|
|
+ if not started:
|
|
|
+ raise Warning('No se pudo arrancar el contenedor')
|
|
|
+
|
|
|
+ def _copy_module(self, name, module):
|
|
|
+ self._copy_modules(name, [module])
|
|
|
+
|
|
|
+ # def _handle_container_event(self, e):
|
|
|
+ # event_type = e.get('Type')
|
|
|
+ # event_action = e.get('Action')
|
|
|
+ # container_id = e.get('Actor').get('ID')
|
|
|
+ #
|
|
|
+ # print(event_type)
|
|
|
+ # print(event_action)
|
|
|
+ # print(container_id)
|
|
|
+ #
|
|
|
+ # def _register_hook(self, cr):
|
|
|
+ # run_watchdog(self._handle_container_event)
|
|
|
+ # super(OdooInstance, self)._register_hook(cr)
|
|
|
|
|
|
name = fields.Char(string='Nombre', size=50)
|
|
|
normalized_name = fields.Char(string='Nombre normalizado', compute='_normalize_name', size=50)
|
|
@@ -124,6 +293,9 @@ class OdooInstance(models.Model):
|
|
|
running = fields.Boolean(string='Está online?', default=False)
|
|
|
payment_plan_id = fields.Many2one(string='Plan de pago', comodel_name='payment.plan', required=True)
|
|
|
|
|
|
+ def _send_email(self, ):
|
|
|
+ email_send()
|
|
|
+
|
|
|
@api.model
|
|
|
def create(self, values):
|
|
|
snaked_name = self._snakeike_name(values.get('name'))
|
|
@@ -132,7 +304,7 @@ class OdooInstance(models.Model):
|
|
|
if not name_is_available:
|
|
|
raise ValidationError('El nombre ya está siendo usado por otro sistema')
|
|
|
|
|
|
- return super(self, OdooInstance).create(values)
|
|
|
+ return super(OdooInstance, self).create(values)
|
|
|
|
|
|
@api.one
|
|
|
@api.onchange('name')
|
|
@@ -150,74 +322,72 @@ class OdooInstance(models.Model):
|
|
|
if self.state not in ('draft', 'suspended'):
|
|
|
raise Warning('No se puede activar un sistema ya activo')
|
|
|
|
|
|
- self._check_name_availability(self.normalized_name)
|
|
|
-
|
|
|
- # # 1. Check name
|
|
|
- # name_is_available = odoo_api.check_name_availability(self.normalized_name)
|
|
|
- #
|
|
|
- # if not name_is_available:
|
|
|
- # raise Warning('El nombre ya está siendo usado por otro sistema')
|
|
|
- #
|
|
|
- # # 2. Get a port
|
|
|
- # port_to_use = odoo_api.randomize_port()
|
|
|
- #
|
|
|
- # # 3. Create dirs
|
|
|
- # make_ok = odoo_api.make_default_dirs(self.normalized_name)
|
|
|
- #
|
|
|
- # if not make_ok:
|
|
|
- # raise Warning('No se pudo crear la estructura de directorios')
|
|
|
- #
|
|
|
- # # 4. Create configuration file
|
|
|
- # config_ok = odoo_api.make_config_file(self.normalized_name)
|
|
|
- #
|
|
|
- # if not config_ok:
|
|
|
- # raise Warning('No se pudo crear el archivo de configuración')
|
|
|
- #
|
|
|
- # # 5. Create database
|
|
|
- # db_ok = odoo_api.create_database(self.normalized_name)
|
|
|
- #
|
|
|
- # if not db_ok:
|
|
|
- # raise Warning('No se pudo crear la base de datos')
|
|
|
- #
|
|
|
- # # 6. Copy database seed
|
|
|
- # seed_copied = odoo_api.copy_database_seed()
|
|
|
- #
|
|
|
- # if not seed_copied:
|
|
|
- # raise Warning('No se pudo copiar la estructura inicial de la base de datos')
|
|
|
- #
|
|
|
- # # 7. Restore database schema
|
|
|
- # restored = odoo_api.restore_database(self.normalized_name)
|
|
|
- #
|
|
|
- # if not restored:
|
|
|
- # raise Warning('No se pudo reestablecer la copia de base de datos')
|
|
|
- #
|
|
|
- # # 8. Remove database seed
|
|
|
- # seed_removed = odoo_api.remove_database_seed()
|
|
|
- #
|
|
|
- # if not seed_removed:
|
|
|
- # raise Warning('No se pudo remover la copia de base de datos')
|
|
|
- #
|
|
|
- # # 9. Create odoo container
|
|
|
- # odoo_created = odoo_api.create_container(self.normalized_name, [port_to_use])
|
|
|
- #
|
|
|
- # if not odoo_created:
|
|
|
- # raise Warning('No se pudo crear el contenedor')
|
|
|
- #
|
|
|
- # # 10. Apply permissions
|
|
|
- # permissions_applied = odoo_api.apply_permissions(self.normalized_name)
|
|
|
- #
|
|
|
- # if not permissions_applied:
|
|
|
- # raise Warning('No se pudo aplicar los permisos correspondientes')
|
|
|
- #
|
|
|
- # # 11. Get internal ip
|
|
|
- # internal_ip = odoo_api.get_internal_ip(self.normalized_name)
|
|
|
- #
|
|
|
- # # 12. Send email
|
|
|
- # # TODO
|
|
|
- #
|
|
|
- # print(internal_ip)
|
|
|
- #
|
|
|
- # self.state = 'activated'
|
|
|
+ # 1. Check name
|
|
|
+ name_is_available = self._check_name_availability(self.normalized_name)
|
|
|
+
|
|
|
+ if not name_is_available:
|
|
|
+ raise Warning('El nombre ya está siendo usado por otro sistema')
|
|
|
+
|
|
|
+ # 2. Get a port
|
|
|
+ port_to_use = self._randomize_port()
|
|
|
+
|
|
|
+ # 3. Create dirs
|
|
|
+ make_ok = self.make_default_dirs(self.normalized_name)
|
|
|
+
|
|
|
+ if not make_ok:
|
|
|
+ raise Warning('No se pudo crear la estructura de directorios')
|
|
|
+
|
|
|
+ # 4. Create configuration file
|
|
|
+ config_ok = self.make_config_file(self.normalized_name)
|
|
|
+
|
|
|
+ if not config_ok:
|
|
|
+ raise Warning('No se pudo crear el archivo de configuración')
|
|
|
+
|
|
|
+ # 5. Create database
|
|
|
+ db_ok = self.create_database(self.normalized_name)
|
|
|
+
|
|
|
+ if not db_ok:
|
|
|
+ raise Warning('No se pudo crear la base de datos')
|
|
|
+
|
|
|
+ # 6. Copy database seed
|
|
|
+ seed_copied = self.copy_database_seed()
|
|
|
+
|
|
|
+ if not seed_copied:
|
|
|
+ raise Warning('No se pudo copiar la estructura inicial de la base de datos')
|
|
|
+
|
|
|
+ # 7. Restore database schema
|
|
|
+ restored = self.restore_database(self.normalized_name)
|
|
|
+
|
|
|
+ if not restored:
|
|
|
+ raise Warning('No se pudo reestablecer la copia de base de datos')
|
|
|
+
|
|
|
+ # 8. Remove database seed
|
|
|
+ seed_removed = self.remove_database_seed()
|
|
|
+
|
|
|
+ if not seed_removed:
|
|
|
+ raise Warning('No se pudo remover la copia de base de datos')
|
|
|
+
|
|
|
+ # 9. Create odoo container
|
|
|
+ odoo_created = self.create_container(self.normalized_name, [port_to_use])
|
|
|
+
|
|
|
+ if not odoo_created:
|
|
|
+ raise Warning('No se pudo crear el contenedor')
|
|
|
+
|
|
|
+ # 10. Apply permissions
|
|
|
+ permissions_applied = self.apply_permissions(self.normalized_name)
|
|
|
+
|
|
|
+ if not permissions_applied:
|
|
|
+ raise Warning('No se pudo aplicar los permisos correspondientes')
|
|
|
+
|
|
|
+ # 11. Get internal ip
|
|
|
+ internal_ip = self.get_internal_ip(self.normalized_name)
|
|
|
+
|
|
|
+ # 12. Send email
|
|
|
+ # TODO
|
|
|
+
|
|
|
+ print(internal_ip)
|
|
|
+
|
|
|
+ self.state = 'activated'
|
|
|
|
|
|
@api.one
|
|
|
def action_disapprove(self):
|
|
@@ -231,18 +401,42 @@ class OdooInstance(models.Model):
|
|
|
if self.state != 'activated':
|
|
|
raise Warning('No se puede suspender un sistema no activo')
|
|
|
|
|
|
+ # 1. Get container status and stop it
|
|
|
+ container_is_running = get_status(self.normalized_name)
|
|
|
+
|
|
|
+ if container_is_running:
|
|
|
+ stopped = stop_container(self.normalized_name)
|
|
|
+
|
|
|
+ if not stopped:
|
|
|
+ raise Warning('No se pudo parar el contenedor %s' % self.normalized_name)
|
|
|
+
|
|
|
self.state = 'suspended'
|
|
|
self.running = False
|
|
|
|
|
|
@api.one
|
|
|
def copy(self):
|
|
|
- raise Warning('Atención', 'No se puede duplicar una instancia. Por favor, cree uno nuevo')
|
|
|
+ raise Warning('Atención', 'Está prohibido duplicar una instancia. Por favor, cree uno nuevo')
|
|
|
|
|
|
@api.one
|
|
|
def action_destroy(self):
|
|
|
if self.state == 'destroyed':
|
|
|
raise Warning('No se puede eliminar un sistema ya eliminado')
|
|
|
|
|
|
+ # 1. Get container status and stop it if necessary
|
|
|
+ container_is_running = get_status(self.normalized_name)
|
|
|
+
|
|
|
+ if container_is_running:
|
|
|
+ stopped = stop_container(self.normalized_name)
|
|
|
+
|
|
|
+ if not stopped:
|
|
|
+ raise Warning('Atención', 'No se pudo parar el contenedor %s' % self.normalized_name)
|
|
|
+
|
|
|
+ # 2. Delete dirs
|
|
|
+ self._delete_dirs(self.normalized_name)
|
|
|
+
|
|
|
+ # 3. Drop database
|
|
|
+ self._drop_database(self.normalized_name)
|
|
|
+
|
|
|
self.state = 'destroyed'
|
|
|
self.running = False
|
|
|
|
|
@@ -251,6 +445,18 @@ class OdooInstance(models.Model):
|
|
|
if self.running:
|
|
|
raise Warning('Atención', 'No se puede arrancar una instancia que ya está arrancada')
|
|
|
|
|
|
+ # 1. Get container status
|
|
|
+ container_is_running = get_status(self.normalized_name)
|
|
|
+
|
|
|
+ if container_is_running:
|
|
|
+ raise Warning('Atención', 'No se puede arrancar una instancia que ya está arrancada')
|
|
|
+
|
|
|
+ # 2. Start container
|
|
|
+ started = start_container(self.normalized_name)
|
|
|
+
|
|
|
+ if not started:
|
|
|
+ raise Warning('Atención', 'No se pudo arrancar el contenedor')
|
|
|
+
|
|
|
self.running = True
|
|
|
|
|
|
@api.one
|
|
@@ -258,6 +464,18 @@ class OdooInstance(models.Model):
|
|
|
if not self.running:
|
|
|
raise Warning('Atención', 'No se puede parar y arrancar una instancia que ya está parada')
|
|
|
|
|
|
+ # 1. Get container status
|
|
|
+ container_is_running = get_status(self.normalized_name)
|
|
|
+
|
|
|
+ if not container_is_running:
|
|
|
+ raise Warning('Atención', 'No se puede parar y arrancar una instancia que ya está parada')
|
|
|
+
|
|
|
+ # 2. Restart container
|
|
|
+ restarted = restart_container(self.normalized_name)
|
|
|
+
|
|
|
+ if not restarted:
|
|
|
+ raise Warning('Atención', 'No se pudo reiniciar el contenedor')
|
|
|
+
|
|
|
self.running = True
|
|
|
|
|
|
@api.model
|