三.cmdb
一.服务器管理:
https://github.com/rfjer/autoAdmin/tree/master/apps/servers
一服务器信息收集方式:
1.物理服务器
跑脚本传(bash/ansible/salt)
golang采集(推荐)---不需任何依赖丢上去便可执行(不在乎有无外网),并发高资源占用低。
2.虚拟化服务器vm/kvm--内网
小公司vmware用的多)---vcenter采集,openstack(大公司),kvm
3.云服务器(aws/阿里/青云)--公网
各自sdk接口
4.容器(docker---k8s管理)
用drf作接口展示即可
二.业务线管理:树型结构
怎么进行业务线管理?---树型结构
参考https://github.com/rfjer/autoAdmin/blob/master/apps/products/models.py 业务线模型
https://github.com/rfjer/autoAdmin/blob/master/apps/products/views.py 业务业后端接口实现
https://github.com/rfjer/autoAdmin/blob/master/apps/products/serializers.py 序列化
后端:
如下业务线表模型中只做两层,所以没有用path,pid=0说明就是一级的,不等于0都是二级的
from django.db import models
from django.contrib.auth import get_user_model User = get_user_model() class Product(models.Model):
service_name = models.CharField("业务线名称", max_length=32, help_text="业务线名称")
pid = models.IntegerField("上级业务线id", db_index=True, help_text="上级业务线id")
module_letter = models.CharField("业务线字母简称", max_length=32, help_text="业务线字母简称")
dev_interface = models.ManyToManyField(User, verbose_name="开发接口人", related_name="dev_interface", help_text="开发接口人")
op_interface = models.ManyToManyField(User, verbose_name="运维接口人", related_name="op_interface", help_text="运维接口人") def __str__(self):
return self.service_name class Meta:
db_table = 'resources_product'
permissions = (
("view_product", "can view products"),
)
ordering = ["id"]
views.py:
二业务线管理: from rest_framework import mixins, viewsets, response, status from .models import Product
from .serializers import ProductSerializer
from .filters import ProductFilter
from servers.models import Server class ProductViewset(viewsets.ModelViewSet):
"""
retrieve:
返回指定业务线信息
list:
返回业务线列表
update:
更新业务线信息
destroy:
删除业务线记录
create:
创建业务线资源
partial_update:
更新部分字段
"""
queryset = Product.objects.all()
serializer_class = ProductSerializer
extra_perms_map = { //这是给get增加权限
"GET": ["products.view_product"]
}
filter_class = ProductFilter
filter_fields = ("pid",) #销魂/删除
def destroy(self, request, *args, **kwargs):
ret = {"status": 0}
instance = self.get_object()
if instance.pid == 0:
# 顶级业务线
# 查找二级级业务线
if Product.objects.filter(pid__exact=instance.id).count() != 0:
ret["status"] = 1
ret["errmsg"] = "该业务下还有二级业务线"
return response.Response(ret, status=status.HTTP_200_OK)
else:
# 二级业务线
if Server.objects.filter(server_purpose__id__exact=instance.id).count() != 0:
ret["status"] = 1
ret["errmsg"] = "该分组下还有产品线,不能删除"
return response.Response(ret, status=status.HTTP_200_OK) self.perform_destroy(instance)
return response.Response(ret, status=status.HTTP_200_OK) class ProductManageViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
"""
list:
业务线管理
"""
queryset = Product.objects.all() def list(self, request, *args, **kwargs):
data = self.get_products()
return response.Response(data) def get_products(self):
ret = []
for obj in self.queryset.filter(pid=0):
node = self.get_node(obj)
node["children"] = self.get_children(obj.id)
ret.append(node)
return ret def get_children(self, pid):
ret = []
for obj in self.queryset.filter(pid=pid):
ret.append(self.get_node(obj))
return ret def get_node(self, product_obj):
node = {}
node["id"] = product_obj.id
node["label"] = product_obj.service_name
node["pid"] = product_obj.pid
return node
serializers.py:
from django.contrib.auth import get_user_model
from rest_framework import serializers
from .models import Product User = get_user_model() class ProductSerializer(serializers.ModelSerializer): def validate_pid(self, pid): #验证pid
if pid > 0: #则是二级业务线(一级业务线与二级是一对多关系)
try:
product_obj = Product.objects.get(pk=pid) #把关联关系转化成对象
if product_obj.pid != 0:
return serializers.ValidationError("上级业务线错误")
except Product.DoesNotExist:
return serializers.ValidationError("上级业务线不存在")
return pid
else:
return 0 def get_user_response(self, user_queryset):
ret = []
for dev in user_queryset:
ret.append({
"username": dev.username,
"name": dev.name,
"email": dev.email,
"id": dev.id,
})
return ret def to_representation(self, instance):
dev_interface = self.get_user_response(instance.dev_interface.all())
op_interface = self.get_user_response(instance.op_interface.all())
ret = super(ProductSerializer, self).to_representation(instance)
ret["dev_interface"] = dev_interface
ret["op_interface"] = op_interface
return ret def update(self, instance, validated_data):
instance.service_name = validated_data.get("service_name", instance.service_name)
instance.module_letter = validated_data.get("module_letter", instance.module_letter)
instance.dev_interface = validated_data.get("dev_interface", instance.dev_interface)
instance.op_interface = validated_data.get("op_interface", instance.op_interface)
instance.save()
return instance class Meta:
model = Product
fields = '__all__'
效果如图:
前端实现:
https://element.eleme.io/#/zh-CN/component/tree --树形控件
https://github.com/rfjer/autoAdminWeb/tree/master/src/views/resource
ProductList.vue:
<template>
<div class="user-list-container">
<el-row :gutter="">
<el-col :span="" >
<el-row :gutter="">
<el-col :span="" >
<el-input
placeholder="输入关键字过滤"
v-model="filterText">
</el-input>
</el-col>
<el-col :span="" >
<el-button @click="addClick">添加</el-button>
</el-col>
</el-row>
<div class="filter-tree">
<el-tree
:data="productTreeList"
:props="defaultProps"
:default-expand-all="expandAll"
:highlight-current="true"
:filter-node-method="filterNode"
ref="tree"
@node-click="treeNodeClick">
</el-tree>
</div>
</el-col>
<el-col :span="">
<el-form ref="productForm" size="mini" :model="productForm" label-width="100px" v-show="showForm" :rules="productRules">
<el-form-item label="业务线名称" prop="service_name">
<el-input v-model="productForm.service_name" :disabled="disabled" placeholder="请输入业务线名称"></el-input>
</el-form-item>
<el-form-item label="字母简称" prop="module_letter">
<el-input v-model="productForm.module_letter" :disabled="disabled" placeholder="请输入字母简称"></el-input>
</el-form-item>
<el-form-item label="上级业务线" prop="pid">
<el-select class="select" v-model="productForm.pid" :disabled="disabled" placeholder="上级业务线">
<el-option
v-for="(item, index) in productLevel"
:key="index"
:label="item.service_name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="运维接口人" prop="op_interface">
<el-select multiple class="select" v-model="productForm.op_interface" :disabled="disabled" filterable placeholder="请选择">
<el-option
v-for="(item, index) in userList"
:key="index"
:label="item.name"
:value="item.id">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.email }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="业务接口人" prop="dev_interface">
<el-select multiple class="select" v-model="productForm.dev_interface" size="mini" :disabled="disabled" filterable placeholder="请选择">
<el-option
v-for="(item, index) in userList"
:key="index"
:label="item.name"
:value="item.id"
size="mini">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.email }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitClick" :disabled="disabled">提交</el-button>
<el-button type="primary" @click="editClick" :disabled="buttonDisabled">修改</el-button>
<el-button type="primary" @click="deleteClick" :disabled="buttonDisabled">删除</el-button>
</el-form-item>
</el-form>
<el-table
class="table"
v-loading="serverListloading"
element-loading-text="拼命加载中"
:data="serverList"
border
v-show="showServerListTable">
<el-table-column
prop="idc.name"
label="机房"
align="center">
</el-table-column>
<el-table-column
prop="hostname"
label="主机名"
align="center">
</el-table-column>
<el-table-column
prop="manage_ip"
label="管理IP"
align="center">
</el-table-column>
<el-table-column
prop="status"
label="状态"
align="center">
</el-table-column>
<el-table-column
prop="last_check"
label="LAST CHECK"
align="center">
</el-table-column>
</el-table>
<div class="text-center" v-show="serverListTotalNum>=10">
<el-pagination
background
@current-change="paginationChange"
layout="total, prev, pager, next, jumper"
:current-page.sync="serverListPage"
:total="serverListTotalNum">
</el-pagination>
</div>
</el-col>
</el-row>
</div>
</template> <script>
import { getUserList } from '@/api/users'
import { getProductTree, getProductLevel, getProductLevelInfo, addProduct, updateProduct, deleteProductLevelInfo, getServerList } from '@/api/resource'
export default {
data() {
return {
productFlag: '',
showForm: false,
disabled: false,
buttonDisabled: false,
name: '',
showServerListTable: false,
server_purpose: 0,
serverList: [],
serverListTotalNum: 0,
serverListPage: 1,
serverListloading: false,
expandAll: false,
productForm: {
service_name: '',
module_letter: '',
dev_interface: [],
op_interface: [],
pid: ''
},
productRules: {
service_name: [
{ required: true, trigger: 'blur', message: '请输入业务线名称' }
],
pid: [
{ required: true, trigger: 'blur', message: '请选择上级业务线' }
],
module_letter: [
{ required: true, trigger: 'change', message: '请输入字母简称' }
]
},
userList: [],
filterText: '',
productTreeList: [],
productLevel: [],
defaultProps: {
children: 'children',
label: 'label'
},
state: 0
}
},
created() {
this.state = 1
},
watch: {
state() {
getUserList({ page_size: 0 }).then(res => {
this.userList = res
})
getProductTree().then(res => {
this.productTreeList = res
})
getProductLevel({ pid: 0, page_size: 0 }).then(res => {
this.productLevel = [{ id: 0, service_name: '顶级' }].concat(res)
})
},
filterText(val) {
this.$refs.tree.filter(val)
}
},
methods: {
filterNode(value, data) {
if (!value) return true
return data.label.indexOf(value) !== -1
},
fetchServerListData() {
this.loading = true
getServerList({ page: this.serverListPage, server_purpose: this.server_purpose }).then(res => {
this.serverList = res.results
this.serverListTotalNum = res.count
this.serverListloading = false
})
},
paginationChange(val) {
this.serverListPage = val
this.fetchServerListData()
},
addClick() {
this.productFlag = 'add'
this.$refs['productForm'].resetFields()
this.showForm = true
this.showServerListTable = false
this.disabled = false
this.buttonDisabled = true
},
editClick() {
this.productFlag = 'edit'
this.disabled = false
this.buttonDisabled = true
},
deleteClick() {
this.$confirm('此操作将删除该业务线, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteProductLevelInfo(this.productForm.id).then(() => {
this.$message({
message: '删除业务线成功',
type: 'success'
})
this.showForm = false
this.showServerListTable = false
this.getProductTreeInfo()
})
}).catch(() => { })
},
submitClick() {
this.$refs['productForm'].validate((valid) => {
if (!valid) {
return
}
if (this.productFlag === 'add') {
const params = Object.assign({}, this.productForm)
addProduct(params).then(res => {
this.$refs['productForm'].resetFields()
this.$message.success('添加业务线成功')
this.showForm = false
this.showServerListTable = false
this.$message({
message: '操作成功',
type: 'success'
})
this.getProductTreeInfo()
})
} else {
const id = this.productForm.id
delete this.productForm.id
const params = Object.assign({}, this.productForm)
updateProduct(id, params).then(res => {
this.$message.success('修改业务线成功')
this.showForm = false
this.showServerListTable = false
this.disabled = true
this.$message({
message: '操作成功',
type: 'success'
})
this.getProductTreeInfo()
})
}
})
},
getProductTreeInfo() {
getProductTree().then(res => {
this.productTreeList = res
})
},
treeNodeClick(data) {
this.$refs['productForm'].resetFields()
getProductLevelInfo(data.id).then(res => {
const op_interface = []
const dev_interface = []
res.op_interface.forEach(item => {
op_interface.push(item.id)
})
res.dev_interface.forEach(item => {
dev_interface.push(item.id)
})
const { id, pid, module_letter, service_name } = res
this.productForm = { id, pid, module_letter, service_name, op_interface, dev_interface }
this.disabled = true
this.buttonDisabled = false
this.showForm = true
if (pid === 0) {
this.showServerListTable = false
} else {
this.serverListPage = 1
this.server_purpose = data.id
this.serverList = []
this.fetchServerListData()
this.showServerListTable = true
}
})
}
}
}
</script>
<style lang="scss" scoped>
.filter-tree {
margin-top: 20px;
}
.select {
width: 100%;
}
</style>
三.用户管理
问题1:默认django提供了一套用户管理的.如下中它提供的字段只有id和usermame,password,email,last_login只此有用,所以一般会给加用户手机号,微信,中方名..,那由于此模型不是我们管理的,所以增加字段就直接加不了.
(python36env) [vagrant@CentOS devops]$ python manage.py dbshell
MariaDB [devops]> desc auth_user;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| password | varchar(128) | NO | | NULL | |
| last_login | datetime(6) | YES | | NULL | |
| is_superuser | tinyint(1) | NO | | NULL | |
| username | varchar(150) | NO | UNI | NULL | |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(150) | NO | | NULL | |
| email | varchar(254) | NO | | NULL | |
| is_staff | tinyint(1) | NO | | NULL | |
| is_active | tinyint(1) | NO | | NULL | |
| date_joined | datetime(6) | NO | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
问题2:用户来源,公司成千上万人,我不可能一条条添加数据:
从公司域库(AD)服务器中把用户信息同步过来.
或LDAP.
1.对接AD/LDAP:
把数据从AD同步过来--(1)同步到系统,
通过AD/LDAP做用户认证--(2)每次用户登录时做请求,
2.SSO(单点登录)
如公司内网一般有wiki,gitlab,运维平台,等各种子系统,而这些系统都对接了AD/LDAP后,用户名密码都是一套通用。
https://pypi.org/project/django-sso/
如何给django用户模型扩展字段:----这一步建议在刚开始做项目时就做好,不要在后期添加,否则会很麻烦
参考https://github.com/rfjer/autoAdmin/blob/master/apps/users/models.py
(1)apps/users/models.py:
from django.db import models # Create your models here.
from django.contrib.auth.models import AbstractUser, Groupclass User(AbstractUser):
name = models.CharField("姓名", max_length=32, null=True, help_text="姓名")
phone = models.CharField("电话", max_length=11, null=True, help_text="手机号")
id_rsa_key = models.TextField(null=True)
id_rsa_pub = models.TextField(null=True) class Meta:
verbose_name = "用户"
ordering = ["id"]
db_table = 'auth_user'
#permissions = (
# ("view_user", "cat view user"), 这里我把这注释了,因为同步不了:老报错与django内置权限冲突
#) def __str__(self):
return self.username
(2)告诉系统用我生成的这张用户表不用系统的用户表settigs.py--即告诉 Django,使用 users 应用下的 User
用户模型。
AUTH_USER_MODEL = "users.User" INSTALLED_APPS = [
....
(3)生效---因为我devop库中没什么数据,所以可直接删库(生产中不建议)
(python36env) [vagrant@CentOS devops]$ python manage.py dbshell
show create database devops;
drop database devops;
CREATE DATABASE `devops` /*!40100 DEFAULT CHARACTER SET utf8 */;
(4)删除apps下所有migrations文件
(python36env) [vagrant@CentOS devops]$ find apps/ -name migrations |xargs rm -rf
这里我不走token所以settings.py中注释掉:
# 'rest_framework.authtoken', (python36env) [vagrant@CentOS devops]$ python manage.py makemigrations
(python36env) [vagrant@CentOS devops]$ python manage.py migrate
最后效果如下:即有默认user中的字段又多了我添加的字段
MariaDB [devops]> desc auth_user;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| password | varchar(128) | NO | | NULL | |
| last_login | datetime(6) | YES | | NULL | |
| is_superuser | tinyint(1) | NO | | NULL | |
| username | varchar(150) | NO | UNI | NULL | |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(150) | NO | | NULL | |
| email | varchar(254) | NO | | NULL | |
| is_staff | tinyint(1) | NO | | NULL | |
| is_active | tinyint(1) | NO | | NULL | |
| date_joined | datetime(6) | NO | | NULL | |
| name | varchar(32) | YES | | NULL | |
| phone | varchar(11) | YES | | NULL | |
| id_rsa_key | longtext | YES | | NULL | |
| id_rsa_pub | longtext | YES |
四.接口权限控制
如下图中数据库权限表,可看到每个模型有三个权限(增删改)---第四个权限得靠我们自己加,
python36env) [vagrant@CentOS devops]$ python manage.py createsuperuser 创建超级用户
Username: admin
Email address: admin@51reboot.com
curl -X POST -d "username=admin&password=123456" http://localhost:8000/api-token-auth/
我后端的drf中权限全局配置是如下:
'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.AllowAny',
'devops.permissions.Permissions', 也就是所有接口默认走它
],
我的项目权限文件如下定义:
11
22
三.cmdb的更多相关文章
- 魅族CMDB运维自动化实践
一.简介 原创:梁鹏 本文是根据魅族系统架构师梁鹏10月20日在msup携手魅族.Flyme.百度云主办的第十三期魅族技术开放日< 魅族CMDB运维自动化实践>演讲中的分享内容整理而成. ...
- cmdb实现三种方式
为什么要做CMDB? 1.实现运维自动化,CMDB是实现运维自动化的基石 2.之前做资产统计的时候,使用execl来统计,为了年底资产审计方便 3.运维日常工作繁琐, 4.运行环境不统一 Agent方 ...
- Python高手之路【三】python基础之函数
基本数据类型补充: set 是一个无序且不重复的元素集合 class set(object): """ set() -> new empty set object ...
- python走起之第三话
一. SET集合 set是一个无序且不重复的元素集 class set(object): """ set() -> new empty set object set ...
- CMDB反思4
CMDB模型设计2 http://blog.vsharing.com/xqscool/A1275233.html 估计大家看到破子的这两篇都有点晕哈,我也有点晕. 两篇对比来看. 第1处,属性部分 ...
- CMDB反思3
CMDB模型设计1 http://blog.vsharing.com/xqscool/A1274634.html 分类的问题上比较有感悟.在之前编写新版的CMDB模型的时候,曾将刀片机.x86服务器. ...
- Python CMDB开发
Python CMDB开发 运维自动化路线: cmdb的开发需要包含三部分功能: 采集硬件数据 API 页面管理 执行流程:服务器的客户端采集硬件数据,然后将硬件信息发送到API,API负责将获取 ...
- python基础(三)
set集合 set集合创建 #方式1: se = {'} #与字典类似dict1 = {'k1','v1','k2','v2'} #方式2: se = set() #创建一个空的集合 list1 = ...
- Python学习笔记——基础篇1【第三周】——set集合
set集合 不允许重复的元素出现(相当于特殊的列表) set 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 练习:寻找差异 # 数据库中原有 old_dic ...
随机推荐
- Java实现 蓝桥杯 算法训练 前缀表达式
算法训练 前缀表达式 时间限制:1.0s 内存限制:512.0MB 问题描述 编写一个程序,以字符串方式输入一个前缀表达式,然后计算它的值.输入格式为:"运算符 对象1 对象2", ...
- Java实现 蓝桥杯VIP 算法训练 学做菜
算法训练 学做菜 时间限制:1.0s 内存限制:256.0MB 问题描述 涛涛立志要做新好青年,他最近在学做菜.由于技术还很生疏,他只会用鸡蛋,西红柿,鸡丁,辣酱这四种原料来做菜,我们给这四种原料标上 ...
- Java实现 蓝桥杯VIP 算法训练 P1102
定义一个学生结构体类型student,包括4个字段,姓名.性别.年龄和成绩.然后在主函数中定义一个结构体数组(长度不超过1000), 并输入每个元素的值,程序使用冒泡排序法将学生按照成绩从小到大的顺序 ...
- Java实现第九届蓝桥杯书号验证
书号验证 2004年起,国际ISBN中心出版了<13位国际标准书号指南>. 原有10位书号前加978作为商品分类标识:校验规则也改变. 校验位的加权算法与10位ISBN的算法不同,具体算法 ...
- java实现第六届蓝桥杯隔行变色
隔行变色 隔行变色 Excel表的格子很多,为了避免把某行的数据和相邻行混淆,可以采用隔行变色的样式. 小明设计的样式为:第1行蓝色,第2行白色,第3行蓝色,第4行白色,- 现在小明想知道,从第21行 ...
- Java对象实例化的过程
1.先为对象分配空间,并按属性类型默认初始化 ps:八种基本数据类型,按照默认方式初始化,其他数据类型默认为null 2.父类属性的初始化(包括代码块,和属性按照代码顺序进行初始化) 3.父类构造函数 ...
- python3 源码阅读-虚拟机运行原理
阅读源码版本python 3.8.3 参考书籍<<Python源码剖析>> 参考书籍<<Python学习手册 第4版>> 官网文档目录介绍 Doc目录主 ...
- 附022.Kubernetes_v1.18.3高可用部署架构一
kubeadm介绍 kubeadm概述 参考附003.Kubeadm部署Kubernetes. kubeadm功能 参考附003.Kubeadm部署Kubernetes. 本方案描述 本方案采用kub ...
- Windows下C,C++开发环境搭建指南
Windows下C,C++开发环境搭建指南 前情提要 基于近一段时间很多网友发邮件反馈,说一些项目编译出现问题,诸如此类的情况. 就觉得很有必要写一篇C,C++开发环境的小指南,统一回复. 1.君欲善 ...
- 听说你的资源被盗用了,那你知道 Nginx 怎么防盗链吗?
上一篇文章讲了 Nginx 中的变量和运行原理,下面就来说一个主要提供变量并修改变量的值的模块,也就是我们要讲的防盗链模块:referer 模块. 简单有效的防盗链手段 场景 如果做过个人站点的同学, ...