Skip to content

Commit

Permalink
Merge pull request YoLoveLife#91 from YoLoveLife/devel
Browse files Browse the repository at this point in the history
大体完成2.0功能
  • Loading branch information
YoLoveLife authored Jun 28, 2018
2 parents 4b2e5e4 + e7355e0 commit 411d2dd
Show file tree
Hide file tree
Showing 78 changed files with 1,403 additions and 317 deletions.
80 changes: 56 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
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)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE.md) :yum: </br>

Author: [YoLoveLife.com](http://www.yolovelife.com) :ok_hand: </br>
:heart: 感谢所有 :star: 我的人 你们是我努力的动力</br>
该项目为devEops的后端部分 前端部分请关注 :heart: [devEops-Web](https://github.com/YoLoveLife/devEopsWeb)</br>

运维体系解决方案(正在成长中)</br>
新的Vue前端已经逐步上线啦!</br>
前后段分离啦!告别之前的Jinja2</br>
:muscle: 实践以资产、应用信息为中心的运维平台</br>
django & rest-framework & bootstrap</br>
django模型 & rest-framework</br>
:muscle: 参考了诸多django项目 我的Django用的真的很棒</br>
travis & django TestCase</br>
:muscle: 单元测试 测试不规范但是努力去做
Expand All @@ -33,40 +34,71 @@ travis & django TestCase</br>
本开发自运维平台致力于IT资源信息的整合与自动化运维,通过服务、应用配置的信息整合来运维提供帮助。</br>
devEops正在不断成长
- 所有运维操作都基于信息整合的正确性和一致性(资产信息管理) :floppy_disk: </br>
- 基于WebSocket以及SSHProxy的软件堡垒机操作 </br>
- :bar_chart: 提供资产信息的统计,应用系统的占比、脚本|剧本的调用次数等</br>
- ~~可临时搜集应用上的信息(如MySQL的status等信息)~~</br>
- ~~所有运维操作(脚本 | 剧本)在提交的时候会自动注入资产信息并通过ansible远程执行~~</br>
- 所有资产信息、架构信息都存储在数据库中供所有运维人员操作</br>
- ~~开发人员可登陆平台提交工单发起防火墙修改、应用发布、日常运维 :clock9: 等操作,运维人员许可操作进行~~</br>
- ~~所有应用发布、日常运维工作日程展示 :date:~~ </br>
- 对接VMware|Aliyun自动拉取信息入库资产 </br>
- :bar_chart: 提供各类信息统计图表(资产|运维)等</br>
- 基于Ansile的运维元操作|任务的定义</br>
- 基于WebSocket以及SSHProxy的工单操作 </br>
- 基于Django自带auth认证体系的资产与权限关联</br>
- 域名管理与解析信息查询与刷新</br>
- 详细权限操作,区分开发人员以及运维人员 </br>

## <a name="looklike"> 平台一览 </a>
### 登陆界面
### :muscle: 登陆界面
![LOGIN](img/login.png)</br>
### 资产管理

### :muscle: 仪表盘界面
:satisfied: 根据资源的检出位置|操作系统类型|运维操作 等图表化展现</br>
并且每周提供可供保存的静态页面的报告 **有!但是很丑** :grin:
![DASHBOARD](img/dashboard.png)

### :muscle: 资产管理
:open_mouth: 资产根据来源(VMWARE|ALIYUN)入库 **运维人员进行归类**
![GROUP](img/group.png)</br>
### 执行基于playbook的元操作
![JUMPER](img/playbook.png)</br>
### 批量主机信息上传
![UPLOAD](img/upload.png)</br>
### 脚本编写
![SCRIPT](img/script.png)</br>
### 时间线展示
![TIMELINE](img/timeline.png)</br>
应用组可定义架构图 关联跳板机以及密钥 :key: </br>
**应用组可全局添加参数 该参数会注入运维操作中的变量中**</br>
![HOST](img/host.png)
主机可以根据任意信息进行检索</br>
根据VMware|ALIYUN **API可进行详细信息(监控|配置)获取查看**</br>

### :muscle: 域名管理
基于树结构的域名管理 :relieved: 定时解析域名 保证记录域名解析正确 **供正反向查询**
![DNS](img/dns.png)

### :muscle: 密钥管理
**基于服务器端直接生成的ssh-rsa密钥** 私钥将会直接加密存储入数据库</br>
只有当您做运维操作|SSH连接的时候才会解析并且使用 :expressionless: </br>
![KEY](img/key.png)

### :muscle: QR-Code
基于Google-Authority的**二次验证** :confused: 目前用户体系中已经包含验证+生成</br>
![QRCODE](img/qrcode.png)

### :muscle: 元操作及任务
基于Ansible的数据模型 将用户的Ansible直接存储入数据库中</br>
当用户使用任务直接从CMDB中导出需要执行的主机列表以及参数列表 :smirk: **注入Ansible-API中**</br>
![META](img/meta.png)
选择元操作所涉及的主机范围 :neutral_face: </br>
![META](img/meta_bt.png)
选择某个应用组下的若干个元操作组成一个任务</br>
![MISSION](img/mission.png)

### :muscle: 工单系统
用户根据自身权限可以发布工单(涉及任务)进行执行</br>
![CODEWORK](img/work.png)
选择工单后用户可以进行执行 :punch: 系统**建立websocket**将所有执行结果返回
![RUN](img/run.png)

## <a name="howtoinstall"> 如何安装 </a>
devEops依赖于python2.7、诸多python第三方模块、mysql数据库、redis缓存</br>
以下操作环境已经拥有python2.7、mysql数据库、redis缓存
devEops依赖于python3.5、诸多python第三方模块、mysql数据库、redis缓存</br>
以下操作环境已经拥有python3.5、mysql数据库、redis缓存
```bash
$ cd path/to/project/folder/

#安装python第三方库
$ pip install -r requirements.txt

#连接本地数据库并创建数据表结构
$ vim apps/deveops/settings.py #DATABASES中输入数据库连接方法
#连接本地资源并创建数据表结构
$ vim deveops/conf.py #里面包含了所有连接数据以及定时任务
$ python apps/manage.py makemigrations
$ python apps/manage.py migrate

Expand Down
40 changes: 39 additions & 1 deletion apps/authority/api/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@
from rest_framework_jwt.views import ObtainJSONWebToken
from deveops.api import WebTokenAuthentication
from authority.permission import user as UserPermission
from deveops.utils import aes
from django.conf import settings
import pyotp
import os
from qrcode import QRCode,constants
import base64

__all__ = [
"UserLoginAPI", "UserInfoAPI", "UserListAPI",
"UserOpsListAPI", "UserUpdateAPI", "UserDeleteAPI",
"UserListByPageAPI", 'UserPagination', 'UserOpsListByPageAPIUserOpsListByPageAPI'
"UserListByPageAPI", 'UserPagination', 'UserOpsListByPageAPIUserOpsListByPageAPI',
'UserQRCodeAPI'
]


Expand Down Expand Up @@ -84,3 +91,34 @@ class UserDeleteAPI(WebTokenAuthentication,generics.DestroyAPIView):
serializer_class = serializers.UserSerializer
queryset = models.ExtendUser.objects.all()
permission_classes = [UserPermission.UserDeleteRequiredMixin,IsAuthenticated]


def get_qrcode(user):
file_name = str(aes.encrypt(user.qrcode),encoding='utf-8')
file = settings.QCODE_ROOT+'/'+file_name+'.png'
if not os.path.exists(file):
data = pyotp.totp.TOTP(user.qrcode).provisioning_uri('=v=', issuer_name="devEops")
qr = QRCode(
version=1,
error_correction=constants.ERROR_CORRECT_L,
box_size=6,
border=4,)
try:
qr.add_data(data)
qr.make(fit=True)
img = qr.make_image()
img.save(file)
return '/media/qrcode/'+ file_name +'.png'
except Exception as e:
return '/media/qrcode/'+ file_name +'.png'
else:
return '/media/qrcode/' + file_name + '.png'


class UserQRCodeAPI(WebTokenAuthentication,generics.ListAPIView):
module = models.ExtendUser
permission_classes = [IsAuthenticated]

def get(self, request, *args, **kwargs):
dist = {"url":get_qrcode(request.user)}
return Response(dist, status=status.HTTP_201_CREATED)
22 changes: 19 additions & 3 deletions apps/authority/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.conf import settings
import socket
import uuid
import pyotp

__all__ = [
"Key", "ExtendUser", "Jumper"
Expand Down Expand Up @@ -61,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
Expand All @@ -85,6 +85,7 @@ class ExtendUser(AbstractUser):
img = models.CharField(max_length=10, default='user.jpg')
phone = models.CharField(max_length=11, default='None',)
full_name = models.CharField(max_length=11, default='未获取')
qrcode = models.CharField(max_length=29,default=pyotp.random_base32(29))
groups = models.ManyToManyField(
Group,
verbose_name=_('groups'),
Expand All @@ -96,7 +97,6 @@ class ExtendUser(AbstractUser):
related_name="user_set",
related_query_name="user",
)

class Meta:
permissions = (
('yo_list_user', u'罗列用户'),
Expand Down Expand Up @@ -144,6 +144,11 @@ def get_group_name(self):
else:
return str.join(list)

def check_qrcode(self,verifycode):
t = pyotp.TOTP(self.qrcode)
result = t.verify(verifycode)
return result


class Jumper(models.Model):
JUMPER_STATUS = (
Expand Down Expand Up @@ -193,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)
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}}'
)
}
}
1 change: 1 addition & 0 deletions apps/authority/urls/api_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
path(r'v1/opsuser/bypage/', user.UserOpsListByPageAPI.as_view()),
path(r'v1/user/<int:pk>/update/', user.UserUpdateAPI.as_view()),
path(r'v1/user/<int:pk>/delete/', user.UserDeleteAPI.as_view()),
path(r'v1/user/qrcode/', user.UserQRCodeAPI.as_view()),
#
# Resource group api
path(r'v1/group/', group.GroupListAPI.as_view()),
Expand Down
28 changes: 10 additions & 18 deletions apps/dashboard/docs/week_bk
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
devEops - 浙报传媒开发自运维平台
devEops - 浙报传媒开发自运维平台(测试)
=====================
{{WEEK_START}} - {{WEEK_END}}
## 目录
* [资产统计](#cmdb)
* [运维统计](#manager)
* [资费提醒](#pay)

## <a name="cmdb"> 资产统计 </a>
### 资产计数
Expand Down Expand Up @@ -32,12 +28,6 @@ VMware机器总数|{{VMWARE_COUNT}}
活跃用户总数| {{ACTIVITY_USER}}


### 运维图表
![work](./work.png)
如图所示是本周内所有执行工单的个数
设计到应用系统


## <a name="pay"> 资费提醒 </a>
**请相关系统管理员及DBA关注资源过期时间及时续费**
### ECS资源过期列表
Expand All @@ -52,12 +42,14 @@ UUID|实例名称|过期时限|数据库版本
{% for rds in RDS_EXPIRED %}{{rds.recognition_id}}|{{rds.instancename}}|{{rds.expired}}天|{{rds.version}}
{% endfor %}

### Redis资源过期列表
UUID|实例名称|过期时限
:--------:|:--------:|:--------:
{{REDIS_EXPIRED}}
### KVStore资源过期列表
UUID|实例名称|过期时限|连接地址|版本
:--------:|:--------:|:--------:|:--------:|:--------:
{% for kvs in KVSTORE_EXPIRED %}{{kvs.recognition_id}}|{{kvs.instancename}}|{{kvs.expired}}天|{{kvs.connect_domain}}|{{kvs.version}}
{% endfor %}

### MongoDB资源过期列表
UUID|实例名称|过期时限
:--------:|:--------:|:--------:
{{MONGODB_EXPIRED}}
UUID|实例名称|过期时限|类型|版本
:--------:|:--------:|:--------:|:--------:|:--------:
{% for mng in MONGODB_EXPIRED %}{{mng.recognition_id}}|{{mng.instancename}}|{{mng.expired}}天|{{mng.type}}|{{mng.version}}
{% endfor %}
16 changes: 8 additions & 8 deletions apps/dashboard/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -22,7 +22,7 @@
connect = redis.StrictRedis(port=REDIS_PORT,db=REDIS_SPACE)


@periodic_task(run_every=crontab(minute=30,hour=1,day_of_week="sunday"))
@periodic_task(run_every=DASHBOARD_TIME)
def weeklyDashboard():
import jinja2
loader = jinja2.FileSystemLoader(settings.BASE_DIR+'/apps/dashboard/docs/', encoding='utf-8')
Expand Down Expand Up @@ -107,10 +107,10 @@ def weeklyDashboard():
f.write(html)

msg = '本周平台周报地址http://deveops.8531.cn:8888/media/dashboard/'+ datetime.datetime.now().strftime('%Y-%m-%d')+'/index.html'
smtp.sendMail('devEops平台运维周报', msg, ['[email protected]','[email protected]','[email protected]','[email protected]'])
smtp.sendMail('devEops平台运维周报', msg, ['[email protected]'])#,'[email protected]','[email protected]','[email protected]'])


@periodic_task(run_every=crontab(minute=1,hour=1,day_of_week="sunday"))
@periodic_task(run_every=EXPIRED_TIME)
def aliyunECSExpiredInfoCatch():
ExpiredAliyunECS.objects.all().delete()
countNumber = aliyun.fetch_ECSPage()
Expand All @@ -127,7 +127,7 @@ def aliyunECSExpiredInfoCatch():
ExpiredAliyunECS(**instance_data).save()


@periodic_task(run_every=crontab(minute=2,hour=1,day_of_week="sunday"))
@periodic_task(run_every=EXPIRED_TIME)
def aliyunRDSInfoCatch():
ExpiredAliyunRDS.objects.all().delete()
countNumber = aliyun.fetch_RDSPage()
Expand All @@ -143,7 +143,7 @@ def aliyunRDSInfoCatch():
ExpiredAliyunRDS(**resolver.AliyunRDS2Json.decode(dt)).save()


@periodic_task(run_every=crontab(minute=3,hour=1,day_of_week="sunday"))
@periodic_task(run_every=EXPIRED_TIME)
def aliyunKVStoreInfoCatch():
ExpiredAliyunKVStore.objects.all().delete()
countNumber = aliyun.fetch_KVStorePage()
Expand All @@ -158,7 +158,7 @@ def aliyunKVStoreInfoCatch():
ExpiredAliyunKVStore(**resolver.AliyunKVStore2Json.decode(dt)).save()


@periodic_task(run_every=crontab(minute=4,hour=1,day_of_week="sunday"))
@periodic_task(run_every=EXPIRED_TIME)
def aliyunMongoDBInfoCatch():
ExpiredAliyunMongoDB.objects.all().delete()
countNumber = aliyun.fetch_MongoDBPage()
Expand All @@ -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')

Expand Down
5 changes: 5 additions & 0 deletions apps/db/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding:utf-8 -*-
# !/usr/bin/env python
# Time 18-6-21
# Author Yo
# Email [email protected]
File renamed without changes.
5 changes: 5 additions & 0 deletions apps/db/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding:utf-8 -*-
# !/usr/bin/env python
# Time 18-6-21
# Author Yo
# Email [email protected]
Loading

0 comments on commit 411d2dd

Please sign in to comment.