diff --git a/README.md b/README.md index 8d8a375a..7024397d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -devEops :laughing: 开发自运维平台 +devEops :laughing: 开发自运维平台 :no_mouth: 后端 ================================ [![Travis Build Status](https://travis-ci.org/YoLoveLife/DevOps.svg?branch=master)](https://travis-ci.org/YoLoveLife/DevOps) [![Github All Releases](https://img.shields.io/github/downloads/atom/atom/total.svg)](https://github.com/YoLoveLife/devEops/releases) @@ -6,11 +6,12 @@ devEops :laughing: 开发自运维平台 Author: [YoLoveLife.com](http://www.yolovelife.com) :ok_hand:
:heart: 感谢所有 :star: 我的人 你们是我努力的动力
+该项目为devEops的后端部分 前端部分请关注 :heart: [devEops-Web](https://github.com/YoLoveLife/devEopsWeb)
运维体系解决方案(正在成长中)
-新的Vue前端已经逐步上线啦!
+前后段分离啦!告别之前的Jinja2
:muscle: 实践以资产、应用信息为中心的运维平台
-django & rest-framework & bootstrap
+django模型 & rest-framework
:muscle: 参考了诸多django项目 我的Django用的真的很棒
travis & django TestCase
:muscle: 单元测试 测试不规范但是努力去做 @@ -33,32 +34,56 @@ travis & django TestCase
本开发自运维平台致力于IT资源信息的整合与自动化运维,通过服务、应用配置的信息整合来运维提供帮助。
devEops正在不断成长 - 所有运维操作都基于信息整合的正确性和一致性(资产信息管理) :floppy_disk:
-- 基于WebSocket以及SSHProxy的软件堡垒机操作
-- :bar_chart: 提供资产信息的统计,应用系统的占比、脚本|剧本的调用次数等
-- ~~可临时搜集应用上的信息(如MySQL的status等信息)~~
-- ~~所有运维操作(脚本 | 剧本)在提交的时候会自动注入资产信息并通过ansible远程执行~~
-- 所有资产信息、架构信息都存储在数据库中供所有运维人员操作
-- ~~开发人员可登陆平台提交工单发起防火墙修改、应用发布、日常运维 :clock9: 等操作,运维人员许可操作进行~~
-- ~~所有应用发布、日常运维工作日程展示 :date:~~
+- 对接VMware|Aliyun自动拉取信息入库资产
+- :bar_chart: 提供各类信息统计图表(资产|运维)等
+- 基于Ansile的运维元操作|任务的定义
+- 基于WebSocket以及SSHProxy的工单操作
+- 基于Django自带auth认证体系的资产与权限关联
+- 域名管理与解析信息查询与刷新
- 详细权限操作,区分开发人员以及运维人员
## 平台一览 ### 登陆界面 ![LOGIN](img/login.png)
+### 仪表盘界面 +根据资源的检出位置|操作系统类型|运维操作 等图表化展现
+~~并且每周提供可供保存的静态页面的报告(有!但是很丑!) +![DASHBOARD](img/dashboard.png) ### 资产管理 +资产根据来源(VMWARE|ALIYUN)入库 运维人员进行归类 ![GROUP](img/group.png)
-### 执行基于playbook的元操作 -![JUMPER](img/playbook.png)
-### 批量主机信息上传 -![UPLOAD](img/upload.png)
-### 脚本编写 -![SCRIPT](img/script.png)
-### 时间线展示 -![TIMELINE](img/timeline.png)
+应用组可定义架构图 关联跳板机以及密钥 :key:
+应用组可全局添加参数 该参数会注入运维操作中的变量中
+![HOST](img/host.png) +主机可以根据任意信息进行检索
+~~根据VMware|ALIYUN API可进行详细信息(~~监控|配置)获取查看
+### 域名管理 +基于树结构的域名管理 定时解析域名 保证记录域名解析正确 供正反向查询 +![DNS](img/dns.png) +### 密钥管理 +基于服务器端直接生成的ssh-rsa密钥 私钥将会直接加密存储入数据库
+只有当您做运维操作|SSH连接的时候才会解析并且使用
+![KEY](img/key.png) +### QR-Code +基于Google-Authority的二次验证 目前用户体系中已经包含验证+生成
+![QRCODE](img/qrcode.png) +### 元操作及任务 +基于Ansible的数据模型 将用户的Ansible直接存储入数据库中
+当用户使用任务直接从CMDB中导出需要执行的主机列表以及参数列表注入Ansible-API中
+![META](img/meta.png) +选择元操作所涉及的主机范围
+![META](img/meta_bt.png) +选择某个应用组下的若干个元操作组成一个任务
+![MISSION](img/mission.png) +### 工单系统 +用户根据自身权限可以发布工单(涉及任务)进行执行
+![CODEWORK](img/work.png) +选择工单后用户可以进行执行 系统建立websocket将所有执行结果返回 +![RUN](img/run.png) ## 如何安装 -devEops依赖于python2.7、诸多python第三方模块、mysql数据库、redis缓存
-以下操作环境已经拥有python2.7、mysql数据库、redis缓存 +devEops依赖于python3.5、诸多python第三方模块、mysql数据库、redis缓存
+以下操作环境已经拥有python3.5、mysql数据库、redis缓存 ```bash $ cd path/to/project/folder/ diff --git a/apps/authority/models.py b/apps/authority/models.py index 4fe8b23f..fd886c37 100644 --- a/apps/authority/models.py +++ b/apps/authority/models.py @@ -62,7 +62,6 @@ def public_key(self): @public_key.setter def public_key(self, public_key): - print('pub',aes.encrypt(public_key).decode()) self._public_key = aes.encrypt(public_key).decode() @property @@ -199,5 +198,16 @@ def check_status(self): self._status = 1 return 1 + @property def to_yaml(self): - return '-o ProxyCommand="ssh -p{PORT} -W %h:%p -q root@{IP} nc"'.format(PORT=self.sshport,IP=self.connect_ip) \ No newline at end of file + return { + u'set_fact': + { + 'ansible_ssh_common_args': + '-o ProxyCommand="ssh -p{PORT} -i {KEY} -W %h:%p root@{IP}"'.format( + PORT=self.sshport, + IP=self.connect_ip, + KEY='{{KEY}}' + ) + } + } \ No newline at end of file diff --git a/apps/dashboard/tasks.py b/apps/dashboard/tasks.py index 9970a612..f963d0da 100644 --- a/apps/dashboard/tasks.py +++ b/apps/dashboard/tasks.py @@ -8,7 +8,7 @@ from celery.task import periodic_task from celery.schedules import crontab from pyecharts import Pie -from deveops.conf import ALIYUN_PAGESIZE,REDIS_PORT,REDIS_SPACE,EXPIREDTIME +from deveops.conf import ALIYUN_PAGESIZE,REDIS_PORT,REDIS_SPACE,EXPIREDTIME,DASHBOARD_TIME,EXPIRED_TIME,MANAGER_TIME from deveops import settings import redis, datetime, json, os from deveops.utils import aliyun @@ -22,7 +22,7 @@ connect = redis.StrictRedis(port=REDIS_PORT,db=REDIS_SPACE) -@periodic_task(run_every=crontab(minute="*")) +@periodic_task(run_every=DASHBOARD_TIME) def weeklyDashboard(): import jinja2 loader = jinja2.FileSystemLoader(settings.BASE_DIR+'/apps/dashboard/docs/', encoding='utf-8') @@ -110,7 +110,7 @@ def weeklyDashboard(): smtp.sendMail('devEops平台运维周报', msg, ['yz2@8531.cn'])#,'wzz@8531.cn','xubin@8531.cn','xuchenliang@8531.cn']) -@periodic_task(run_every=crontab(minute=1,hour=1)) +@periodic_task(run_every=EXPIRED_TIME) def aliyunECSExpiredInfoCatch(): ExpiredAliyunECS.objects.all().delete() countNumber = aliyun.fetch_ECSPage() @@ -127,7 +127,7 @@ def aliyunECSExpiredInfoCatch(): ExpiredAliyunECS(**instance_data).save() -@periodic_task(run_every=crontab(minute=2,hour=1)) +@periodic_task(run_every=EXPIRED_TIME) def aliyunRDSInfoCatch(): ExpiredAliyunRDS.objects.all().delete() countNumber = aliyun.fetch_RDSPage() @@ -143,7 +143,7 @@ def aliyunRDSInfoCatch(): ExpiredAliyunRDS(**resolver.AliyunRDS2Json.decode(dt)).save() -@periodic_task(run_every=crontab(minute=3,hour=1)) +@periodic_task(run_every=EXPIRED_TIME) def aliyunKVStoreInfoCatch(): ExpiredAliyunKVStore.objects.all().delete() countNumber = aliyun.fetch_KVStorePage() @@ -158,7 +158,7 @@ def aliyunKVStoreInfoCatch(): ExpiredAliyunKVStore(**resolver.AliyunKVStore2Json.decode(dt)).save() -@periodic_task(run_every=crontab(minute=4,hour=1)) +@periodic_task(run_every=EXPIRED_TIME) def aliyunMongoDBInfoCatch(): ExpiredAliyunMongoDB.objects.all().delete() countNumber = aliyun.fetch_MongoDBPage() @@ -175,7 +175,7 @@ def aliyunMongoDBInfoCatch(): ExpiredAliyunMongoDB(**resolver.AliyunMongoDB2Json.decode(dt)).save() -@periodic_task(run_every=crontab(minute=10,hour=1)) +@periodic_task(run_every=MANAGER_TIME) def managerStatusCatch(): connect.delete('MANAGER_STATUS') diff --git a/apps/manager/filter.py b/apps/manager/filter.py index 1287d0dc..41844387 100644 --- a/apps/manager/filter.py +++ b/apps/manager/filter.py @@ -13,7 +13,6 @@ class HostFilter(django_filters.FilterSet): connect_ip = django_filters.CharFilter(method="connect_ip_filter") - service_ip = django_filters.CharFilter(method="service_ip_filter") info = django_filters.CharFilter(method="info_filter") systype = django_filters.CharFilter(method="systype_filter") position = django_filters.CharFilter(method="position_filter") @@ -21,16 +20,12 @@ class HostFilter(django_filters.FilterSet): class Meta: model = models.Host - fields = ['groups', 'connect_ip', 'service_ip', 'hostname', 'sshport', 'info', 'systype', 'position'] + fields = ['groups', 'connect_ip', 'hostname', 'sshport', 'info', 'systype', 'position'] @staticmethod def connect_ip_filter(queryset, first_name, value): return queryset.filter(connect_ip__icontains=value) - @staticmethod - def service_ip_filter(queryset, first_name, value): - return queryset.filter(service_ip__icontains=value) - @staticmethod def info_filter(queryset, first_name, value): details = models.HostDetail.objects.filter(info__icontains=value) diff --git a/apps/manager/models.py b/apps/manager/models.py index db5b5d3d..1aab8500 100644 --- a/apps/manager/models.py +++ b/apps/manager/models.py @@ -156,7 +156,6 @@ class Host(models.Model): # 相关信息 # connect_ip = models.GenericIPAddressField(default='', null=False) connect_ip = models.CharField(max_length=15, default='', null=False) - service_ip = models.CharField(max_length=15, default='0.0.0.0', null=True) # service_ip = models.GenericIPAddressField(default='0.0.0.0', null=True) # 主机名称 diff --git a/apps/manager/query.py b/apps/manager/query.py deleted file mode 100644 index 8eb03a16..00000000 --- a/apps/manager/query.py +++ /dev/null @@ -1,41 +0,0 @@ -import models -import json -from django.db.models import Q - -def systemtypeQuery(): - list = [] - choices = models.System_Type.objects.all() - for choice in choices: - dit = {} - dit['name'] = choice.name - dit['value'] = models.Host.objects.filter(systemtype=choice).count() - list.append(dit) - return json.dumps(list, ensure_ascii=False, encoding='UTF-8') - - -def groupQuery(): - list = [] - grouplist=models.Group.objects.all() - - for group in grouplist: - dit = {} - dit['name'] = group.name - dit['value'] = group.hosts.count() - list.append(dit) - return json.dumps(list, ensure_ascii=False, encoding='UTF-8') - - -def cleanQuery(**kwargs): - list = [] - for key in kwargs: - list.append((key+'__contains', kwargs[key]),) - return list - - -def hostQuery(*args,**kwargs): - list = cleanQuery(**kwargs) - q = Q() - q.connector = 'AND' - q.children = list - return models.Host.objects.filter(q) - diff --git a/apps/manager/serializers.py b/apps/manager/serializers.py index f68521de..2be5905e 100644 --- a/apps/manager/serializers.py +++ b/apps/manager/serializers.py @@ -12,11 +12,11 @@ class GroupSerializer(serializers.HyperlinkedModelSerializer): users = serializers.PrimaryKeyRelatedField(many=True, queryset=ExtendUser.objects.all()) - pmn_groups = serializers.PrimaryKeyRelatedField(many=True, queryset=models.PerGroup.objects.all()) + pmn_groups = serializers.PrimaryKeyRelatedField(required=False, many=True, queryset=models.PerGroup.objects.all()) key = serializers.PrimaryKeyRelatedField(required=False, queryset=models.Key.objects.all(), allow_null=True) jumper = serializers.PrimaryKeyRelatedField(required=False, queryset=models.Jumper.objects.all(), allow_null=True) _status = serializers.IntegerField(required=True, source='status',) - _framework = serializers.PrimaryKeyRelatedField(queryset=models.FILE.objects.all(), allow_null=True) + _framework = serializers.PrimaryKeyRelatedField(required=False, queryset=models.FILE.objects.all(), allow_null=True, write_only=True) framework = serializers.ImageField(source="_framework.image", read_only=True) class Meta: model = models.Group @@ -26,6 +26,9 @@ class Meta: read_only_fields = ( 'id', 'uuid', 'framework' ) + write_onlu_fields = ( + '_framework' + ) def update(self, instance, validated_data): # instance.framework_update() @@ -111,7 +114,7 @@ class HostSerializer(serializers.ModelSerializer): class Meta: model = models.Host fields = ( - 'id', 'detail', 'connect_ip', 'service_ip', 'hostname', 'sshport', '_status', 'groups', + 'id', 'detail', 'connect_ip', 'hostname', 'sshport', '_status', 'groups', 'passwd', 'uuid' ) read_only_fields = ( diff --git a/apps/manager/tasks.py b/apps/manager/tasks.py index 8d7e5f2b..d3e58501 100644 --- a/apps/manager/tasks.py +++ b/apps/manager/tasks.py @@ -7,14 +7,9 @@ from celery.task import periodic_task from celery.schedules import crontab from manager.models import Host,HostDetail,Position,System_Type -from deveops.conf import REDIS_PORT,REDIS_SPACE,EXPIREDTIME -import redis +from django.conf import settings -connect = redis.StrictRedis(port=REDIS_PORT,db=REDIS_SPACE) - -from celery import shared_task - -@periodic_task(run_every=crontab(minute=50,hour=0)) +@periodic_task(run_every=settings.MANAGER_TIME) def aliyunECSInfoCatch(): from deveops.utils import aliyun from deveops.conf import ALIYUN_PAGESIZE @@ -68,52 +63,89 @@ def aliyunECSInfoCatch(): # @periodic_task(run_every=crontab(minute=0,hour=[0,3,6,9,12,15,18,21])) -@periodic_task(run_every=crontab(minute=31,hour=8)) -def vmwareInfoCatch(): - from deveops.utils import vmware - children = vmware.fetch_AllInstance() - position = None - systype = None - if Position.objects.filter(name__contains='集团内').exists(): - position = Position.objects.filter(name__contains='集团内').get() +# @periodic_task(run_every=crontab(minute=31,hour=8)) +# def vmwareInfoCatch(): +# from deveops.utils import vmware +# children = vmware.fetch_AllInstance() +# position = None +# systype = None +# if Position.objects.filter(name__contains='集团内').exists(): +# position = Position.objects.filter(name__contains='集团内').get() +# else: +# position = Position.objects.create(name='集团内') +# +# for child in children: +# ''' +# {'privateMemory': 8500, +# 'powerState': 'poweredOn', 'name': 'DWETL02', 'uptimeSeconds': 12389645, 'numCpu': 8, 'overallCpuUsage': 0, +# 'memoryMB': 16384, 'memorySizeMB': 16384, 'guestMemoryUsage': 0, 'committed': 24335576477L, 'hostMemoryUsage': 9265, +# 'ipAddress': '10.100.63.69', 'sharedMemory': 1662, 'unshared': 24212668416L} +# ''' +# list = vmware.FetchInfo(child) +# status = 1 +# if not list['powerState'] == 'poweredOn': +# status = 0 +# continue +# +# if System_Type.objects.filter(name=list['guestFullName']).count() ==0: +# systype = System_Type.objects.create(name=list['guestFullName']) +# else: +# systype = System_Type.objects.filter(name=list['guestFullName']).get() +# +# if not list.__contains__('ipAddress'): +# continue +# if list['ipAddress'] is None: +# continue +# +# query = Host.objects.filter(detail__vmware_id=list['uuid'] ,connect_ip=list['ipAddress']) +# if not query.exists(): +# detail_instance = HostDetail.objects.create(vmware_id=list['uuid'], info='', position=position, systemtype=systype) +# host_instance = Host.objects.create( +# detail=detail_instance, +# connect_ip=list['ipAddress'], +# hostname=list['name'], +# status=status, +# password='nopassword' +# ) +# else: +# host_instance = query.get() +# host_instance.detail.save() +# host_instance.status = status +# host_instance.save() + +def host_maker(dict_models): + systype_query = System_Type.objects.filter(name=dict_models['detail']['systemtype']) + if not systype_query.exists(): + systype = System_Type.objects.create(name=dict_models['detail']['systemtype']) + dict_models['detail']['systemtype'] = systype else: - position = Position.objects.create(name='集团内') - - for child in children: - ''' - {'privateMemory': 8500, - 'powerState': 'poweredOn', 'name': 'DWETL02', 'uptimeSeconds': 12389645, 'numCpu': 8, 'overallCpuUsage': 0, - 'memoryMB': 16384, 'memorySizeMB': 16384, 'guestMemoryUsage': 0, 'committed': 24335576477L, 'hostMemoryUsage': 9265, - 'ipAddress': '10.100.63.69', 'sharedMemory': 1662, 'unshared': 24212668416L} - ''' - list = vmware.FetchInfo(child) - status = 1 - if not list['powerState'] == 'poweredOn': - status = 0 - continue - - if System_Type.objects.filter(name=list['guestFullName']).count() ==0: - systype = System_Type.objects.create(name=list['guestFullName']) - else: - systype = System_Type.objects.filter(name=list['guestFullName']).get() - - if not list.__contains__('ipAddress'): - continue - if list['ipAddress'] is None: - continue - - query = Host.objects.filter(detail__vmware_id=list['uuid'] ,connect_ip=list['ipAddress']) - if not query.exists(): - detail_instance = HostDetail.objects.create(vmware_id=list['uuid'], info='', position=position, systemtype=systype) - host_instance = Host.objects.create( - detail=detail_instance, - connect_ip=list['ipAddress'], - hostname=list['name'], - status=status, - password='nopassword' - ) - else: - host_instance = query.get() - host_instance.detail.save() - host_instance.status = status - host_instance.save() \ No newline at end of file + dict_models['detail']['systemtype'] = systype_query.get() + + position_query = Position.objects.filter(name=dict_models['detail']['position']) + if not position_query.exists(): + posistion = Position.objects.create(name=dict_models['detail']['position']) + dict_models['detail']['position'] = posistion + else: + dict_models['detail']['position'] = position_query.get() + + detail_dict = dict_models.pop('detail') + detail = HostDetail.objects.create(**detail_dict) + dict_models['detail'] = detail + + host = Host.objects.create(**dict_models) + + + +@periodic_task(run_every=settings.MANAGER_TIME) +def vmware2cmdb(): + from deveops.tools import vmware + API = vmware.VmwareTool() + childrens = API.get_all_vms() + for child in childrens: + dict_models = API.get_vm_models(child) + host_query = Host.objects.filter(detail__vmware_id=dict_models['detail']['vmware_id'], connect_ip=dict_models['connect_ip']) + if not host_query.exists(): + host_maker(dict_models) + + + diff --git a/apps/ops/ansible/playbook.py b/apps/ops/ansible/playbook.py index 174936e1..b814886b 100644 --- a/apps/ops/ansible/playbook.py +++ b/apps/ops/ansible/playbook.py @@ -16,7 +16,7 @@ class Playbook(object): - def __init__(self, vars_dict, host_list, consumer, key, push_mission): + def __init__(self, host_list, consumer, key, push_mission): self.loader = DataLoader() self.options = Options( connection='smart', module_path='', forks=100, become=None, @@ -26,11 +26,10 @@ def __init__(self, vars_dict, host_list, consumer, key, push_mission): self.key = key self.stdout_callback = callback.AnsibleCallback(consumer, push_mission) self.consumer = consumer - print('ddr',host_list) + self.inventory = InventoryManager(loader=self.loader, sources=host_list+',') self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory) - self.variable_manager.extra_vars = vars_dict self.play = [] def delete_key(self): @@ -38,9 +37,15 @@ def delete_key(self): if os.path.exists(self.key): os.remove(self.key) + def import_vars(self, vars_dict): + self.consumer.send('Load Vars =>\r\n') + vars_dict['KEY']=self.key + self.variable_manager.extra_vars = vars_dict + def import_task(self, play_source): self.consumer.send('Load Task =>\r\n') for source in play_source: + print('source',source) self.play.append(Play().load(source, variable_manager=self.variable_manager, loader=self.loader)) def run(self): @@ -57,7 +62,7 @@ def run(self): self.consumer.send("Start => \r\n") for p in self.play: result = tqm.run(p) - # self.delete_key() + self.delete_key() self.consumer.send('执行完毕\r\n') finally: diff --git a/apps/ops/consumers.py b/apps/ops/consumers.py index 323ab98c..7a5fdb7b 100644 --- a/apps/ops/consumers.py +++ b/apps/ops/consumers.py @@ -33,6 +33,7 @@ def websocket_receive(self, message): pass def websocket_disconnect(self, message): + print('closecloseclosecloseclosecloseclose') self.close() def websocket_connect(self, message): @@ -56,8 +57,8 @@ def websocket_connect(self, message): # 判断该工单是否具备可执行的先决条件 try: self.write_key(work.mission.group.key, KEY) - import uuid - isinstance(work.mission.group.jumper.uuid, uuid.UUID) + # import uuid + # isinstance(work.mission.group.jumper.uuid, uuid.UUID) except AttributeError as attr_error: self.send('\r\n您执行的任务缺少必要的密钥或者跳板机请联系管理员解决') self.close() diff --git a/apps/ops/interactive.py b/apps/ops/interactive.py index 1ed99ccb..3a72f3bc 100644 --- a/apps/ops/interactive.py +++ b/apps/ops/interactive.py @@ -25,7 +25,8 @@ def __init__(self, work, play_source, inventory, key, vars_dict, consumer): self.work = work def run(self): - p = playbook.Playbook(self.vars_dict, self.inventory, self.consumer, self.key, self.work.push_mission) + p = playbook.Playbook(self.inventory, self.consumer, self.key, self.work.push_mission) + p.import_vars(self.vars_dict) p.import_task(self.play_source) p.run() self.work.status = 3 diff --git a/apps/ops/models.py b/apps/ops/models.py index c41b8dd0..a117a391 100644 --- a/apps/ops/models.py +++ b/apps/ops/models.py @@ -85,38 +85,53 @@ def file_list(self): def to_yaml(self): tasks = [] hosts_list = [] - obj = {} for host in self.hosts.all(): - if host._status == 1: + if host.status == 1: hosts_list.append(host.connect_ip) - if len(hosts_list) == 0: - # 主机列表为空说明本地执行 - for content in self.contents.all().order_by('sort'): - tasks.append(content.to_yaml) - obj = { - 'tasks': tasks, - 'gather_facts': 'no', - 'hosts': 'localhost', - } - else: - jumper = self.group.jumper - if jumper is not None: - proxy_task = { - u'set_fact': - { - 'ansible_ssh_common_args': - '-o ProxyCommand="ssh -p{PORT} -W %h:%p root@{IP}"'.format(PORT=jumper.sshport, IP=jumper.connect_ip) - } - } - tasks.append(proxy_task) - for content in self.contents.all().order_by('sort'): - tasks.append(content.to_yaml) - obj = { - 'tasks': tasks, - 'gather_facts': 'no', - 'hosts': ','.join(hosts_list)+',', - } - return obj + if self.group.jumper is not None: + tasks.append(self.group.jumper.to_yaml) + + for content in self.contents.all().order_by('sort'): + tasks.append(content.to_yaml) + + return { + 'tasks': tasks, + 'gather_facts': 'no', + 'hosts': hosts_list or 'localhost', + } + + # + # for host in self.hosts.all(): + # if host._status == 1: + # hosts_list.append(host.connect_ip) + # if len(hosts_list) == 0: + # # 主机列表为空说明本地执行 + # for content in self.contents.all().order_by('sort'): + # tasks.append(content.to_yaml) + # obj = { + # 'tasks': tasks, + # 'gather_facts': 'no', + # 'hosts': 'localhost', + # } + # else: + # jumper = self.group.jumper + # if jumper is not None: + # proxy_task = { + # u'set_fact': + # { + # 'ansible_ssh_common_args': + # '-o ProxyCommand="ssh -p{PORT} -W %h:%p root@{IP}"'.format(PORT=jumper.sshport, IP=jumper.connect_ip) + # } + # } + # tasks.append(proxy_task) + # for content in self.contents.all().order_by('sort'): + # tasks.append(content.to_yaml) + # obj = { + # 'tasks': tasks, + # 'gather_facts': 'no', + # 'hosts': ','.join(hosts_list)+',', + # } + # return obj class Mission(models.Model): diff --git a/apps/yodns/api.py b/apps/yodns/api.py index b5a826df..342b2cf6 100644 --- a/apps/yodns/api.py +++ b/apps/yodns/api.py @@ -70,4 +70,4 @@ def delete(self, request, *args, **kwargs): if dns_obj.sons.exists(): return Response({'detail':'该域名节点下存在子节点'}, status=status.HTTP_406_NOT_ACCEPTABLE) else: - return super(DNSDeleteAPI, self).create(request, *args, **kwargs) \ No newline at end of file + return super(DNSDeleteAPI, self).delete(request, *args, **kwargs) \ No newline at end of file diff --git a/apps/yodns/tasks.py b/apps/yodns/tasks.py index 03ac053b..004bb3d6 100644 --- a/apps/yodns/tasks.py +++ b/apps/yodns/tasks.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, unicode_literals from celery.task import periodic_task from celery.schedules import crontab -from deveops.conf import INNER_DNS,OUTER_DNS +from django.conf import settings from yodns.models import DNS from django.db.models import Q from dns import resolver @@ -30,9 +30,9 @@ def reflush(obj,nameserver): return '' -@periodic_task(run_every=crontab(minute='*')) +@periodic_task(run_every=settings.DNS_TIME) def DNSFlush(): for dns in DNS.objects.all().exclude(Q(father__isnull=True)|Q(father__father__isnull=True)): - dns.inner_dig = reflush(dns,INNER_DNS) - dns.dig = reflush(dns,OUTER_DNS) + dns.inner_dig = reflush(dns,settings.INNER_DNS) + dns.dig = reflush(dns,settings.OUTER_DNS) dns.save() \ No newline at end of file diff --git a/deveops/routing.py b/deveops/routing.py index 53c38127..46c88ff6 100644 --- a/deveops/routing.py +++ b/deveops/routing.py @@ -19,7 +19,7 @@ # manager_routing # ) # ), - 'websocket': SessionMiddleware( + 'websocket': AuthMiddlewareStack( URLRouter( ops_routing ) diff --git a/deveops/settings.py b/deveops/settings.py index a7d35f7b..34cef5fa 100644 --- a/deveops/settings.py +++ b/deveops/settings.py @@ -242,6 +242,23 @@ else: pass +#VMARE +VMWARE_USERNAME = DEVEOPS_CONF.VMWARE_USERNAME +VMWARE_PASSWD = DEVEOPS_CONF.VMWARE_PASSWD +VMWARE_SERVER = DEVEOPS_CONF.VMWARE_SERVER + +# DNS +INNER_DNS = DEVEOPS_CONF.INNER_DNS +OUTER_DNS = DEVEOPS_CONF.OUTER_DNS + + +#CRONTAB +DASHBOARD_TIME = DEVEOPS_CONF.DASHBOARD_TIME +EXPIRED_TIME = DEVEOPS_CONF.EXPIRED_TIME +MANAGER_TIME = DEVEOPS_CONF.MANAGER_TIME +DNS_TIME = DEVEOPS_CONF.DNS_TIME + + #CHANNEL CHANNEL_LAYERS = { diff --git a/deveops/tools/__init__.py b/deveops/tools/__init__.py new file mode 100644 index 00000000..104ed2e4 --- /dev/null +++ b/deveops/tools/__init__.py @@ -0,0 +1,5 @@ +# -*- coding:utf-8 -*- +# !/usr/bin/env python +# Time 18-6-26 +# Author Yo +# Email YoLoveLife@outlook.com \ No newline at end of file diff --git a/deveops/tools/vmware.py b/deveops/tools/vmware.py new file mode 100644 index 00000000..43d11005 --- /dev/null +++ b/deveops/tools/vmware.py @@ -0,0 +1,116 @@ +# -*- coding:utf-8 -*- +# !/usr/bin/env python +# Time 18-6-26 +# Author Yo +# Email YoLoveLife@outlook.com +import os +import django +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "deveops.settings") +django.setup() + + + + +import atexit +from pyVim import connect +from pyVmomi import vmodl +from pyVmomi import vim +from django.conf import settings as DJANGO_SETTINGS + +class VmwareTool(object): + def __init__(self): + self.service_instance = connect.SmartConnectNoSSL( + host=DJANGO_SETTINGS.VMWARE_SERVER, + user=DJANGO_SETTINGS.VMWARE_USERNAME, + pwd=DJANGO_SETTINGS.VMWARE_PASSWD, + port=443 + ) + + atexit.register(connect.Disconnect, self.service_instance) + + def get_all_vms(self): + try: + content = self.service_instance.RetrieveContent() + container = content.rootFolder + viewType = [vim.VirtualMachine] + recursive = True + containerView = content.viewManager.CreateContainerView( + container, viewType, recursive + ) + children = containerView.view + return children + except vmodl.MethodFault as error: + return -1 + + @staticmethod + def get_vm_status(info): + if info != 'poweredOn': + return 0 + else: + return 1 + + @staticmethod + def get_vm_models(vm): + ''' + :param vm: VMware API 返回的vm對象 + :return: 返回一個可以直接被models存儲的字典對象 + ''' + return { + 'hostname': vm.config.name, + 'connect_ip': vm.summary.guest.ipAddress or '127.0.0.1', + 'sshport': 52000, + 'status': VmwareTool.get_vm_status(vm.summary.runtime.powerState), + 'detail':{ + 'systemtype': vm.config.guestFullName, + 'position': 'Center{IP}'.format(IP=DJANGO_SETTINGS.VMWARE_SERVER), + 'vmware_id': vm.config.uuid, + }, + } + + @staticmethod + def get_vm_detail(vm): + ''' + :param vm: VMware API 返回的vm對象 + :return: 返回該主機所有信息字典 + ''' + return { + 'extend': { + 'memory': vm.config.hardware.memoryMB, + 'cpus': vm.config.hardware.numCPU, + } + } + + def get_vm_monitor(self,vm): + try: + content = self.service_instance.RetrieveContent() + perfManager = content.perfManager + metricId = vim.PerformanceManager.MetricId(counterId=2, instance="*") + import datetime + startTime = datetime.datetime.now() - datetime.timedelta(hours=3) + endTime = datetime.datetime.now() + # query = vim.PerformanceManager.QuerySpec(maxSample=1, + # entity=vm, + # metricId=[metricId], + # startTime=startTime, + # endTime=endTime) + # query = vim.PerformanceManager.QueryAvailablePerfMetric(entity=vm, + # startTime=startTime, + # endTime=endTime) + query = perfManager.QueryPerfProviderSummary(entity=vm) + print(query) + # print(perfManager.QueryPerf(querySpec=[query])) + # print(perfManager.QueryPerfCounter(counterId=[24,])) + # print(perfManager.QueryPerfCounterByLevel(level=1)) + ''' + 2 CPU 在该时间间隔内的使用情况(百分比) CPU + 24 内存使用情况,表示为占总的配置或可用内存的百分比 MEMORY + 136 收集时间间隔内每秒钟磁盘平均读取次数 DISK OUT + 137 收集时间间隔内每秒钟磁盘平均写入次数 DISK IN + 146 该时间间隔内收到的数据包数 NETWORK + ''' + except vmodl.MethodFault as e: + print("Caught vmodl fault : " + e.msg) + return -1 + except Exception as e: + print("Caught exception : " + str(e)) + return -1 \ No newline at end of file diff --git a/deveops/utils/checkpass.py b/deveops/utils/checkpass.py deleted file mode 100644 index 9e54d5c4..00000000 --- a/deveops/utils/checkpass.py +++ /dev/null @@ -1,50 +0,0 @@ -# -*- coding:utf-8 -*- -# !/usr/bin/env python -# Time 17-11-24 -# Author Yo -# Email YoLoveLife@outlook.com - -import re -def checkLen(passwd): - return len(passwd)>12 - -def checkContainUpper(passwd): - pattern = re.compile('[A-Z]+') - match = pattern.findall(passwd) - if match: - return True - else: - return False - -def checkContainNum(passwd): - pattern = re.compile('[0-9]+') - match = pattern.findall(passwd) - if match: - return True - else: - return False - -def checkContainLower(passwd): - pattern = re.compile('[a-z]+') - match = pattern.findall(passwd) - if match: - return True - else: - return False - -def checkSymbol(passwd): - pattern = re.compile('([^a-z0-9A-Z])+') - match = pattern.findall(passwd) - if match: - return True - else: - return False - -def checkPassword(passwd): - # lenOK = checkLen(passwd) - upperOK = checkContainUpper(passwd) - lowerOK = checkContainLower(passwd) - # numOK = checkContainNum(passwd) - # symbolOK = checkSymbol(passwd) - # return (lenOK and upperOK and lowerOK and numOK and symbolOK) - return (upperOK and lowerOK) \ No newline at end of file diff --git a/deveops/utils/ddr.py b/deveops/utils/ddr.py new file mode 100644 index 00000000..267f9624 --- /dev/null +++ b/deveops/utils/ddr.py @@ -0,0 +1,45 @@ +# -*- coding:utf-8 -*- +# !/usr/bin/env python +# Time 18-6-26 +# Author Yo +# Email YoLoveLife@outlook.com +import os +import django +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "deveops.settings") +django.setup() + +import atexit +from pyVim import connect +from pyVmomi import vmodl +from pyVmomi import vim +from django.conf import settings as DJANGO_SETTINGS +si = connect.SmartConnectNoSSL( + host=DJANGO_SETTINGS.VMWARE_SERVER, + user=DJANGO_SETTINGS.VMWARE_USERNAME, + pwd=DJANGO_SETTINGS.VMWARE_PASSWD, + port=443 +) + +atexit.register(connect.Disconnect, si) + +# search_index = si.content.searchIndex + +content = si.RetrieveContent() +container = content.rootFolder +viewType = [vim.VirtualMachine] +recursive = True +containerView = content.viewManager.CreateContainerView(container, viewType, recursive) +children = containerView.view +count = 0 +print(children) +for child in children: + count = count + 1 + +print(count) + + +# vm = search_index.FindByUuid(None, '422691e4-0217-57c9-349b-6e5c17ec5602', True, True) +# if vm is None: +# print('none') +# else: +# print(vm.summary.config.name) \ No newline at end of file diff --git a/img/dashboard.png b/img/dashboard.png new file mode 100644 index 00000000..0ee292c4 Binary files /dev/null and b/img/dashboard.png differ diff --git a/img/dns.png b/img/dns.png new file mode 100644 index 00000000..dd9c4593 Binary files /dev/null and b/img/dns.png differ diff --git a/img/group.png b/img/group.png index 05309eba..40969943 100644 Binary files a/img/group.png and b/img/group.png differ diff --git a/img/host.png b/img/host.png new file mode 100644 index 00000000..338312b4 Binary files /dev/null and b/img/host.png differ diff --git a/img/jumper.png b/img/jumper.png deleted file mode 100644 index 59a76303..00000000 Binary files a/img/jumper.png and /dev/null differ diff --git a/img/key.png b/img/key.png new file mode 100644 index 00000000..baf16848 Binary files /dev/null and b/img/key.png differ diff --git a/img/login.png b/img/login.png index c2838891..66a1db9e 100644 Binary files a/img/login.png and b/img/login.png differ diff --git a/img/meta.png b/img/meta.png new file mode 100644 index 00000000..ab3b69b2 Binary files /dev/null and b/img/meta.png differ diff --git a/img/meta_bt.png b/img/meta_bt.png new file mode 100644 index 00000000..1cf72a7c Binary files /dev/null and b/img/meta_bt.png differ diff --git a/img/mission.png b/img/mission.png new file mode 100644 index 00000000..ffa92341 Binary files /dev/null and b/img/mission.png differ diff --git a/img/playbook.png b/img/playbook.png deleted file mode 100644 index e41171ae..00000000 Binary files a/img/playbook.png and /dev/null differ diff --git a/img/qrcode.png b/img/qrcode.png new file mode 100644 index 00000000..0ebd2c74 Binary files /dev/null and b/img/qrcode.png differ diff --git a/img/run.png b/img/run.png new file mode 100644 index 00000000..56c1d5b9 Binary files /dev/null and b/img/run.png differ diff --git a/img/script.png b/img/script.png deleted file mode 100644 index 390c0ea3..00000000 Binary files a/img/script.png and /dev/null differ diff --git a/img/timeline.png b/img/timeline.png deleted file mode 100644 index d0561866..00000000 Binary files a/img/timeline.png and /dev/null differ diff --git a/img/upload.png b/img/upload.png deleted file mode 100644 index 84c5cc8d..00000000 Binary files a/img/upload.png and /dev/null differ diff --git a/img/user.png b/img/user.png new file mode 100644 index 00000000..f87250c4 Binary files /dev/null and b/img/user.png differ diff --git a/img/work.png b/img/work.png new file mode 100644 index 00000000..3b6aa6fe Binary files /dev/null and b/img/work.png differ diff --git a/requirements.txt b/requirements.txt index 90216484..5d533a14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,13 @@ +aioredis==1.1.0 +aliyun-python-sdk-cms==6.0.6 aliyun-python-sdk-core==2.3.5 aliyun-python-sdk-core-v3==2.8.6 +aliyun-python-sdk-dds==1.0.1 +aliyun-python-sdk-domain==3.5.0 aliyun-python-sdk-ecs==4.8.0 +aliyun-python-sdk-r-kvstore==2.0.2 +aliyun-python-sdk-rds==2.1.2 +aliyunsdkcore==1.0.2 amqp==2.2.2 ansible==2.5.2 asgiref==2.3.0 @@ -11,9 +18,12 @@ autobahn==18.5.1 Automat==0.6.0 bcrypt==3.1.4 billiard==3.5.0.3 -celery==4.1.0 +celery==4.2.0rc4 +certifi==2018.4.16 cffi==1.11.5 channels==2.1.1 +channels-redis==2.2.1 +chardet==3.0.4 constantly==15.1.0 cryptography==2.2.2 daphne==2.1.1 @@ -24,30 +34,57 @@ django-cors-headers==2.2.0 django-filter==1.1.0 djangorestframework==3.8.2 djangorestframework-jwt==1.11.0 +dnspython==1.15.0 +docopt==0.6.2 +dukpy==0.2.0 ecdsa==0.13 +future==0.16.0 +hiredis==0.2.0 hyperlink==18.0.0 idna==2.6 incremental==17.5.0 itsdangerous==0.24 +javascripthon==0.10 Jinja2==2.10 -kombu==4.1.0 +jupyter-echarts-pypkg==0.1.1 +kombu==4.2.0 +lml==0.0.2 +macropy3==1.1.0b2 +markdown2==2.3.5 MarkupSafe==1.0 +msgpack==0.5.6 mysqlclient==1.3.12 paramiko==2.4.1 Pillow==5.1.0 +prompt-toolkit==2.0.3 pyasn1==0.4.2 pyasn1-modules==0.2.1 pycparser==2.18 +pycrypto==2.6.1 pycryptodome==3.6.1 +pyecharts==0.5.5 +pyecharts-javascripthon==0.0.6 +pyecharts-jupyter-installer==0.0.3 +pyecharts-snapshot==0.1.6 +pyflakes==1.6.0 +Pygments==2.2.0 PyJWT==1.6.1 PyNaCl==1.2.1 +pyotp==2.2.6 python-ldap==3.0.0 pytz==2018.4 +pyvim==2.0.22 +pyvmomi==6.7.0 PyYAML==3.12 +qrcode==6.0 redis==2.10.6 +requests==2.18.4 +rsa==3.4.2 six==1.11.0 sshpubkeys==3.1.0 Twisted==17.9.0 txaio==2.10.0 +urllib3==1.22 vine==1.1.4 +wcwidth==0.1.7 zope.interface==4.5.0 \ No newline at end of file