0
点赞
收藏
分享

微信扫一扫

8. CMDB后端开发(下)

王老师说 2023-05-03 阅读 58

CMDB后端开发(下)

API平台开发

新建主机功能

腾讯云

  • 在线API调试平台:https://console.cloud.tencent.com/api/explorer
  • API文档:https://cloud.tencent.com/document/product/296/19850
  • Python SDK:https://github.com/TencentCloud/tencentcloud-sdk-python
  • 获取AceessKey地址:https://console.cloud.tencent.com/cam/capi
  • 安装腾讯云sdk: pip install tencentcloud-sdk-python -i https://mirrors.tencent.com/pypi/simple/
  1. 腾讯云信息获取脚本: devops_api/libs/tencent_cloud.py
from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.cvm.v20170312 import cvm_client, models

class TCloud():
    def __init__(self, secret_id, secret_key):
        self.secret_id = secret_id
        self.secret_key = secret_key
        self.cred = credential.Credential(self.secret_id, self.secret_key)

    def region_list(self):
        client = cvm_client.CvmClient(self.cred, None)
        req = models.DescribeRegionsRequest()  # 获取地区
        try:
            resp = client.DescribeRegions(req)  # resp=[{"Region": "ap-guangzhou", "RegionName": "华南地区(广州)", "RegionState": "AVAILABLE"}, ]
            resp.code = 200
            return resp
        except TencentCloudSDKException as e:
            return {'code': '500', 'msg': e.message}

    def zone_list(self, region_id):
        client = cvm_client.CvmClient(self.cred, region_id)
        req = models.DescribeZonesRequest()
        try:
            resp = client.DescribeZones(req)
            resp.code = 200
            return resp
        except TencentCloudSDKException as e:
            return {'code': '500', 'msg': e.message}

    def instance_list(self, region_id):
        client = cvm_client.CvmClient(self.cred, region_id)  # 获取北京区域
        req = models.DescribeInstancesRequest()
        # req.InstanceIds = "ins-1511w4tn" #根据实例id获取
        try:
            resp = client.DescribeInstances(req)
            resp.code = 200
            return resp
        except TencentCloudSDKException as e:
            return {'code': '500', 'msg': e.message}

if __name__ == '__main__':
    cloud = TCloud("AKIDKUP3QWNxGI4ZnkQpwnMgiTxA7mAH8i02", "nJYWDtJSKwGJ5aokPownMegRu61f27wU")
    #result = cloud.region_list()
    #result = cloud.zone_list("ap-beijing")
    result = cloud.instance_list("ap-beijing")
    print(result)
  1. 获取腾讯云主机region, 示例信息接口: devops_api/cmdb/views.py
...
from libs.tencent_cloud import TCloud

class TencentCloudView(APIView):
    """
    腾讯云获取云主机信息
    """
    def get(self, request):
        """
        返回所有区域
        """
        secret_id = request.query_params.get('secret_id')
        secret_key = request.query_params.get('secret_key')

        # cloud = TencentCloud("AKIDKUP3QWNxGI4ZnkQpwnMgiTxA7mAH8i02", "nJYWDtJSKwGJ5aokPownMegRu61f27wU")
        cloud = TCloud(secret_id, secret_key)
        region_result = cloud.region_list()

        code = region_result.code
        if code == 200:
            # 二次处理,固定字段名,与阿里云的一致
            region = []
            for r in region_result.RegionSet:
                region.append({"region_id": r.Region, 'region_name': r.RegionName})
            res = {'code': code, 'msg': '获取区域列表成功!', 'data': region}
        else:
            res = {'code': code, 'msg': region_result.msg}
        return Response(res)

    def post(self, request):
        """
        根据区域名称创建机房,再导入云主机(关联机房)到数据库
        """
        secret_id = request.data.get('secret_id')
        secret_key = request.data.get('secret_key')
        server_group_id = int(request.data.get('server_group'))
        region_id = request.data.get('region')
        ssh_ip = request.data.get('ssh_ip')
        ssh_port = int(request.data.get('ssh_port'))

        cloud = TCloud(secret_id, secret_key)
        instance_result = cloud.instance_list(region_id)

        instance_list = []
        if instance_result.code == 200:
            instance_list = instance_result.InstanceSet
            if instance_result.TotalCount == 0:
                res = {'code': 500, 'msg': '该区域未发现云主机,请重新选择!'}
                return Response(res)
        elif instance_result.code == 500:
            res = {'code': 500, 'msg': '%s' %instance_result['msg']}
            return Response(res)

        # InstanceSet中可用区字段值是英文
        # 先获取可用区英文与中文对应,下面遍历主机再获取中文名
        zone_result = cloud.zone_list(region_id)
        zone_dict = {}
        for z in zone_result.ZoneSet:
            zone_dict[z.Zone] = z.ZoneName

        # 获取主机所在可用区
        # 可用区,机房表:机房名称
        host_zone_set = set()
        for host in instance_list:
            zone = host.Placement.Zone  # 可用区,例如 ap-beijing-1
            host_zone_set.add(zone_dict[zone])  # 获取中文名

        # 根据可用区创建机房
        for zone in host_zone_set:
            # 如果存在不创建
            idc = Idc.objects.filter(name=zone)
            if not idc:
                city = ""
                region_result = cloud.region_list()
                for r in region_result.RegionSet:  # 获取区域对应中文名
                    if r.Region == region_id:
                        city = r.RegionName

                Idc.objects.create(
                    name=zone,
                    city=city,
                    provider="腾讯云"
                )

        # 导入云主机信息到数据库
        for host in instance_list:
            zone = host.Placement.Zone
            instance_id = host.InstanceId  # 实例ID
            instance_name = host.InstanceName # 机器名称

            os_version = host.OsName
            private_ip = host.PrivateIpAddresses
            public_ip = host.PublicIpAddresses
            cpu = "%s核" %host.CPU
            memory = "%sG" %host.Memory

            disk = [{'device': 'None', 'size': host.SystemDisk.DiskSize, 'type': 'None'}]  # 默认保存是系统盘
            data_list = host.DataDisks
            if data_list:
                for d in data_list:
                    disk.append({'device': 'None', 'size': d.DiskSize, 'type': 'None'})

            create_date = time.strftime("%Y-%m-%d",time.strptime(host.CreatedTime, "%Y-%m-%dT%H:%M:%SZ"))
            expired_time = time.strftime("%Y-%m-%d %H:%M:%S",time.strptime(host.ExpiredTime, "%Y-%m-%dT%H:%M:%SZ"))
            # state = host.InstanceState

            # 创建服务器
            idc_name = zone_dict[zone]
            idc = Idc.objects.get(name=idc_name)

            if ssh_ip == "public":
                ssh_ip = public_ip[0]  # 使用第一个IP连接
            elif ssh_ip == "private":
                ssh_ip = private_ip[0]

            # 如果instance_id不存在才创建
            data = {'idc': idc,
                    'name': instance_name,
                    'hostname': instance_id,
                    'ssh_ip': ssh_ip,
                    'ssh_port': ssh_port,
                    'machine_type': 'cloud_vm',
                    'os_version': os_version,
                    'public_ip': public_ip,
                    'private_ip': private_ip,
                    'cpu_num': cpu,
                    'memory': memory,
                    'disk': disk,
                    'put_shelves_date': create_date,
                    'expire_datetime': expired_time,
                    'is_verified': 'verified'}
            server = Server.objects.filter(hostname=instance_id)
            if not server:
                server = Server.objects.create(**data)
                # 分组多对多
                group = ServerGroup.objects.get(id=server_group_id) # 根据id查询分组
                server.server_group.add(group) # 将服务器添加到分组
            else:
                server.update(**data)

        res = {'code': 200, 'msg': '导入云主机成功'}
        return Response(res)
        
  1. 路由配置: devops_api/devops_api/urls.py
...
from cmdb.views import CreateHostView,ExcelCreateHostView,AliyunCloudView,TencentCloudView
...
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^api/login/$', CustomAuthToken.as_view()),
    re_path('^api/change_password/$', token_auth.ChangeUserPasswordView.as_view()),
    re_path('^api/cmdb/create_host/$', CreateHostView.as_view()),
    re_path('^api/cmdb/excel_create_host/$', ExcelCreateHostView.as_view()),
    re_path('^api/cmdb/aliyun_cloud/$', AliyunCloudView.as_view()),
    re_path('^api/cmdb/tencent_cloud/$', TencentCloudView.as_view()),
]
...
  1. Apipost get 接口测试

image-20230503134002901

  1. Apipost post 接口测试

image-20230503140701051

  1. Idc数据查看:

image-20230503140751593

  1. Server 数据查看

image-20230503142207061

数据同步接口开发

  • 主要用于将主机信息同步更新到数据库
  1. 创建同步视图: devops_api/cmdb/views.py
...
class HostCollectView(APIView):
    def get(self, request):
        hostname = request.query_params.get('hostname')
        server = Server.objects.get(hostname=hostname)
        ssh_ip = server.ssh_ip
        ssh_port = server.ssh_port

        # 未绑定凭据并且没有选择凭据
        credential_id = request.query_params.get('credential_id')
        if not server.credential and not credential_id:
            result = {'code': 500, 'msg': '未发现凭据,请选择!'}
            return Response(result)
        elif server.credential:
            credential_id = int(server.credential.id)
        elif credential_id:
            credential_id = int(request.query_params.get('credential_id'))

        credential = Credential.objects.get(id=credential_id)

        username = credential.username
        if credential.auth_mode == 1:
            password = credential.password
            ssh = SSH(ssh_ip, ssh_port, username, password=password)
        else:
            private_key = credential.private_key
            ssh = SSH(ssh_ip, ssh_port, username, key=private_key)

        # 先SSH基本测试
        test = ssh.test()
        if test['code'] == 200:
            local_file = os.path.join(settings.BASE_DIR, 'cmdb', 'files', 'host_collect.py')
            remote_file = os.path.join('/tmp/host_collect.py')
            ssh.scp(local_file, remote_file)
            result = ssh.command('python %s' %remote_file)

            if result['code'] == 200:  # 采集脚本执行成功
                # 再进一步判断客户端采集脚本提交结果

                data = json.loads(result['data'])
                Server.objects.filter(hostname=hostname).update(**data)

                # 更新凭据ID
                server = Server.objects.get(hostname=hostname)
                server.credential = credential
                server.is_verified = 'verified'
                server.save()

                result = {'code': 200, 'msg': '主机配置同步成功'}

            else:
                result = {'code': 500, 'msg': '主机配置同步失败!错误:%s' %result['msg']}
        else:
            result = {'code': 500, 'msg': 'SSH连接异常!错误:%s' %test['msg']}
        return Response(result)
  1. 修改路由: devops_api/devops_api/urls.py
...
from cmdb.views import CreateHostView,ExcelCreateHostView,AliyunCloudView,TencentCloudView,HostCollectView
...
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^api/login/$', CustomAuthToken.as_view()),
    re_path('^api/change_password/$', token_auth.ChangeUserPasswordView.as_view()),
    re_path('^api/cmdb/create_host/$', CreateHostView.as_view()),
    re_path('^api/cmdb/excel_create_host/$', ExcelCreateHostView.as_view()),
    re_path('^api/cmdb/aliyun_cloud/$', AliyunCloudView.as_view()),
    re_path('^api/cmdb/tencent_cloud/$', TencentCloudView.as_view()),
    re_path('^api/cmdb/host_collect/$', HostCollectView.as_view()),
]
...
  1. 找个本地虚拟机故意改错配置: 4核改1核

image-20230503143145934

  1. Apipost 同步测试

image-20230503143649482

  1. 检查是否同步成功

image-20230503143734488

启用接口文档

  1. 安装swagger
pip install django-rest-swagger
  1. 项目中启用swagger: devops_api/devops_api/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework.authtoken',
    'rest_framework_swagger',
    'django_filters',
    'cmdb',
    'system_config'
]

...
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'libs.pagination.MyPagination',
    # 认证
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],
    # 权限
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated'  # 登录后就能访问所有API
    ],
     # API接口文档
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
  1. 配置路由: devops_api/devops_api/urls.py
...
#接口文档
from rest_framework_swagger.views import get_swagger_view
schema_view = get_swagger_view(title='接口文档')

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^api/docs/$', schema_view),
    re_path('^api/login/$', CustomAuthToken.as_view()),
    re_path('^api/change_password/$', token_auth.ChangeUserPasswordView.as_view()),
    re_path('^api/cmdb/create_host/$', CreateHostView.as_view()),
    re_path('^api/cmdb/excel_create_host/$', ExcelCreateHostView.as_view()),
    re_path('^api/cmdb/aliyun_cloud/$', AliyunCloudView.as_view()),
    re_path('^api/cmdb/tencent_cloud/$', TencentCloudView.as_view()),
    re_path('^api/cmdb/host_collect/$', HostCollectView.as_view()),
]
...
  1. 测试查看

image-20230503144645383

举报

相关推荐

8.原型链

8.事件对象

8.【CSS浮动】

8.静态文件

CMS 8.表格

0 条评论