├── .gitignore ├── LICENSE ├── README.md ├── config.py ├── dbutil ├── __init__.py └── dbutil.py ├── flask_web.py ├── static ├── img │ └── favicon.ico └── vendor │ ├── alert │ ├── sweet-alert.css │ └── sweet-alert.min.js │ ├── animate.css │ ├── bootstrap │ ├── css │ │ └── bootstrap.min.css │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ │ └── bootstrap.js │ ├── datatable │ ├── datatable.css │ └── datatable.js │ ├── datepicker │ ├── bootstrap-datepicker.min.js │ ├── bootstrap-datepicker.zh-CN.min.js │ └── bootstrap-datepicker3.min.css │ ├── echarts │ └── echarts-all.js │ ├── flatui │ ├── css │ │ └── flat-ui.min.css │ ├── fonts │ │ ├── glyphicons │ │ │ ├── flat-ui-icons-regular.eot │ │ │ ├── flat-ui-icons-regular.svg │ │ │ ├── flat-ui-icons-regular.ttf │ │ │ ├── flat-ui-icons-regular.woff │ │ │ └── selection.json │ │ └── lato │ │ │ ├── lato-black.eot │ │ │ ├── lato-black.svg │ │ │ ├── lato-black.ttf │ │ │ ├── lato-black.woff │ │ │ ├── lato-bold.eot │ │ │ ├── lato-bold.svg │ │ │ ├── lato-bold.ttf │ │ │ ├── lato-bold.woff │ │ │ ├── lato-bolditalic.eot │ │ │ ├── lato-bolditalic.svg │ │ │ ├── lato-bolditalic.ttf │ │ │ ├── lato-bolditalic.woff │ │ │ ├── lato-italic.eot │ │ │ ├── lato-italic.svg │ │ │ ├── lato-italic.ttf │ │ │ ├── lato-italic.woff │ │ │ ├── lato-light.eot │ │ │ ├── lato-light.svg │ │ │ ├── lato-light.ttf │ │ │ ├── lato-light.woff │ │ │ ├── lato-regular.eot │ │ │ ├── lato-regular.svg │ │ │ ├── lato-regular.ttf │ │ │ └── lato-regular.woff │ └── img │ │ ├── icons │ │ ├── png │ │ │ ├── Book.png │ │ │ ├── Calendar.png │ │ │ ├── Chat.png │ │ │ ├── Clipboard.png │ │ │ ├── Compas.png │ │ │ ├── Gift-Box.png │ │ │ ├── Infinity-Loop.png │ │ │ ├── Mail.png │ │ │ ├── Map.png │ │ │ ├── Pensils.png │ │ │ ├── Pocket.png │ │ │ ├── Retina-Ready.png │ │ │ ├── Toilet-Paper.png │ │ │ └── Watches.png │ │ └── svg │ │ │ ├── book.svg │ │ │ ├── calendar.svg │ │ │ ├── chat.svg │ │ │ ├── clipboard.svg │ │ │ ├── clocks.svg │ │ │ ├── compas.svg │ │ │ ├── gift-box.svg │ │ │ ├── loop.svg │ │ │ ├── mail.svg │ │ │ ├── map.svg │ │ │ ├── paper-bag.svg │ │ │ ├── pencils.svg │ │ │ ├── retina.svg │ │ │ ├── ribbon.svg │ │ │ └── toilet-paper.svg │ │ └── login │ │ ├── imac-2x.png │ │ └── imac.png │ ├── font-awesome │ ├── css │ │ └── font-awesome.min.css │ └── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ └── fontawesome-webfont.woff │ ├── jquery │ └── jquery.min.js │ ├── metismenu │ ├── metis-menu.min.css │ └── metis-menu.min.js │ ├── ngprogress │ ├── ngprogress.css │ └── ngprogress.js │ ├── sb-admin-2.css │ ├── sb-admin-2.js │ ├── select │ ├── select2.min.css │ ├── select2.min.js │ └── zh-CN.js │ ├── timeline.css │ ├── underscore.js │ └── validate │ ├── bootstrap.js │ └── validate.js ├── templates ├── index.html ├── layout.html ├── login.html └── page │ ├── caninet.html │ ├── host.html │ ├── product.html │ ├── raidtype.html │ └── user.html └── woniu-build.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 woniuppp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # [woniu-cmdb](http://shengxinjing.cn/woniu-cmdb/) 5 | ### :snail: 奇技淫巧--写配置文件生成增删改查系统:mushroom: 6 | ### [视频教程](http://v.qq.com/page/u/p/9/u01775jmmp9.html) 7 | 8 | 9 | ### 进度表   10 | 11 | 12 | 13 | 14 | 需求 | 负责人 | 进度 | (预计) 完成时间 | 您可以做什么 15 | ---|:---:|---|:---:|:---: 16 | 干掉配置,前端控制 | 蜗牛 | ![progress](http://progressed.io/bar/10) | **1** | 使用 17 | 18 | 19 | 20 | 21 | [线上demo](http://admin.51reboot.com/),此页面都是一个配置文件自动生成的 22 | 23 | 详细的文章介绍和实现原理分析会发布在我的[博客](https://github.com/shengxinjing/my_blog/issues),敬请期待 24 | 25 | 26 | > 运维人员都不喜欢搞CMDB,因为有很多前端的内容,但CMDB却在运维圈占有重要的地位,开发CMDB就是各种增删改查,之后我有个想法,做一个写配置文件就自动生成页面的CMDB, 请支持我的woniu-cmdb,喜欢请star 27 | 28 | ### 写好配置文件,自从生成页增删改查面不是梦 29 | 30 | 此项目不仅限于cmdb,各种管理系统,都可以用此项目配置,改成学生老师啥的,就变成了学校内部的mis系统,我会一直维护这个项目,大家有新需求请提issue 31 | 32 | ### 效果图(我们只写左边的配置,右边的是自动生成的) 33 | ![](http://7xjoq9.com1.z0.glb.clouddn.com/cmdb01.png) 34 | 35 | 36 | ### 简单配置,生成页面 37 | 命令只有两个 38 | 39 | ``` 40 | python woniu-build.py init # 初始化数据库+根据配置生成文件 41 | 42 | python woniu-build.py 仅根据配置生成文件 43 | ``` 44 | ### 使用指南 45 | 46 | 1. 下载该项目到本地 47 | 2. config.py是我们唯一要修改的文件 48 | 2. 修改config.py里的db_config变量,配置数据库的host,用户名,密码和要操作的数据库 49 | 50 | ```python 51 | db_config = { 52 | 'host':'localhost', 53 | 'user':'root', 54 | 'passwd':"", 55 | 'db':'cmdb' 56 | } 57 | 58 | ``` 59 | 60 | 61 | 3. 修改config.py的page_config变量,此变量是设置具体的页面变量,先做一个简单的配置 62 | 63 | ``` 64 | 65 | page_config = { 66 | # menu是一个list,包含所有的页面信息 67 | "menu":[{ 68 | //页面的名字,和数据库表一致 69 | "name": 'user', 70 | // 显示的页面标题 71 | "title": '用户管理', 72 | 73 | # 页面里具体的字段,如果有两个字段,配置两个即可,包含name和title 74 | "data": [{ 75 | "name": 'username', 76 | "title": '用户名' 77 | },{ 78 | "name":'password', 79 | "title":'密码' 80 | }] 81 | }}] 82 | } 83 | 84 | ``` 85 | 86 | 4. 执行 python woniu-build.py 处理文件,启动flask_web.py,浏览器访问http://localhost:9092/ 87 | 5. 默认有一个用户,账号和密码都是51reboot 88 | 89 | 90 | ### 字段详解 91 | 92 | 1. page_config配置 93 | 94 | ``` 95 | menu:下面具体介绍,页面具体的字段 96 | favicon:页面标签的小logo 默认用reboot的 97 | title:页面标签的标题,默认是woniu-cmdb 98 | brand_name:项目左上角显示文字,默认是woniu-cmdb 99 | 100 | ``` 101 | 102 | 2. menu配置详解 103 | ``` 104 | 105 | { 106 | name:名字和数据库表名一直 107 | titile:中文 108 | modal_detail:是否用模态窗展示详情(有隐藏字段没展示) 109 | 具体字段数据 110 | data:[ 111 | { 112 | name: 113 | title: 114 | type:类型,默认input text 115 | value:select直接从value里渲染,不发ajax和preload,如果没有type,就是input里的value属性 116 | 117 | select_type:获取数据的action_type的值,和对应的name字段一致 118 | toname:preload数据里,完成id到name得转换显示,select默认true 119 | hide:默认false,true的话隐藏此字段 120 | 121 | option_val list的显示字段 默认id 122 | option_name list的显示字段 默认name 123 | 124 | } 125 | ] 126 | } 127 | ``` 128 | 129 | todolist: 130 | * 登录权限+页面权限 131 | * 更丰富的前端组件,现在只支持输入,日期和下啦框 132 | * 大家有需求请提issues 我会持续维护这个项目, 133 | 134 | ### 依赖 135 | 136 | 本项目python依赖flask和mysqldb模块,直接pip安装一下即可 137 | 138 | 139 | 140 | 如果您觉得有我写的东西对你帮助,可以打赏点钱给我支付宝支付宝316783812@qq.com或者扫二维码 141 | 142 | ![](http://7xjoq9.com1.z0.glb.clouddn.com/zhifubao.png) 143 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | 4 | # 数据库配置 5 | 6 | 7 | db_config = { 8 | 'host':'localhost', 9 | 'user':'root', 10 | 'passwd':"", 11 | 'db':'cmdb' 12 | } 13 | 14 | 15 | page_config = { 16 | "brand_name":'51Reboot', 17 | 'title':'hello reboot', 18 | "favicon":'https://pic1.zhimg.com/6d660dd4156c64bfad13ff97d79c2f98_l.jpg', 19 | "menu":[ 20 | { 21 | # user配置最好不要修改,是和登陆认证相关的,直接在下面加配置即可 22 | "name": 'user', 23 | "title": '用户管理', 24 | "data": [{ 25 | "name": 'username', 26 | "title": '用户名' 27 | },{ 28 | "name":'password', 29 | "title":'密码' 30 | }] 31 | }, 32 | { 33 | # user配置最好不要修改,是和登陆认证相关的,直接在下面加配置即可 34 | "name": 'test', 35 | "title": '测试', 36 | "data": [{ 37 | "name": 'username', 38 | "title": '用户名' 39 | },{ 40 | "name":'password', 41 | "title":'密码', 42 | "empty":"yes" 43 | 44 | }] 45 | }, 46 | { 47 | "name":'caninet', 48 | "title":'机柜', 49 | "data":[{ 50 | "name":"name", 51 | "title":'机柜名' 52 | }] 53 | }, 54 | { 55 | "name":"host", 56 | "title":"服务器", 57 | "data":[{ 58 | "name":"caninet", 59 | "title":'机柜', 60 | "type":'select', 61 | "select_type":'caninet' 62 | },{ 63 | "name":"hostname", 64 | "title":'主机名' 65 | },{ 66 | 'name':'asset_no', 67 | 'title':'资产号' 68 | },{ 69 | "name":'end_time', 70 | "title":"过期日期", 71 | "type":'date' 72 | },{ 73 | "name":'ups', 74 | "title":'是否开启', 75 | "type":'select', 76 | "value":{0:'开启',1:'关闭'} 77 | }] 78 | } 79 | ] 80 | } 81 | 82 | 83 | 84 | 85 | 86 | # ,{ 87 | # "name": 'host', 88 | # "title": '服务器', 89 | # "data": [{ 90 | # "name": 'cabinet', 91 | # "title": '机柜' 92 | # },{ 93 | # "name":'hostname', 94 | # "title":'主机名' 95 | # }] 96 | # },{ 97 | # "title": '业务', 98 | # "sub":[ 99 | # { 100 | # 'name': 'product', 101 | # 'title': '业务线', 102 | # 'data': [{ 103 | # 'name': 'service_name', 104 | # 'title': '服务名' 105 | # },{ 106 | # 'name':'module_letter', 107 | # 'title':'模块简称' 108 | # },{ 109 | # 'name':'dev_interface', 110 | # 'title':'开发者' 111 | # },{ 112 | # 'name':'op_interface', 113 | # 'title':'运维接口人' 114 | # }] 115 | # }, 116 | # { 117 | # 'name': 'raidtype', 118 | # 'title': 'Raid厂商', 119 | # 'data': [{ 120 | # 'name': 'name', 121 | # 'title': 'Raid厂商' 122 | # }] 123 | # } 124 | 125 | 126 | # ] 127 | # } -------------------------------------------------------------------------------- /dbutil/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shengxinjing/woniu-cmdb/64722689dd1e9334a89a93683cdad825b9ef76d8/dbutil/__init__.py -------------------------------------------------------------------------------- /dbutil/dbutil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #encoding:utf8 3 | import json 4 | import time,random 5 | import datetime 6 | import MySQLdb 7 | import MySQLdb.cursors 8 | 9 | class DB: 10 | conn = None 11 | db = None 12 | host = None 13 | 14 | def __init__(self, host, mysql_user, mysql_pass, mysql_db): 15 | self.host = host 16 | self.mysql_user = mysql_user 17 | self.mysql_pass = mysql_pass 18 | self.mysql_db = mysql_db 19 | def connect(self): 20 | self.conn = MySQLdb.connect(host=self.host, user=self.mysql_user, passwd=self.mysql_pass, db=self.mysql_db, charset="utf8", connect_timeout=600, compress=True,cursorclass = MySQLdb.cursors.DictCursor) 21 | self.conn.autocommit(True) 22 | def execute(self, sql): 23 | try: 24 | cursor = self.conn.cursor() 25 | cursor.execute(sql) 26 | except (AttributeError, MySQLdb.OperationalError): 27 | try: 28 | cursor.close() 29 | self.conn.close() 30 | except: 31 | pass 32 | time.sleep(1) 33 | try: 34 | self.connect() 35 | print "reconnect DB" 36 | cursor = self.conn.cursor() 37 | cursor.execute(sql) 38 | except (AttributeError, MySQLdb.OperationalError): 39 | time.sleep(2) 40 | self.connect() 41 | print "reconnect DB" 42 | cursor = self.conn.cursor() 43 | cursor.execute(sql) 44 | 45 | return cursor 46 | -------------------------------------------------------------------------------- /flask_web.py: -------------------------------------------------------------------------------- 1 | from flask import Flask,request,render_template,redirect,url_for,session 2 | from config import db_config,page_config 3 | from dbutil.dbutil import DB 4 | # import requests 5 | import json 6 | import sys 7 | reload(sys) 8 | sys.setdefaultencoding("utf-8") 9 | app = Flask(__name__) 10 | app.secret_key = '\xca\x0c\x86\x04\x98@\x02b\x1b7\x8c\x88]\x1b\xd7"+\xe6px@\xc3#\\' 11 | db = DB(host=db_config['host'], mysql_user=db_config['user'], mysql_pass=db_config['passwd'], \ 12 | mysql_db=db_config['db']) 13 | page_config.setdefault('favicon','/static/img/favicon.ico') 14 | page_config.setdefault('title','Woniu-cmdb') 15 | page_config.setdefault('brand_name','Woniu-cmdb') 16 | 17 | @app.route('/login',methods=['GET','POST']) 18 | def login(): 19 | if 'username' in session: 20 | return redirect('/') 21 | if request.method == "POST": 22 | name = request.form.get('username') 23 | passwd = request.form.get('password') 24 | print name 25 | print passwd 26 | obj = {"result":1} 27 | if name and passwd: 28 | sql = 'select * from user where username="%s" and password="%s"'%(name,passwd) 29 | print sql 30 | cur = db.execute(sql) 31 | # print cur.fetchone() 32 | if cur.fetchone(): 33 | obj["result"] = 0 34 | session['username'] = name 35 | return json.dumps(obj) 36 | else: 37 | return render_template('login.html') 38 | @app.route('/logout') 39 | def logout(): 40 | session.pop('username') 41 | return redirect('/login') 42 | 43 | @app.route('/page/