feat: 增加 DB Listen Port 映射规则

pull/8892/head
Jiangjie.Bai 2022-09-21 20:56:40 +08:00
parent 567b62516a
commit a0c61ab8cb
10 changed files with 167 additions and 2 deletions

View File

@ -1,15 +1,19 @@
# coding: utf-8
#
from orgs.mixins.api import OrgBulkModelViewSet
from rest_framework import generics, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from common.tree import TreeNodeSerializer
from common.mixins.api import SuggestionMixin
from ..utils import db_port_manager
from .. import serializers
from ..models import Application
__all__ = ['ApplicationViewSet']
__all__ = ['ApplicationViewSet', 'DBListenPortViewSet']
class ApplicationViewSet(SuggestionMixin, OrgBulkModelViewSet):
@ -37,3 +41,27 @@ class ApplicationViewSet(SuggestionMixin, OrgBulkModelViewSet):
tree_nodes = Application.create_tree_nodes(queryset, show_count=show_count)
serializer = self.get_serializer(tree_nodes, many=True)
return Response(serializer.data)
class DBListenPortViewSet(GenericViewSet):
rbac_perms = {
'GET': 'applications.view_application',
'list': 'applications.view_application',
'db_info': 'applications.view_application',
}
http_method_names = ['get', 'post']
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=['post'], detail=False, url_path='db-info')
def db_info(self, request, *args, **kwargs):
port = request.data.get("port")
db, msg = db_port_manager.get_db_by_port(port)
if db is None:
data = {'error': msg}
return Response(data=data, status=status.HTTP_404_NOT_FOUND)
serializer = serializers.AppSerializer(instance=db)
return Response(data=serializer.data, status=status.HTTP_201_CREATED)

View File

@ -7,3 +7,7 @@ from django.apps import AppConfig
class ApplicationsConfig(AppConfig):
name = 'applications'
verbose_name = _('Applications')
def ready(self):
from . import signal_handlers
super().ready()

View File

@ -12,7 +12,6 @@ from common.utils import is_uuid
from assets.models import Asset, SystemUser
from ..const import OracleVersion
from ..utils import KubernetesTree
from .. import const
@ -175,6 +174,7 @@ class ApplicationTreeNodeMixin:
return pid
def as_tree_node(self, pid, k8s_as_tree=False):
from ..utils import KubernetesTree
if self.type == const.AppType.k8s and k8s_as_tree:
node = KubernetesTree(pid).as_tree_node(self)
else:

View File

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
#
from django.db.models.signals import post_save, post_delete
from common.signals import django_ready
from django.dispatch import receiver
from common.utils import get_logger
from .models import Application
from .utils import db_port_manager
logger = get_logger(__file__)
@receiver(django_ready)
def init_db_port_mapper(sender, **kwargs):
logger.info('Init db port mapper')
db_port_manager.init()
@receiver(post_save, sender=Application)
def on_db_app_created(sender, instance: Application, created, **kwargs):
if not instance.category_db:
return
if not created:
return
db_port_manager.add(instance)
@receiver(post_delete, sender=Application)
def on_db_app_delete(sender, instance, **kwargs):
if not instance.category_db:
return
db_port_manager.pop(instance)

View File

@ -13,6 +13,7 @@ router.register(r'applications', api.ApplicationViewSet, 'application')
router.register(r'accounts', api.ApplicationAccountViewSet, 'application-account')
router.register(r'system-users-apps-relations', api.SystemUserAppRelationViewSet, 'system-users-apps-relation')
router.register(r'account-secrets', api.ApplicationAccountSecretViewSet, 'application-account-secret')
router.register(r'db-listen-ports', api.DBListenPortViewSet, 'db-listen-ports')
urlpatterns = [

View File

@ -2,3 +2,4 @@
#
from .kubernetes_util import *
from .db_port_mapper import *

View File

@ -0,0 +1,88 @@
from common.decorator import Singleton
from django.core.cache import cache
from django.conf import settings
from applications.const import AppCategory
from applications.models import Application
from common.utils import get_logger
from common.utils import get_object_or_none
logger = get_logger(__file__)
@Singleton
class DBPortManager(object):
""" 管理端口-数据库ID的映射, Magnus 要使用 """
CACHE_KEY = 'PORT_DB_MAPPER'
def __init__(self):
self.port_start = settings.MAGNUS_DB_PORTS_START
self.port_limit = settings.MAGNUS_DB_PORTS_LIMIT_COUNT
self.port_end = self.port_start + self.port_limit + 1
# 可以使用的端口列表
self.all_usable_ports = [i for i in range(self.port_start, self.port_end)]
def init(self):
db_ids = Application.objects.filter(category=AppCategory.db).values_list('id', flat=True)
db_ids = [str(i) for i in db_ids]
mapper = dict(zip(self.all_usable_ports, list(db_ids)))
self.set_mapper(mapper)
def get_db_by_port(self, port):
mapper = self.get_mapper()
db_id = mapper.get(port, None)
if db_id:
db = get_object_or_none(Application, id=db_id)
if not db:
msg = 'Database not exists, database id: {}'.format(db_id)
else:
msg = ''
else:
db = None
msg = 'Port not in port-db mapper, port: {}'.format(port)
return db, msg
def add(self, db: Application):
mapper = self.get_mapper()
usable_port = self.get_next_usable_port()
if not usable_port:
return False
mapper.update({usable_port: str(db.id)})
self.set_mapper(mapper)
return True
def pop(self, db: Application):
mapper = self.get_mapper()
to_delete_port = None
for port, db_id in mapper.items():
if db_id == str(db.id):
to_delete_port = port
break
mapper.pop(to_delete_port, None)
self.set_mapper(mapper)
def get_next_usable_port(self):
already_use_ports = self.get_already_use_ports()
usable_ports = list(set(self.all_usable_ports) - set(already_use_ports))
if len(usable_ports) > 1:
return usable_ports[0]
else:
already_use_ports = self.get_already_use_ports()
msg = 'No port is usable, All usable port count: {}, Already use port count: {}'.format(
len(self.all_usable_ports), len(already_use_ports)
)
logger.warning(msg)
def get_already_use_ports(self):
mapper = self.get_mapper()
return list(mapper.keys())
def get_mapper(self):
return cache.get(self.CACHE_KEY, {})
def set_mapper(self, value):
cache.set(self.CACHE_KEY, value, timeout=None)
db_port_manager = DBPortManager()

View File

@ -484,6 +484,9 @@ class Config(dict):
'SERVER_REPLAY_STORAGE': {},
'SECURITY_DATA_CRYPTO_ALGO': None,
'GMSSL_ENABLED': False,
# Magnus 组件需要监听的端口范围
'MAGNUS_DB_PORTS_START': 30000,
'MAGNUS_DB_PORTS_LIMIT_COUNT': 1000,
# 记录清理清理
'LOGIN_LOG_KEEP_DAYS': 200,

View File

@ -177,3 +177,7 @@ HELP_SUPPORT_URL = CONFIG.HELP_SUPPORT_URL
SESSION_RSA_PRIVATE_KEY_NAME = 'jms_private_key'
SESSION_RSA_PUBLIC_KEY_NAME = 'jms_public_key'
# Magnus DB Port
MAGNUS_DB_PORTS_START = CONFIG.MAGNUS_DB_PORTS_START
MAGNUS_DB_PORTS_LIMIT_COUNT = CONFIG.MAGNUS_DB_PORTS_LIMIT_COUNT

View File

@ -14,6 +14,8 @@ class Endpoint(JMSModel):
http_port = PortField(default=80, verbose_name=_('HTTP Port'))
ssh_port = PortField(default=2222, verbose_name=_('SSH Port'))
rdp_port = PortField(default=3389, verbose_name=_('RDP Port'))
# Todo: Delete
mysql_port = PortField(default=33060, verbose_name=_('MySQL Port'))
mariadb_port = PortField(default=33061, verbose_name=_('MariaDB Port'))
postgresql_port = PortField(default=54320, verbose_name=_('PostgreSQL Port'))