From ba4e6e9a9f46cfc0d6bd67c37a6d69e954d8736e Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 19 Oct 2020 20:13:01 +0800 Subject: [PATCH] =?UTF-8?q?refacter:=20=E9=87=8D=E6=9E=84application?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/applications/api/__init__.py | 1 + apps/applications/api/application.py | 20 +++ .../migrations/0006_application.py | 37 ++++++ apps/applications/models/__init__.py | 1 + apps/applications/models/application.py | 115 ++++++++++++++++++ apps/applications/serializers/__init__.py | 1 + apps/applications/serializers/application.py | 46 +++++++ apps/applications/serializers/database_app.py | 26 +++- apps/applications/serializers/k8s_app.py | 7 +- apps/applications/serializers/remote_app.py | 41 ++++++- apps/applications/urls/api_urls.py | 1 + apps/common/drf/metadata.py | 5 + 12 files changed, 292 insertions(+), 9 deletions(-) create mode 100644 apps/applications/api/application.py create mode 100644 apps/applications/migrations/0006_application.py create mode 100644 apps/applications/models/application.py create mode 100644 apps/applications/serializers/application.py diff --git a/apps/applications/api/__init__.py b/apps/applications/api/__init__.py index 0e6e940ee..b232a1ece 100644 --- a/apps/applications/api/__init__.py +++ b/apps/applications/api/__init__.py @@ -1,3 +1,4 @@ +from .application import * from .remote_app import * from .database_app import * from .k8s_app import * diff --git a/apps/applications/api/application.py b/apps/applications/api/application.py new file mode 100644 index 000000000..9bc3bc752 --- /dev/null +++ b/apps/applications/api/application.py @@ -0,0 +1,20 @@ +# coding: utf-8 +# + +from orgs.mixins.api import OrgBulkModelViewSet + +from .. import models +from .. import serializers +from ..hands import IsOrgAdminOrAppUser + +__all__ = [ + 'ApplicationViewSet', +] + + +class ApplicationViewSet(OrgBulkModelViewSet): + model = models.Application + filter_fields = ('name',) + search_fields = filter_fields + permission_classes = (IsOrgAdminOrAppUser,) + serializer_class = serializers.ApplicationSerializer diff --git a/apps/applications/migrations/0006_application.py b/apps/applications/migrations/0006_application.py new file mode 100644 index 000000000..1eaba15ee --- /dev/null +++ b/apps/applications/migrations/0006_application.py @@ -0,0 +1,37 @@ +# Generated by Django 2.2.13 on 2020-10-19 12:01 + +from django.db import migrations, models +import django.db.models.deletion +import django_mysql.models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0057_fill_node_value_assets_amount_and_parent_key'), + ('applications', '0005_k8sapp'), + ] + + operations = [ + migrations.CreateModel( + name='Application', + fields=[ + ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('name', models.CharField(max_length=128, verbose_name='Name')), + ('category', models.CharField(choices=[('db', 'Database'), ('remote_app', 'Remote app'), ('cloud', 'Cloud')], max_length=16, verbose_name='Category')), + ('type', models.CharField(choices=[('mysql', 'MySQL'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('mariadb', 'MariaDB'), ('chrome', 'Chrome'), ('mysql_workbench', 'MySQL Workbench'), ('vmware_client', 'vSphere Client'), ('custom', 'Custom'), ('k8s', 'Kubernetes')], max_length=16, verbose_name='Type')), + ('attrs', django_mysql.models.JSONField(default=dict)), + ('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')), + ('domain', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.Domain')), + ], + options={ + 'ordering': ('name',), + 'unique_together': {('org_id', 'name')}, + }, + ), + ] diff --git a/apps/applications/models/__init__.py b/apps/applications/models/__init__.py index 0e6e940ee..b232a1ece 100644 --- a/apps/applications/models/__init__.py +++ b/apps/applications/models/__init__.py @@ -1,3 +1,4 @@ +from .application import * from .remote_app import * from .database_app import * from .k8s_app import * diff --git a/apps/applications/models/application.py b/apps/applications/models/application.py new file mode 100644 index 000000000..40b319f27 --- /dev/null +++ b/apps/applications/models/application.py @@ -0,0 +1,115 @@ +from itertools import chain + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django_mysql.models import JSONField, QuerySet + +from orgs.mixins.models import OrgModelMixin +from common.mixins import CommonModelMixin +from common.db.models import ChoiceSet + + +class DBType(ChoiceSet): + mysql = 'mysql', 'MySQL' + oracle = 'oracle', 'Oracle' + pgsql = 'postgresql', 'PostgreSQL' + mariadb = 'mariadb', 'MariaDB' + + @classmethod + def get_type_serializer_cls_mapper(cls): + from ..serializers import database_app + mapper = { + cls.mysql: database_app.MySQLAttrsSerializer, + cls.oracle: database_app.OracleAttrsSerializer, + cls.pgsql: database_app.PostgreAttrsSerializer, + cls.mariadb: database_app.MariaDBAttrsSerializer, + } + return mapper + + +class RemoteAppType(ChoiceSet): + chrome = 'chrome', 'Chrome' + mysql_workbench = 'mysql_workbench', 'MySQL Workbench' + vmware_client = 'vmware_client', 'vSphere Client' + custom = 'custom', _('Custom') + + @classmethod + def get_type_serializer_cls_mapper(cls): + from ..serializers import remote_app + mapper = { + cls.chrome: remote_app.ChromeAttrsSerializer, + cls.mysql_workbench: remote_app.MySQLWorkbenchAttrsSerializer, + cls.vmware_client: remote_app.VMwareClientAttrsSerializer, + cls.custom: remote_app.CustomRemoteAppAttrsSeralizers, + } + return mapper + + +class CloudType(ChoiceSet): + k8s = 'k8s', 'Kubernetes' + + @classmethod + def get_type_serializer_cls_mapper(cls): + from ..serializers import k8s_app + mapper = { + cls.k8s: k8s_app.K8sAttrsSerializer, + } + return mapper + + +class Category(ChoiceSet): + db = 'db', _('Database') + remote_app = 'remote_app', _('Remote app') + cloud = 'cloud', 'Cloud' + + @classmethod + def get_category_type_mapper(cls): + return { + cls.db: DBType, + cls.remote_app: RemoteAppType, + cls.cloud: CloudType + } + + @classmethod + def get_category_type_choices_mapper(cls): + return { + name: tp.choices + for name, tp in cls.get_category_type_mapper().items() + } + + @classmethod + def get_type_choices(cls, category): + return cls.get_category_type_choices_mapper().get(category, []) + + @classmethod + def get_all_type_choices(cls): + all_grouped_choices = tuple(cls.get_category_type_choices_mapper().values()) + return tuple(chain(*all_grouped_choices)) + + @classmethod + def get_all_type_serializer_mapper(cls): + mapper = {} + for tp in cls.get_category_type_mapper().values(): + mapper.update(tp.get_type_serializer_cls_mapper()) + return mapper + + @classmethod + def get_type_serializer_cls(cls, tp): + mapper = cls.get_all_type_serializer_mapper() + return mapper.get(tp, None) + + +class Application(CommonModelMixin, OrgModelMixin): + name = models.CharField(max_length=128, verbose_name=_('Name')) + domain = models.ForeignKey('assets.Domain', on_delete=models.SET_NULL, null=True) + category = models.CharField(max_length=16, choices=Category.choices, verbose_name=_('Category')) + type = models.CharField(max_length=16, choices=Category.get_all_type_choices(), verbose_name=_('Type')) + attrs = JSONField() + comment = models.TextField( + max_length=128, default='', blank=True, verbose_name=_('Comment') + ) + objects = QuerySet.as_manager() + + class Meta: + unique_together = [('org_id', 'name')] + ordering = ('name',) diff --git a/apps/applications/serializers/__init__.py b/apps/applications/serializers/__init__.py index 0e6e940ee..b232a1ece 100644 --- a/apps/applications/serializers/__init__.py +++ b/apps/applications/serializers/__init__.py @@ -1,3 +1,4 @@ +from .application import * from .remote_app import * from .database_app import * from .k8s_app import * diff --git a/apps/applications/serializers/application.py b/apps/applications/serializers/application.py new file mode 100644 index 000000000..068a2f4db --- /dev/null +++ b/apps/applications/serializers/application.py @@ -0,0 +1,46 @@ +# coding: utf-8 +# + +from orgs.mixins.serializers import BulkOrgResourceModelSerializer + +from .. import models + +__all__ = [ + 'ApplicationSerializer', +] + + +class ApplicationSerializer(BulkOrgResourceModelSerializer): + + class Meta: + model = models.Application + fields = [ + 'id', 'name', 'category', 'type', 'get_type_display', 'attrs', + 'created_by', 'date_created', 'date_updated', 'comment' + ] + read_only_fields = [ + 'created_by', 'date_created', 'date_updated', 'get_type_display', + ] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + app_type = '' + attrs_data = {} + if self.instance: + app_type = self.instance.type + attrs_data = self.instance.attrs + request = self.context.get('request') + if request: + app_type = request.query_params.get('type') + if hasattr(self, 'initial_data'): + app_type = self.initial_data.get('type') + attrs_data = self.initial_data.get('attrs') + if not app_type: + return + attrs_cls = models.Category.get_type_serializer_cls(app_type) + if attrs_data: + attrs_serializer = attrs_cls(data=attrs_data) + else: + attrs_serializer = attrs_cls() + self.fields['attrs'] = attrs_serializer + diff --git a/apps/applications/serializers/database_app.py b/apps/applications/serializers/database_app.py index 43b873193..9cbc286ef 100644 --- a/apps/applications/serializers/database_app.py +++ b/apps/applications/serializers/database_app.py @@ -1,14 +1,34 @@ # coding: utf-8 # +from rest_framework import serializers +from django.utils.translation import ugettext_lazy as _ from orgs.mixins.serializers import BulkOrgResourceModelSerializer from common.serializers import AdaptedBulkListSerializer from .. import models -__all__ = [ - 'DatabaseAppSerializer', -] + +class DatabaseAttrsSerializer(serializers.Serializer): + host = serializers.CharField() + port = serializers.IntegerField() + database = serializers.CharField(allow_blank=True, allow_null=True) + + +class MySQLAttrsSerializer(DatabaseAttrsSerializer): + port = serializers.IntegerField(default=3306, label=_('Port')) + + +class PostgreAttrsSerializer(DatabaseAttrsSerializer): + port = serializers.IntegerField(default=5432) + + +class OracleAttrsSerializer(DatabaseAttrsSerializer): + port = serializers.IntegerField(default=1521) + + +class MariaDBAttrsSerializer(MySQLAttrsSerializer): + pass class DatabaseAppSerializer(BulkOrgResourceModelSerializer): diff --git a/apps/applications/serializers/k8s_app.py b/apps/applications/serializers/k8s_app.py index 68fafbc86..d6327906d 100644 --- a/apps/applications/serializers/k8s_app.py +++ b/apps/applications/serializers/k8s_app.py @@ -1,11 +1,12 @@ from rest_framework import serializers +from django.utils.translation import ugettext_lazy as _ from orgs.mixins.serializers import BulkOrgResourceModelSerializer from .. import models -__all__ = [ - 'K8sAppSerializer', -] + +class K8sAttrsSerializer(serializers.Serializer): + cluster = serializers.CharField(label=_('Cluster')) class K8sAppSerializer(BulkOrgResourceModelSerializer): diff --git a/apps/applications/serializers/remote_app.py b/apps/applications/serializers/remote_app.py index fb9a7a270..c66f60bfb 100644 --- a/apps/applications/serializers/remote_app.py +++ b/apps/applications/serializers/remote_app.py @@ -2,19 +2,54 @@ # import copy +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from common.serializers import AdaptedBulkListSerializer from common.fields.serializer import CustomMetaDictField from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from assets.models import Asset from .. import const from ..models import RemoteApp -__all__ = [ - 'RemoteAppSerializer', 'RemoteAppConnectionInfoSerializer', -] +class RemoteAppAttrsSerializer(serializers.Serializer): + asset = serializers.PrimaryKeyRelatedField(queryset=Asset.objects, label=_("Assets")) + path = serializers.CharField(label=_('Remote App path')) + + +class ChromeAttrsSerializer(RemoteAppAttrsSerializer): + REMOTE_APP_PATH = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe' + path = serializers.CharField(label=_('Remote App path'), default=REMOTE_APP_PATH) + chrome_target = serializers.CharField(label=_('Target URL')) + chrome_username = serializers.CharField(label=_('Username')) + chrome_password = serializers.CharField(write_only=True, label=_('Password')) + + +class MySQLWorkbenchAttrsSerializer(RemoteAppAttrsSerializer): + REMOTE_APP_PATH = 'C:\Program Files\MySQL\MySQL Workbench 8.0 CE\MySQLWorkbench.exe' + path = serializers.CharField(label=_('Remote App path'), default=REMOTE_APP_PATH) + mysql_workbench_ip = serializers.CharField(label=_('IP')) + mysql_workbench_port = serializers.IntegerField(label=_('Port')) + mysql_workbench_name = serializers.CharField(label=_('Database')) + mysql_workbench_username = serializers.CharField(label=_('Username')) + mysql_workbench_password = serializers.CharField(write_only=True, label=_('Password')) + + +class VMwareClientAttrsSerializer(RemoteAppAttrsSerializer): + REMOTE_APP_PATH = 'C:\Program Files (x86)\VMware\Infrastructure\Virtual Infrastructure Client\Launcher\VpxClient.exe' + path = serializers.CharField(label=_('Remote App path'), default=REMOTE_APP_PATH) + vmware_target = serializers.CharField(label=_('Target URL')) + vmware_username = serializers.CharField(label=_('Username')) + vmware_password = serializers.CharField(write_only=True, label=_('Password')) + + +class CustomRemoteAppAttrsSeralizers(RemoteAppAttrsSerializer): + custom_cmdline = serializers.CharField(label=_('Operating parameter')) + custom_target = serializers.CharField(label=_('Target url')) + custom_username = serializers.CharField(label=_('Username')) + custom_password = serializers.CharField(write_only=True, label=_('Password')) class RemoteAppParamsDictField(CustomMetaDictField): diff --git a/apps/applications/urls/api_urls.py b/apps/applications/urls/api_urls.py index 42d0fe524..bfe685d4c 100644 --- a/apps/applications/urls/api_urls.py +++ b/apps/applications/urls/api_urls.py @@ -10,6 +10,7 @@ from .. import api app_name = 'applications' router = BulkRouter() +router.register(r'applications', api.ApplicationViewSet, 'application') router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app') router.register(r'database-apps', api.DatabaseAppViewSet, 'database-app') router.register(r'k8s-apps', api.K8sAppViewSet, 'k8s-app') diff --git a/apps/common/drf/metadata.py b/apps/common/drf/metadata.py index cae11cab3..6020e66fb 100644 --- a/apps/common/drf/metadata.py +++ b/apps/common/drf/metadata.py @@ -7,6 +7,7 @@ from collections import OrderedDict from django.core.exceptions import PermissionDenied from django.http import Http404 from django.utils.encoding import force_text +from rest_framework.fields import empty from rest_framework.metadata import SimpleMetadata from rest_framework import exceptions, serializers @@ -58,6 +59,10 @@ class SimpleMetadataWithFilters(SimpleMetadata): field_info['type'] = self.label_lookup[field] field_info['required'] = getattr(field, 'required', False) + default = getattr(field, 'default', False) + if default and isinstance(default, (str, int)): + field_info['default'] = default + for attr in self.attrs: value = getattr(field, attr, None) if value is not None and value != '':