mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-04-27 03:02:20 +00:00
feat: remove oracle dyn port
This commit is contained in:
parent
623c800d31
commit
4fd8efd043
@ -2,6 +2,5 @@
|
||||
#
|
||||
from .applet import *
|
||||
from .component import *
|
||||
from .db_listen_port import *
|
||||
from .session import *
|
||||
from .virtualapp import *
|
||||
|
@ -1,34 +0,0 @@
|
||||
# coding: utf-8
|
||||
#
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import GenericViewSet
|
||||
|
||||
from assets.serializers.asset.database import DatabaseWithGatewaySerializer
|
||||
from orgs.utils import tmp_to_org
|
||||
from ..utils import db_port_manager, DBPortManager
|
||||
|
||||
db_port_manager: DBPortManager
|
||||
|
||||
__all__ = ['DBListenPortViewSet']
|
||||
|
||||
|
||||
class DBListenPortViewSet(GenericViewSet):
|
||||
rbac_perms = {
|
||||
'*': ['assets.view_asset'],
|
||||
}
|
||||
http_method_names = ['get']
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
ports = db_port_manager.get_already_use_ports()
|
||||
return Response(data=ports, status=status.HTTP_200_OK)
|
||||
|
||||
@action(methods=['get'], detail=False, url_path='db-info')
|
||||
def db_info(self, request, *args, **kwargs):
|
||||
port = request.query_params.get("port")
|
||||
db = db_port_manager.get_db_by_port(port)
|
||||
|
||||
with tmp_to_org(db.org):
|
||||
serializer = DatabaseWithGatewaySerializer(instance=db)
|
||||
return Response(data=serializer.data, status=status.HTTP_200_OK)
|
20
apps/terminal/migrations/0006_endpoint_oracle_port.py
Normal file
20
apps/terminal/migrations/0006_endpoint_oracle_port.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 4.1.13 on 2025-04-01 07:44
|
||||
|
||||
import common.db.fields
|
||||
import django.core.validators
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('terminal', '0005_endpoint_vnc_port'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='endpoint',
|
||||
name='oracle_port',
|
||||
field=common.db.fields.PortField(default=15210, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='Oracle port'),
|
||||
),
|
||||
]
|
@ -22,6 +22,7 @@ class Endpoint(JMSBaseModel):
|
||||
postgresql_port = PortField(default=54320, verbose_name=_('PostgreSQL port'))
|
||||
redis_port = PortField(default=63790, verbose_name=_('Redis port'))
|
||||
sqlserver_port = PortField(default=14330, verbose_name=_('SQLServer port'))
|
||||
oracle_port = PortField(default=15210,verbose_name=_('Oracle port'))
|
||||
vnc_port = PortField(default=15900, verbose_name=_('VNC port'))
|
||||
|
||||
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
|
||||
@ -37,17 +38,10 @@ class Endpoint(JMSBaseModel):
|
||||
return self.name
|
||||
|
||||
def get_port(self, target_instance, protocol):
|
||||
from terminal.utils import db_port_manager
|
||||
from assets.const import DatabaseTypes, Protocol
|
||||
|
||||
if isinstance(target_instance, Asset) and \
|
||||
target_instance.is_type(DatabaseTypes.ORACLE) and \
|
||||
protocol == Protocol.oracle:
|
||||
port = db_port_manager.get_port_by_db(target_instance)
|
||||
else:
|
||||
if protocol in [Protocol.sftp, Protocol.telnet]:
|
||||
protocol = Protocol.ssh
|
||||
port = getattr(self, f'{protocol}_port', 0)
|
||||
from assets.const import Protocol
|
||||
if protocol in [Protocol.sftp, Protocol.telnet]:
|
||||
protocol = Protocol.ssh
|
||||
port = getattr(self, f'{protocol}_port', 0)
|
||||
return port
|
||||
|
||||
def is_default(self):
|
||||
|
@ -5,30 +5,18 @@ from acls.serializers.rules import ip_group_child_validator, ip_group_help_text
|
||||
from common.serializers import BulkModelSerializer
|
||||
from common.serializers.fields import ObjectRelatedField
|
||||
from ..models import Endpoint, EndpointRule
|
||||
from ..utils import db_port_manager
|
||||
|
||||
__all__ = ['EndpointSerializer', 'EndpointRuleSerializer']
|
||||
|
||||
|
||||
class EndpointSerializer(BulkModelSerializer):
|
||||
# 解决 luna 处理繁琐的问题, 返回 magnus 监听的当前 db 的 port
|
||||
oracle_port = serializers.SerializerMethodField(label=_('Oracle port'))
|
||||
oracle_port_range = serializers.CharField(
|
||||
max_length=128, default=db_port_manager.oracle_port_range, read_only=True,
|
||||
label=_('Oracle port range'),
|
||||
help_text=_(
|
||||
'Oracle proxy server listen port is dynamic, Each additional Oracle '
|
||||
'database instance adds a port listener'
|
||||
)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Endpoint
|
||||
fields_mini = ['id', 'name']
|
||||
fields_small = [
|
||||
'host', 'https_port', 'http_port', 'ssh_port', 'rdp_port',
|
||||
'mysql_port', 'mariadb_port', 'postgresql_port', 'redis_port', 'vnc_port',
|
||||
'oracle_port_range', 'oracle_port', 'sqlserver_port', 'is_active'
|
||||
'oracle_port', 'sqlserver_port', 'is_active'
|
||||
]
|
||||
fields = fields_mini + fields_small + [
|
||||
'comment', 'date_created', 'date_updated', 'created_by'
|
||||
@ -41,13 +29,6 @@ class EndpointSerializer(BulkModelSerializer):
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
def get_oracle_port(self, obj: Endpoint):
|
||||
view = self.context.get('view')
|
||||
if not view or view.action not in ['smart']:
|
||||
return 0
|
||||
return obj.get_port(view.target_instance, view.target_protocol)
|
||||
|
||||
def get_extra_kwargs(self):
|
||||
extra_kwargs = super().get_extra_kwargs()
|
||||
model_fields = self.Meta.model._meta.fields
|
||||
|
@ -1,5 +1,4 @@
|
||||
from .applet import *
|
||||
from .db_port import *
|
||||
from .session import *
|
||||
from .session_sharing import *
|
||||
from .terminal import *
|
||||
|
@ -12,9 +12,7 @@ from orgs.utils import tmp_to_builtin_org
|
||||
from users.models import User
|
||||
from ..models import Applet, AppletHost
|
||||
from ..tasks import applet_host_generate_accounts
|
||||
from ..utils import DBPortManager
|
||||
|
||||
db_port_manager: DBPortManager
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
|
@ -1,38 +0,0 @@
|
||||
from django.db.models.signals import post_delete, post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from assets.models import Asset, Database
|
||||
from common.decorators import on_transaction_commit
|
||||
from common.signals import django_ready
|
||||
from common.utils import get_logger
|
||||
from ..utils import db_port_manager
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@receiver(django_ready)
|
||||
def check_db_port_mapper(sender, **kwargs):
|
||||
logger.info('Check oracle ports (MAGNUS_ORACLE_PORTS)')
|
||||
try:
|
||||
db_port_manager.check()
|
||||
except Exception as e:
|
||||
# 新部署会显示 assets_database 表不存在
|
||||
logger.warning('(Ignore) {}'.format(e))
|
||||
|
||||
|
||||
@receiver(post_save, sender=Database)
|
||||
def on_db_created(sender, instance: Database, created, **kwargs):
|
||||
if instance.type != 'oracle':
|
||||
return
|
||||
if not created:
|
||||
return
|
||||
logger.info("Oracle create signal recv: {} {}".format(instance, instance.type))
|
||||
db_port_manager.check()
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Database)
|
||||
def on_db_delete(sender, instance, **kwargs):
|
||||
if instance.type != 'oracle':
|
||||
return
|
||||
logger.info("Oracle delete signal recv: {}".format(instance))
|
||||
db_port_manager.check()
|
@ -7,9 +7,6 @@ from common.utils import get_logger
|
||||
from common.utils.connection import RedisPubSub
|
||||
from ..const import TaskNameType
|
||||
from ..models import Task, Session
|
||||
from ..utils import DBPortManager
|
||||
|
||||
db_port_manager: DBPortManager
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
@ -29,7 +29,6 @@ router.register(r'applet-hosts/((?P<host>[^/.]+)/)?applets', api.AppletHostApple
|
||||
router.register(r'applet-hosts', api.AppletHostViewSet, 'applet-host')
|
||||
router.register(r'applet-publications', api.AppletPublicationViewSet, 'applet-publication')
|
||||
router.register(r'applet-host-deployments', api.AppletHostDeploymentViewSet, 'applet-host-deployment')
|
||||
router.register(r'db-listen-ports', api.DBListenPortViewSet, 'db-listen-ports')
|
||||
router.register(r'virtual-apps', api.VirtualAppViewSet, 'virtual-app')
|
||||
router.register(r'app-providers', api.AppProviderViewSet, 'app-provider')
|
||||
router.register(r'app-providers/((?P<provider>[^/.]+)/)?apps', api.AppProviderAppViewSet, 'app-provider-app')
|
||||
|
@ -1,3 +1,2 @@
|
||||
from .components import *
|
||||
from .common import *
|
||||
from .db_port_mapper import *
|
||||
|
@ -1,156 +0,0 @@
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from assets.const import DatabaseTypes
|
||||
from assets.models import Database
|
||||
from common.decorators import Singleton
|
||||
from common.exceptions import JMSException
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from orgs.utils import tmp_to_root_org
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@Singleton
|
||||
class DBPortManager:
|
||||
""" 管理端口-数据库ID的映射, Magnus 要使用 """
|
||||
CACHE_KEY = 'PORT_DB_MAPPER'
|
||||
|
||||
def __init__(self):
|
||||
oracle_ports = self.oracle_port_range
|
||||
try:
|
||||
port_start, port_end = oracle_ports.split('-')
|
||||
port_start, port_end = int(port_start), int(port_end)
|
||||
except Exception as e:
|
||||
logger.error('MAGNUS_ORACLE_PORTS config error: {}'.format(e))
|
||||
port_start, port_end = 30000, 30100
|
||||
|
||||
self.port_start, self.port_end = port_start, port_end
|
||||
# 可以使用的端口列表
|
||||
self.all_avail_ports = list(range(self.port_start, self.port_end + 1))
|
||||
|
||||
@property
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
def fetch_dbs():
|
||||
with tmp_to_root_org():
|
||||
dbs = Database.objects.filter(platform__type=DatabaseTypes.ORACLE).order_by('id')
|
||||
return dbs
|
||||
|
||||
def check(self):
|
||||
dbs = self.fetch_dbs()
|
||||
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)
|
||||
|
||||
if db_ids_to_add or db_ids_to_pop:
|
||||
self.set_mapper(mapper)
|
||||
|
||||
if settings.DEBUG:
|
||||
logger.debug("Oracle listen ports: {}".format(len(mapper.keys())))
|
||||
|
||||
def init(self):
|
||||
dbs = self.fetch_dbs()
|
||||
db_ids = dbs.values_list('id', flat=True)
|
||||
db_ids = [str(i) for i in db_ids]
|
||||
mapper = dict(zip(self.all_avail_ports, list(db_ids)))
|
||||
self.set_mapper(mapper)
|
||||
return mapper
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def get_port_by_db(self, db, raise_exception=True):
|
||||
mapper = self.get_mapper()
|
||||
for port, db_id in mapper.items():
|
||||
if db_id == str(db.id):
|
||||
return port
|
||||
|
||||
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)
|
||||
|
||||
def get_db_by_port(self, port):
|
||||
try:
|
||||
port = int(port)
|
||||
except Exception as e:
|
||||
raise JMSException('Port type error: {}'.format(e))
|
||||
mapper = self.get_mapper()
|
||||
db_id = mapper.get(port, None)
|
||||
if not db_id:
|
||||
raise JMSException('Database not in port-db mapper, port: {}'.format(port))
|
||||
with tmp_to_root_org():
|
||||
db = get_object_or_none(Database, id=db_id)
|
||||
if not db:
|
||||
raise JMSException('Database not exists, db id: {}'.format(db_id))
|
||||
return db
|
||||
|
||||
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:
|
||||
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(
|
||||
len(self.all_avail_ports), len(already_use_ports)
|
||||
)
|
||||
error = msg + tips
|
||||
raise JMSException(error)
|
||||
port = avail_ports[0]
|
||||
logger.debug('Get next available port: {}'.format(port))
|
||||
return port
|
||||
|
||||
def get_already_use_ports(self):
|
||||
mapper = self.get_mapper()
|
||||
return sorted([int(i) for i in mapper.keys()])
|
||||
|
||||
@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
|
||||
|
||||
def get_mapper(self):
|
||||
mapper = cache.get(self.CACHE_KEY, {})
|
||||
if not mapper or self.oracle_ports_setting_changed():
|
||||
# redis 可能被清空,重新初始化一下
|
||||
mapper = self.init()
|
||||
return mapper
|
||||
|
||||
def set_mapper(self, value):
|
||||
"""
|
||||
value: {
|
||||
port: db_id
|
||||
}
|
||||
"""
|
||||
cache.set(self.CACHE_KEY, value, timeout=None)
|
||||
cache.set('MAGNUS_ORACLE_PORTS', settings.MAGNUS_ORACLE_PORTS)
|
||||
|
||||
|
||||
db_port_manager = DBPortManager()
|
Loading…
Reference in New Issue
Block a user