2022-09-21 12:56:40 +00:00
|
|
|
from django.conf import settings
|
2022-12-05 08:07:14 +00:00
|
|
|
from django.core.cache import cache
|
2023-07-24 03:52:25 +00:00
|
|
|
from django.utils.translation import gettext_lazy as _
|
2022-09-22 11:09:39 +00:00
|
|
|
|
2023-01-16 11:02:09 +00:00
|
|
|
from assets.const import DatabaseTypes
|
|
|
|
from assets.models import Database
|
2023-02-08 02:14:09 +00:00
|
|
|
from common.decorators import Singleton
|
2022-09-22 11:09:39 +00:00
|
|
|
from common.exceptions import JMSException
|
2022-12-05 08:07:14 +00:00
|
|
|
from common.utils import get_logger, get_object_or_none
|
2022-09-22 09:23:17 +00:00
|
|
|
from orgs.utils import tmp_to_root_org
|
2022-09-21 12:56:40 +00:00
|
|
|
|
|
|
|
logger = get_logger(__file__)
|
|
|
|
|
|
|
|
|
|
|
|
@Singleton
|
2024-08-27 03:32:38 +00:00
|
|
|
class DBPortManager:
|
2022-09-21 12:56:40 +00:00
|
|
|
""" 管理端口-数据库ID的映射, Magnus 要使用 """
|
|
|
|
CACHE_KEY = 'PORT_DB_MAPPER'
|
|
|
|
|
|
|
|
def __init__(self):
|
2023-01-16 11:02:09 +00:00
|
|
|
oracle_ports = self.oracle_port_range
|
2022-10-19 12:03:06 +00:00
|
|
|
try:
|
2023-01-16 11:02:09 +00:00
|
|
|
port_start, port_end = oracle_ports.split('-')
|
2022-10-19 12:03:06 +00:00
|
|
|
port_start, port_end = int(port_start), int(port_end)
|
|
|
|
except Exception as e:
|
2023-01-16 11:02:09 +00:00
|
|
|
logger.error('MAGNUS_ORACLE_PORTS config error: {}'.format(e))
|
2022-10-19 12:03:06 +00:00
|
|
|
port_start, port_end = 30000, 30100
|
|
|
|
|
|
|
|
self.port_start, self.port_end = port_start, port_end
|
2022-09-21 12:56:40 +00:00
|
|
|
# 可以使用的端口列表
|
2023-01-16 11:02:09 +00:00
|
|
|
self.all_avail_ports = list(range(self.port_start, self.port_end + 1))
|
2022-09-22 09:23:17 +00:00
|
|
|
|
|
|
|
@property
|
2023-01-16 11:02:09 +00:00
|
|
|
def oracle_port_range(self):
|
|
|
|
oracle_ports = settings.MAGNUS_ORACLE_PORTS
|
|
|
|
if not oracle_ports and settings.MAGNUS_PORTS:
|
|
|
|
oracle_ports = settings.MAGNUS_PORTS
|
|
|
|
return oracle_ports
|
2022-09-21 12:56:40 +00:00
|
|
|
|
2022-12-21 10:38:29 +00:00
|
|
|
@staticmethod
|
|
|
|
def fetch_dbs():
|
2022-09-22 10:47:16 +00:00
|
|
|
with tmp_to_root_org():
|
2023-01-16 11:02:09 +00:00
|
|
|
dbs = Database.objects.filter(platform__type=DatabaseTypes.ORACLE).order_by('id')
|
2022-12-21 10:38:29 +00:00
|
|
|
return dbs
|
|
|
|
|
|
|
|
def check(self):
|
|
|
|
dbs = self.fetch_dbs()
|
2023-01-16 11:02:09 +00:00
|
|
|
mapper = self.get_mapper()
|
|
|
|
db_ids = [str(db.id) for db in dbs]
|
|
|
|
db_ids_to_add = list(set(db_ids) - set(mapper.values()))
|
|
|
|
mapper = self.bulk_add(db_ids_to_add, mapper)
|
|
|
|
|
|
|
|
db_ids_to_pop = set(mapper.values()) - set(db_ids)
|
|
|
|
mapper = self.bulk_pop(db_ids_to_pop, mapper)
|
2024-08-27 03:32:38 +00:00
|
|
|
|
|
|
|
if db_ids_to_add or db_ids_to_pop:
|
|
|
|
self.set_mapper(mapper)
|
2023-01-16 11:02:09 +00:00
|
|
|
|
|
|
|
if settings.DEBUG:
|
|
|
|
logger.debug("Oracle listen ports: {}".format(len(mapper.keys())))
|
2022-12-21 10:38:29 +00:00
|
|
|
|
|
|
|
def init(self):
|
|
|
|
dbs = self.fetch_dbs()
|
|
|
|
db_ids = dbs.values_list('id', flat=True)
|
2022-09-21 12:56:40 +00:00
|
|
|
db_ids = [str(i) for i in db_ids]
|
2023-01-16 11:02:09 +00:00
|
|
|
mapper = dict(zip(self.all_avail_ports, list(db_ids)))
|
2022-09-21 12:56:40 +00:00
|
|
|
self.set_mapper(mapper)
|
2024-08-27 03:32:38 +00:00
|
|
|
return mapper
|
2022-09-21 12:56:40 +00:00
|
|
|
|
2023-01-16 11:02:09 +00:00
|
|
|
def bulk_add(self, db_ids, mapper):
|
|
|
|
for db_id in db_ids:
|
|
|
|
avail_port = self.get_next_avail_port(mapper)
|
|
|
|
mapper[avail_port] = str(db_id)
|
|
|
|
return mapper
|
2022-09-21 12:56:40 +00:00
|
|
|
|
2023-01-16 11:02:09 +00:00
|
|
|
def bulk_pop(self, db_ids, mapper):
|
|
|
|
new_mapper = {port: str(db_id) for port, db_id in mapper.items() if db_id not in db_ids}
|
|
|
|
return new_mapper
|
2022-09-21 12:56:40 +00:00
|
|
|
|
2022-11-16 12:57:41 +00:00
|
|
|
def get_port_by_db(self, db, raise_exception=True):
|
2022-10-20 09:37:25 +00:00
|
|
|
mapper = self.get_mapper()
|
|
|
|
for port, db_id in mapper.items():
|
|
|
|
if db_id == str(db.id):
|
|
|
|
return port
|
2023-07-20 06:46:56 +00:00
|
|
|
|
2022-11-16 12:57:41 +00:00
|
|
|
if raise_exception:
|
|
|
|
error = _(
|
|
|
|
'No available port is matched. '
|
|
|
|
'The number of databases may have exceeded the number of ports '
|
|
|
|
'open to the database agent service, '
|
|
|
|
'Contact the administrator to open more ports.'
|
|
|
|
)
|
|
|
|
raise JMSException(error)
|
2022-09-22 06:48:20 +00:00
|
|
|
|
|
|
|
def get_db_by_port(self, port):
|
2022-09-22 11:18:38 +00:00
|
|
|
try:
|
|
|
|
port = int(port)
|
|
|
|
except Exception as e:
|
|
|
|
raise JMSException('Port type error: {}'.format(e))
|
2022-09-22 06:48:20 +00:00
|
|
|
mapper = self.get_mapper()
|
|
|
|
db_id = mapper.get(port, None)
|
2022-09-22 10:47:16 +00:00
|
|
|
if not db_id:
|
|
|
|
raise JMSException('Database not in port-db mapper, port: {}'.format(port))
|
|
|
|
with tmp_to_root_org():
|
2023-01-16 11:02:09 +00:00
|
|
|
db = get_object_or_none(Database, id=db_id)
|
2022-09-22 10:47:16 +00:00
|
|
|
if not db:
|
|
|
|
raise JMSException('Database not exists, db id: {}'.format(db_id))
|
|
|
|
return db
|
|
|
|
|
2023-01-16 11:02:09 +00:00
|
|
|
def get_next_avail_port(self, mapper=None):
|
|
|
|
if mapper is None:
|
|
|
|
mapper = self.get_mapper()
|
|
|
|
already_use_ports = [int(i) for i in mapper.keys()]
|
|
|
|
avail_ports = sorted(list(set(self.all_avail_ports) - set(already_use_ports)))
|
|
|
|
if len(avail_ports) <= 0:
|
2022-09-22 11:09:39 +00:00
|
|
|
msg = _('No ports can be used, check and modify the limit on the number '
|
|
|
|
'of ports that Magnus listens on in the configuration file.')
|
|
|
|
tips = _('All available port count: {}, Already use port count: {}').format(
|
2023-01-16 11:02:09 +00:00
|
|
|
len(self.all_avail_ports), len(already_use_ports)
|
2022-09-22 10:47:16 +00:00
|
|
|
)
|
2022-09-22 11:09:39 +00:00
|
|
|
error = msg + tips
|
|
|
|
raise JMSException(error)
|
2023-01-16 11:02:09 +00:00
|
|
|
port = avail_ports[0]
|
2022-09-22 10:47:16 +00:00
|
|
|
logger.debug('Get next available port: {}'.format(port))
|
|
|
|
return port
|
2022-09-21 12:56:40 +00:00
|
|
|
|
|
|
|
def get_already_use_ports(self):
|
|
|
|
mapper = self.get_mapper()
|
2023-01-16 11:02:09 +00:00
|
|
|
return sorted([int(i) for i in mapper.keys()])
|
2022-09-21 12:56:40 +00:00
|
|
|
|
2024-08-27 03:32:38 +00:00
|
|
|
@staticmethod
|
|
|
|
def oracle_ports_setting_changed():
|
|
|
|
oracle_ports_cache = cache.get('MAGNUS_ORACLE_PORTS') or ''
|
|
|
|
if settings.MAGNUS_ORACLE_PORTS.split('-')[0] != oracle_ports_cache.split('-')[0]:
|
|
|
|
logger.info('Oracle ports setting changed')
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2022-09-21 12:56:40 +00:00
|
|
|
def get_mapper(self):
|
2022-09-22 10:47:16 +00:00
|
|
|
mapper = cache.get(self.CACHE_KEY, {})
|
2024-08-27 03:32:38 +00:00
|
|
|
if not mapper or self.oracle_ports_setting_changed():
|
2022-09-22 10:47:16 +00:00
|
|
|
# redis 可能被清空,重新初始化一下
|
2024-08-27 03:32:38 +00:00
|
|
|
mapper = self.init()
|
|
|
|
return mapper
|
2022-09-21 12:56:40 +00:00
|
|
|
|
|
|
|
def set_mapper(self, value):
|
2022-09-22 06:48:20 +00:00
|
|
|
"""
|
|
|
|
value: {
|
|
|
|
port: db_id
|
|
|
|
}
|
|
|
|
"""
|
2022-09-21 12:56:40 +00:00
|
|
|
cache.set(self.CACHE_KEY, value, timeout=None)
|
2024-08-27 03:32:38 +00:00
|
|
|
cache.set('MAGNUS_ORACLE_PORTS', settings.MAGNUS_ORACLE_PORTS)
|
2022-09-21 12:56:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
db_port_manager = DBPortManager()
|