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