mirror of https://github.com/veops/cmdb.git
This commit is contained in:
parent
2d8264ab6f
commit
5f6ce54875
|
@ -1,126 +0,0 @@
|
||||||
# encoding=utf-8
|
|
||||||
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
from logging.handlers import SMTPHandler
|
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
|
||||||
|
|
||||||
from flask import Flask
|
|
||||||
from flask import request
|
|
||||||
from flask import g
|
|
||||||
from flask.ext.babel import Babel
|
|
||||||
from flask.ext.principal import identity_loaded
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from extensions import mail
|
|
||||||
from extensions import cache
|
|
||||||
from extensions import celery
|
|
||||||
from core import attribute
|
|
||||||
from core import citype
|
|
||||||
from core import cityperelation
|
|
||||||
from core import cirelation
|
|
||||||
from core import ci
|
|
||||||
from core import history
|
|
||||||
from core import account
|
|
||||||
from core import special
|
|
||||||
from models.account import User
|
|
||||||
from lib.template import filters
|
|
||||||
|
|
||||||
|
|
||||||
APP_NAME = "CMDB-API"
|
|
||||||
|
|
||||||
MODULES = (
|
|
||||||
(attribute, "/api/v0.1/attributes"),
|
|
||||||
(citype, "/api/v0.1/citypes"),
|
|
||||||
(cityperelation, "/api/v0.1/cityperelations"),
|
|
||||||
(cirelation, "/api/v0.1/cirelations"),
|
|
||||||
(ci, "/api/v0.1/ci"),
|
|
||||||
(history, "/api/v0.1/history"),
|
|
||||||
(account, "/api/v0.1/accounts"),
|
|
||||||
(special, ""),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def make_app(config=None, modules=None):
|
|
||||||
modules = modules
|
|
||||||
if not modules:
|
|
||||||
modules = MODULES
|
|
||||||
app = Flask(APP_NAME)
|
|
||||||
app.config.from_pyfile(config)
|
|
||||||
configure_extensions(app)
|
|
||||||
configure_i18n(app)
|
|
||||||
configure_identity(app)
|
|
||||||
configure_blueprints(app, modules)
|
|
||||||
configure_logging(app)
|
|
||||||
configure_template_filters(app)
|
|
||||||
return app
|
|
||||||
|
|
||||||
|
|
||||||
def configure_extensions(app):
|
|
||||||
db.app = app
|
|
||||||
celery.init_app(app)
|
|
||||||
db.init_app(app)
|
|
||||||
mail.init_app(app)
|
|
||||||
cache.init_app(app)
|
|
||||||
celery.init_app(app)
|
|
||||||
|
|
||||||
|
|
||||||
def configure_i18n(app):
|
|
||||||
babel = Babel(app)
|
|
||||||
|
|
||||||
@babel.localeselector
|
|
||||||
def get_locale():
|
|
||||||
accept_languages = app.config.get('ACCEPT_LANGUAGES', ['en', 'zh'])
|
|
||||||
return request.accept_languages.best_match(accept_languages)
|
|
||||||
|
|
||||||
|
|
||||||
def configure_modules(app, modules):
|
|
||||||
for module, url_prefix in modules:
|
|
||||||
app.register_module(module, url_prefix=url_prefix)
|
|
||||||
|
|
||||||
|
|
||||||
def configure_blueprints(app, modules):
|
|
||||||
for module, url_prefix in modules:
|
|
||||||
app.register_blueprint(module, url_prefix=url_prefix)
|
|
||||||
|
|
||||||
|
|
||||||
def configure_identity(app):
|
|
||||||
|
|
||||||
@identity_loaded.connect_via(app)
|
|
||||||
def on_identity_loaded(sender, identity):
|
|
||||||
g.user = User.query.from_identity(identity)
|
|
||||||
|
|
||||||
|
|
||||||
def configure_logging(app):
|
|
||||||
hostname = os.uname()[1]
|
|
||||||
mail_handler = SMTPHandler(
|
|
||||||
app.config['MAIL_SERVER'],
|
|
||||||
app.config['DEFAULT_MAIL_SENDER'],
|
|
||||||
app.config['ADMINS'],
|
|
||||||
'[%s] CMDB error' % hostname,
|
|
||||||
(
|
|
||||||
app.config['MAIL_USERNAME'],
|
|
||||||
app.config['MAIL_PASSWORD'],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
mail_formater = logging.Formatter(
|
|
||||||
"%(asctime)s %(levelname)s %(pathname)s %(lineno)d\n%(message)s")
|
|
||||||
mail_handler.setFormatter(mail_formater)
|
|
||||||
mail_handler.setLevel(logging.ERROR)
|
|
||||||
if not app.debug:
|
|
||||||
app.logger.addHandler(mail_handler)
|
|
||||||
formatter = logging.Formatter(
|
|
||||||
"%(asctime)s %(levelname)s %(pathname)s %(lineno)d - %(message)s")
|
|
||||||
log_file = app.config['LOG_PATH']
|
|
||||||
file_handler = TimedRotatingFileHandler(
|
|
||||||
log_file, when='d', interval=1, backupCount=7)
|
|
||||||
file_handler.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
|
||||||
file_handler.setFormatter(formatter)
|
|
||||||
app.logger.addHandler(file_handler)
|
|
||||||
app.logger.setLevel(getattr(logging, app.config['LOG_LEVEL']))
|
|
||||||
|
|
||||||
|
|
||||||
def configure_template_filters(app):
|
|
||||||
for name in dir(filters):
|
|
||||||
if callable(getattr(filters, name)):
|
|
||||||
app.add_template_filter(getattr(filters, name))
|
|
|
@ -1,637 +0,0 @@
|
||||||
# CMDB API文档
|
|
||||||
|
|
||||||
## 状态返回码的定义
|
|
||||||
* 200: 成功
|
|
||||||
* 400:失败
|
|
||||||
* 401:未授权
|
|
||||||
* 404:url not found
|
|
||||||
* 408:超时
|
|
||||||
* 410:资源删除
|
|
||||||
* 500: 服务器错误
|
|
||||||
|
|
||||||
|
|
||||||
## 用户接口
|
|
||||||
|
|
||||||
### CI搜索接口
|
|
||||||
|
|
||||||
* GET `/api/v0.1/ci/s`
|
|
||||||
* 参数
|
|
||||||
* `string:_type` 搜索的ci_type,多个用分号隔开, 例如: _type:(server;vservser)
|
|
||||||
* `string:q` 搜索表达式, 例如`q=hostname:cmdb*`
|
|
||||||
* `string:fl` 返回字段(id, attr_name, attr_alias均可),英文半角逗号分隔
|
|
||||||
* `string:ret_key` 返回字段类型 `Enum("id", "name", "alias")` 默认 `name`
|
|
||||||
* `count` 指定一次返回CI数
|
|
||||||
* `facet` 属性字段,逗号分隔,返回属性字段对应的所有值
|
|
||||||
|
|
||||||
* 搜索表达式:
|
|
||||||
* 简单的字符串
|
|
||||||
* `attribute:value` 指定属性搜索, `attribute`可以是`id`,`attr_name`和`attr_alias`
|
|
||||||
* 以上的组合,逗号分隔
|
|
||||||
|
|
||||||
* 组合查询支持
|
|
||||||
* `AND`关系-`默认关系`
|
|
||||||
* `OR`关系 - eg.`-hostname:cmdb*`、
|
|
||||||
* `NOT`关系-属性字段前加`~`eg. `~hostname:cmdb*`
|
|
||||||
* `IN`查询. eg. `hostname:(cmdb*;cmdb-web*)` 小括号, 分号分隔
|
|
||||||
* `RANGE`查询. eg. `hostname:[cmdb* _TO_ cmdb-web*]` `_TO_`分隔
|
|
||||||
* `COMPARISON`查询. eg. `cpu_core_num:>5` 支持`>, >=, <, <=`
|
|
||||||
|
|
||||||
## api key 认证
|
|
||||||
|
|
||||||
每个用户会自动生成一个 `api key` 和 一个`secret`, 通过API接口使用的时候,需要提供一个参数 `_key`值为您的`api key`, 以及参数`_secret`值为除`_key`以外的参数,按照**参数名的字典序**排列,并连接到`url path` + `secret`之后的`sha1`**十六进制**值。
|
|
||||||
|
|
||||||
|
|
||||||
## 管理接口
|
|
||||||
|
|
||||||
### Attribute管理接口
|
|
||||||
* GET `/api/v0.1/attributes` 列出所有属性
|
|
||||||
* param
|
|
||||||
* `string:q` 属性名称或者别名,允许为空
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"numfound": 1,
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"attr_name": "idc",
|
|
||||||
"is_choice": true,
|
|
||||||
"choice_value": ["南汇", "欧阳路"],
|
|
||||||
"attr_id": 1,
|
|
||||||
"is_multivalue": false,
|
|
||||||
"attr_alias": "IDC",
|
|
||||||
"value_type": "text",
|
|
||||||
"is_uniq": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
|
|
||||||
* GET `/api/v0.1/attributes/<string:attr_name>`、 `/api/v0.1/attributes/<int:attr_id>` 根据属性名称、别名或ID获取属性
|
|
||||||
* param
|
|
||||||
* `string:attr_name` 属性名称或别名
|
|
||||||
* `int:attr_id` 属性ID
|
|
||||||
* `attr_id`和`attr_name`选其一
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"attribute": {
|
|
||||||
"attr_name": "idc",
|
|
||||||
"is_choice": true,
|
|
||||||
"choice_value": ["南汇", "欧阳路"],
|
|
||||||
"attr_id": 1,
|
|
||||||
"is_multivalue": false,
|
|
||||||
"attr_alias": "IDC",
|
|
||||||
"value_type": "text",
|
|
||||||
"is_uniq": false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `404` 找不到属性
|
|
||||||
|
|
||||||
* POST `/api/v0.1/attributes` 增加新的属性
|
|
||||||
* param
|
|
||||||
* `string:attr_name` 属性名称
|
|
||||||
* `string:attr_alias` 属性别名,可为空,为空时等于`attr_name`
|
|
||||||
* `boolean:choice_value` 若属性有预定义值, 则不能为空
|
|
||||||
* `boolean:is_multivalue` 属性是否允许多值,默认`False`
|
|
||||||
* `boolean:is_uniq` 属性是否唯一,默认`False`
|
|
||||||
* `string:value_type` 属性值类型, `Enum("text", "int", "float", "date")`, 默认`text`
|
|
||||||
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"attr_id":1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `500` 属性已存在
|
|
||||||
* `500` 属性增加失败
|
|
||||||
|
|
||||||
* PUT `/api/v0.1/attributes/<int:attr_id>` 修改属性
|
|
||||||
* param
|
|
||||||
* `string:attr_name` 属性名称
|
|
||||||
* `string:attr_alias` 属性别名,可为空,为空时等于`attr_name`
|
|
||||||
* `boolean:choice_value` 若属性有预定义值, 则不能为空
|
|
||||||
* `boolean:is_multivalue` 属性是否允许多值,值为0或者1,默认`False`
|
|
||||||
* `boolean:is_uniq` 属性是否唯一,值为0或者1,默认`False`
|
|
||||||
* `string:value_type` 属性值类型, `Enum("text", "int", "float", "date")`, 默认`text`
|
|
||||||
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"attr_id":1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `500` 属性已存在
|
|
||||||
* `500` 属性增加失败
|
|
||||||
|
|
||||||
* DELETE `/api/v0.1/attributes/<int:attr_id>` 根据ID删除属性
|
|
||||||
* param
|
|
||||||
* `int:attr_id` 属性ID
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"message":"attribute %s deleted" % attr_name
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `404` 属性不存在
|
|
||||||
* `500` 删除属性失败
|
|
||||||
|
|
||||||
#### CIType属性管理
|
|
||||||
|
|
||||||
* GET `/api/v0.1/attributes/citype/<int:type_id>` 根据type_id查询固有属性列表
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"attr_name": "idc",
|
|
||||||
"is_choice": true,
|
|
||||||
"choice_value": ["南汇", "欧阳路"],
|
|
||||||
"attr_id": 1,
|
|
||||||
"is_multivalue": false,
|
|
||||||
"attr_alias": "IDC",
|
|
||||||
"value_type": "text",
|
|
||||||
"is_uniq": false
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"type_id": 1,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* POST `/api/v0.1/attributes/citype/<int:type_id>` 根据`attr_id`增加CIType的属性
|
|
||||||
* param
|
|
||||||
* `string:attr_id` `,`分隔的`attr_id`
|
|
||||||
* `int:is_required` 0或者1
|
|
||||||
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"attributes":[1, 2, 3]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `404` CIType不存在
|
|
||||||
* `404` 属性不存在
|
|
||||||
* `500` 增加失败
|
|
||||||
|
|
||||||
* DELETE `/api/v0.1/attributes/citype/<int:type_id>` 删除CIType的属性
|
|
||||||
* param
|
|
||||||
* `string:attr_id` `,`分隔的`attr_id`
|
|
||||||
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"attributes":[1, 2, 3]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `404` CIType不存在
|
|
||||||
* `404` 属性不存在
|
|
||||||
* `500` 增加失败
|
|
||||||
|
|
||||||
|
|
||||||
### CIType管理接口
|
|
||||||
|
|
||||||
* `/api/v0.1/citypes` 列出所有CI类型
|
|
||||||
* param `string:type_name` 类型名称,允许为空
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"numfound": 2,
|
|
||||||
"citypes": [
|
|
||||||
{
|
|
||||||
"uniq_key": "sn",
|
|
||||||
"type_name": "物理机",
|
|
||||||
"type_id": 1,
|
|
||||||
"enabled": True,
|
|
||||||
"icon_url": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uniq_key": "uuid",
|
|
||||||
"type_name": "KVM",
|
|
||||||
"type_id": 2,
|
|
||||||
"enabled": True,
|
|
||||||
"icon_url": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
* GET `/api/v0.1/citypes/query` 查询CI类型
|
|
||||||
* param `string:type` 可以是type_id, type_name, type_alias
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"citype": {
|
|
||||||
"type_name": "software",
|
|
||||||
"type_id": 4,
|
|
||||||
"icon_url": "",
|
|
||||||
"type_alias": "\u8f6f\u4ef6",
|
|
||||||
"enabled": true,
|
|
||||||
"uniq_key": 21
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error
|
|
||||||
* `400` message=输入参数缺失
|
|
||||||
* `404` message='citype is not found'
|
|
||||||
|
|
||||||
* POST `/api/v0.1/citypes` 增加新CIType
|
|
||||||
* param (下列参数任意一个或多个)
|
|
||||||
* `string:type_name` CIType名称
|
|
||||||
* `string:type_alias` 类型别名,可为空
|
|
||||||
* `int:_id` 唯一属性ID
|
|
||||||
* `string:unique` 唯一属性名称
|
|
||||||
* `_id`和`unique`只能二选一
|
|
||||||
* `icon_url`
|
|
||||||
* `enabled` 0/1
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"type_id": 2
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `400` message=输入参数缺失
|
|
||||||
* `500` message=CIType已存在
|
|
||||||
* `500` message=唯一属性不存在
|
|
||||||
* `500` message=唯一属性不是唯一的
|
|
||||||
|
|
||||||
* PUT `/api/v0.1/citypes/<int:type_id>` 修改CIType
|
|
||||||
* param (下列参数任意一个或多个)
|
|
||||||
* `string:type_name` CIType名称
|
|
||||||
* `string:type_alias` 类型别名,可为空
|
|
||||||
* `int:_id` 唯一属性ID
|
|
||||||
* `string:unique` 唯一属性名称
|
|
||||||
* `_id`和`unique`只能二选一
|
|
||||||
* `icon_url`
|
|
||||||
* `enabled` 0/1
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"type_id": 2
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `400` message=输入参数缺失
|
|
||||||
* `500` message=CIType已存在
|
|
||||||
* `500` message=唯一属性不存在
|
|
||||||
* `500` message=唯一属性不是唯一的
|
|
||||||
|
|
||||||
* GET/POST `/api/v0.1/citypes/enable/<int:type_id>` 修改CIType
|
|
||||||
* param
|
|
||||||
* `enabled` 0 or 1
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"type_id": 2
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `500` 设置失败
|
|
||||||
* `404` CIType不存在
|
|
||||||
|
|
||||||
* DELETE `/api/v0.1/citypes/<int:type_id>` 根据ID删除CIType
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"message":"ci type %s deleted" % type_name
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error
|
|
||||||
* `500` 删除失败
|
|
||||||
* `404` CIType不存在
|
|
||||||
|
|
||||||
### CITypeRelation管理接口
|
|
||||||
|
|
||||||
* GET `/api/v0.1/cityperelations/types` 列出所有CIType关系类型名
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"relation_types": ["连接", "位置", "附属", "部署"],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
* GET `/api/v0.1/cityperelations/<int:parent>/children` 返回所有child id
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"children": [
|
|
||||||
{
|
|
||||||
"ctr_id": 1,
|
|
||||||
"type_name": "project",
|
|
||||||
"type_id": 2,
|
|
||||||
"icon_url": "",
|
|
||||||
"type_alias": "应用",
|
|
||||||
"enabled": true,
|
|
||||||
"uniq_key": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
* GET `/api/v0.1/cityperelations/<int:child>/parents` 返回parent id
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"parents": [{'parent':1, 'relaltion_type': 'containes', "ctr_id":1}],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
|
|
||||||
* POST `/api/v0.1/cityperelations/<int:parent>/<int:child>` 增加CIType关系
|
|
||||||
* param
|
|
||||||
* `string:relation_type` 类型名称
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"ctr_id":1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error
|
|
||||||
* `500` 增加失败
|
|
||||||
* `404` CIType不存在
|
|
||||||
|
|
||||||
* DELETE `/api/v0.1/cityperelations/<int:ctr_id>` 根据`ctr_id`删除CIType关系
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"message":"CIType relation %s deleted" % type
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error
|
|
||||||
* `500` 删除失败
|
|
||||||
* `404` 关系不存在
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### CI管理接口
|
|
||||||
|
|
||||||
* GET `/api/v0.1/ci/type/<int:type_id>` 查询CIType的所有CI,一次返回25条记录
|
|
||||||
* param
|
|
||||||
* `string:fields` 返回属性名、id,逗号隔开
|
|
||||||
* `string:ret_key` 返回属性key,默认'name',还可是'id', 'alias'
|
|
||||||
* `int:page` 页码
|
|
||||||
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"numfound": 1,
|
|
||||||
"type_id":1,
|
|
||||||
"page": 1,
|
|
||||||
"cis": [
|
|
||||||
{
|
|
||||||
"ci_type": "KVM",
|
|
||||||
"_type": 1,
|
|
||||||
"nic": [
|
|
||||||
2
|
|
||||||
],
|
|
||||||
"hostname": "xxxxxx",
|
|
||||||
"_unique": "xxxxxx",
|
|
||||||
"_id": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* erorr
|
|
||||||
* `404` CIType不存在
|
|
||||||
|
|
||||||
* GET `/api/v0.1/ci/<int:ci_id>` 查询CI
|
|
||||||
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"ci": {
|
|
||||||
"ci_type": "KVM",
|
|
||||||
"_type": 1,
|
|
||||||
"nic": [2],
|
|
||||||
"hostname": "xxxxx",
|
|
||||||
"_unique": "xxxxx",
|
|
||||||
"_id": 1
|
|
||||||
},
|
|
||||||
"ci_id": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* erorr 无
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* POST `/api/v0.1/ci` 增加CI
|
|
||||||
* param
|
|
||||||
* `string:ci_type` CIType name 或者id
|
|
||||||
* `string:_no_attribute_policy` 当添加不存在的attribute时的策略, 默认`ignore`
|
|
||||||
* 其他url参数`k=v`: `k` 为属性名(id或别名亦可), `v`为对应的值
|
|
||||||
* 此CIType的`unique`字段必须包含在url参数中
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"ci_id":1,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* erorr
|
|
||||||
* `500` 添加失败
|
|
||||||
|
|
||||||
* PUT `/api/v0.1/ci` 修改CI
|
|
||||||
* param
|
|
||||||
* `string:ci_type` CIType name 或者id
|
|
||||||
* `string:_no_attribute_policy` 当添加不存在的attribute时的策略, 默认`ignore`
|
|
||||||
* 其他url参数`k=v`: `k` 为属性名(id或别名亦可), `v`为对应的值
|
|
||||||
* 此CIType的`unique`字段必须包含在url参数中
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"ci_id":1,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* erorr
|
|
||||||
* `500` 添加失败
|
|
||||||
|
|
||||||
* DELETE `/api/v0.1/ci/<int:ci_id>` 删除ci
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"message":"ok",
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* erorr
|
|
||||||
* `500` 删除失败
|
|
||||||
|
|
||||||
|
|
||||||
## CIRelaiton管理接口
|
|
||||||
|
|
||||||
* GET `/api/v0.1/cirelations/types` 列出所有CI关系类型名
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"relation_types": ["connect", "install", "deploy", "contain"],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
* GET `/api/v0.1/cirelations/<int:first_ci>/second_cis` 返回所有second id
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"numfound": 1,
|
|
||||||
"second_cis": [
|
|
||||||
{
|
|
||||||
"ci_type": "project",
|
|
||||||
"ci_type_alias": "应用",
|
|
||||||
"_type": 2,
|
|
||||||
"_id": 18,
|
|
||||||
"project_name": "cmdb-api"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
* GET `/api/v0.1/cirelations/<int:second_ci>/first_cis` 返回first ci id
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"first_cis": [
|
|
||||||
{
|
|
||||||
"ci_type": "project",
|
|
||||||
"ci_type_alias": "应用",
|
|
||||||
"_type": 2,
|
|
||||||
"_id": 18,
|
|
||||||
"project_name": "cmdb-api"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"numfound": 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
|
|
||||||
* POST `/api/v0.1/cirelations/<int:first_ci>/<int:second_ci>` 增加CI关系
|
|
||||||
* param
|
|
||||||
* `int: more` more实例
|
|
||||||
* `string:relation_type` 类型名称
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"cr_id":1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error
|
|
||||||
* `500` 增加失败
|
|
||||||
* `404` CI不存在
|
|
||||||
|
|
||||||
* DELETE `/api/v0.1/cirelations/delete/<int:cr_id>` 根据`cr_id`删除CI关系
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"message":"CIType relation %s deleted" % type
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error
|
|
||||||
* `500` 删除失败
|
|
||||||
* `404` 关系不存在
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 历史记录管理接口
|
|
||||||
* GET `/api/v0.1/history/record` 查询历史记录
|
|
||||||
* param
|
|
||||||
* `int: page`
|
|
||||||
* `string: username` 变更用户
|
|
||||||
* `string: start` 变更开始时间
|
|
||||||
* `string: end` 变更结束时间
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"username": "",
|
|
||||||
"start": "2014-12-31 14:57:43",
|
|
||||||
"end": "2015-01-07 14:57:43",
|
|
||||||
"records": [
|
|
||||||
{
|
|
||||||
"origin": null,
|
|
||||||
"attr_history": [],
|
|
||||||
"timestamp": "2015-01-01 22:12:39",
|
|
||||||
"reason": null,
|
|
||||||
"rel_history": {
|
|
||||||
"add": 1
|
|
||||||
},
|
|
||||||
"user": 1,
|
|
||||||
"record_id": 1234,
|
|
||||||
"ticket_id": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
* error 无
|
|
||||||
|
|
||||||
* GET `/api/v0.1/history/<int:record_id>` 历史记录详情
|
|
||||||
* return
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"username": "pycook",
|
|
||||||
"timestamp": "2015-01-02 20:21:16",
|
|
||||||
"rel_history": {
|
|
||||||
"add": [
|
|
||||||
[
|
|
||||||
123,
|
|
||||||
"deploy",
|
|
||||||
234
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"delete": []
|
|
||||||
},
|
|
||||||
"attr_history": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* error
|
|
||||||
* `404` 该记录不存在
|
|
|
@ -1,99 +0,0 @@
|
||||||
# CMDB查询 API文档
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 用户接口
|
|
||||||
|
|
||||||
### CI通用搜索接口
|
|
||||||
|
|
||||||
* GET `/api/v0.1/ci/s`
|
|
||||||
* 参数
|
|
||||||
* `string:_type` 搜索的ci_type,多个用分号隔开, 例如: _type:(docker;kvm)
|
|
||||||
* `string:q` 搜索表达式, 例如`q=hostname:cmdb*`
|
|
||||||
* `string:fl` 返回字段(id, attr_name, attr_alias均可),英文半角逗号分隔
|
|
||||||
* `string:ret_key` 返回字段类型 `Enum("id", "name", "alias")` 默认 `name`
|
|
||||||
* `count` 指定一次返回CI数
|
|
||||||
* `facet` 属性字段,逗号分隔,返回属性字段对应的所有值
|
|
||||||
* `wt` 返回的数据格式,默认为`json`, 可选参数为`xml`
|
|
||||||
|
|
||||||
* 搜索表达式:
|
|
||||||
* 简单的字符串
|
|
||||||
* `attribute:value` 指定属性搜索, `attribute`可以是`id`,`attr_name`和`attr_alias`
|
|
||||||
* 以上的组合,逗号分隔
|
|
||||||
|
|
||||||
* 组合查询支持
|
|
||||||
* `AND`关系-`默认关系`
|
|
||||||
* `OR`关系 - eg.`-hostname:cmdb*`、
|
|
||||||
* `NOT`关系-属性字段前加`~`eg. `~hostname:cmdb*`
|
|
||||||
* `IN`查询. eg. `hostname:(cmdb*;cmdb-web*)` 小括号, 分号分隔
|
|
||||||
* `RANGE`查询. eg. `hostname:[cmdb* _TO_ cmdb-web*]` `_TO_`分隔
|
|
||||||
* `COMPARISON`查询. eg. `cpu_count:>5` 支持`>, >=, <, <=`
|
|
||||||
|
|
||||||
* 返回结果
|
|
||||||
* 搜索表达式 `/api/v0.1/ci/s?q=_type:kvm,status:在线,idc:南汇,private_ip:10.1.1.1*&page=1&fl=hostname,private_ip&facet=private_ip&count=1`
|
|
||||||
* 返回数据(默认json)
|
|
||||||
|
|
||||||
```
|
|
||||||
{
|
|
||||||
facet: {
|
|
||||||
private_ip: [
|
|
||||||
[
|
|
||||||
"10.1.1.11",
|
|
||||||
1,
|
|
||||||
"private_ip"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"10.1.1.12",
|
|
||||||
1,
|
|
||||||
"private_ip"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"10.1.1.13",
|
|
||||||
1,
|
|
||||||
"private_ip"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
total: 1,
|
|
||||||
numfound: 3,
|
|
||||||
result: [
|
|
||||||
{
|
|
||||||
ci_type: "kvm",
|
|
||||||
_type: 8,
|
|
||||||
_id: 3213,
|
|
||||||
hostname: "xxx11",
|
|
||||||
private_ip: [
|
|
||||||
"10.1.1.11"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ci_type: "kvm",
|
|
||||||
_type: 8,
|
|
||||||
_id: 123232,
|
|
||||||
hostname: "xxx12",
|
|
||||||
private_ip: [
|
|
||||||
"10.1.1.12"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ci_type: "kvm",
|
|
||||||
_type: 8,
|
|
||||||
_id: 123513,
|
|
||||||
hostname: "xxx13",
|
|
||||||
private_ip: [
|
|
||||||
"10.1.1.13"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
counter: {
|
|
||||||
kvm: 3
|
|
||||||
},
|
|
||||||
page: 1
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### CI专用搜索接口
|
|
||||||
##### 根据需求实现
|
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
|
@ -1,55 +0,0 @@
|
||||||
# coding: utf-8
|
|
||||||
# common
|
|
||||||
|
|
||||||
DEBUG = True
|
|
||||||
SECRET_KEY = 'dsfdjsf@3213!@JKJWL'
|
|
||||||
HOST = 'http://127.0.0.1:5000'
|
|
||||||
|
|
||||||
# # database
|
|
||||||
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://mysqluser:password@127.0.0.1:3306/cmdb?charset=utf8'
|
|
||||||
|
|
||||||
SQLALCHEMY_ECHO = False
|
|
||||||
SQLALCHEMY_POOL_SIZE = 10
|
|
||||||
SQLALCHEMY_POOL_RECYCLE = 300
|
|
||||||
|
|
||||||
# # cache
|
|
||||||
CACHE_TYPE = "redis"
|
|
||||||
CACHE_REDIS_HOST = "127.0.0.1"
|
|
||||||
CACHE_REDIS_PORT = 6379
|
|
||||||
CACHE_KEY_PREFIX = "CMDB-API"
|
|
||||||
CACHE_DEFAULT_TIMEOUT = 3000
|
|
||||||
|
|
||||||
# # i18n
|
|
||||||
ACCEPT_LANGUAGES = ['en', 'zh']
|
|
||||||
BABEL_DEFAULT_LOCALE = 'zh'
|
|
||||||
BABEL_DEFAULT_TIMEZONE = 'Asia/Shanghai'
|
|
||||||
|
|
||||||
# # log
|
|
||||||
LOG_PATH = './logs/app.log'
|
|
||||||
|
|
||||||
LOG_LEVEL = 'DEBUG'
|
|
||||||
ADMINS = ('@')
|
|
||||||
|
|
||||||
# # mail
|
|
||||||
MAIL_SERVER = ''
|
|
||||||
MAIL_PORT = 25
|
|
||||||
MAIL_USE_TLS = False
|
|
||||||
MAIL_USE_SSL = False
|
|
||||||
MAIL_DEBUG = True
|
|
||||||
MAIL_USERNAME = ''
|
|
||||||
MAIL_PASSWORD = ''
|
|
||||||
DEFAULT_MAIL_SENDER = ''
|
|
||||||
|
|
||||||
|
|
||||||
# # queue
|
|
||||||
CELERY_RESULT_BACKEND = "redis://127.0.0.1//"
|
|
||||||
BROKER_URL = 'redis://127.0.0.1//'
|
|
||||||
BROKER_VHOST = '/'
|
|
||||||
|
|
||||||
|
|
||||||
# # pagination
|
|
||||||
PER_PAGE_COUNT_RANGE = (10, 25, 50, 100)
|
|
||||||
DEFAULT_PAGE_COUNT = 25
|
|
||||||
|
|
||||||
|
|
||||||
WHITE_LIST = ["127.0.0.1"]
|
|
|
@ -1,11 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from attribute import attribute
|
|
||||||
from ci_type import citype
|
|
||||||
from ci_type_relation import cityperelation
|
|
||||||
from ci_relation import cirelation
|
|
||||||
from ci import ci
|
|
||||||
from history import history
|
|
||||||
from account import account
|
|
||||||
from special import special
|
|
|
@ -1,98 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import request
|
|
||||||
|
|
||||||
from flask import g
|
|
||||||
from flask import abort
|
|
||||||
from flask import jsonify
|
|
||||||
|
|
||||||
from models import row2dict
|
|
||||||
from lib.account import AccountManager
|
|
||||||
from lib.auth import auth_with_key
|
|
||||||
|
|
||||||
|
|
||||||
account = Blueprint('account', __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@account.route("/<int:uid>", methods=["GET"])
|
|
||||||
@auth_with_key
|
|
||||||
def get_user(uid=None):
|
|
||||||
manager = AccountManager()
|
|
||||||
user = manager.get_user_by_uid(uid)
|
|
||||||
if user:
|
|
||||||
return jsonify(rolenames=user.rolenames, user=row2dict(user))
|
|
||||||
else:
|
|
||||||
return jsonify(user=None)
|
|
||||||
|
|
||||||
|
|
||||||
@account.route("", methods=["POST"])
|
|
||||||
@auth_with_key
|
|
||||||
def create_user():
|
|
||||||
manager = AccountManager()
|
|
||||||
params = {}
|
|
||||||
for k, v in request.values.iteritems():
|
|
||||||
params[k] = v
|
|
||||||
user = manager.create_user(**params)
|
|
||||||
return jsonify(user=row2dict(user))
|
|
||||||
|
|
||||||
|
|
||||||
@account.route("/<int:uid>", methods=["PUT"])
|
|
||||||
@auth_with_key
|
|
||||||
def update_user(uid=None):
|
|
||||||
manager = AccountManager()
|
|
||||||
params = {}
|
|
||||||
for k, v in request.values.iteritems():
|
|
||||||
params[k] = v
|
|
||||||
ret, res = manager.update_user(uid, **params)
|
|
||||||
if not ret:
|
|
||||||
abort(res[0], res[1])
|
|
||||||
return jsonify(user=row2dict(res), rolenames=res.rolenames)
|
|
||||||
|
|
||||||
|
|
||||||
@account.route("/<int:uid>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_user(uid=None):
|
|
||||||
manager = AccountManager()
|
|
||||||
ret, res = manager.delete_user(uid)
|
|
||||||
if not ret:
|
|
||||||
abort(res[0], res[1])
|
|
||||||
return jsonify(uid=uid)
|
|
||||||
|
|
||||||
|
|
||||||
@account.route("/validate", methods=["POST"])
|
|
||||||
@auth_with_key
|
|
||||||
def validate():
|
|
||||||
username = request.values.get("username")
|
|
||||||
password = request.values.get("password")
|
|
||||||
manager = AccountManager()
|
|
||||||
user, authenticated = manager.validate(username, password)
|
|
||||||
if user and not authenticated:
|
|
||||||
return jsonify(code=401, user=row2dict(user), rolenames=user.rolenames)
|
|
||||||
elif not user:
|
|
||||||
return jsonify(code=404, message="user is not existed")
|
|
||||||
return jsonify(code=200, user=row2dict(user), rolenames=user.rolenames)
|
|
||||||
|
|
||||||
|
|
||||||
@account.route("/key", methods=["PUT"])
|
|
||||||
@auth_with_key
|
|
||||||
def update_key():
|
|
||||||
manager = AccountManager()
|
|
||||||
ret, res = manager.reset_key(g.user.uid)
|
|
||||||
if not ret:
|
|
||||||
abort(res[0], res[1])
|
|
||||||
return jsonify(user=row2dict(res), rolenames=res.rolenames)
|
|
||||||
|
|
||||||
|
|
||||||
@account.route("/password", methods=["PUT"])
|
|
||||||
@auth_with_key
|
|
||||||
def update_password():
|
|
||||||
manager = AccountManager()
|
|
||||||
old = request.values.get("password")
|
|
||||||
new = request.values.get("new_password")
|
|
||||||
confirm = request.values.get("confirm")
|
|
||||||
ret, res = manager.update_password(g.user.uid, old, new, confirm)
|
|
||||||
if not ret:
|
|
||||||
abort(res[0], res[1])
|
|
||||||
return jsonify(user=row2dict(res), rolenames=res.rolenames)
|
|
|
@ -1,145 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import jsonify
|
|
||||||
from flask import request
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import abort
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from lib.attribute import AttributeManager
|
|
||||||
from lib.ci_type import CITypeAttributeManager
|
|
||||||
from lib.decorator import argument_required
|
|
||||||
from lib.exception import InvalidUsageError
|
|
||||||
from lib.auth import auth_with_key
|
|
||||||
|
|
||||||
attribute = Blueprint("attribute", __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@attribute.route("", methods=["GET"])
|
|
||||||
def get_attributes():
|
|
||||||
q = request.values.get("q")
|
|
||||||
attrs = AttributeManager().get_attributes(name=q)
|
|
||||||
count = len(attrs)
|
|
||||||
return jsonify(numfound=count, attributes=attrs)
|
|
||||||
|
|
||||||
|
|
||||||
@attribute.route("/<string:attr_name>", methods=["GET"])
|
|
||||||
@attribute.route("/<int:attr_id>", methods=["GET"])
|
|
||||||
def get_attribute(attr_name=None, attr_id=None):
|
|
||||||
attr_manager = AttributeManager()
|
|
||||||
attr_dict = None
|
|
||||||
if attr_name is not None:
|
|
||||||
attr_dict = attr_manager.get_attribute_by_name(attr_name)
|
|
||||||
if attr_dict is None:
|
|
||||||
attr_dict = attr_manager.get_attribute_by_alias(attr_name)
|
|
||||||
elif attr_id is not None:
|
|
||||||
attr_dict = attr_manager.get_attribute_by_id(attr_id)
|
|
||||||
if attr_dict is not None:
|
|
||||||
return jsonify(attribute=attr_dict)
|
|
||||||
abort(404, "attribute not found")
|
|
||||||
|
|
||||||
|
|
||||||
@attribute.route("", methods=["POST"])
|
|
||||||
@auth_with_key
|
|
||||||
def create_attribute():
|
|
||||||
with argument_required("attr_name"):
|
|
||||||
attr_name = request.values.get("attr_name")
|
|
||||||
current_app.logger.info(attr_name)
|
|
||||||
attr_alias = request.values.get("attr_alias", attr_name)
|
|
||||||
choice_value = request.values.get("choice_value")
|
|
||||||
is_multivalue = request.values.get("is_multivalue", False)
|
|
||||||
is_uniq = request.values.get("is_uniq", False)
|
|
||||||
is_index = request.values.get("is_index", False)
|
|
||||||
value_type = request.values.get("value_type", "text")
|
|
||||||
try:
|
|
||||||
is_multivalue = int(is_multivalue)
|
|
||||||
is_uniq = int(is_uniq)
|
|
||||||
is_index = int(is_index)
|
|
||||||
except ValueError:
|
|
||||||
raise InvalidUsageError("argument format is error")
|
|
||||||
attr_manager = AttributeManager()
|
|
||||||
kwargs = {"choice_value": choice_value, "is_multivalue": is_multivalue,
|
|
||||||
"is_uniq": is_uniq, "value_type": value_type,
|
|
||||||
"is_index": is_index}
|
|
||||||
ret, res = attr_manager.add(attr_name, attr_alias, **kwargs)
|
|
||||||
if not ret:
|
|
||||||
return abort(500, res)
|
|
||||||
return jsonify(attr_id=res)
|
|
||||||
|
|
||||||
|
|
||||||
@attribute.route("/<int:attr_id>", methods=["PUT"])
|
|
||||||
@auth_with_key
|
|
||||||
def update_attribute(attr_id=None):
|
|
||||||
with argument_required("attr_name"):
|
|
||||||
attr_name = request.values.get("attr_name")
|
|
||||||
attr_alias = request.values.get("attr_alias", attr_name)
|
|
||||||
choice_value = request.values.get("choice_value")
|
|
||||||
is_multivalue = request.values.get("is_multivalue", False)
|
|
||||||
is_uniq = request.values.get("is_uniq", False)
|
|
||||||
value_type = request.values.get("value_type", "text")
|
|
||||||
try:
|
|
||||||
is_multivalue = int(is_multivalue)
|
|
||||||
is_uniq = int(is_uniq)
|
|
||||||
except ValueError:
|
|
||||||
raise InvalidUsageError("argument format is error")
|
|
||||||
attr_manager = AttributeManager()
|
|
||||||
kwargs = {"choice_value": choice_value, "is_multivalue": is_multivalue,
|
|
||||||
"is_uniq": is_uniq, "value_type": value_type}
|
|
||||||
ret, res = attr_manager.update(attr_id, attr_name,
|
|
||||||
attr_alias, **kwargs)
|
|
||||||
if not ret:
|
|
||||||
return abort(500, res)
|
|
||||||
return jsonify(attr_id=res)
|
|
||||||
|
|
||||||
|
|
||||||
@attribute.route("/<int:attr_id>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_attribute(attr_id=None):
|
|
||||||
attr_manager = AttributeManager()
|
|
||||||
res = attr_manager.delete(attr_id)
|
|
||||||
return jsonify(message="attribute {0} deleted".format(res))
|
|
||||||
|
|
||||||
|
|
||||||
@attribute.route("/citype/<int:type_id>", methods=["GET"])
|
|
||||||
def get_attributes_by_type(type_id=None):
|
|
||||||
manager = CITypeAttributeManager()
|
|
||||||
from models.cmdb import CITypeCache, CIAttributeCache
|
|
||||||
|
|
||||||
t = CITypeCache.get(type_id)
|
|
||||||
if not t:
|
|
||||||
return abort(400, "CIType {0} is not existed".format(type_id))
|
|
||||||
uniq_id = t.uniq_id
|
|
||||||
unique = CIAttributeCache.get(uniq_id).attr_name
|
|
||||||
return jsonify(attributes=manager.get_attributes_by_type_id(type_id),
|
|
||||||
type_id=type_id, uniq_id=uniq_id, unique=unique)
|
|
||||||
|
|
||||||
|
|
||||||
@attribute.route("/citype/<int:type_id>", methods=["POST"])
|
|
||||||
@auth_with_key
|
|
||||||
def create_attributes_to_citype(type_id=None):
|
|
||||||
with argument_required("attr_id"):
|
|
||||||
attr_ids = request.values.get("attr_id", "")
|
|
||||||
is_required = request.values.get("is_required", False)
|
|
||||||
attr_id_list = attr_ids.strip().split(",")
|
|
||||||
if "" in attr_id_list:
|
|
||||||
attr_id_list.remove("")
|
|
||||||
attr_id_list = map(int, attr_id_list)
|
|
||||||
try:
|
|
||||||
is_required = int(is_required)
|
|
||||||
except ValueError:
|
|
||||||
abort(500, "argument format is error")
|
|
||||||
manager = CITypeAttributeManager()
|
|
||||||
manager.add(type_id, attr_id_list, is_required=is_required)
|
|
||||||
return jsonify(attributes=attr_id_list)
|
|
||||||
|
|
||||||
|
|
||||||
@attribute.route("/citype/<int:type_id>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_attribute_in_type(type_id=None):
|
|
||||||
with argument_required("attr_id"):
|
|
||||||
attr_ids = request.values.get("attr_id", "")
|
|
||||||
attr_id_list = attr_ids.strip().split(",")
|
|
||||||
manager = CITypeAttributeManager()
|
|
||||||
manager.delete(type_id, attr_id_list)
|
|
||||||
return jsonify(attributes=attr_id_list)
|
|
|
@ -1,189 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
import sys
|
|
||||||
reload(sys)
|
|
||||||
sys.setdefaultencoding("utf-8")
|
|
||||||
import time
|
|
||||||
import urllib
|
|
||||||
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import request
|
|
||||||
from flask import jsonify
|
|
||||||
from flask import current_app
|
|
||||||
from flask import make_response
|
|
||||||
from flask import render_template
|
|
||||||
from flask import abort
|
|
||||||
|
|
||||||
from lib.auth import auth_with_key
|
|
||||||
from lib.ci import CIManager
|
|
||||||
from lib.search import Search
|
|
||||||
from lib.search import SearchError
|
|
||||||
from lib.utils import get_page
|
|
||||||
from lib.utils import get_per_page
|
|
||||||
from models.ci_type import CITypeCache
|
|
||||||
|
|
||||||
ci = Blueprint("ci", __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@ci.route("/type/<int:type_id>", methods=["GET"])
|
|
||||||
def get_cis_by_type(type_id=None):
|
|
||||||
fields = request.args.get("fields", "").strip().split(",")
|
|
||||||
fields = filter(lambda x: x != "", fields)
|
|
||||||
|
|
||||||
ret_key = request.args.get("ret_key", "name")
|
|
||||||
if ret_key not in ('name', 'alias', 'id'):
|
|
||||||
ret_key = 'name'
|
|
||||||
|
|
||||||
page = get_page(request.values.get("page", 1))
|
|
||||||
count = get_per_page(request.values.get("count"))
|
|
||||||
manager = CIManager()
|
|
||||||
res = manager.get_cis_by_type(type_id, ret_key=ret_key,
|
|
||||||
fields=fields, page=page, per_page=count)
|
|
||||||
return jsonify(type_id=type_id, numfound=res[0],
|
|
||||||
total=len(res[2]), page=res[1], cis=res[2])
|
|
||||||
|
|
||||||
|
|
||||||
@ci.route("/<int:ci_id>", methods=['GET'])
|
|
||||||
def get_ci(ci_id=None):
|
|
||||||
fields = request.args.get("fields", "").strip().split(",")
|
|
||||||
fields = filter(lambda x: x != "", fields)
|
|
||||||
|
|
||||||
ret_key = request.args.get("ret_key", "name")
|
|
||||||
if ret_key not in ('name', 'alias', 'id'):
|
|
||||||
ret_key = 'name'
|
|
||||||
|
|
||||||
manager = CIManager()
|
|
||||||
ci = manager.get_ci_by_id(ci_id, ret_key=ret_key, fields=fields)
|
|
||||||
return jsonify(ci_id=ci_id, ci=ci)
|
|
||||||
|
|
||||||
|
|
||||||
@ci.route("/s", methods=["GET"])
|
|
||||||
@ci.route("/search", methods=["GET"])
|
|
||||||
def search():
|
|
||||||
"""@params: q: query statement
|
|
||||||
fl: filter by column
|
|
||||||
count: the number of ci
|
|
||||||
ret_key: id, name, alias
|
|
||||||
facet: statistic
|
|
||||||
wt: result format
|
|
||||||
"""
|
|
||||||
page = get_page(request.values.get("page", 1))
|
|
||||||
count = get_per_page(request.values.get("count"))
|
|
||||||
|
|
||||||
query = request.values.get('q', "")
|
|
||||||
fl = request.values.get('fl', "").split(",")
|
|
||||||
ret_key = request.values.get('ret_key', "name")
|
|
||||||
if ret_key not in ('name', 'alias', 'id'):
|
|
||||||
ret_key = 'name'
|
|
||||||
facet = request.values.get("facet", "").split(",")
|
|
||||||
wt = request.values.get('wt', 'json')
|
|
||||||
fl = filter(lambda x: x != "", fl)
|
|
||||||
facet = filter(lambda x: x != "", facet)
|
|
||||||
sort = request.values.get("sort")
|
|
||||||
|
|
||||||
start = time.time()
|
|
||||||
s = Search(query, fl, facet, page, ret_key, count, sort)
|
|
||||||
try:
|
|
||||||
response, counter, total, page, numfound, facet = s.search()
|
|
||||||
except SearchError, e:
|
|
||||||
return abort(400, str(e))
|
|
||||||
except Exception, e:
|
|
||||||
current_app.logger.error(str(e))
|
|
||||||
return abort(500, "search unknown error")
|
|
||||||
|
|
||||||
if wt == 'xml':
|
|
||||||
res = make_response(
|
|
||||||
render_template("search.xml",
|
|
||||||
counter=counter,
|
|
||||||
total=total,
|
|
||||||
result=response,
|
|
||||||
page=page,
|
|
||||||
numfound=numfound,
|
|
||||||
facet=facet))
|
|
||||||
res.headers['Content-type'] = 'text/xml'
|
|
||||||
return res
|
|
||||||
current_app.logger.debug("search time is :{0}".format(
|
|
||||||
time.time() - start))
|
|
||||||
return jsonify(numfound=numfound,
|
|
||||||
total=total,
|
|
||||||
page=page,
|
|
||||||
facet=facet,
|
|
||||||
counter=counter,
|
|
||||||
result=response)
|
|
||||||
|
|
||||||
|
|
||||||
@ci.route("", methods=["POST"])
|
|
||||||
@auth_with_key
|
|
||||||
def create_ci():
|
|
||||||
ci_type = request.values.get("ci_type")
|
|
||||||
_no_attribute_policy = request.values.get("_no_attribute_policy", "ignore")
|
|
||||||
|
|
||||||
ci_dict = dict()
|
|
||||||
for k, v in request.values.iteritems():
|
|
||||||
if k != "ci_type" and not k.startswith("_"):
|
|
||||||
ci_dict[k] = v.strip()
|
|
||||||
|
|
||||||
manager = CIManager()
|
|
||||||
current_app.logger.debug(ci_dict)
|
|
||||||
ci_id = manager.add(ci_type, exist_policy="reject",
|
|
||||||
_no_attribute_policy=_no_attribute_policy, **ci_dict)
|
|
||||||
return jsonify(ci_id=ci_id)
|
|
||||||
|
|
||||||
|
|
||||||
@ci.route("", methods=["PUT"])
|
|
||||||
@auth_with_key
|
|
||||||
def update_ci():
|
|
||||||
if request.data:
|
|
||||||
args = dict()
|
|
||||||
_args = request.data.split("&")
|
|
||||||
for arg in _args:
|
|
||||||
if arg:
|
|
||||||
args[arg.split("=")[0]] = \
|
|
||||||
urllib.unquote(urllib.unquote(arg.split("=")[1]))
|
|
||||||
else:
|
|
||||||
args = request.values
|
|
||||||
|
|
||||||
ci_type = args.get("ci_type")
|
|
||||||
_no_attribute_policy = args.get("_no_attribute_policy", "ignore")
|
|
||||||
ci_dict = dict()
|
|
||||||
for k, v in args.items():
|
|
||||||
if k != "ci_type" and not k.startswith("_"):
|
|
||||||
ci_dict[k] = v.strip()
|
|
||||||
|
|
||||||
manager = CIManager()
|
|
||||||
ci_id = manager.add(ci_type, exist_policy="replace",
|
|
||||||
_no_attribute_policy=_no_attribute_policy, **ci_dict)
|
|
||||||
return jsonify(ci_id=ci_id)
|
|
||||||
|
|
||||||
|
|
||||||
@ci.route("/<int:ci_id>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_ci(ci_id=None):
|
|
||||||
manager = CIManager()
|
|
||||||
manager.delete(ci_id)
|
|
||||||
return jsonify(message="ok")
|
|
||||||
|
|
||||||
|
|
||||||
@ci.route("/heartbeat/<string:ci_type>/<string:unique>", methods=["POST"])
|
|
||||||
def add_heartbeat(ci_type, unique):
|
|
||||||
if not unique or not ci_type:
|
|
||||||
return jsonify(message="error")
|
|
||||||
# return jsonify(message="ok")
|
|
||||||
return jsonify(message=CIManager().add_heartbeat(ci_type, unique))
|
|
||||||
|
|
||||||
|
|
||||||
@ci.route("/heartbeat", methods=["GET"])
|
|
||||||
def get_heartbeat():
|
|
||||||
page = get_page(request.values.get("page", 1))
|
|
||||||
ci_type = request.values.get("ci_type", "").strip()
|
|
||||||
try:
|
|
||||||
ci_type = CITypeCache.get(ci_type).type_id
|
|
||||||
except:
|
|
||||||
return jsonify(numfound=0, result=[])
|
|
||||||
agent_status = request.values.get("agent_status", None)
|
|
||||||
if agent_status:
|
|
||||||
agent_status = int(agent_status)
|
|
||||||
numfound, result = CIManager().get_heartbeat(page,
|
|
||||||
ci_type,
|
|
||||||
agent_status=agent_status)
|
|
||||||
return jsonify(numfound=numfound, result=result)
|
|
|
@ -1,70 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import jsonify
|
|
||||||
from flask import request
|
|
||||||
|
|
||||||
from lib.ci import CIRelationManager
|
|
||||||
from lib.utils import get_page
|
|
||||||
from lib.utils import get_per_page
|
|
||||||
from lib.auth import auth_with_key
|
|
||||||
|
|
||||||
|
|
||||||
cirelation = Blueprint("cirelation", __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@cirelation.route("/types", methods=["GET"])
|
|
||||||
def get_types():
|
|
||||||
manager = CIRelationManager()
|
|
||||||
return jsonify(relation_types=manager.relation_types)
|
|
||||||
|
|
||||||
|
|
||||||
@cirelation.route("/<int:first_ci>/second_cis", methods=["GET"])
|
|
||||||
def get_second_cis_by_first_ci(first_ci=None):
|
|
||||||
page = get_page(request.values.get("page", 1))
|
|
||||||
count = get_per_page(request.values.get("count"))
|
|
||||||
relation_type = request.values.get("relation_type", "contain")
|
|
||||||
manager = CIRelationManager()
|
|
||||||
numfound, total, second_cis = manager.get_second_cis(
|
|
||||||
first_ci, page=page, per_page=count, relation_type=relation_type)
|
|
||||||
return jsonify(numfound=numfound, total=total,
|
|
||||||
page=page, second_cis=second_cis)
|
|
||||||
|
|
||||||
|
|
||||||
@cirelation.route("/<int:second_ci>/first_cis", methods=["GET"])
|
|
||||||
def get_first_cis_by_second_ci(second_ci=None):
|
|
||||||
page = get_page(request.values.get("page", 1))
|
|
||||||
count = get_per_page(request.values.get("count"))
|
|
||||||
relation_type = request.values.get("relation_type", "contain")
|
|
||||||
|
|
||||||
manager = CIRelationManager()
|
|
||||||
numfound, total, first_cis = manager.get_first_cis(
|
|
||||||
second_ci, per_page=count, page=page, relation_type=relation_type)
|
|
||||||
return jsonify(numfound=numfound, total=total,
|
|
||||||
page=page, first_cis=first_cis)
|
|
||||||
|
|
||||||
|
|
||||||
@cirelation.route("/<int:first_ci>/<int:second_ci>", methods=["POST"])
|
|
||||||
@auth_with_key
|
|
||||||
def create_ci_relation(first_ci=None, second_ci=None):
|
|
||||||
relation_type = request.values.get("relation_type", "contain")
|
|
||||||
manager = CIRelationManager()
|
|
||||||
res = manager.add(first_ci, second_ci, relation_type=relation_type)
|
|
||||||
return jsonify(cr_id=res)
|
|
||||||
|
|
||||||
|
|
||||||
@cirelation.route("/<int:cr_id>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_ci_relation(cr_id=None):
|
|
||||||
manager = CIRelationManager()
|
|
||||||
manager.delete(cr_id)
|
|
||||||
return jsonify(message="CIType Relation is deleted")
|
|
||||||
|
|
||||||
|
|
||||||
@cirelation.route("/<int:first_ci>/<int:second_ci>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_ci_relation_2(first_ci, second_ci):
|
|
||||||
manager = CIRelationManager()
|
|
||||||
manager.delete_2(first_ci, second_ci)
|
|
||||||
return jsonify(message="CIType Relation is deleted")
|
|
|
@ -1,89 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import jsonify
|
|
||||||
from flask import request
|
|
||||||
from flask import abort
|
|
||||||
|
|
||||||
from lib.ci_type import CITypeManager
|
|
||||||
from lib.decorator import argument_required
|
|
||||||
from lib.auth import auth_with_key
|
|
||||||
|
|
||||||
|
|
||||||
citype = Blueprint("citype", __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@citype.route("", methods=["GET"])
|
|
||||||
def get_citypes():
|
|
||||||
type_name = request.args.get("type_name")
|
|
||||||
manager = CITypeManager()
|
|
||||||
citypes = manager.get_citypes(type_name)
|
|
||||||
count = len(citypes)
|
|
||||||
return jsonify(numfound=count, citypes=citypes)
|
|
||||||
|
|
||||||
|
|
||||||
@citype.route("/query", methods=["GET"])
|
|
||||||
def query():
|
|
||||||
with argument_required("type"):
|
|
||||||
_type = request.args.get("type")
|
|
||||||
manager = CITypeManager()
|
|
||||||
res = manager.query(_type)
|
|
||||||
return jsonify(citype=res)
|
|
||||||
|
|
||||||
|
|
||||||
@citype.route("", methods=["POST"])
|
|
||||||
@auth_with_key
|
|
||||||
def create_citype():
|
|
||||||
with argument_required("type_name"):
|
|
||||||
type_name = request.values.get("type_name")
|
|
||||||
type_alias = request.values.get("type_alias")
|
|
||||||
if type_alias is None:
|
|
||||||
type_alias = type_name
|
|
||||||
_id = request.values.get("_id")
|
|
||||||
unique = request.values.get("unique")
|
|
||||||
enabled = request.values.get("enabled", True)
|
|
||||||
icon_url = request.values.get("icon_url", "")
|
|
||||||
manager = CITypeManager()
|
|
||||||
ret, res = manager.add(type_name, type_alias, _id=_id,
|
|
||||||
unique=unique, enabled=enabled,
|
|
||||||
icon_url=icon_url)
|
|
||||||
if ret:
|
|
||||||
return jsonify(type_id=res)
|
|
||||||
abort(500, res)
|
|
||||||
|
|
||||||
|
|
||||||
@citype.route("/<int:type_id>", methods=["PUT"])
|
|
||||||
@auth_with_key
|
|
||||||
def update_citype(type_id=None):
|
|
||||||
type_name = request.values.get("type_name")
|
|
||||||
type_alias = request.values.get("type_alias")
|
|
||||||
_id = request.values.get("_id")
|
|
||||||
unique = request.values.get("unique")
|
|
||||||
icon_url = request.values.get("icon_url")
|
|
||||||
enabled = request.values.get("enabled")
|
|
||||||
enabled = False if enabled in (0, "0") else True \
|
|
||||||
if enabled is not None else None
|
|
||||||
manager = CITypeManager()
|
|
||||||
ret, res = manager.update(type_id, type_name, type_alias, _id=_id,
|
|
||||||
unique=unique, icon_url=icon_url,
|
|
||||||
enabled=enabled)
|
|
||||||
if ret:
|
|
||||||
return jsonify(type_id=type_id)
|
|
||||||
abort(500, res)
|
|
||||||
|
|
||||||
|
|
||||||
@citype.route("/<int:type_id>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_citype(type_id=None):
|
|
||||||
manager = CITypeManager()
|
|
||||||
res = manager.delete(type_id)
|
|
||||||
return jsonify(message=res)
|
|
||||||
|
|
||||||
|
|
||||||
@citype.route("/enable/<int:type_id>", methods=["GET", "POST"])
|
|
||||||
def enable(type_id=None):
|
|
||||||
enable = request.values.get("enable", True)
|
|
||||||
manager = CITypeManager()
|
|
||||||
manager.set_enabled(type_id, enabled=enable)
|
|
||||||
return jsonify(type_id=type_id)
|
|
|
@ -1,55 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import jsonify
|
|
||||||
from flask import request
|
|
||||||
|
|
||||||
from lib.ci_type import CITypeRelationManager
|
|
||||||
from lib.auth import auth_with_key
|
|
||||||
|
|
||||||
|
|
||||||
cityperelation = Blueprint("cityperelation", __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@cityperelation.route("/types", methods=["GET"])
|
|
||||||
def get_types():
|
|
||||||
manager = CITypeRelationManager()
|
|
||||||
return jsonify(relation_types=manager.relation_types)
|
|
||||||
|
|
||||||
|
|
||||||
@cityperelation.route("/<int:parent>/children", methods=["GET"])
|
|
||||||
def get_children_by_parent(parent=None):
|
|
||||||
manager = CITypeRelationManager()
|
|
||||||
return jsonify(children=manager.get_children(parent))
|
|
||||||
|
|
||||||
|
|
||||||
@cityperelation.route("/<int:child>/parents", methods=["GET"])
|
|
||||||
def get_parents_by_child(child=None):
|
|
||||||
manager = CITypeRelationManager()
|
|
||||||
return jsonify(parents=manager.get_parents(child))
|
|
||||||
|
|
||||||
|
|
||||||
@cityperelation.route("/<int:parent>/<int:child>", methods=["POST"])
|
|
||||||
@auth_with_key
|
|
||||||
def create_citype_realtions(parent=None, child=None):
|
|
||||||
relation_type = request.values.get("relation_type", "contain")
|
|
||||||
manager = CITypeRelationManager()
|
|
||||||
res = manager.add(parent, child, relation_type=relation_type)
|
|
||||||
return jsonify(ctr_id=res)
|
|
||||||
|
|
||||||
|
|
||||||
@cityperelation.route("/<int:ctr_id>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_citype_relation(ctr_id=None):
|
|
||||||
manager = CITypeRelationManager()
|
|
||||||
manager.delete(ctr_id)
|
|
||||||
return jsonify(message="CIType Relation is deleted")
|
|
||||||
|
|
||||||
|
|
||||||
@cityperelation.route("/<int:parent>/<int:child>", methods=["DELETE"])
|
|
||||||
@auth_with_key
|
|
||||||
def delete_citype_relation_2(parent=None, child=None):
|
|
||||||
manager = CITypeRelationManager()
|
|
||||||
manager.delete_2(parent, child)
|
|
||||||
return jsonify(message="CIType Relation is deleted")
|
|
|
@ -1,116 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from flask import jsonify
|
|
||||||
from flask import current_app
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import request
|
|
||||||
from flask import abort
|
|
||||||
|
|
||||||
from models.history import OperationRecord
|
|
||||||
from models.history import CIRelationHistory
|
|
||||||
from models.history import CIAttributeHistory
|
|
||||||
from models.attribute import CIAttributeCache
|
|
||||||
from extensions import db
|
|
||||||
from models import row2dict
|
|
||||||
from models.account import UserCache
|
|
||||||
from lib.ci import CIManager
|
|
||||||
from lib.utils import get_page
|
|
||||||
|
|
||||||
history = Blueprint("history", __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@history.route("/record", methods=["GET"])
|
|
||||||
def get_record():
|
|
||||||
page = get_page(request.values.get("page", 1))
|
|
||||||
_start = request.values.get("start")
|
|
||||||
_end = request.values.get("end")
|
|
||||||
username = request.values.get("username", "")
|
|
||||||
per_page_cnt = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
start, end = None, None
|
|
||||||
if _start:
|
|
||||||
try:
|
|
||||||
start = datetime.datetime.strptime(_start, '%Y-%m-%d %H:%M:%S')
|
|
||||||
except ValueError:
|
|
||||||
abort(400, 'incorrect start date time')
|
|
||||||
if _end:
|
|
||||||
try:
|
|
||||||
end = datetime.datetime.strptime(_end, '%Y-%m-%d %H:%M:%S')
|
|
||||||
except ValueError:
|
|
||||||
abort(400, 'incorrect end date time')
|
|
||||||
records = db.session.query(OperationRecord)
|
|
||||||
numfound = db.session.query(db.func.count(OperationRecord.record_id))
|
|
||||||
if start:
|
|
||||||
records = records.filter(OperationRecord.timestamp >= start)
|
|
||||||
numfound = numfound.filter(OperationRecord.timestamp >= start)
|
|
||||||
if end:
|
|
||||||
records = records.filter(OperationRecord.timestamp <= end)
|
|
||||||
numfound = records.filter(OperationRecord.timestamp <= end)
|
|
||||||
if username:
|
|
||||||
user = UserCache.get(username)
|
|
||||||
if user:
|
|
||||||
records = records.filter(OperationRecord.uid == user.uid)
|
|
||||||
else:
|
|
||||||
return jsonify(numfound=0, records=[],
|
|
||||||
page=1, total=0, start=_start,
|
|
||||||
end=_end, username=username)
|
|
||||||
records = records.order_by(-OperationRecord.record_id).offset(
|
|
||||||
per_page_cnt * (page - 1)).limit(per_page_cnt).all()
|
|
||||||
total = len(records)
|
|
||||||
numfound = numfound.first()[0]
|
|
||||||
res = []
|
|
||||||
for record in records:
|
|
||||||
_res = row2dict(record)
|
|
||||||
_res["user"] = UserCache.get(_res.get("uid")).nickname \
|
|
||||||
if UserCache.get(_res.get("uid")).nickname \
|
|
||||||
else UserCache.get(_res.get("uid")).username
|
|
||||||
attr_history = db.session.query(CIAttributeHistory.attr_id).filter(
|
|
||||||
CIAttributeHistory.record_id == _res.get("record_id")).all()
|
|
||||||
_res["attr_history"] = [CIAttributeCache.get(h.attr_id).attr_alias
|
|
||||||
for h in attr_history]
|
|
||||||
rel_history = db.session.query(CIRelationHistory.operate_type).filter(
|
|
||||||
CIRelationHistory.record_id == _res.get("record_id")).all()
|
|
||||||
rel_statis = {}
|
|
||||||
for rel in rel_history:
|
|
||||||
if rel.operate_type not in rel_statis:
|
|
||||||
rel_statis[rel.operate_type] = 1
|
|
||||||
else:
|
|
||||||
rel_statis[rel.res.operate_type] += 1
|
|
||||||
_res["rel_history"] = rel_statis
|
|
||||||
res.append(_res)
|
|
||||||
|
|
||||||
return jsonify(numfound=numfound, records=res, page=page, total=total,
|
|
||||||
start=_start, end=_end, username=username)
|
|
||||||
|
|
||||||
|
|
||||||
@history.route("/<int:record_id>", methods=["GET"])
|
|
||||||
def get_detail_by_record(record_id=None):
|
|
||||||
record = db.session.query(OperationRecord).filter(
|
|
||||||
OperationRecord.record_id == record_id).first()
|
|
||||||
if record is None:
|
|
||||||
abort(404, "record is not found")
|
|
||||||
username = UserCache.get(record.uid).nickname \
|
|
||||||
if UserCache.get(record.uid).nickname \
|
|
||||||
else UserCache.get(record.uid).username
|
|
||||||
timestamp = record.timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
attr_history = db.session.query(CIAttributeHistory).filter(
|
|
||||||
CIAttributeHistory.record_id == record_id).all()
|
|
||||||
rel_history = db.session.query(CIRelationHistory).filter(
|
|
||||||
CIRelationHistory.record_id == record_id).all()
|
|
||||||
attr_dict, rel_dict = dict(), {"add": [], "delete": []}
|
|
||||||
for attr_h in attr_history:
|
|
||||||
attr_dict[CIAttributeCache.get(attr_h.attr_id).attr_alias] = {
|
|
||||||
"old": attr_h.old, "new": attr_h.new,
|
|
||||||
"operate_type": attr_h.operate_type}
|
|
||||||
manager = CIManager()
|
|
||||||
for rel_h in rel_history:
|
|
||||||
_, first = manager.get_ci_by_id(rel_h.first_ci_id)
|
|
||||||
_, second = manager.get_ci_by_id(rel_h.second_ci_id)
|
|
||||||
rel_dict[rel_h.operate_type].append(
|
|
||||||
(first, rel_h.relation_type, second))
|
|
||||||
|
|
||||||
return jsonify(username=username, timestamp=timestamp,
|
|
||||||
attr_history=attr_dict,
|
|
||||||
rel_history=rel_dict)
|
|
|
@ -1,16 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import Blueprint
|
|
||||||
from flask import jsonify
|
|
||||||
|
|
||||||
|
|
||||||
special = Blueprint(__name__, "special")
|
|
||||||
|
|
||||||
|
|
||||||
@special.route("/api/v0.1/special", methods=["GET"])
|
|
||||||
def index():
|
|
||||||
"""
|
|
||||||
定义专用接口
|
|
||||||
"""
|
|
||||||
return jsonify(code=200)
|
|
|
@ -1,12 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import Blueprint
|
|
||||||
|
|
||||||
|
|
||||||
statis = Blueprint("statis", __name__)
|
|
||||||
|
|
||||||
|
|
||||||
@statis.route("")
|
|
||||||
def statis():
|
|
||||||
pass
|
|
|
@ -1,16 +0,0 @@
|
||||||
# encoding=utf-8
|
|
||||||
|
|
||||||
|
|
||||||
from flask.ext.mail import Mail
|
|
||||||
from flask.ext.sqlalchemy import SQLAlchemy
|
|
||||||
from flask.ext.cache import Cache
|
|
||||||
from flask.ext.celery import Celery
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['mail', 'db', 'cache', 'celery']
|
|
||||||
|
|
||||||
|
|
||||||
mail = Mail()
|
|
||||||
db = SQLAlchemy()
|
|
||||||
cache = Cache()
|
|
||||||
celery = Celery()
|
|
|
@ -1,72 +0,0 @@
|
||||||
# encoding=utf-8
|
|
||||||
|
|
||||||
from flask_script import Command, Option
|
|
||||||
|
|
||||||
|
|
||||||
class GunicornServer(Command):
|
|
||||||
description = 'Run the app within Gunicorn'
|
|
||||||
|
|
||||||
def __init__(self, host='127.0.0.1', port=5000, workers=8,
|
|
||||||
worker_class="gevent", daemon=False):
|
|
||||||
self.port = port
|
|
||||||
self.host = host
|
|
||||||
self.workers = workers
|
|
||||||
self.worker_class = worker_class
|
|
||||||
self.daemon = daemon
|
|
||||||
|
|
||||||
def get_options(self):
|
|
||||||
return (
|
|
||||||
Option('-H', '--host',
|
|
||||||
dest='host',
|
|
||||||
default=self.host),
|
|
||||||
|
|
||||||
Option('-p', '--port',
|
|
||||||
dest='port',
|
|
||||||
type=int,
|
|
||||||
default=self.port),
|
|
||||||
|
|
||||||
Option('-w', '--workers',
|
|
||||||
dest='workers',
|
|
||||||
type=int,
|
|
||||||
default=self.workers),
|
|
||||||
|
|
||||||
Option("-c", "--worker_class",
|
|
||||||
dest='worker_class',
|
|
||||||
type=str,
|
|
||||||
default=self.worker_class),
|
|
||||||
|
|
||||||
Option("-d", "--daemon",
|
|
||||||
dest="daemon",
|
|
||||||
type=bool,
|
|
||||||
default=self.daemon)
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle(self, app, host, port, workers, worker_class, daemon):
|
|
||||||
|
|
||||||
from gunicorn import version_info
|
|
||||||
|
|
||||||
if version_info < (0, 9, 0):
|
|
||||||
from gunicorn.arbiter import Arbiter
|
|
||||||
from gunicorn.config import Config
|
|
||||||
|
|
||||||
arbiter = Arbiter(Config({'bind': "%s:%d" % (host, int(port)),
|
|
||||||
'workers': workers,
|
|
||||||
'worker_class': worker_class,
|
|
||||||
'daemon': daemon}), app)
|
|
||||||
arbiter.run()
|
|
||||||
else:
|
|
||||||
from gunicorn.app.base import Application
|
|
||||||
|
|
||||||
class FlaskApplication(Application):
|
|
||||||
def init(self, parser, opts, args):
|
|
||||||
return {
|
|
||||||
'bind': '{0}:{1}'.format(host, port),
|
|
||||||
'workers': workers,
|
|
||||||
'worker_class': worker_class,
|
|
||||||
'daemon': daemon
|
|
||||||
}
|
|
||||||
|
|
||||||
def load(self):
|
|
||||||
return app
|
|
||||||
|
|
||||||
FlaskApplication().run()
|
|
|
@ -1,4 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = []
|
|
|
@ -1,145 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import uuid
|
|
||||||
import random
|
|
||||||
import string
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
from flask import abort
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from models.account import UserCache
|
|
||||||
from models.account import User
|
|
||||||
from models.account import UserRole
|
|
||||||
|
|
||||||
|
|
||||||
class AccountManager(object):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_user_by_uid(self, uid):
|
|
||||||
user = UserCache.get(uid)
|
|
||||||
return user
|
|
||||||
|
|
||||||
def _generate_key(self):
|
|
||||||
key = uuid.uuid4().hex
|
|
||||||
secret = ''.join(random.sample(string.ascii_letters +
|
|
||||||
string.digits + '~!@#$%^&*?', 32))
|
|
||||||
return key, secret
|
|
||||||
|
|
||||||
def validate(self, username, password):
|
|
||||||
user, authenticated = User.query.authenticate(username, password)
|
|
||||||
return user, authenticated
|
|
||||||
|
|
||||||
def create_user(self, **kwargs):
|
|
||||||
username = kwargs.get("username")
|
|
||||||
if username:
|
|
||||||
user = UserCache.get(username)
|
|
||||||
if user is not None:
|
|
||||||
user, authenticated = self.validate(
|
|
||||||
username, kwargs.get("password"))
|
|
||||||
if authenticated:
|
|
||||||
return user
|
|
||||||
else:
|
|
||||||
return abort(401, "authenticate validate failed")
|
|
||||||
else:
|
|
||||||
return abort(400, "argument username is required")
|
|
||||||
user = User()
|
|
||||||
email = kwargs.get("email", "")
|
|
||||||
if not email:
|
|
||||||
return abort(400, "argument email is required")
|
|
||||||
user.email = email
|
|
||||||
user.password = kwargs.get("password")
|
|
||||||
user.username = kwargs.get("username", "")
|
|
||||||
user.nickname = kwargs.get("nickname") if kwargs.get("nickname") \
|
|
||||||
else kwargs.get("username", "")
|
|
||||||
key, secret = self._generate_key()
|
|
||||||
user.key = key
|
|
||||||
user.secret = secret
|
|
||||||
user.date_joined = datetime.datetime.now()
|
|
||||||
user.block = 0
|
|
||||||
|
|
||||||
db.session.add(user)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("create user is error {0}".format(str(e)))
|
|
||||||
return abort(500, "create user is error, {0}".format(str(e)))
|
|
||||||
return user
|
|
||||||
|
|
||||||
def update_user(self, uid, **kwargs):
|
|
||||||
user = UserCache.get(uid)
|
|
||||||
if user is None:
|
|
||||||
return abort(400, "the user[{0}] is not existed".format(uid))
|
|
||||||
user.username = kwargs.get("username", "") \
|
|
||||||
if kwargs.get("username") else user.username
|
|
||||||
user.nickname = kwargs.get("nickname") \
|
|
||||||
if kwargs.get("nickname") else user.nickname
|
|
||||||
user.department = kwargs.get("department") \
|
|
||||||
if kwargs.get("department") else user.department
|
|
||||||
user.catalog = kwargs.get("catalog") \
|
|
||||||
if kwargs.get("catalog") else user.catalog
|
|
||||||
user.email = kwargs.get("email") \
|
|
||||||
if kwargs.get("email") else user.email
|
|
||||||
user.mobile = kwargs.get("mobile") \
|
|
||||||
if kwargs.get("mobile") else user.mobile
|
|
||||||
db.session.add(user)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("create user is error {0}".format(str(e)))
|
|
||||||
return abort(500, "create user is error, {0}".format(str(e)))
|
|
||||||
return True, user
|
|
||||||
|
|
||||||
def delete_user(self, uid):
|
|
||||||
user = UserCache.get(uid)
|
|
||||||
if user is None:
|
|
||||||
return abort(400, "the user[{0}] is not existed".format(uid))
|
|
||||||
db.session.query(UserRole).filter(UserRole.uid == uid).delete()
|
|
||||||
db.session.delete(user)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("delete user error, {0}".format(str(e)))
|
|
||||||
return abort(500, "delete user error, {0}".format(str(e)))
|
|
||||||
return True, uid
|
|
||||||
|
|
||||||
def update_password(self, uid, old, new, confirm):
|
|
||||||
user = User.query.get(uid)
|
|
||||||
if not user:
|
|
||||||
return abort(400, "user is not existed")
|
|
||||||
if not user.check_password(old):
|
|
||||||
return abort(400, "invalidate old password")
|
|
||||||
if not (new and confirm and new == confirm):
|
|
||||||
return abort(400, """Password cannot be empty,
|
|
||||||
two inputs must be the same""")
|
|
||||||
user.password = new
|
|
||||||
db.session.add(user)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("set password error, %s" % str(e))
|
|
||||||
return abort(500, "set password errors, {0:s}".format(str(e)))
|
|
||||||
return True, user
|
|
||||||
|
|
||||||
def reset_key(self, uid):
|
|
||||||
user = UserCache.get(uid)
|
|
||||||
if user is None:
|
|
||||||
return abort(400, "the user[{0}] is not existed".format(uid))
|
|
||||||
key, secret = self._generate_key()
|
|
||||||
user.key = key
|
|
||||||
user.secret = secret
|
|
||||||
db.session.add(user)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("reset key is error, {0}".format(str(e)))
|
|
||||||
return abort(500, "reset key is error, {0}".format(str(e)))
|
|
||||||
return True, user
|
|
|
@ -1,167 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
from flask import abort
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from models.attribute import CIAttribute
|
|
||||||
from models.attribute import CIAttributeCache
|
|
||||||
from models import row2dict
|
|
||||||
from lib.const import type_map
|
|
||||||
|
|
||||||
|
|
||||||
class AttributeManager(object):
|
|
||||||
"""
|
|
||||||
CI attributes manager
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _get_choice_value(self, attr_id, value_type):
|
|
||||||
_table = type_map.get("choice").get(value_type)
|
|
||||||
choice_values = db.session.query(_table.value).filter(
|
|
||||||
_table.attr_id == attr_id).all()
|
|
||||||
return [choice_value.value for choice_value in choice_values]
|
|
||||||
|
|
||||||
def _add_choice_value(self, choice_value, attr_id, value_type):
|
|
||||||
_table = type_map.get("choice").get(value_type)
|
|
||||||
db.session.query(_table).filter(_table.attr_id == attr_id).delete()
|
|
||||||
db.session.flush()
|
|
||||||
for v in choice_value.strip().split(","):
|
|
||||||
table = _table()
|
|
||||||
table.attr_id = attr_id
|
|
||||||
table.value = v
|
|
||||||
db.session.add(table)
|
|
||||||
db.session.flush()
|
|
||||||
|
|
||||||
def get_attributes(self, name=None):
|
|
||||||
"""
|
|
||||||
return attribute by name,
|
|
||||||
if name is None, then return all attributes
|
|
||||||
"""
|
|
||||||
attrs = db.session.query(CIAttribute).filter(
|
|
||||||
CIAttribute.attr_name.ilike("%{0}%".format(name))).all() \
|
|
||||||
if name is not None else db.session.query(CIAttribute).all()
|
|
||||||
res = list()
|
|
||||||
for attr in attrs:
|
|
||||||
attr_dict = row2dict(attr)
|
|
||||||
if attr.is_choice:
|
|
||||||
attr_dict["choice_value"] = self._get_choice_value(
|
|
||||||
attr.attr_id, attr.value_type)
|
|
||||||
res.append(attr_dict)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_attribute_by_name(self, attr_name):
|
|
||||||
attr = db.session.query(CIAttribute).filter(
|
|
||||||
CIAttribute.attr_name == attr_name).first()
|
|
||||||
if attr:
|
|
||||||
attr_dict = row2dict(attr)
|
|
||||||
if attr.is_choice:
|
|
||||||
attr_dict["choice_value"] = self._get_choice_value(
|
|
||||||
attr.attr_id, attr.value_type)
|
|
||||||
return attr_dict
|
|
||||||
|
|
||||||
def get_attribute_by_alias(self, attr_alias):
|
|
||||||
attr = db.session.query(CIAttribute).filter(
|
|
||||||
CIAttribute.attr_alias == attr_alias).first()
|
|
||||||
if attr:
|
|
||||||
attr_dict = row2dict(attr)
|
|
||||||
if attr.is_choice:
|
|
||||||
attr_dict["choice_value"] = self._get_choice_value(
|
|
||||||
attr.attr_id, attr.value_type)
|
|
||||||
return attr_dict
|
|
||||||
|
|
||||||
def get_attribute_by_id(self, attr_id):
|
|
||||||
attr = db.session.query(CIAttribute).filter(
|
|
||||||
CIAttribute.attr_id == attr_id).first()
|
|
||||||
if attr:
|
|
||||||
attr_dict = row2dict(attr)
|
|
||||||
if attr.is_choice:
|
|
||||||
attr_dict["choice_value"] = self._get_choice_value(
|
|
||||||
attr.attr_id, attr.value_type)
|
|
||||||
return attr_dict
|
|
||||||
|
|
||||||
def add(self, attr_name, attr_alias, **kwargs):
|
|
||||||
choice_value = kwargs.get("choice_value", False)
|
|
||||||
attr = CIAttributeCache.get(attr_name)
|
|
||||||
if attr is not None:
|
|
||||||
return False, "attribute {0} is already existed".format(attr_name)
|
|
||||||
is_choice = False
|
|
||||||
if choice_value:
|
|
||||||
is_choice = True
|
|
||||||
if not attr_alias:
|
|
||||||
attr_alias = attr_name
|
|
||||||
attr = CIAttribute()
|
|
||||||
attr.attr_name = attr_name
|
|
||||||
attr.attr_alias = attr_alias
|
|
||||||
attr.is_choice = is_choice
|
|
||||||
attr.is_multivalue = kwargs.get("is_multivalue", False)
|
|
||||||
attr.is_uniq = kwargs.get("is_uniq", False)
|
|
||||||
attr.is_index = kwargs.get("is_index", False)
|
|
||||||
attr.value_type = kwargs.get("value_type", "text")
|
|
||||||
db.session.add(attr)
|
|
||||||
db.session.flush()
|
|
||||||
|
|
||||||
if choice_value:
|
|
||||||
self._add_choice_value(choice_value, attr.attr_id, attr.value_type)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("add attribute error, {0}".format(str(e)))
|
|
||||||
return False, str(e)
|
|
||||||
CIAttributeCache.clean(attr)
|
|
||||||
return True, attr.attr_id
|
|
||||||
|
|
||||||
def update(self, attr_id, *args, **kwargs):
|
|
||||||
attr = db.session.query(CIAttribute).filter_by(attr_id=attr_id).first()
|
|
||||||
if not attr:
|
|
||||||
return False, "CI attribute you want to update is not existed"
|
|
||||||
choice_value = kwargs.get("choice_value", False)
|
|
||||||
is_choice = False
|
|
||||||
if choice_value:
|
|
||||||
is_choice = True
|
|
||||||
attr.attr_name = args[0]
|
|
||||||
attr.attr_alias = args[1]
|
|
||||||
if not args[1]:
|
|
||||||
attr.attr_alias = args[0]
|
|
||||||
attr.is_choice = is_choice
|
|
||||||
attr.is_multivalue = kwargs.get("is_multivalue", False)
|
|
||||||
attr.is_uniq = kwargs.get("is_uniq", False)
|
|
||||||
attr.value_type = kwargs.get("value_type", "text")
|
|
||||||
db.session.add(attr)
|
|
||||||
db.session.flush()
|
|
||||||
if is_choice:
|
|
||||||
self._add_choice_value(choice_value, attr.attr_id, attr.value_type)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("update attribute error, {0}".format(
|
|
||||||
str(e)))
|
|
||||||
return False, str(e)
|
|
||||||
CIAttributeCache.clean(attr)
|
|
||||||
return True, attr.attr_id
|
|
||||||
|
|
||||||
def delete(self, attr_id):
|
|
||||||
attr, name = db.session.query(CIAttribute).filter_by(
|
|
||||||
attr_id=attr_id).first(), None
|
|
||||||
if attr:
|
|
||||||
if attr.is_choice:
|
|
||||||
choice_table = type_map["choice"].get(attr.value_type)
|
|
||||||
db.session.query(choice_table).filter(
|
|
||||||
choice_table.attr_id == attr_id).delete()
|
|
||||||
name = attr.attr_name
|
|
||||||
CIAttributeCache.clean(attr)
|
|
||||||
db.session.delete(attr)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("delete attribute error, {0}".format(
|
|
||||||
str(e)))
|
|
||||||
return abort(500, str(e))
|
|
||||||
else:
|
|
||||||
return abort(404, "attribute you want to delete is not existed")
|
|
||||||
return name
|
|
|
@ -1,68 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
import urllib
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
from flask import g
|
|
||||||
from flask import request
|
|
||||||
from flask import abort
|
|
||||||
from flask.ext.principal import identity_changed
|
|
||||||
from flask.ext.principal import Identity
|
|
||||||
from flask.ext.principal import AnonymousIdentity
|
|
||||||
|
|
||||||
from models.account import User
|
|
||||||
from models.account import UserCache
|
|
||||||
|
|
||||||
|
|
||||||
def auth_with_key(func):
|
|
||||||
@wraps(func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
if isinstance(getattr(g, 'user', None), User):
|
|
||||||
identity_changed.send(current_app._get_current_object(),
|
|
||||||
identity=Identity(g.user.uid))
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
ip = request.remote_addr
|
|
||||||
if request.data:
|
|
||||||
request_args = dict()
|
|
||||||
_args = request.data.split("&")
|
|
||||||
for arg in _args:
|
|
||||||
if arg:
|
|
||||||
request_args[arg.split("=")[0]] = \
|
|
||||||
urllib.unquote(arg.split("=")[1])
|
|
||||||
else:
|
|
||||||
request_args = request.values
|
|
||||||
|
|
||||||
key = request_args.get('_key')
|
|
||||||
secret = request_args.get('_secret')
|
|
||||||
if not key and not secret and \
|
|
||||||
ip.strip() in current_app.config.get("WHITE_LIST"):
|
|
||||||
ip = ip.strip()
|
|
||||||
user = UserCache.get(ip)
|
|
||||||
if user:
|
|
||||||
identity_changed.send(current_app._get_current_object(),
|
|
||||||
identity=Identity(user.uid))
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
else:
|
|
||||||
identity_changed.send(current_app._get_current_object(),
|
|
||||||
identity=AnonymousIdentity())
|
|
||||||
return abort(400, "invalid _key and _secret")
|
|
||||||
|
|
||||||
path = request.path
|
|
||||||
|
|
||||||
keys = sorted(request_args.keys())
|
|
||||||
req_args = [request_args[k] for k in keys
|
|
||||||
if str(k) not in ("_key", "_secret")]
|
|
||||||
current_app.logger.debug('args is %s' % req_args)
|
|
||||||
user, authenticated = User.query.authenticate_with_key(
|
|
||||||
key, secret, req_args, path)
|
|
||||||
if user and authenticated:
|
|
||||||
identity_changed.send(current_app._get_current_object(),
|
|
||||||
identity=Identity(user.get("uid")))
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
else:
|
|
||||||
identity_changed.send(current_app._get_current_object(),
|
|
||||||
identity=AnonymousIdentity())
|
|
||||||
return abort(400, "invalid _key and _secret")
|
|
||||||
|
|
||||||
return wrapper
|
|
|
@ -1,677 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import uuid
|
|
||||||
import time
|
|
||||||
import datetime
|
|
||||||
import json
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
from flask import abort
|
|
||||||
from sqlalchemy import or_
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from models.ci import CI
|
|
||||||
from models.ci_relation import CIRelation
|
|
||||||
from models.ci_type import CITypeAttribute
|
|
||||||
from models.ci_type import CITypeCache
|
|
||||||
from models.ci_type import CITypeSpecCache
|
|
||||||
from models.history import CIAttributeHistory
|
|
||||||
from models.attribute import CIAttributeCache
|
|
||||||
from lib.const import TableMap
|
|
||||||
from lib.const import type_map
|
|
||||||
from lib.value import AttributeValueManager
|
|
||||||
from lib.history import CIAttributeHistoryManger
|
|
||||||
from lib.history import CIRelationHistoryManager
|
|
||||||
from lib.query_sql import QUERY_HOSTS_NUM_BY_PRODUCT
|
|
||||||
from lib.query_sql import QUERY_HOSTS_NUM_BY_BU
|
|
||||||
from lib.query_sql import QUERY_HOSTS_NUM_BY_PROJECT
|
|
||||||
from lib.query_sql import QUERY_CIS_BY_IDS
|
|
||||||
from lib.query_sql import QUERY_CIS_BY_VALUE_TABLE
|
|
||||||
from lib.utils import rd
|
|
||||||
from tasks.cmdb import ci_cache
|
|
||||||
from tasks.cmdb import ci_delete
|
|
||||||
|
|
||||||
|
|
||||||
class CIManager(object):
|
|
||||||
""" manage CI interface
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_ci_by_id(self, ci_id, ret_key="name",
|
|
||||||
fields=None, need_children=True, use_master=False):
|
|
||||||
"""@params: `ret_key` is one of 'name', 'id', 'alias'
|
|
||||||
`fields` is list of attribute name/alias/id
|
|
||||||
"""
|
|
||||||
ci = CI.query.get(ci_id) or \
|
|
||||||
abort(404, "CI {0} is not existed".format(ci_id))
|
|
||||||
|
|
||||||
res = dict()
|
|
||||||
|
|
||||||
if need_children:
|
|
||||||
children = self.get_children(ci_id, ret_key=ret_key) # one floor
|
|
||||||
res.update(children)
|
|
||||||
ci_type = CITypeCache.get(ci.type_id)
|
|
||||||
res["ci_type"] = ci_type.type_name
|
|
||||||
uniq_key = CIAttributeCache.get(ci_type.uniq_id)
|
|
||||||
if not fields: # fields are all attributes
|
|
||||||
attr_ids = db.session.query(CITypeAttribute.attr_id).filter_by(
|
|
||||||
type_id=ci.type_id)
|
|
||||||
fields = [CIAttributeCache.get(_.attr_id).attr_name
|
|
||||||
for _ in attr_ids]
|
|
||||||
|
|
||||||
if uniq_key.attr_name not in fields:
|
|
||||||
fields.append(uniq_key.attr_name)
|
|
||||||
if fields:
|
|
||||||
value_manager = AttributeValueManager()
|
|
||||||
_res = value_manager._get_attr_values(
|
|
||||||
fields, ci_id,
|
|
||||||
ret_key=ret_key, uniq_key=uniq_key, use_master=use_master)
|
|
||||||
res.update(_res)
|
|
||||||
res['_type'] = ci_type.type_id
|
|
||||||
res['_id'] = ci_id
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_ci_by_ids(self, ci_id_list, ret_key="name", fields=None):
|
|
||||||
result = list()
|
|
||||||
for ci_id in ci_id_list:
|
|
||||||
res = self.get_ci_by_id(ci_id, ret_key=ret_key, fields=fields)
|
|
||||||
result.append(res)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_children(self, ci_id, ret_key='name', relation_type="contain"):
|
|
||||||
second_cis = db.session.query(CIRelation.second_ci_id).filter(
|
|
||||||
CIRelation.first_ci_id == ci_id).filter(or_(
|
|
||||||
CIRelation.relation_type == relation_type,
|
|
||||||
CIRelation.relation_type == "deploy"))
|
|
||||||
second_ci_ids = (second_ci.second_ci_id for second_ci in second_cis)
|
|
||||||
ci_types = {}
|
|
||||||
for ci_id in second_ci_ids:
|
|
||||||
type_id = db.session.query(CI.type_id).filter(
|
|
||||||
CI.ci_id == ci_id).first().type_id
|
|
||||||
if type_id not in ci_types:
|
|
||||||
ci_types[type_id] = [ci_id]
|
|
||||||
else:
|
|
||||||
ci_types[type_id].append(ci_id)
|
|
||||||
res = {}
|
|
||||||
for type_id in ci_types:
|
|
||||||
ci_type = CITypeCache.get(type_id)
|
|
||||||
children = get_cis_by_ids(map(str, ci_types.get(type_id)),
|
|
||||||
ret_key=ret_key)
|
|
||||||
res[ci_type.type_name] = children
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_cis_by_type(self, type_id, ret_key="name", fields="",
|
|
||||||
page=1, per_page=None):
|
|
||||||
if per_page is None:
|
|
||||||
per_page = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
cis = db.session.query(CI.ci_id).filter(CI.type_id == type_id)
|
|
||||||
numfound = cis.count()
|
|
||||||
cis = cis.offset((page - 1) * per_page).limit(per_page)
|
|
||||||
res = list()
|
|
||||||
ci_ids = [str(ci.ci_id) for ci in cis]
|
|
||||||
if ci_ids:
|
|
||||||
res = get_cis_by_ids(ci_ids, ret_key, fields)
|
|
||||||
return numfound, page, res
|
|
||||||
|
|
||||||
def ci_is_exist(self, ci_type, unique_key, unique):
|
|
||||||
table = TableMap(attr_name=unique_key.attr_name).table
|
|
||||||
unique = db.session.query(table).filter(
|
|
||||||
table.attr_id == unique_key.attr_id).filter(
|
|
||||||
table.value == unique).first()
|
|
||||||
if unique:
|
|
||||||
return db.session.query(CI).filter(
|
|
||||||
CI.ci_id == unique.ci_id).first()
|
|
||||||
|
|
||||||
def _delete_ci_by_id(self, ci_id):
|
|
||||||
db.session.query(CI.ci_id).filter(CI.ci_id == ci_id).delete()
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("delete ci is error, {0}".format(str(e)))
|
|
||||||
|
|
||||||
def add(self, ci_type_name, exist_policy="replace",
|
|
||||||
_no_attribute_policy="ignore", **ci_dict):
|
|
||||||
ci_existed = False
|
|
||||||
ci_type = CITypeCache.get(ci_type_name) or \
|
|
||||||
abort(404, "CIType {0} is not existed".format(ci_type_name))
|
|
||||||
|
|
||||||
unique_key = CIAttributeCache.get(ci_type.uniq_id) \
|
|
||||||
or abort(500, 'illegality unique attribute')
|
|
||||||
|
|
||||||
unique = ci_dict.get(unique_key.attr_name) \
|
|
||||||
or abort(500, '{0} missing'.format(unique_key.attr_name))
|
|
||||||
|
|
||||||
old_ci = self.ci_is_exist(ci_type, unique_key, unique)
|
|
||||||
if old_ci is not None:
|
|
||||||
ci_existed = True
|
|
||||||
if exist_policy == 'reject':
|
|
||||||
return abort(500, 'CI is existed')
|
|
||||||
if old_ci.type_id != ci_type.type_id: # update ci_type
|
|
||||||
old_ci.type_id = ci_type.type_id
|
|
||||||
db.session.add(old_ci)
|
|
||||||
db.session.flush()
|
|
||||||
ci = old_ci
|
|
||||||
else:
|
|
||||||
if exist_policy == 'need':
|
|
||||||
return abort(404, 'CI {0} not exist'.format(unique))
|
|
||||||
ci = CI()
|
|
||||||
ci.type_id = ci_type.type_id
|
|
||||||
_uuid = uuid.uuid4().hex
|
|
||||||
ci.uuid = _uuid
|
|
||||||
ci.created_time = datetime.datetime.now()
|
|
||||||
db.session.add(ci)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error('add CI error: {0}'.format(str(e)))
|
|
||||||
return abort(500, 'add CI error')
|
|
||||||
value_manager = AttributeValueManager()
|
|
||||||
histories = list()
|
|
||||||
for p, v in ci_dict.items():
|
|
||||||
ret, res = value_manager.add_attr_value(
|
|
||||||
p, v, ci.ci_id, ci_type,
|
|
||||||
_no_attribute_policy=_no_attribute_policy,
|
|
||||||
ci_existed=ci_existed)
|
|
||||||
if not ret:
|
|
||||||
db.session.rollback()
|
|
||||||
if not ci_existed:
|
|
||||||
self.delete(ci.ci_id)
|
|
||||||
current_app.logger.info(res)
|
|
||||||
return abort(500, res)
|
|
||||||
if res is not None:
|
|
||||||
histories.append(res)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error(str(e))
|
|
||||||
db.session.rollback()
|
|
||||||
if not ci_existed: # only add
|
|
||||||
self.delete(ci.ci_id)
|
|
||||||
return abort(500, "add CI error")
|
|
||||||
his_manager = CIAttributeHistoryManger()
|
|
||||||
his_manager.add(ci.ci_id, histories)
|
|
||||||
ci_cache.apply_async([ci.ci_id], queue="cmdb_async")
|
|
||||||
return ci.ci_id
|
|
||||||
|
|
||||||
def delete(self, ci_id):
|
|
||||||
ci = db.session.query(CI).filter(CI.ci_id == ci_id).first()
|
|
||||||
if ci is not None:
|
|
||||||
attrs = db.session.query(CITypeAttribute.attr_id).filter(
|
|
||||||
CITypeAttribute.type_id == ci.type_id).all()
|
|
||||||
attr_names = []
|
|
||||||
for attr in attrs:
|
|
||||||
attr_names.append(CIAttributeCache.get(attr.attr_id).attr_name)
|
|
||||||
attr_names = set(attr_names)
|
|
||||||
for attr_name in attr_names:
|
|
||||||
Table = TableMap(attr_name=attr_name).table
|
|
||||||
db.session.query(Table).filter(Table.ci_id == ci_id).delete()
|
|
||||||
db.session.query(CIRelation).filter(
|
|
||||||
CIRelation.first_ci_id == ci_id).delete()
|
|
||||||
db.session.query(CIRelation).filter(
|
|
||||||
CIRelation.second_ci_id == ci_id).delete()
|
|
||||||
db.session.query(CIAttributeHistory).filter(
|
|
||||||
CIAttributeHistory.ci_id == ci_id).delete()
|
|
||||||
|
|
||||||
db.session.flush()
|
|
||||||
db.session.delete(ci)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("delete CI error, {0}".format(str(e)))
|
|
||||||
return abort(500, "delete CI error, {0}".format(str(e)))
|
|
||||||
# TODO: write history
|
|
||||||
ci_delete.apply_async([ci.ci_id], queue="cmdb_async")
|
|
||||||
return ci_id
|
|
||||||
return abort(404, "CI {0} not found".format(ci_id))
|
|
||||||
|
|
||||||
def add_heartbeat(self, ci_type, unique):
|
|
||||||
ci_type = CITypeCache.get(ci_type)
|
|
||||||
if not ci_type:
|
|
||||||
return 'error'
|
|
||||||
uniq_key = CIAttributeCache.get(ci_type.uniq_id)
|
|
||||||
Table = TableMap(attr_name=uniq_key.attr_name).table
|
|
||||||
ci_id = db.session.query(Table.ci_id).filter(
|
|
||||||
Table.attr_id == uniq_key.attr_id).filter(
|
|
||||||
Table.value == unique).first()
|
|
||||||
if ci_id is None:
|
|
||||||
return 'error'
|
|
||||||
ci = db.session.query(CI).filter(CI.ci_id == ci_id.ci_id).first()
|
|
||||||
if ci is None:
|
|
||||||
return 'error'
|
|
||||||
|
|
||||||
ci.heartbeat = datetime.datetime.now()
|
|
||||||
|
|
||||||
db.session.add(ci)
|
|
||||||
db.session.commit()
|
|
||||||
return "ok"
|
|
||||||
|
|
||||||
def get_heartbeat(self, page, type_id, agent_status=None):
|
|
||||||
query = db.session.query(CI.ci_id, CI.heartbeat)
|
|
||||||
expire = datetime.datetime.now() - datetime.timedelta(minutes=72)
|
|
||||||
if type_id:
|
|
||||||
query = query.filter(CI.type_id == type_id)
|
|
||||||
else:
|
|
||||||
query = query.filter(db.or_(CI.type_id == 7, CI.type_id == 8))
|
|
||||||
if agent_status == -1:
|
|
||||||
query = query.filter(CI.heartbeat == None)
|
|
||||||
elif agent_status == 0:
|
|
||||||
query = query.filter(CI.heartbeat <= expire)
|
|
||||||
elif agent_status == 1:
|
|
||||||
query = query.filter(CI.heartbeat > expire)
|
|
||||||
numfound = query.count()
|
|
||||||
per_page_count = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
cis = query.offset((page - 1) * per_page_count).limit(
|
|
||||||
per_page_count).all()
|
|
||||||
ci_ids = [ci.ci_id for ci in cis]
|
|
||||||
heartbeat_dict = {}
|
|
||||||
for ci in cis:
|
|
||||||
if agent_status is not None:
|
|
||||||
heartbeat_dict[ci.ci_id] = agent_status
|
|
||||||
else:
|
|
||||||
if ci.heartbeat is None:
|
|
||||||
heartbeat_dict[ci.ci_id] = -1
|
|
||||||
elif ci.heartbeat <= expire:
|
|
||||||
heartbeat_dict[ci.ci_id] = 0
|
|
||||||
else:
|
|
||||||
heartbeat_dict[ci.ci_id] = 1
|
|
||||||
current_app.logger.debug(heartbeat_dict)
|
|
||||||
ci_ids = map(str, ci_ids)
|
|
||||||
res = get_cis_by_ids(ci_ids, fields=["hostname", "private_ip"])
|
|
||||||
result = [(i.get("hostname"), i.get("private_ip")[0], i.get("ci_type"),
|
|
||||||
heartbeat_dict.get(i.get("_id"))) for i in res
|
|
||||||
if i.get("private_ip")]
|
|
||||||
return numfound, result
|
|
||||||
|
|
||||||
|
|
||||||
class CIRelationManager(object):
|
|
||||||
"""
|
|
||||||
manage relation between CIs
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
def relation_types(self):
|
|
||||||
""" all CIType relation types
|
|
||||||
"""
|
|
||||||
from lib.const import CI_RELATION_TYPES
|
|
||||||
|
|
||||||
return CI_RELATION_TYPES
|
|
||||||
|
|
||||||
def get_second_cis(self, first_ci, relation_type="contain",
|
|
||||||
page=1, per_page=None, **kwargs):
|
|
||||||
if per_page is None:
|
|
||||||
per_page = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
second_cis = db.session.query(
|
|
||||||
CI.ci_id).join(
|
|
||||||
CIRelation, CIRelation.second_ci_id == CI.ci_id).filter(
|
|
||||||
CIRelation.first_ci_id == first_ci).filter(
|
|
||||||
CIRelation.relation_type == relation_type)
|
|
||||||
if kwargs: # special for devices
|
|
||||||
second_cis = self._query_wrap_for_device(second_cis, **kwargs)
|
|
||||||
numfound = second_cis.count()
|
|
||||||
second_cis = second_cis.offset(
|
|
||||||
(page - 1) * per_page).limit(per_page).all()
|
|
||||||
ci_ids = [str(son.ci_id) for son in second_cis]
|
|
||||||
total = len(ci_ids)
|
|
||||||
result = get_cis_by_ids(ci_ids)
|
|
||||||
return numfound, total, result
|
|
||||||
|
|
||||||
def get_grandsons(self, ci_id, page=1, per_page=None, **kwargs):
|
|
||||||
if per_page is None:
|
|
||||||
per_page = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
children = db.session.query(CIRelation.second_ci_id).filter(
|
|
||||||
CIRelation.first_ci_id == ci_id).subquery()
|
|
||||||
grandsons = db.session.query(CIRelation.second_ci_id).join(
|
|
||||||
children,
|
|
||||||
children.c.second_ci_id == CIRelation.first_ci_id).subquery()
|
|
||||||
grandsons = db.session.query(CI.ci_id).join(
|
|
||||||
grandsons, grandsons.c.second_ci_id == CI.ci_id)
|
|
||||||
if kwargs:
|
|
||||||
grandsons = self._query_wrap_for_device(grandsons, **kwargs)
|
|
||||||
|
|
||||||
numfound = grandsons.count()
|
|
||||||
grandsons = grandsons.offset(
|
|
||||||
(page - 1) * per_page).limit(per_page).all()
|
|
||||||
if not grandsons:
|
|
||||||
return 0, 0, []
|
|
||||||
ci_ids = [str(son.ci_id) for son in grandsons]
|
|
||||||
total = len(ci_ids)
|
|
||||||
result = get_cis_by_ids(ci_ids)
|
|
||||||
|
|
||||||
return numfound, total, result
|
|
||||||
|
|
||||||
def _sort_handler(self, sort_by, query_sql):
|
|
||||||
|
|
||||||
if sort_by.startswith("+"):
|
|
||||||
sort_type = "asc"
|
|
||||||
sort_by = sort_by[1:]
|
|
||||||
elif sort_by.startswith("-"):
|
|
||||||
sort_type = "desc"
|
|
||||||
sort_by = sort_by[1:]
|
|
||||||
else:
|
|
||||||
sort_type = "asc"
|
|
||||||
attr = CIAttributeCache.get(sort_by)
|
|
||||||
if attr is None:
|
|
||||||
return query_sql
|
|
||||||
|
|
||||||
attr_id = attr.attr_id
|
|
||||||
Table = TableMap(attr_name=sort_by).table
|
|
||||||
|
|
||||||
CI_table = query_sql.subquery()
|
|
||||||
query_sql = db.session.query(CI_table.c.ci_id, Table.value).join(
|
|
||||||
Table, Table.ci_id == CI_table.c.ci_id).filter(
|
|
||||||
Table.attr_id == attr_id).order_by(
|
|
||||||
getattr(Table.value, sort_type)())
|
|
||||||
|
|
||||||
return query_sql
|
|
||||||
|
|
||||||
def _query_wrap_for_device(self, query_sql, **kwargs):
|
|
||||||
_type = kwargs.pop("_type", False) or kwargs.pop("type", False) \
|
|
||||||
or kwargs.pop("ci_type", False)
|
|
||||||
if _type:
|
|
||||||
ci_type = CITypeCache.get(_type)
|
|
||||||
if ci_type is None:
|
|
||||||
return
|
|
||||||
query_sql = query_sql.filter(CI.type_id == ci_type.type_id)
|
|
||||||
|
|
||||||
for k, v in kwargs.iteritems():
|
|
||||||
attr = CIAttributeCache.get(k)
|
|
||||||
if attr is None:
|
|
||||||
continue
|
|
||||||
Table = TableMap(attr_name=k).table
|
|
||||||
CI_table = query_sql.subquery()
|
|
||||||
query_sql = db.session.query(CI_table.c.ci_id).join(
|
|
||||||
Table, Table.ci_id == CI_table.c.ci_id).filter(
|
|
||||||
Table.attr_id == attr.attr_id).filter(
|
|
||||||
Table.value.ilike(v.replace("*", "%")))
|
|
||||||
|
|
||||||
current_app.logger.debug(query_sql)
|
|
||||||
sort_by = kwargs.pop("sort", False)
|
|
||||||
if sort_by:
|
|
||||||
query_sql = self._sort_handler(sort_by, query_sql)
|
|
||||||
return query_sql
|
|
||||||
|
|
||||||
def get_great_grandsons(self, ci_id, page=1, per_page=None, **kwargs):
|
|
||||||
if per_page is None:
|
|
||||||
per_page = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
|
|
||||||
children = db.session.query(CIRelation.second_ci_id).filter(
|
|
||||||
CIRelation.first_ci_id == ci_id).subquery()
|
|
||||||
grandsons = db.session.query(CIRelation.second_ci_id).join(
|
|
||||||
children,
|
|
||||||
children.c.second_ci_id == CIRelation.first_ci_id).subquery()
|
|
||||||
great_grandsons = db.session.query(CIRelation.second_ci_id).join(
|
|
||||||
grandsons,
|
|
||||||
grandsons.c.second_ci_id == CIRelation.first_ci_id).subquery()
|
|
||||||
great_grandsons = db.session.query(CI.ci_id).join(
|
|
||||||
great_grandsons, great_grandsons.c.second_ci_id == CI.ci_id)
|
|
||||||
if kwargs:
|
|
||||||
great_grandsons = self._query_wrap_for_device(
|
|
||||||
great_grandsons, **kwargs)
|
|
||||||
if great_grandsons is None:
|
|
||||||
return 0, 0, []
|
|
||||||
numfound = great_grandsons.count()
|
|
||||||
great_grandsons = great_grandsons.offset(
|
|
||||||
(page - 1) * per_page).limit(per_page).all()
|
|
||||||
ci_ids = [str(son.ci_id) for son in great_grandsons]
|
|
||||||
total = len(ci_ids)
|
|
||||||
result = get_cis_by_ids(ci_ids)
|
|
||||||
|
|
||||||
return numfound, total, result
|
|
||||||
|
|
||||||
def get_first_cis(self, second_ci, relation_type="contain",
|
|
||||||
page=1, per_page=None):
|
|
||||||
"""only for CI Type
|
|
||||||
"""
|
|
||||||
if per_page is None:
|
|
||||||
per_page = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
first_cis = db.session.query(CIRelation.first_ci_id).filter(
|
|
||||||
CIRelation.second_ci_id == second_ci).filter(
|
|
||||||
CIRelation.relation_type == relation_type)
|
|
||||||
numfound = first_cis.count()
|
|
||||||
first_cis = first_cis.offset(
|
|
||||||
(page - 1) * per_page).limit(per_page).all()
|
|
||||||
result = []
|
|
||||||
first_ci_ids = [str(first_ci.first_ci_id) for first_ci in first_cis]
|
|
||||||
total = len(first_ci_ids)
|
|
||||||
if first_ci_ids:
|
|
||||||
result = get_cis_by_ids(first_ci_ids)
|
|
||||||
return numfound, total, result
|
|
||||||
|
|
||||||
def get_grandfather(self, ci_id, relation_type="contain"):
|
|
||||||
"""only for CI Type
|
|
||||||
"""
|
|
||||||
grandfather = db.session.query(CIRelation.first_ci_id).filter(
|
|
||||||
CIRelation.second_ci_id.in_(db.session.query(
|
|
||||||
CIRelation.first_ci_id).filter(
|
|
||||||
CIRelation.second_ci_id == ci_id).filter(
|
|
||||||
CIRelation.relation_type == relation_type))).filter(
|
|
||||||
CIRelation.relation_type == relation_type).first()
|
|
||||||
if grandfather:
|
|
||||||
return CIManager().get_ci_by_id(grandfather.first_ci_id,
|
|
||||||
need_children=False)
|
|
||||||
|
|
||||||
def add(self, first_ci, second_ci, more=None, relation_type="contain"):
|
|
||||||
ci = db.session.query(CI.ci_id).filter(CI.ci_id == first_ci).first()
|
|
||||||
if ci is None:
|
|
||||||
return abort(404, "first_ci {0} is not existed".format(first_ci))
|
|
||||||
c = db.session.query(CI.ci_id).filter(CI.ci_id == second_ci).first()
|
|
||||||
if c is None:
|
|
||||||
return abort(404, "second_ci {0} is not existed".format(
|
|
||||||
second_ci))
|
|
||||||
existed = db.session.query(CIRelation.cr_id).filter(
|
|
||||||
CIRelation.first_ci_id == first_ci).filter(
|
|
||||||
CIRelation.second_ci_id == second_ci).first()
|
|
||||||
if existed is not None:
|
|
||||||
return existed.cr_id
|
|
||||||
cr = CIRelation()
|
|
||||||
cr.first_ci_id = first_ci
|
|
||||||
cr.second_ci_id = second_ci
|
|
||||||
if more is not None:
|
|
||||||
cr.more = more
|
|
||||||
cr.relation_type = relation_type
|
|
||||||
db.session.add(cr)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("add CIRelation is error, {0}".format(
|
|
||||||
str(e)))
|
|
||||||
return abort(500, "add CIRelation is error, {0}".format(str(e)))
|
|
||||||
# write history
|
|
||||||
his_manager = CIRelationHistoryManager()
|
|
||||||
his_manager.add(cr.cr_id, cr.first_ci_id, cr.second_ci_id,
|
|
||||||
relation_type, operate_type="add")
|
|
||||||
return cr.cr_id
|
|
||||||
|
|
||||||
def delete(self, cr_id):
|
|
||||||
cr = db.session.query(CIRelation).filter(
|
|
||||||
CIRelation.cr_id == cr_id).first()
|
|
||||||
cr_id = cr.cr_id
|
|
||||||
first_ci = cr.first_ci_id
|
|
||||||
second_ci = cr.second_ci_id
|
|
||||||
if cr is not None:
|
|
||||||
db.session.delete(cr)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"delete CIRelation is error, {0}".format(str(e)))
|
|
||||||
return abort(
|
|
||||||
500, "delete CIRelation is error, {0}".format(str(e)))
|
|
||||||
his_manager = CIRelationHistoryManager()
|
|
||||||
his_manager.add(cr_id, first_ci, second_ci,
|
|
||||||
cr.relation_type, operate_type="delete")
|
|
||||||
return True
|
|
||||||
return abort(404, "CI relation is not existed")
|
|
||||||
|
|
||||||
def delete_2(self, first_ci, second_ci):
|
|
||||||
cr = db.session.query(CIRelation).filter(
|
|
||||||
CIRelation.first_ci_id == first_ci).filter(
|
|
||||||
CIRelation.second_ci_id == second_ci).first()
|
|
||||||
return self.delete(cr.cr_id)
|
|
||||||
|
|
||||||
|
|
||||||
class HostNumStatis(object):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_hosts_by_project(self, project_id_list=None):
|
|
||||||
res = {}
|
|
||||||
if not project_id_list:
|
|
||||||
project = CITypeCache.get("project")
|
|
||||||
projects = db.session.query(CI.ci_id).filter(
|
|
||||||
CI.type_id == project.type_id).all()
|
|
||||||
project_id_list = (project.ci_id for project in projects)
|
|
||||||
project_id_list = map(str, project_id_list)
|
|
||||||
project_ids = ",".join(project_id_list)
|
|
||||||
nums = db.session.execute(QUERY_HOSTS_NUM_BY_PROJECT.format(
|
|
||||||
"".join(["(", project_ids, ")"]))).fetchall()
|
|
||||||
if nums:
|
|
||||||
for ci_id in project_id_list:
|
|
||||||
res[int(ci_id)] = 0
|
|
||||||
for ci_id, num in nums:
|
|
||||||
res[ci_id] = num
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_hosts_by_product(self, product_id_list=None):
|
|
||||||
res = {}
|
|
||||||
if not product_id_list:
|
|
||||||
product = CITypeCache.get("product")
|
|
||||||
products = db.session.query(CI.ci_id).filter(
|
|
||||||
CI.type_id == product.type_id).all()
|
|
||||||
product_id_list = (product.ci_id for product in products)
|
|
||||||
product_id_list = map(str, product_id_list)
|
|
||||||
product_ids = ",".join(product_id_list)
|
|
||||||
nums = db.session.execute(QUERY_HOSTS_NUM_BY_PRODUCT.format(
|
|
||||||
"".join(["(", product_ids, ")"]))).fetchall()
|
|
||||||
if nums:
|
|
||||||
for ci_id in product_id_list:
|
|
||||||
res[int(ci_id)] = 0
|
|
||||||
for ci_id, num in nums:
|
|
||||||
res[ci_id] = num
|
|
||||||
return res
|
|
||||||
|
|
||||||
def get_hosts_by_bu(self, bu_id_list=None):
|
|
||||||
res = {}
|
|
||||||
if not bu_id_list:
|
|
||||||
bu = CITypeCache.get("bu")
|
|
||||||
bus = db.session.query(CI.ci_id).filter(
|
|
||||||
CI.type_id == bu.type_id).all()
|
|
||||||
bu_id_list = (bu.ci_id for bu in bus)
|
|
||||||
bu_id_list = map(str, bu_id_list)
|
|
||||||
bu_ids = ",".join(bu_id_list)
|
|
||||||
current_app.logger.debug(QUERY_HOSTS_NUM_BY_BU.format(
|
|
||||||
"".join(["(", bu_ids, ")"])))
|
|
||||||
if not bu_ids:
|
|
||||||
return res
|
|
||||||
nums = db.session.execute(
|
|
||||||
QUERY_HOSTS_NUM_BY_BU.format(
|
|
||||||
"".join(["(", bu_ids, ")"]))).fetchall()
|
|
||||||
if nums:
|
|
||||||
for ci_id in bu_id_list:
|
|
||||||
res[int(ci_id)] = 0
|
|
||||||
for ci_id, num in nums:
|
|
||||||
res[ci_id] = num
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def get_cis_by_ids(ci_ids, ret_key="name", fields="", value_tables=None):
|
|
||||||
""" argument ci_ids are string list of CI instance ID, eg. ['1', '2']
|
|
||||||
"""
|
|
||||||
if not ci_ids:
|
|
||||||
return []
|
|
||||||
start = time.time()
|
|
||||||
ci_id_tuple = tuple(map(int, ci_ids))
|
|
||||||
res = rd.get(ci_id_tuple)
|
|
||||||
if res is not None and None not in res and ret_key == "name":
|
|
||||||
res = map(json.loads, res)
|
|
||||||
if not fields:
|
|
||||||
return res
|
|
||||||
else:
|
|
||||||
_res = []
|
|
||||||
for d in res:
|
|
||||||
_d = dict()
|
|
||||||
_d["_id"], _d["_type"] = d.get("_id"), d.get("_type")
|
|
||||||
_d["ci_type"] = d.get("ci_type")
|
|
||||||
for field in fields:
|
|
||||||
_d[field] = d.get(field)
|
|
||||||
_res.append(_d)
|
|
||||||
current_app.logger.debug("filter time: %s" % (time.time() - start))
|
|
||||||
return _res
|
|
||||||
current_app.logger.warning("cache not hit...............")
|
|
||||||
if not fields:
|
|
||||||
_fields = ""
|
|
||||||
else:
|
|
||||||
_fields = list()
|
|
||||||
for field in fields:
|
|
||||||
attr = CIAttributeCache.get(field)
|
|
||||||
if attr is not None:
|
|
||||||
_fields.append(str(attr.attr_id))
|
|
||||||
_fields = "WHERE A.attr_id in ({0})".format(",".join(_fields))
|
|
||||||
ci_ids = ",".join(ci_ids)
|
|
||||||
if value_tables is None:
|
|
||||||
value_tables = type_map["table_name"].values()
|
|
||||||
current_app.logger.debug(value_tables)
|
|
||||||
value_sql = " UNION ".join([QUERY_CIS_BY_VALUE_TABLE.format(value_table,
|
|
||||||
ci_ids)
|
|
||||||
for value_table in value_tables])
|
|
||||||
query_sql = QUERY_CIS_BY_IDS.format(ci_ids, _fields, value_sql)
|
|
||||||
current_app.logger.debug(query_sql)
|
|
||||||
start = time.time()
|
|
||||||
hosts = db.session.execute(query_sql).fetchall()
|
|
||||||
current_app.logger.info("get cis time is: {0}".format(
|
|
||||||
time.time() - start))
|
|
||||||
|
|
||||||
ci_list = set()
|
|
||||||
res = list()
|
|
||||||
ci_dict = dict()
|
|
||||||
start = time.time()
|
|
||||||
for ci_id, type_id, attr_id, attr_name, \
|
|
||||||
attr_alias, value, value_type, is_multivalue in hosts:
|
|
||||||
if ci_id not in ci_list:
|
|
||||||
ci_dict = dict()
|
|
||||||
ci_type = CITypeSpecCache.get(type_id)
|
|
||||||
ci_dict["_id"] = ci_id
|
|
||||||
ci_dict["_type"] = type_id
|
|
||||||
ci_dict["ci_type"] = ci_type.type_name
|
|
||||||
ci_dict["ci_type_alias"] = ci_type.type_alias
|
|
||||||
ci_list.add(ci_id)
|
|
||||||
res.append(ci_dict)
|
|
||||||
if ret_key == "name":
|
|
||||||
if is_multivalue:
|
|
||||||
if isinstance(ci_dict.get(attr_name), list):
|
|
||||||
ci_dict[attr_name].append(value)
|
|
||||||
else:
|
|
||||||
ci_dict[attr_name] = [value]
|
|
||||||
else:
|
|
||||||
ci_dict[attr_name] = value
|
|
||||||
elif ret_key == "alias":
|
|
||||||
if is_multivalue:
|
|
||||||
if isinstance(ci_dict.get(attr_alias), list):
|
|
||||||
ci_dict[attr_alias].append(value)
|
|
||||||
else:
|
|
||||||
ci_dict[attr_alias] = [value]
|
|
||||||
else:
|
|
||||||
ci_dict[attr_alias] = value
|
|
||||||
elif ret_key == "id":
|
|
||||||
if is_multivalue:
|
|
||||||
if isinstance(ci_dict.get(attr_id), list):
|
|
||||||
ci_dict[attr_id].append(value)
|
|
||||||
else:
|
|
||||||
ci_dict[attr_id] = [value]
|
|
||||||
else:
|
|
||||||
ci_dict[attr_id] = value
|
|
||||||
|
|
||||||
current_app.logger.debug("result parser time is: {0}".format(
|
|
||||||
time.time() - start))
|
|
||||||
return res
|
|
|
@ -1,315 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
from flask import abort
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from models import row2dict
|
|
||||||
from models.ci_type import CITypeAttribute
|
|
||||||
from models.ci_type import CIType
|
|
||||||
from models.ci_type import CITypeAttributeCache
|
|
||||||
from models.ci_type import CITypeCache
|
|
||||||
from models.ci_type_relation import CITypeRelation
|
|
||||||
from models.attribute import CIAttributeCache
|
|
||||||
from lib.attribute import AttributeManager
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeAttributeManager(object):
|
|
||||||
"""
|
|
||||||
manage CIType's attributes, include query, add, update, delete
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_attributes_by_type_id(self, type_id):
|
|
||||||
attrs = CITypeAttributeCache.get(type_id)
|
|
||||||
attr_manager = AttributeManager()
|
|
||||||
result = list()
|
|
||||||
for attr in attrs:
|
|
||||||
attr_dict = attr_manager.get_attribute_by_id(attr.attr_id)
|
|
||||||
attr_dict["is_required"] = attr.is_required
|
|
||||||
result.append(attr_dict)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def add(self, type_id, attr_ids=None, is_required=False):
|
|
||||||
"""
|
|
||||||
add attributes to CIType, attr_ids are list
|
|
||||||
"""
|
|
||||||
if not attr_ids or not isinstance(attr_ids, list):
|
|
||||||
return abort(500, "attr_ids must be required")
|
|
||||||
ci_type = CITypeCache.get(type_id)
|
|
||||||
if ci_type is None:
|
|
||||||
return abort(404, "CIType ID({0}) is not existed".format(type_id))
|
|
||||||
for attr_id in attr_ids:
|
|
||||||
attr = CIAttributeCache.get(attr_id)
|
|
||||||
if attr is None:
|
|
||||||
return abort(404,
|
|
||||||
"attribute id {0} is not existed".format(attr_id))
|
|
||||||
existed = db.session.query(CITypeAttribute.attr_id).filter_by(
|
|
||||||
type_id=type_id).filter_by(attr_id=attr_id).first()
|
|
||||||
if existed is not None:
|
|
||||||
continue
|
|
||||||
current_app.logger.debug(attr_id)
|
|
||||||
db.session.add(CITypeAttribute(
|
|
||||||
type_id=type_id, attr_id=attr_id, is_required=is_required))
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"add attribute to CIType is error, {0}".format(str(e)))
|
|
||||||
return abort(
|
|
||||||
500, "add attribute to CIType is error, maybe duplicate entry")
|
|
||||||
|
|
||||||
CITypeAttributeCache.clean(type_id)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def delete(self, type_id, attr_ids=None):
|
|
||||||
"""
|
|
||||||
delete attributes at CIType, attr_ids are list
|
|
||||||
"""
|
|
||||||
if not attr_ids or not isinstance(attr_ids, list):
|
|
||||||
return abort(
|
|
||||||
500, "delete attribute of CIType, attr_ids must be required")
|
|
||||||
ci_type = CITypeCache.get(type_id)
|
|
||||||
if ci_type is None:
|
|
||||||
return abort(
|
|
||||||
404, "CIType ID({0}) is not existed".format(type_id))
|
|
||||||
for attr_id in attr_ids:
|
|
||||||
attr = CIAttributeCache.get(attr_id)
|
|
||||||
if attr is None:
|
|
||||||
return abort(
|
|
||||||
404, "attribute id {0} is not existed".format(attr_id))
|
|
||||||
db.session.query(CITypeAttribute).filter_by(
|
|
||||||
type_id=type_id).filter_by(attr_id=attr_id).delete()
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"delete attributes of CIType is error, {0}".format(str(e)))
|
|
||||||
return abort(500, "delete attributes of CIType is error")
|
|
||||||
CITypeAttributeCache.clean(type_id)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeManager(object):
|
|
||||||
"""
|
|
||||||
manage CIType
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_citypes(self, type_name=None):
|
|
||||||
ci_types = db.session.query(CIType).all() if type_name is None else \
|
|
||||||
db.session.query(CIType).filter(
|
|
||||||
CIType.type_name.ilike("%{0}%".format(type_name))).all()
|
|
||||||
res = list()
|
|
||||||
for ci_type in ci_types:
|
|
||||||
type_dict = row2dict(ci_type)
|
|
||||||
type_dict["uniq_key"] = CIAttributeCache.get(
|
|
||||||
type_dict["uniq_id"]).attr_name
|
|
||||||
res.append(type_dict)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def query(self, _type):
|
|
||||||
citype = CITypeCache.get(_type)
|
|
||||||
if citype:
|
|
||||||
return row2dict(citype)
|
|
||||||
return abort(404, "citype is not found")
|
|
||||||
|
|
||||||
def add(self, type_name, type_alias, _id=None, unique=None,
|
|
||||||
icon_url="", enabled=True):
|
|
||||||
uniq_key = CIAttributeCache.get(_id) or CIAttributeCache.get(unique)
|
|
||||||
if uniq_key is None:
|
|
||||||
return False, "uniq_key is not existed"
|
|
||||||
citype = CITypeCache.get(type_name)
|
|
||||||
if citype:
|
|
||||||
return False, "this CIType {0} is existed".format(type_name)
|
|
||||||
_citype = CIType()
|
|
||||||
_citype.type_name = type_name
|
|
||||||
_citype.type_alias = type_alias
|
|
||||||
_citype.uniq_id = uniq_key.attr_id
|
|
||||||
_citype.enabled = enabled
|
|
||||||
_citype.icon_url = icon_url
|
|
||||||
db.session.add(_citype)
|
|
||||||
db.session.flush()
|
|
||||||
_citype_attr = CITypeAttribute()
|
|
||||||
_citype_attr.attr_id = uniq_key.attr_id
|
|
||||||
_citype_attr.type_id = _citype.type_id
|
|
||||||
_citype_attr.is_required = True
|
|
||||||
db.session.add(_citype_attr)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("add CIType is error, {0}".format(str(e)))
|
|
||||||
return False, str(e)
|
|
||||||
CITypeCache.clean(type_name)
|
|
||||||
return True, _citype.type_id
|
|
||||||
|
|
||||||
def update(self, type_id, type_name, type_alias, _id=None, unique=None,
|
|
||||||
icon_url="", enabled=None):
|
|
||||||
citype = CITypeCache.get(type_id)
|
|
||||||
if citype is None:
|
|
||||||
return False, "CIType {0} is not existed".format(type_name)
|
|
||||||
uniq_key = CIAttributeCache.get(_id) or CIAttributeCache.get(unique)
|
|
||||||
if uniq_key is not None:
|
|
||||||
citype.uniq_id = uniq_key.attr_id
|
|
||||||
citype_attr = db.session.query(CITypeAttribute).filter(
|
|
||||||
CITypeAttribute.type_id == type_id).filter(
|
|
||||||
CITypeAttribute.attr_id == uniq_key.attr_id).first()
|
|
||||||
if citype_attr is None:
|
|
||||||
citype_attr = CITypeAttribute()
|
|
||||||
citype_attr.attr_id = uniq_key.attr_id
|
|
||||||
citype_attr.type_id = type_id
|
|
||||||
citype_attr.is_required = True
|
|
||||||
db.session.add(citype_attr)
|
|
||||||
if type_name:
|
|
||||||
citype.type_name = type_name
|
|
||||||
if type_alias:
|
|
||||||
citype.type_alias = type_alias
|
|
||||||
if icon_url:
|
|
||||||
citype.icon_url = icon_url
|
|
||||||
if enabled is not None:
|
|
||||||
citype.enabled = enabled
|
|
||||||
db.session.add(citype)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error("add CIType is error, {0}".format(str(e)))
|
|
||||||
return False, str(e)
|
|
||||||
CITypeCache.clean(type_id)
|
|
||||||
return True, type_id
|
|
||||||
|
|
||||||
def set_enabled(self, type_id, enabled=True):
|
|
||||||
citype = CITypeCache.get(type_id)
|
|
||||||
if citype is None:
|
|
||||||
return abort(404, "CIType[{0}] is not existed".format(type_id))
|
|
||||||
citype.enabled = enabled
|
|
||||||
db.session.add(citype)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"set CIType enabled is error, {0}".format(str(e)))
|
|
||||||
return abort(500, str(e))
|
|
||||||
return type_id
|
|
||||||
|
|
||||||
def delete(self, type_id):
|
|
||||||
citype = db.session.query(CIType).filter_by(type_id=type_id).first()
|
|
||||||
type_name = citype.type_name
|
|
||||||
if citype:
|
|
||||||
db.session.delete(citype)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"delete CIType is error, {0}".format(str(e)))
|
|
||||||
return abort(500, str(e))
|
|
||||||
CITypeCache.clean(type_id)
|
|
||||||
return "CIType {0} deleted".format(type_name)
|
|
||||||
return abort(404, "CIType is not existed")
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeRelationManager(object):
|
|
||||||
"""
|
|
||||||
manage relation between CITypes
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
|
||||||
def relation_types(self):
|
|
||||||
""" all CIType relation types
|
|
||||||
"""
|
|
||||||
from lib.const import CITYPE_RELATION_TYPES
|
|
||||||
|
|
||||||
return CITYPE_RELATION_TYPES
|
|
||||||
|
|
||||||
def get_children(self, parent_id):
|
|
||||||
children = db.session.query(CITypeRelation).filter(
|
|
||||||
CITypeRelation.parent_id == parent_id).all()
|
|
||||||
result = []
|
|
||||||
for child in children:
|
|
||||||
ctr_id = child.ctr_id
|
|
||||||
citype = CITypeCache.get(child.child_id)
|
|
||||||
citype_dict = row2dict(citype)
|
|
||||||
citype_dict["ctr_id"] = ctr_id
|
|
||||||
manager = CITypeAttributeManager()
|
|
||||||
citype_dict["attributes"] = manager.get_attributes_by_type_id(
|
|
||||||
citype.type_id)
|
|
||||||
citype_dict["relation_type"] = child.relation_type
|
|
||||||
result.append(citype_dict)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_parents(self, child_id):
|
|
||||||
parents = db.session.query(CITypeRelation).filter(
|
|
||||||
CITypeRelation.child_id == child_id).all()
|
|
||||||
result = []
|
|
||||||
for parent in parents:
|
|
||||||
ctr_id = parent.ctr_id
|
|
||||||
citype = CITypeCache.get(parent.parent_id)
|
|
||||||
citype_dict = row2dict(citype)
|
|
||||||
citype_dict["ctr_id"] = ctr_id
|
|
||||||
manager = CITypeAttributeManager()
|
|
||||||
citype_dict["attributes"] = manager.get_attributes_by_type_id(
|
|
||||||
citype.type_id)
|
|
||||||
citype_dict["relation_type"] = parent.relation_type
|
|
||||||
result.append(citype_dict)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def add(self, parent, child, relation_type="contain"):
|
|
||||||
p = CITypeCache.get(parent)
|
|
||||||
if p is None:
|
|
||||||
return abort(404, "parent {0} is not existed".format(parent))
|
|
||||||
c = CITypeCache.get(child)
|
|
||||||
if c is None:
|
|
||||||
return abort(404, "child {0} is not existed".format(child))
|
|
||||||
existed = db.session.query(CITypeRelation.ctr_id).filter_by(
|
|
||||||
parent_id=parent).filter_by(child_id=child).first()
|
|
||||||
if existed is not None:
|
|
||||||
return True, existed.ctr_id
|
|
||||||
ctr = CITypeRelation()
|
|
||||||
ctr.parent_id = parent
|
|
||||||
ctr.child_id = child
|
|
||||||
ctr.relation_type = relation_type
|
|
||||||
db.session.add(ctr)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"add CITypeRelation is error, {0}".format(str(e)))
|
|
||||||
return abort(
|
|
||||||
500, "add CITypeRelation is error, {0}".format(str(e)))
|
|
||||||
return ctr.ctr_id
|
|
||||||
|
|
||||||
def delete(self, ctr_id):
|
|
||||||
ctr = db.session.query(CITypeRelation).filter(
|
|
||||||
CITypeRelation.ctr_id == ctr_id).first()
|
|
||||||
if ctr:
|
|
||||||
db.session.delete(ctr)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"delete CITypeRelation is error, {0}".format(str(e)))
|
|
||||||
return abort(
|
|
||||||
500, "delete CITypeRelation is error, {0}".format(str(e)))
|
|
||||||
return True
|
|
||||||
return abort(404, "CIType relation is not existed")
|
|
||||||
|
|
||||||
def delete_2(self, parent, child):
|
|
||||||
ctr = db.session.query(CITypeRelation).filter(
|
|
||||||
CITypeRelation.parent_id == parent).filter(
|
|
||||||
CITypeRelation.child_id == child).first()
|
|
||||||
return self.delete(ctr.ctr_id)
|
|
|
@ -1,99 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from models.attribute import TextChoice
|
|
||||||
from models.attribute import FloatChoice
|
|
||||||
from models.attribute import IntegerChoice
|
|
||||||
from models.attribute import CIAttributeCache
|
|
||||||
from models.ci_value import CIValueText
|
|
||||||
from models.ci_value import CIValueInteger
|
|
||||||
from models.ci_value import CIValueFloat
|
|
||||||
from models.ci_value import CIValueDateTime
|
|
||||||
from models.ci_value import CIIndexValueDateTime
|
|
||||||
from models.ci_value import CIIndexValueFloat
|
|
||||||
from models.ci_value import CIIndexValueInteger
|
|
||||||
from models.ci_value import CIIndexValueText
|
|
||||||
|
|
||||||
|
|
||||||
def string2int(x):
|
|
||||||
return int(float(x))
|
|
||||||
|
|
||||||
|
|
||||||
def str2datetime(x):
|
|
||||||
try:
|
|
||||||
v = datetime.datetime.strptime(x, "%Y-%m-%d")
|
|
||||||
return v
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
v = datetime.datetime.strptime(x, "%Y-%m-%d %H:%M:%S")
|
|
||||||
return v
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
raise ValueError
|
|
||||||
|
|
||||||
|
|
||||||
type_map = {
|
|
||||||
'converter': {
|
|
||||||
'int': string2int,
|
|
||||||
'float': float,
|
|
||||||
'text': unicode,
|
|
||||||
'datetime': str2datetime,
|
|
||||||
},
|
|
||||||
'choice': {
|
|
||||||
'int': IntegerChoice,
|
|
||||||
'float': FloatChoice,
|
|
||||||
'text': TextChoice,
|
|
||||||
},
|
|
||||||
'table': {
|
|
||||||
'int': CIValueInteger,
|
|
||||||
'text': CIValueText,
|
|
||||||
'datetime': CIValueDateTime,
|
|
||||||
'float': CIValueFloat,
|
|
||||||
'index_int': CIIndexValueInteger,
|
|
||||||
'index_text': CIIndexValueText,
|
|
||||||
'index_datetime': CIIndexValueDateTime,
|
|
||||||
'index_float': CIIndexValueFloat,
|
|
||||||
},
|
|
||||||
'table_name': {
|
|
||||||
'int': 'integers',
|
|
||||||
'text': 'texts',
|
|
||||||
'datetime': 'datetime',
|
|
||||||
'float': 'floats',
|
|
||||||
'index_int': 'index_integers',
|
|
||||||
'index_text': 'index_texts',
|
|
||||||
'index_datetime': 'index_datetime',
|
|
||||||
'index_float': 'index_floats',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TableMap():
|
|
||||||
def __init__(self, attr_name=None):
|
|
||||||
self.attr_name = attr_name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def table(self):
|
|
||||||
if self.attr_name is not None:
|
|
||||||
attr = CIAttributeCache.get(self.attr_name)
|
|
||||||
if attr.is_index:
|
|
||||||
i = "index_{0}".format(attr.value_type)
|
|
||||||
else:
|
|
||||||
i = attr.value_type
|
|
||||||
return type_map["table"].get(i)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def table_name(self):
|
|
||||||
if self.attr_name is not None:
|
|
||||||
attr = CIAttributeCache.get(self.attr_name)
|
|
||||||
if attr.is_index:
|
|
||||||
i = "index_{0}".format(attr.value_type)
|
|
||||||
else:
|
|
||||||
i = attr.value_type
|
|
||||||
return type_map["table_name"].get(i)
|
|
||||||
|
|
||||||
|
|
||||||
CITYPE_RELATION_TYPES = ["connect", "deploy", "install", "contain"]
|
|
||||||
CI_RELATION_TYPES = ["connect", "deploy", "install", "contain"]
|
|
|
@ -1,74 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import time
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
from flask import request
|
|
||||||
from flask import render_template
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from lib.exception import InvalidUsageError
|
|
||||||
|
|
||||||
|
|
||||||
def templated(template=None):
|
|
||||||
def decorator(f):
|
|
||||||
@wraps(f)
|
|
||||||
def decorated_function(*args, **kwargs):
|
|
||||||
template_name = template
|
|
||||||
if template_name is None:
|
|
||||||
template_name = request.endpoint.replace('.', '/') + '.html'
|
|
||||||
ctx = f(*args, **kwargs)
|
|
||||||
if ctx is None:
|
|
||||||
ctx = {}
|
|
||||||
elif not isinstance(ctx, dict):
|
|
||||||
return ctx
|
|
||||||
return render_template(template_name, **ctx)
|
|
||||||
|
|
||||||
return decorated_function
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
def argument_required1(*args_required):
|
|
||||||
from manage import InvalidUsageError
|
|
||||||
|
|
||||||
def decorator(f):
|
|
||||||
@wraps(f)
|
|
||||||
def decorated_function(*args, **kwargs):
|
|
||||||
for arg in args_required:
|
|
||||||
if request.values.get(arg, None) is None:
|
|
||||||
raise InvalidUsageError(
|
|
||||||
"argument {0} is required".format(arg), 400)
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
|
|
||||||
return decorated_function
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
|
|
||||||
class argument_required(object):
|
|
||||||
def __init__(self, *args):
|
|
||||||
self.args = args
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
for arg in self.args:
|
|
||||||
if not request.values.get(arg):
|
|
||||||
raise InvalidUsageError(
|
|
||||||
"argument {0} is required".format(arg), status_code=400)
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def url_statistic(f):
|
|
||||||
@wraps(f)
|
|
||||||
def decorated_func(*args, **kwargs):
|
|
||||||
start = time.time()
|
|
||||||
r = f(*args, **kwargs)
|
|
||||||
spend = time.time() - start
|
|
||||||
url = request.path
|
|
||||||
current_app.logger.info(url)
|
|
||||||
current_app.logger.info(spend)
|
|
||||||
return r
|
|
||||||
return decorated_func
|
|
|
@ -1,17 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidUsageError(Exception):
|
|
||||||
status_code = 400
|
|
||||||
|
|
||||||
def __init__(self, message, status_code=None, payload=None):
|
|
||||||
Exception.__init__(self)
|
|
||||||
self.message = message
|
|
||||||
if status_code is not None:
|
|
||||||
self.status_code = status_code
|
|
||||||
self.payload = payload
|
|
||||||
|
|
||||||
def to_dict(self):
|
|
||||||
rv = dict(self.payload or ())
|
|
||||||
rv['message'] = self.message
|
|
||||||
return rv
|
|
|
@ -1,75 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
from flask import g
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from models.history import OperationRecord
|
|
||||||
from models.history import CIAttributeHistory
|
|
||||||
from models.history import CIRelationHistory
|
|
||||||
|
|
||||||
|
|
||||||
class CIAttributeHistoryManger(object):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add(self, ci_id, history_list):
|
|
||||||
if history_list:
|
|
||||||
record = OperationRecord()
|
|
||||||
record.uid = g.user.uid
|
|
||||||
record.timestamp = datetime.datetime.now()
|
|
||||||
db.session.add(record)
|
|
||||||
db.session.commit()
|
|
||||||
for attr_id, operate_type, old, new in history_list:
|
|
||||||
history = CIAttributeHistory()
|
|
||||||
history.attr_id = attr_id
|
|
||||||
history.operate_type = operate_type
|
|
||||||
history.old = old
|
|
||||||
history.new = new
|
|
||||||
history.ci_id = ci_id
|
|
||||||
history.record_id = record.record_id
|
|
||||||
db.session.add(history)
|
|
||||||
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"add attribute history error, {0}".format(str(e)))
|
|
||||||
return False, "add attribute history error, {0}".format(str(e))
|
|
||||||
return True, None
|
|
||||||
|
|
||||||
|
|
||||||
class CIRelationHistoryManager(object):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add(self, relation, first_ci, second_ci,
|
|
||||||
relation_type, operate_type="add"):
|
|
||||||
record = OperationRecord()
|
|
||||||
record.uid = g.user.uid
|
|
||||||
record.timestamp = datetime.datetime.now()
|
|
||||||
db.session.add(record)
|
|
||||||
db.session.flush()
|
|
||||||
|
|
||||||
history = CIRelationHistory()
|
|
||||||
history.relation = relation
|
|
||||||
history.record_id = record.record_id
|
|
||||||
history.operate_type = operate_type
|
|
||||||
history.first_ci_id = first_ci
|
|
||||||
history.second_ci_id = second_ci
|
|
||||||
history.relation_type = relation_type
|
|
||||||
db.session.add(history)
|
|
||||||
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"add relation history is error, {0}".format(str(e)))
|
|
||||||
return False, "add relation history is error, {0}".format(str(e))
|
|
||||||
return True, None
|
|
|
@ -1,86 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
from flask.ext.mail import Message
|
|
||||||
|
|
||||||
from extensions import mail
|
|
||||||
from models.account import User
|
|
||||||
|
|
||||||
|
|
||||||
def sendmail(users, subject, message, html=False, app=None):
|
|
||||||
if app:
|
|
||||||
mail.app = app
|
|
||||||
else:
|
|
||||||
app = current_app
|
|
||||||
recipients = [x.email for x in users if isinstance(x, User)]
|
|
||||||
recipients.extend(
|
|
||||||
[x for x in users if isinstance(x, basestring) and '@' in x])
|
|
||||||
sender = app.config.get('DEFAULT_MAIL_SENDER')
|
|
||||||
if html:
|
|
||||||
msg = Message(recipients=recipients,
|
|
||||||
html=message,
|
|
||||||
subject=subject,
|
|
||||||
sender=sender)
|
|
||||||
else:
|
|
||||||
msg = Message(recipients=recipients,
|
|
||||||
body=message,
|
|
||||||
subject=subject,
|
|
||||||
sender=sender)
|
|
||||||
mail.send(msg)
|
|
||||||
|
|
||||||
|
|
||||||
from email.mime.multipart import MIMEMultipart
|
|
||||||
from email.mime.text import MIMEText
|
|
||||||
from email.header import Header
|
|
||||||
from email.mime.image import MIMEImage
|
|
||||||
import smtplib
|
|
||||||
import time
|
|
||||||
from email import Utils
|
|
||||||
|
|
||||||
|
|
||||||
def send_mail(sender, receiver, subject, content, ctype="html", pics=(),
|
|
||||||
smtpserver='mail.51ping.com',
|
|
||||||
username="networkbench@51ping.com", password="12qwaszx"):
|
|
||||||
"""subject and body are unicode objects"""
|
|
||||||
if ctype == "html":
|
|
||||||
msg = MIMEText(content, 'html', 'utf-8')
|
|
||||||
else:
|
|
||||||
msg = MIMEText(content, 'plain', 'utf-8')
|
|
||||||
|
|
||||||
if len(pics) != 0:
|
|
||||||
msgRoot = MIMEMultipart('related')
|
|
||||||
msgText = MIMEText(content, 'html', 'utf-8')
|
|
||||||
msgRoot.attach(msgText)
|
|
||||||
i = 1
|
|
||||||
for pic in pics:
|
|
||||||
fp = open(pic, "rb")
|
|
||||||
image = MIMEImage(fp.read())
|
|
||||||
fp.close()
|
|
||||||
image.add_header('Content-ID', '<img%02d>' % i)
|
|
||||||
msgRoot.attach(image)
|
|
||||||
i += 1
|
|
||||||
msg = msgRoot
|
|
||||||
|
|
||||||
msg['Subject'] = Header(subject, 'utf-8')
|
|
||||||
msg['From'] = sender
|
|
||||||
msg['To'] = ';'.join(receiver)
|
|
||||||
msg['Message-ID'] = Utils.make_msgid()
|
|
||||||
msg['date'] = time.strftime('%a, %d %b %Y %H:%M:%S %z')
|
|
||||||
|
|
||||||
smtp = smtplib.SMTP()
|
|
||||||
smtp.connect(smtpserver, 25)
|
|
||||||
smtp.login(username, password)
|
|
||||||
smtp.sendmail(sender, receiver, msg.as_string())
|
|
||||||
smtp.quit()
|
|
||||||
|
|
||||||
|
|
||||||
def send_sms(mobile, content):
|
|
||||||
sms_uri = current_app.config.get("SMS_URI") % (mobile, content)
|
|
||||||
try:
|
|
||||||
current_app.logger.info(sms_uri)
|
|
||||||
requests.get(sms_uri)
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error("send sms error, %s" % str(e))
|
|
|
@ -1,107 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
QUERY_HOSTS_BY_APP = """
|
|
||||||
SELECT *
|
|
||||||
FROM cis
|
|
||||||
INNER JOIN ci_relations AS cr ON cis.`ci_id`=cr.`second_ci`
|
|
||||||
WHERE cr.`first_ci` = {0:d} LIMIT {1:d}, {2:d};
|
|
||||||
"""
|
|
||||||
|
|
||||||
QUERY_HOSTS_NUM_BY_PROJECT = """
|
|
||||||
SELECT cr.first_ci_id,
|
|
||||||
count(DISTINCT cr.second_ci_id)
|
|
||||||
FROM ci_relations AS cr
|
|
||||||
WHERE cr.first_ci_id IN {0}
|
|
||||||
GROUP BY cr.first_ci_id
|
|
||||||
"""
|
|
||||||
|
|
||||||
QUERY_HOSTS_NUM_BY_BU = """
|
|
||||||
SELECT B.first_ci_id,
|
|
||||||
count(DISTINCT cr.second_ci_id)
|
|
||||||
FROM
|
|
||||||
(SELECT A.first_ci_id,
|
|
||||||
cr.second_ci_id
|
|
||||||
FROM
|
|
||||||
(SELECT cr.first_ci_id,
|
|
||||||
cis.ci_id
|
|
||||||
FROM cis
|
|
||||||
INNER JOIN ci_relations AS cr ON cis.ci_id=cr.second_ci_id
|
|
||||||
WHERE cr.first_ci_id IN {0}) AS A
|
|
||||||
INNER JOIN ci_relations AS cr ON cr.first_ci_id=A.ci_id) AS B
|
|
||||||
INNER JOIN ci_relations AS cr ON B.second_ci_id=cr.first_ci_id
|
|
||||||
GROUP BY B.first_ci_id
|
|
||||||
"""
|
|
||||||
|
|
||||||
QUERY_HOSTS_NUM_BY_PRODUCT = """
|
|
||||||
SELECT A.first_ci_id,
|
|
||||||
count(DISTINCT cr.second_ci_id)
|
|
||||||
FROM
|
|
||||||
(SELECT cr.first_ci_id,
|
|
||||||
cis.ci_id
|
|
||||||
FROM cis
|
|
||||||
INNER JOIN ci_relations AS cr ON cis.ci_id=cr.second_ci_id
|
|
||||||
WHERE cr.first_ci_id IN {0}) AS A
|
|
||||||
INNER JOIN ci_relations AS cr ON cr.first_ci_id=A.ci_id
|
|
||||||
GROUP BY A.first_ci_id;
|
|
||||||
"""
|
|
||||||
|
|
||||||
QUERY_CIS_BY_VALUE_TABLE = """
|
|
||||||
SELECT attr.attr_name,
|
|
||||||
attr.attr_alias,
|
|
||||||
attr.value_type,
|
|
||||||
attr.is_multivalue,
|
|
||||||
cis.type_id,
|
|
||||||
{0}.ci_id,
|
|
||||||
{0}.attr_id,
|
|
||||||
{0}.value
|
|
||||||
FROM {0}
|
|
||||||
INNER JOIN cis ON {0}.ci_id=cis.ci_id
|
|
||||||
AND {0}.`ci_id` IN ({1})
|
|
||||||
INNER JOIN ci_attributes as attr ON attr.attr_id = {0}.attr_id
|
|
||||||
"""
|
|
||||||
|
|
||||||
QUERY_CIS_BY_IDS = """
|
|
||||||
SELECT A.ci_id,
|
|
||||||
A.type_id,
|
|
||||||
A.attr_id,
|
|
||||||
A.attr_name,
|
|
||||||
A.attr_alias,
|
|
||||||
A.value,
|
|
||||||
A.value_type,
|
|
||||||
A.is_multivalue
|
|
||||||
FROM
|
|
||||||
({2}) AS A {1}
|
|
||||||
ORDER BY A.ci_id;
|
|
||||||
"""
|
|
||||||
|
|
||||||
FACET_QUERY1 = """
|
|
||||||
SELECT {0}.value,
|
|
||||||
count({0}.ci_id)
|
|
||||||
FROM {0}
|
|
||||||
INNER JOIN ci_attributes AS attr ON attr.attr_id={0}.attr_id
|
|
||||||
WHERE attr.attr_name="{1}"
|
|
||||||
GROUP BY {0}.ci_id;
|
|
||||||
"""
|
|
||||||
|
|
||||||
FACET_QUERY = """
|
|
||||||
SELECT {0}.value,
|
|
||||||
count({0}.ci_id)
|
|
||||||
FROM {0}
|
|
||||||
INNER JOIN ({1}) AS B ON B.ci_id={0}.ci_id
|
|
||||||
WHERE {0}.attr_id={2:d}
|
|
||||||
GROUP BY {0}.ci_id
|
|
||||||
"""
|
|
||||||
|
|
||||||
QUERY_CI_BY_ATTR_NAME = """
|
|
||||||
SELECT {0}.ci_id
|
|
||||||
FROM {0}
|
|
||||||
WHERE {0}.attr_id={1:d}
|
|
||||||
AND {0}.value {2}
|
|
||||||
"""
|
|
||||||
|
|
||||||
QUERY_CI_BY_TYPE = """
|
|
||||||
SELECT cis.ci_id
|
|
||||||
FROM cis
|
|
||||||
WHERE cis.type_id in ({0})
|
|
||||||
"""
|
|
|
@ -1,348 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from lib.const import TableMap
|
|
||||||
from models.attribute import CIAttributeCache
|
|
||||||
from models.ci_type import CITypeCache
|
|
||||||
from extensions import db
|
|
||||||
from models import CI
|
|
||||||
from lib.ci import get_cis_by_ids
|
|
||||||
from lib.query_sql import FACET_QUERY
|
|
||||||
from lib.query_sql import QUERY_CI_BY_TYPE
|
|
||||||
from lib.query_sql import QUERY_CI_BY_ATTR_NAME
|
|
||||||
|
|
||||||
|
|
||||||
class SearchError(Exception):
|
|
||||||
def __init__(self, v):
|
|
||||||
self.v = v
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.v
|
|
||||||
|
|
||||||
|
|
||||||
class Search(object):
|
|
||||||
def __init__(self, query=None, fl=None, facet_field=None,
|
|
||||||
page=1, ret_key="name", count=1, sort=None):
|
|
||||||
self.orig_query = query
|
|
||||||
self.fl = fl
|
|
||||||
self.facet_field = facet_field
|
|
||||||
self.page = page
|
|
||||||
self.ret_key = ret_key
|
|
||||||
try:
|
|
||||||
self.count = int(count)
|
|
||||||
except ValueError:
|
|
||||||
self.count = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
self.sort = sort
|
|
||||||
self.query_sql = ""
|
|
||||||
self.type_id_list = []
|
|
||||||
|
|
||||||
def tor_proc(self, key):
|
|
||||||
tor = list()
|
|
||||||
if key.startswith("+"):
|
|
||||||
tor.append('&')
|
|
||||||
key = key[1:].strip()
|
|
||||||
elif key.startswith("-"):
|
|
||||||
tor.append('|')
|
|
||||||
key = key[1:].strip()
|
|
||||||
elif key.startswith("~"):
|
|
||||||
tor.append('~')
|
|
||||||
key = key[1:].strip()
|
|
||||||
if not tor:
|
|
||||||
tor = ['&', '']
|
|
||||||
if len(tor) < 2:
|
|
||||||
tor.append('')
|
|
||||||
return tor, key
|
|
||||||
|
|
||||||
def attr_name_proc(self, key):
|
|
||||||
tor, key = self.tor_proc(key)
|
|
||||||
if key in ('ci_type', 'type', '_type'):
|
|
||||||
return '_type', 'text', tor, None
|
|
||||||
if key in ('id', 'ci_id', '_id'):
|
|
||||||
return '_id', 'text', tor, None
|
|
||||||
attr = CIAttributeCache.get(key)
|
|
||||||
if attr is not None:
|
|
||||||
# if not attr.is_index:
|
|
||||||
# raise SearchError("{0} is not indexed".format(attr.attr_name))
|
|
||||||
field_name = attr.attr_name
|
|
||||||
return field_name, attr.value_type, tor, attr
|
|
||||||
else:
|
|
||||||
raise SearchError("{0} is not existed".format(key))
|
|
||||||
|
|
||||||
def type_query_handler(self, v, only_type_query):
|
|
||||||
new_v = [v]
|
|
||||||
if v.startswith("(") and v.endswith(")"):
|
|
||||||
new_v = v[1:-1].split(";")
|
|
||||||
for _v in new_v:
|
|
||||||
ci_type = CITypeCache.get(_v)
|
|
||||||
if ci_type is not None:
|
|
||||||
self.type_id_list.append(str(ci_type.type_id))
|
|
||||||
if self.type_id_list:
|
|
||||||
type_ids = ",".join(self.type_id_list)
|
|
||||||
_query_sql = QUERY_CI_BY_TYPE.format(type_ids)
|
|
||||||
if only_type_query:
|
|
||||||
return _query_sql
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def in_query_handler(self, attr, v):
|
|
||||||
new_v = v[1:-1].split(";")
|
|
||||||
table_name = TableMap(attr_name=attr.attr_name).table_name
|
|
||||||
_query_sql = QUERY_CI_BY_ATTR_NAME.format(
|
|
||||||
table_name, attr.attr_id,
|
|
||||||
" OR {0}.value ".format(table_name).join(['LIKE "{0}"'.format(
|
|
||||||
_v.replace("*", "%")) for _v in new_v]))
|
|
||||||
return _query_sql
|
|
||||||
|
|
||||||
def range_query_handler(self, attr, v):
|
|
||||||
start, end = [x.strip() for x in v[1:-1].split("_TO_")]
|
|
||||||
table_name = TableMap(attr_name=attr.attr_name).table_name
|
|
||||||
_query_sql = QUERY_CI_BY_ATTR_NAME.format(
|
|
||||||
table_name, attr.attr_id, "BETWEEN '{0}' AND '{1}'".format(
|
|
||||||
start.replace("*", "%"), end.replace("*", "%")))
|
|
||||||
return _query_sql
|
|
||||||
|
|
||||||
def comparison_query_handler(self, attr, v):
|
|
||||||
table_name = TableMap(attr_name=attr.attr_name).table_name
|
|
||||||
if (v.startswith("<") and not v.startswith("<=")) or \
|
|
||||||
(v.startswith(">") and not v.startswith(">=")):
|
|
||||||
_query_sql = QUERY_CI_BY_ATTR_NAME.format(
|
|
||||||
table_name, attr.attr_id, "{0} '{1}'".format(
|
|
||||||
v[0], v[1:].replace("*", "%")))
|
|
||||||
elif v.startswith(">=") or v.startswith("<="):
|
|
||||||
_query_sql = QUERY_CI_BY_ATTR_NAME.format(
|
|
||||||
table_name, attr.attr_id, "{0} '{1}'".format(
|
|
||||||
v[:2], v[2:].replace("*", "%")))
|
|
||||||
return _query_sql
|
|
||||||
|
|
||||||
def sort_query_handler(self, field, query_sql, only_type_query):
|
|
||||||
if field is None:
|
|
||||||
field = ""
|
|
||||||
if field.startswith("+"):
|
|
||||||
field = field[1:]
|
|
||||||
sort_type = "ASC"
|
|
||||||
elif field.startswith("-"):
|
|
||||||
field = field[1:]
|
|
||||||
sort_type = "DESC"
|
|
||||||
else:
|
|
||||||
sort_type = "ASC"
|
|
||||||
|
|
||||||
if field in ("_id", "ci_id") or not field:
|
|
||||||
if only_type_query:
|
|
||||||
return """SELECT SQL_CALC_FOUND_ROWS DISTINCT B.ci_id
|
|
||||||
FROM ({0}) AS B {1}""".format(
|
|
||||||
query_sql,
|
|
||||||
"ORDER BY B.ci_id {1} LIMIT {0:d}, {2};".format(
|
|
||||||
(self.page - 1) * self.count, sort_type, self.count))
|
|
||||||
elif self.type_id_list:
|
|
||||||
return """SELECT SQL_CALC_FOUND_ROWS DISTINCT B.ci_id
|
|
||||||
FROM ({0}) AS B {1}""".format(
|
|
||||||
query_sql,
|
|
||||||
"INNER JOIN cis on cis.ci_id=B.ci_id "
|
|
||||||
"WHERE cis.type_id in ({3}) "
|
|
||||||
"ORDER BY B.ci_id {1} LIMIT {0:d}, {2};".format(
|
|
||||||
(self.page - 1) * self.count, sort_type, self.count,
|
|
||||||
",".join(self.type_id_list)))
|
|
||||||
else:
|
|
||||||
return """SELECT SQL_CALC_FOUND_ROWS DISTINCT B.ci_id
|
|
||||||
FROM ({0}) AS B {1}""".format(
|
|
||||||
query_sql,
|
|
||||||
"INNER JOIN cis on cis.ci_id=B.ci_id "
|
|
||||||
"ORDER BY B.ci_id {1} LIMIT {0:d}, {2};".format(
|
|
||||||
(self.page - 1) * self.count, sort_type, self.count))
|
|
||||||
else:
|
|
||||||
attr = CIAttributeCache.get(field)
|
|
||||||
attr_id = attr.attr_id
|
|
||||||
|
|
||||||
table_name = TableMap(attr_name=attr.attr_name).table_name
|
|
||||||
_v_query_sql = """SELECT {0}.ci_id, {1}.value FROM
|
|
||||||
({2}) AS {0} INNER JOIN {1} ON {1}.ci_id = {0}.ci_id
|
|
||||||
WHERE {1}.attr_id = {3}""".format("ALIAS", table_name,
|
|
||||||
query_sql, attr_id)
|
|
||||||
new_table = _v_query_sql
|
|
||||||
if only_type_query:
|
|
||||||
return "SELECT SQL_CALC_FOUND_ROWS DISTINCT C.ci_id " \
|
|
||||||
"FROM ({0}) AS C " \
|
|
||||||
"ORDER BY C.value {2} " \
|
|
||||||
"LIMIT {1:d}, {3};".format(new_table,
|
|
||||||
(self.page - 1) * self.count,
|
|
||||||
sort_type, self.count)
|
|
||||||
elif self.type_id_list:
|
|
||||||
return """SELECT SQL_CALC_FOUND_ROWS DISTINCT C.ci_id
|
|
||||||
FROM ({0}) AS C
|
|
||||||
INNER JOIN cis on cis.ci_id=C.ci_id
|
|
||||||
WHERE cis.type_id in ({4})
|
|
||||||
ORDER BY C.value {2}
|
|
||||||
LIMIT {1:d}, {3};""".format(new_table,
|
|
||||||
(self.page - 1) * self.count,
|
|
||||||
sort_type, self.count,
|
|
||||||
",".join(self.type_id_list))
|
|
||||||
else:
|
|
||||||
return """SELECT SQL_CALC_FOUND_ROWS DISTINCT C.ci_id
|
|
||||||
FROM ({0}) AS C
|
|
||||||
ORDER BY C.value {2}
|
|
||||||
LIMIT {1:d}, {3};""".format(new_table,
|
|
||||||
(self.page - 1) * self.count,
|
|
||||||
sort_type, self.count)
|
|
||||||
|
|
||||||
def _wrap_sql(self, tor, alias, _query_sql, query_sql):
|
|
||||||
if tor[0] == "&":
|
|
||||||
query_sql = """SELECT * FROM ({0}) as {1}
|
|
||||||
INNER JOIN ({2}) as {3} USING(ci_id)""".format(
|
|
||||||
query_sql, alias, _query_sql, alias + "A")
|
|
||||||
elif tor[0] == "|":
|
|
||||||
query_sql = "SELECT * FROM ({0}) as {1} UNION ALL ({2})".format(
|
|
||||||
query_sql, alias, _query_sql)
|
|
||||||
elif tor[0] == "~":
|
|
||||||
query_sql = "SELECT * FROM ({0}) as {1} LEFT JOIN ({2}) as {3} " \
|
|
||||||
"USING(ci_id) WHERE {3}.ci_id is NULL".format(
|
|
||||||
query_sql, alias, _query_sql, alias + "A")
|
|
||||||
return query_sql
|
|
||||||
|
|
||||||
def _execute_sql(self, query_sql, only_type_query):
|
|
||||||
v_query_sql = self.sort_query_handler(self.sort, query_sql,
|
|
||||||
only_type_query)
|
|
||||||
start = time.time()
|
|
||||||
execute = db.session.execute
|
|
||||||
current_app.logger.debug(v_query_sql)
|
|
||||||
res = execute(v_query_sql).fetchall()
|
|
||||||
end_time = time.time()
|
|
||||||
current_app.logger.debug("query ci ids time is: {0}".format(
|
|
||||||
end_time - start))
|
|
||||||
numfound = execute("SELECT FOUND_ROWS();").fetchall()[0][0]
|
|
||||||
current_app.logger.debug("statistics ci ids time is: {0}".format(
|
|
||||||
time.time() - end_time)
|
|
||||||
)
|
|
||||||
return numfound, res
|
|
||||||
|
|
||||||
def query_build_raw(self):
|
|
||||||
query_sql, alias, tor = "", "A", ["&"]
|
|
||||||
is_first = True
|
|
||||||
only_type_query = False
|
|
||||||
queries = self.orig_query.split(",")
|
|
||||||
queries = filter(lambda x: x != "", queries)
|
|
||||||
for q in queries:
|
|
||||||
if q.startswith("_type"):
|
|
||||||
queries.remove(q)
|
|
||||||
queries.insert(0, q)
|
|
||||||
if len(queries) == 1 or queries[1].startswith("-") or \
|
|
||||||
queries[1].startswith("~"):
|
|
||||||
only_type_query = True
|
|
||||||
break
|
|
||||||
current_app.logger.debug(queries)
|
|
||||||
special = True
|
|
||||||
for q in queries:
|
|
||||||
_query_sql = ""
|
|
||||||
if ":" in q:
|
|
||||||
k = q.split(":")[0].strip()
|
|
||||||
v = ":".join(q.split(":")[1:]).strip()
|
|
||||||
current_app.logger.info(v)
|
|
||||||
field, field_type, tor, attr = self.attr_name_proc(k)
|
|
||||||
if field == "_type":
|
|
||||||
_query_sql = self.type_query_handler(v, only_type_query)
|
|
||||||
current_app.logger.debug(_query_sql)
|
|
||||||
elif field == "_id": # exclude all others
|
|
||||||
_ci_ids = [str(v)]
|
|
||||||
ci = db.session.query(CI.ci_id).filter(
|
|
||||||
CI.ci_id == int(v)).first()
|
|
||||||
if ci is not None:
|
|
||||||
return 1, _ci_ids
|
|
||||||
elif field:
|
|
||||||
if attr is None:
|
|
||||||
raise SearchError("{0} is not found".format(field))
|
|
||||||
# in query
|
|
||||||
if v.startswith("(") and v.endswith(")"):
|
|
||||||
_query_sql = self.in_query_handler(attr, v)
|
|
||||||
# range query
|
|
||||||
elif v.startswith("[") and v.endswith("]") and "_TO_" in v:
|
|
||||||
_query_sql = self.range_query_handler(attr, v)
|
|
||||||
# comparison query
|
|
||||||
elif v.startswith(">=") or v.startswith("<=") or \
|
|
||||||
v.startswith(">") or v.startswith("<"):
|
|
||||||
_query_sql = self.comparison_query_handler(attr, v)
|
|
||||||
else:
|
|
||||||
table_name = \
|
|
||||||
TableMap(attr_name=attr.attr_name).table_name
|
|
||||||
_query_sql = QUERY_CI_BY_ATTR_NAME.format(
|
|
||||||
table_name, attr.attr_id,
|
|
||||||
'LIKE "{0}"'.format(v.replace("*", "%")))
|
|
||||||
else:
|
|
||||||
return 0, []
|
|
||||||
elif q:
|
|
||||||
return 0, []
|
|
||||||
|
|
||||||
if is_first and _query_sql and not only_type_query:
|
|
||||||
query_sql = "SELECT * FROM ({0}) AS {1}".format(_query_sql,
|
|
||||||
alias)
|
|
||||||
is_first = False
|
|
||||||
alias += "A"
|
|
||||||
elif only_type_query and special:
|
|
||||||
is_first = False
|
|
||||||
special = False
|
|
||||||
query_sql = _query_sql
|
|
||||||
elif _query_sql:
|
|
||||||
query_sql = self._wrap_sql(tor, alias, _query_sql, query_sql)
|
|
||||||
alias += "AA"
|
|
||||||
|
|
||||||
_start = time.time()
|
|
||||||
if query_sql:
|
|
||||||
self.query_sql = query_sql
|
|
||||||
current_app.logger.debug(query_sql)
|
|
||||||
numfound, res = self._execute_sql(query_sql, only_type_query)
|
|
||||||
current_app.logger.info("query ci ids is: {0}".format(
|
|
||||||
time.time() - _start))
|
|
||||||
return numfound, [_res[0] for _res in res]
|
|
||||||
return 0, []
|
|
||||||
|
|
||||||
def facet_build(self):
|
|
||||||
facet = {}
|
|
||||||
for f in self.facet_field:
|
|
||||||
k, field_type, _, attr = self.attr_name_proc(f)
|
|
||||||
if k:
|
|
||||||
table_name = TableMap(attr_name=k).table_name
|
|
||||||
query_sql = FACET_QUERY.format(
|
|
||||||
table_name, self.query_sql, attr.attr_id)
|
|
||||||
result = db.session.execute(query_sql).fetchall()
|
|
||||||
facet[k] = result
|
|
||||||
facet_result = dict()
|
|
||||||
for k, v in facet.items():
|
|
||||||
if not k.startswith('_'):
|
|
||||||
a = getattr(CIAttributeCache.get(k), "attr_%s" % self.ret_key)
|
|
||||||
facet_result[a] = list()
|
|
||||||
for f in v:
|
|
||||||
if f[1] != 0:
|
|
||||||
facet_result[a].append((f[0], f[1], a))
|
|
||||||
return facet_result
|
|
||||||
|
|
||||||
def fl_build(self):
|
|
||||||
_fl = list()
|
|
||||||
for f in self.fl:
|
|
||||||
k, _, _, _ = self.attr_name_proc(f)
|
|
||||||
if k:
|
|
||||||
_fl.append(k)
|
|
||||||
return _fl
|
|
||||||
|
|
||||||
def search(self):
|
|
||||||
numfound, ci_ids = self.query_build_raw()
|
|
||||||
ci_ids = map(str, ci_ids)
|
|
||||||
_fl = self.fl_build()
|
|
||||||
|
|
||||||
if self.facet_field and numfound:
|
|
||||||
facet = self.facet_build()
|
|
||||||
else:
|
|
||||||
facet = dict()
|
|
||||||
|
|
||||||
response, counter = [], {}
|
|
||||||
if ci_ids:
|
|
||||||
response = get_cis_by_ids(ci_ids, ret_key=self.ret_key, fields=_fl)
|
|
||||||
for res in response:
|
|
||||||
ci_type = res.get("ci_type")
|
|
||||||
if ci_type not in counter.keys():
|
|
||||||
counter[ci_type] = 0
|
|
||||||
counter[ci_type] += 1
|
|
||||||
total = len(response)
|
|
||||||
return response, counter, total, self.page, numfound, facet
|
|
|
@ -1 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
|
@ -1,9 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
def convert_to_list(v):
|
|
||||||
if isinstance(v, list):
|
|
||||||
return v
|
|
||||||
if isinstance(v, tuple):
|
|
||||||
return list(v)
|
|
||||||
return [v, ]
|
|
|
@ -1,74 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import redis
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
import settings
|
|
||||||
|
|
||||||
|
|
||||||
class RedisHandler(object):
|
|
||||||
def __init__(self):
|
|
||||||
try:
|
|
||||||
pool = redis.ConnectionPool(
|
|
||||||
max_connections=settings.REDIS_MAX_CONN,
|
|
||||||
host=settings.REDIS_HOST,
|
|
||||||
port=settings.REDIS_PORT,
|
|
||||||
db=settings.REDIS_DB)
|
|
||||||
self.r = redis.Redis(connection_pool=pool)
|
|
||||||
except Exception as e:
|
|
||||||
print e
|
|
||||||
current_app.logger.error("init redis connection failed")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def instance(cls):
|
|
||||||
if not hasattr(cls, "_instance"):
|
|
||||||
cls._instance = cls()
|
|
||||||
return cls._instance
|
|
||||||
|
|
||||||
def get(self, ci_ids, key="CMDB_CI"):
|
|
||||||
try:
|
|
||||||
value = self.r.hmget(key, ci_ids)
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error("get redis error, %s" % str(e))
|
|
||||||
return
|
|
||||||
return value
|
|
||||||
|
|
||||||
def _set(self, ci, key="CMDB_CI"):
|
|
||||||
try:
|
|
||||||
self.r.hmset(key, ci)
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error("set redis error, %s" % str(e))
|
|
||||||
|
|
||||||
def add(self, ci):
|
|
||||||
self._set(ci)
|
|
||||||
|
|
||||||
def delete(self, ci_id, key="CMDB_CI"):
|
|
||||||
try:
|
|
||||||
ret = self.r.hdel(key, ci_id)
|
|
||||||
if not ret:
|
|
||||||
current_app.logger.warn("ci [%d] is not in redis" % ci_id)
|
|
||||||
except Exception as e:
|
|
||||||
current_app.logger.error("delete redis key error, %s" % str(e))
|
|
||||||
|
|
||||||
rd = RedisHandler.instance()
|
|
||||||
|
|
||||||
|
|
||||||
def get_page(page):
|
|
||||||
try:
|
|
||||||
page = int(page)
|
|
||||||
except ValueError:
|
|
||||||
page = 1
|
|
||||||
if page < 1:
|
|
||||||
page = 1
|
|
||||||
return page
|
|
||||||
|
|
||||||
|
|
||||||
def get_per_page(per_page):
|
|
||||||
try:
|
|
||||||
per_page = int(per_page)
|
|
||||||
except:
|
|
||||||
per_page = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
if per_page < 1:
|
|
||||||
per_page = current_app.config.get("DEFAULT_PAGE_COUNT")
|
|
||||||
return per_page
|
|
|
@ -1,170 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from models.attribute import CIAttributeCache
|
|
||||||
from lib.attribute import AttributeManager
|
|
||||||
from lib.const import type_map
|
|
||||||
from lib.const import TableMap
|
|
||||||
|
|
||||||
|
|
||||||
class AttributeValueManager(object):
|
|
||||||
"""
|
|
||||||
manage CI attribute values
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _get_attr(self, key):
|
|
||||||
"""key is one of attr_id, attr_name and attr_alias
|
|
||||||
"""
|
|
||||||
attr = CIAttributeCache.get(key)
|
|
||||||
return attr
|
|
||||||
|
|
||||||
def _get_attr_values(self, fields, ci_id,
|
|
||||||
ret_key="name",
|
|
||||||
uniq_key=None,
|
|
||||||
use_master=False):
|
|
||||||
res = dict()
|
|
||||||
for field in fields:
|
|
||||||
attr = CIAttributeCache.get(field)
|
|
||||||
if not attr:
|
|
||||||
current_app.logger.warn('attribute %s not found' % field)
|
|
||||||
return res
|
|
||||||
table = TableMap(attr_name=attr.attr_name).table
|
|
||||||
if use_master:
|
|
||||||
rs = db.session().using_bind("master").query(
|
|
||||||
table.value).filter_by(ci_id=ci_id).filter_by(
|
|
||||||
attr_id=attr.attr_id)
|
|
||||||
else:
|
|
||||||
rs = db.session.query(table.value).filter_by(
|
|
||||||
ci_id=ci_id).filter_by(attr_id=attr.attr_id)
|
|
||||||
field_name = getattr(attr, "attr_{0}".format(ret_key))
|
|
||||||
try:
|
|
||||||
if attr.is_multivalue:
|
|
||||||
if attr.value_type == 'datetime':
|
|
||||||
res[field_name] = [datetime.datetime.strftime(
|
|
||||||
x.value, '%Y-%m-%d %H:%M:%S') for x in rs.all()]
|
|
||||||
else:
|
|
||||||
res[field_name] = [x.value for x in rs.all()]
|
|
||||||
else:
|
|
||||||
x = rs.first()
|
|
||||||
if x:
|
|
||||||
if attr.value_type == 'datetime':
|
|
||||||
res[field_name] = datetime.datetime.strftime(
|
|
||||||
rs.first().value, '%Y-%m-%d %H:%M:%S')
|
|
||||||
else:
|
|
||||||
res[field_name] = rs.first().value
|
|
||||||
else:
|
|
||||||
res[field_name] = None
|
|
||||||
except AttributeError as e:
|
|
||||||
current_app.logger.warn("get ci by id error, {0}".format(e))
|
|
||||||
if attr.is_multivalue:
|
|
||||||
res[field_name] = list()
|
|
||||||
else:
|
|
||||||
res[field_name] = ""
|
|
||||||
if uniq_key is not None and attr.attr_id == uniq_key.attr_id \
|
|
||||||
and rs.first() is not None:
|
|
||||||
res['unique'] = uniq_key.attr_name
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _validate(self, attr, value, table, ci_id):
|
|
||||||
converter = type_map.get("converter").get(attr.value_type)
|
|
||||||
try:
|
|
||||||
v = converter(value)
|
|
||||||
except ValueError:
|
|
||||||
return False, "attribute value {0} converter fail".format(value)
|
|
||||||
if attr.is_choice:
|
|
||||||
choice_list = AttributeManager()._get_choice_value(
|
|
||||||
attr.attr_id, attr.value_type)
|
|
||||||
if v not in choice_list:
|
|
||||||
return False, "{0} is not existed in choice values".format(
|
|
||||||
value)
|
|
||||||
elif attr.is_uniq:
|
|
||||||
old_value = db.session.query(table.attr_id).filter(
|
|
||||||
table.attr_id == attr.attr_id).filter(
|
|
||||||
table.value == v).filter(table.ci_id != ci_id).first()
|
|
||||||
if old_value is not None:
|
|
||||||
return False, "attribute {0} value {1} must be unique".format(
|
|
||||||
attr.attr_name, value)
|
|
||||||
return True, v
|
|
||||||
|
|
||||||
def add_attr_value(self, key, value, ci_id, ci_type,
|
|
||||||
_no_attribute_policy="ignore", ci_existed=False):
|
|
||||||
"""key is one of attr_id, attr_name and attr_alias
|
|
||||||
"""
|
|
||||||
attr = self._get_attr(key)
|
|
||||||
if attr is None:
|
|
||||||
if _no_attribute_policy == 'ignore':
|
|
||||||
return True, None
|
|
||||||
if _no_attribute_policy == 'reject':
|
|
||||||
return False, 'attribute {0} not exist'.format(key)
|
|
||||||
table, old_value, old_value_table = TableMap(
|
|
||||||
attr_name=attr.attr_name).table, None, None
|
|
||||||
if ci_existed:
|
|
||||||
old_value_table = db.session.query(table).filter(
|
|
||||||
table.attr_id == attr.attr_id).filter(
|
|
||||||
table.ci_id == ci_id).first()
|
|
||||||
if old_value_table is not None:
|
|
||||||
old_value = old_value_table.value
|
|
||||||
if not value and ci_existed:
|
|
||||||
db.session.query(table).filter(
|
|
||||||
table.attr_id == attr.attr_id).filter(
|
|
||||||
table.ci_id == ci_id).delete()
|
|
||||||
if old_value:
|
|
||||||
return True, (attr.attr_id, "delete", old_value, None)
|
|
||||||
else:
|
|
||||||
return True, None
|
|
||||||
elif not value:
|
|
||||||
return True, None
|
|
||||||
if not attr.is_multivalue:
|
|
||||||
ret, res = self._validate(attr, value, table, ci_id)
|
|
||||||
if not ret:
|
|
||||||
return False, res
|
|
||||||
value_table = table()
|
|
||||||
if ci_existed: # for history
|
|
||||||
old = db.session.query(table).filter(
|
|
||||||
table.attr_id == attr.attr_id).filter(
|
|
||||||
table.value == value).filter(
|
|
||||||
table.ci_id == ci_id).first()
|
|
||||||
if old is not None:
|
|
||||||
return True, None
|
|
||||||
elif old_value_table:
|
|
||||||
value_table = old_value_table
|
|
||||||
value_table.ci_id = ci_id
|
|
||||||
value_table.attr_id = attr.attr_id
|
|
||||||
value_table.value = res
|
|
||||||
db.session.add(value_table)
|
|
||||||
elif attr.is_multivalue:
|
|
||||||
if ci_existed:
|
|
||||||
db.session.query(table).filter(
|
|
||||||
table.attr_id == attr.attr_id).filter(
|
|
||||||
table.ci_id == ci_id).delete()
|
|
||||||
|
|
||||||
for v in value.strip().split(","):
|
|
||||||
ret, res = self._validate(attr, v, table, ci_id)
|
|
||||||
if not ret:
|
|
||||||
return False, res
|
|
||||||
value_table = table()
|
|
||||||
value_table.ci_id = ci_id
|
|
||||||
value_table.attr_id = attr.attr_id
|
|
||||||
value_table.value = res
|
|
||||||
db.session.add(value_table)
|
|
||||||
try:
|
|
||||||
db.session.commit()
|
|
||||||
except Exception as e:
|
|
||||||
db.session.rollback()
|
|
||||||
current_app.logger.error(
|
|
||||||
"add attribute value is error, {0}".format(str(e)))
|
|
||||||
return False, "add attribute value is error, {0}".format(str(e))
|
|
||||||
if ci_existed:
|
|
||||||
if old_value != value:
|
|
||||||
return True, (attr.attr_id, "update", old_value, value)
|
|
||||||
else:
|
|
||||||
return True, None
|
|
||||||
return True, (attr.attr_id, "add", None, value)
|
|
|
@ -1,77 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask import jsonify
|
|
||||||
from flask import make_response
|
|
||||||
from flask.ext.script import Manager
|
|
||||||
from flask.ext.script import prompt_bool
|
|
||||||
from flask.ext.celery import install_commands as install_celery_command
|
|
||||||
|
|
||||||
from __init__ import make_app
|
|
||||||
from extensions import db
|
|
||||||
from gunicornserver import GunicornServer
|
|
||||||
from lib.exception import InvalidUsageError
|
|
||||||
|
|
||||||
|
|
||||||
app = make_app('config.cfg')
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(InvalidUsageError)
|
|
||||||
def handle_invalid_usage(error):
|
|
||||||
response = jsonify(error.to_dict())
|
|
||||||
response.status_code = error.status_code
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
|
||||||
def not_found(error):
|
|
||||||
return make_response(jsonify({'message': error.description}), 404)
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(400)
|
|
||||||
def bad_request(error):
|
|
||||||
return make_response(jsonify({'message': error.description}), 400)
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(401)
|
|
||||||
def auth_lack(error):
|
|
||||||
return make_response(jsonify({'message': error.description}), 401)
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(403)
|
|
||||||
def exception_403(error):
|
|
||||||
return make_response(jsonify({'message': error.description}), 403)
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(405)
|
|
||||||
def exception_405(error):
|
|
||||||
return make_response(jsonify({'message': error.description}), 405)
|
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(500)
|
|
||||||
def server_error(error):
|
|
||||||
return make_response(jsonify({"message": error.description}), 500)
|
|
||||||
|
|
||||||
|
|
||||||
manager = Manager(app)
|
|
||||||
|
|
||||||
install_celery_command(manager)
|
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
|
||||||
def db_setup():
|
|
||||||
"create all database tables"
|
|
||||||
db.create_all()
|
|
||||||
|
|
||||||
|
|
||||||
@manager.command
|
|
||||||
def db_dropall():
|
|
||||||
"drop all databse tables"
|
|
||||||
if prompt_bool("Are you sure ? You will lose all your data !"):
|
|
||||||
db.drop_all()
|
|
||||||
|
|
||||||
|
|
||||||
manager.add_command("run", GunicornServer())
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
manager.run(default_command="runserver")
|
|
|
@ -1,24 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
def row2dict(row):
|
|
||||||
d = dict()
|
|
||||||
for c in row.__table__.columns:
|
|
||||||
if not isinstance(getattr(row, c.name),
|
|
||||||
(basestring, long, int, float, list, tuple, dict)) \
|
|
||||||
and getattr(row, c.name):
|
|
||||||
d[c.name] = getattr(row, c.name).strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
else:
|
|
||||||
d[c.name] = getattr(row, c.name)
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
from account import *
|
|
||||||
from attribute import *
|
|
||||||
from ci import *
|
|
||||||
from ci_relation import *
|
|
||||||
from ci_type import *
|
|
||||||
from ci_type_relation import *
|
|
||||||
from ci_value import *
|
|
||||||
from history import *
|
|
||||||
from statis import *
|
|
|
@ -1,230 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
import copy
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from werkzeug.utils import cached_property
|
|
||||||
from flask.ext.sqlalchemy import BaseQuery
|
|
||||||
from flask.ext.principal import RoleNeed
|
|
||||||
from flask.ext.principal import UserNeed
|
|
||||||
from flask.ext.principal import Permission
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from extensions import cache
|
|
||||||
from permissions import admin
|
|
||||||
from models import row2dict
|
|
||||||
|
|
||||||
|
|
||||||
class UserQuery(BaseQuery):
|
|
||||||
def from_identity(self, identity):
|
|
||||||
"""
|
|
||||||
Loads user from flask.ext.principal.Identity instance and
|
|
||||||
assigns permissions from user.
|
|
||||||
|
|
||||||
A "user" instance is monkey patched to the identity instance.
|
|
||||||
|
|
||||||
If no user found then None is returned.
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
_id = identity.id
|
|
||||||
if _id:
|
|
||||||
_id = int(_id)
|
|
||||||
user = self.get(_id)
|
|
||||||
except ValueError:
|
|
||||||
user = None
|
|
||||||
except Exception:
|
|
||||||
user = None
|
|
||||||
if user:
|
|
||||||
identity.provides.update(user.provides)
|
|
||||||
identity.user = user
|
|
||||||
return user
|
|
||||||
|
|
||||||
def authenticate(self, login, password):
|
|
||||||
user = self.filter(db.or_(User.username == login,
|
|
||||||
User.email == login)).first()
|
|
||||||
if user:
|
|
||||||
authenticated = user.check_password(password)
|
|
||||||
else:
|
|
||||||
authenticated = False
|
|
||||||
return user, authenticated
|
|
||||||
|
|
||||||
def authenticate_with_key(self, key, secret, args, path):
|
|
||||||
user = self.filter(User.key == key).filter(User.block == 0).first()
|
|
||||||
if not user:
|
|
||||||
return None, False
|
|
||||||
if user and hashlib.sha1('%s%s%s' % (
|
|
||||||
path, user.secret, "".join(args))).hexdigest() == secret:
|
|
||||||
authenticated = True
|
|
||||||
else:
|
|
||||||
authenticated = False
|
|
||||||
return row2dict(user), authenticated
|
|
||||||
|
|
||||||
def search(self, key):
|
|
||||||
query = self.filter(db.or_(User.email == key,
|
|
||||||
User.nickname.ilike('%' + key + '%'),
|
|
||||||
User.username.ilike('%' + key + '%')))
|
|
||||||
return query
|
|
||||||
|
|
||||||
def get_by_username(self, username):
|
|
||||||
user = self.filter(User.username == username).first()
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get_by_nickname(self, nickname):
|
|
||||||
user = self.filter(User.nickname == nickname).first()
|
|
||||||
return user
|
|
||||||
|
|
||||||
def get(self, uid):
|
|
||||||
user = self.filter(User.uid == uid).first()
|
|
||||||
return copy.deepcopy(user)
|
|
||||||
|
|
||||||
def is_exits(self, username):
|
|
||||||
user = self.filter(User.username == username).first()
|
|
||||||
return user is not None
|
|
||||||
|
|
||||||
|
|
||||||
class User(db.Model):
|
|
||||||
__tablename__ = 'users'
|
|
||||||
query_class = UserQuery
|
|
||||||
|
|
||||||
ADMIN = 1
|
|
||||||
|
|
||||||
uid = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
username = db.Column(db.String(32), unique=True)
|
|
||||||
nickname = db.Column(db.String(20), nullable=True)
|
|
||||||
department = db.Column(db.String(20))
|
|
||||||
catalog = db.Column(db.String(64))
|
|
||||||
email = db.Column(db.String(100), unique=True, nullable=False)
|
|
||||||
mobile = db.Column(db.String(14), unique=True)
|
|
||||||
_password = db.Column("password", db.String(80), nullable=False)
|
|
||||||
key = db.Column(db.String(32), nullable=False)
|
|
||||||
secret = db.Column(db.String(32), nullable=False)
|
|
||||||
date_joined = db.Column(db.DateTime, default=datetime.utcnow)
|
|
||||||
last_login = db.Column(db.DateTime, default=datetime.utcnow)
|
|
||||||
block = db.Column(db.Boolean, default=False)
|
|
||||||
has_logined = db.Column(db.Boolean, default=False)
|
|
||||||
|
|
||||||
class Permissions(object):
|
|
||||||
def __init__(self, obj):
|
|
||||||
self.obj = obj
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def is_admin(self):
|
|
||||||
return Permission(UserNeed(self.obj.id)) & admin
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(User, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.username
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def permissions(self):
|
|
||||||
return self.Permissions(self)
|
|
||||||
|
|
||||||
def _get_password(self):
|
|
||||||
return self._password
|
|
||||||
|
|
||||||
def _set_password(self, password):
|
|
||||||
self._password = password
|
|
||||||
|
|
||||||
password = db.synonym("_password", descriptor=property(
|
|
||||||
_get_password, _set_password))
|
|
||||||
|
|
||||||
def check_password(self, password):
|
|
||||||
return self.password == password
|
|
||||||
|
|
||||||
@cached_property
|
|
||||||
def provides(self):
|
|
||||||
needs = [RoleNeed('authenticated'), UserNeed(self.uid)]
|
|
||||||
for r in self.rolenames:
|
|
||||||
needs.append(RoleNeed(r))
|
|
||||||
if self.is_admin:
|
|
||||||
needs.append(RoleNeed('admin'))
|
|
||||||
return needs
|
|
||||||
|
|
||||||
@property
|
|
||||||
def roles(self):
|
|
||||||
urs = db.session.query(UserRole.rid).filter(
|
|
||||||
UserRole.uid == self.uid).all()
|
|
||||||
return [x.rid for x in urs]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rolenames(self):
|
|
||||||
return [db.session.query(Role.role_name).filter(
|
|
||||||
Role.rid == rid).first().role_name for rid in self.roles]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_admin(self):
|
|
||||||
return self.ADMIN in self.roles
|
|
||||||
|
|
||||||
|
|
||||||
class Role(db.Model):
|
|
||||||
__tablename__ = 'roles'
|
|
||||||
|
|
||||||
rid = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
role_name = db.Column(db.String(64), nullable=False, unique=True)
|
|
||||||
|
|
||||||
|
|
||||||
class UserRole(db.Model):
|
|
||||||
__tablename__ = 'users_roles'
|
|
||||||
|
|
||||||
uid = db.Column(db.Integer, db.ForeignKey('users.uid'), primary_key=True)
|
|
||||||
rid = db.Column(db.Integer, db.ForeignKey('roles.rid'), primary_key=True)
|
|
||||||
|
|
||||||
|
|
||||||
class UserCache(object):
|
|
||||||
@classmethod
|
|
||||||
def get(cls, key):
|
|
||||||
user = cache.get("User::uid::%s" % key) or \
|
|
||||||
cache.get("User::username::%s" % key) or \
|
|
||||||
cache.get("User::nickname::%s" % key)
|
|
||||||
if not user:
|
|
||||||
user = User.query.get(key) or \
|
|
||||||
User.query.get_by_username(key) or \
|
|
||||||
User.query.get_by_nickname(key)
|
|
||||||
if user:
|
|
||||||
cls.set(user)
|
|
||||||
return user
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set(cls, user):
|
|
||||||
cache.set("User::uid::%s" % user.uid, user)
|
|
||||||
cache.set("User::username::%s" % user.username, user)
|
|
||||||
cache.set("User::nickname::%s" % user.nickname, user)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clean(cls, user):
|
|
||||||
cache.delete("User::uid::%s" % user.uid)
|
|
||||||
cache.delete("User::username::%s" % user.username)
|
|
||||||
cache.delete("User::nickname::%s" % user.nickname)
|
|
||||||
|
|
||||||
|
|
||||||
class RoleCache(object):
|
|
||||||
@classmethod
|
|
||||||
def get(cls, rid):
|
|
||||||
role = None
|
|
||||||
if isinstance(rid, (int, long)):
|
|
||||||
role = cache.get("Role::rid::%s" % rid)
|
|
||||||
if not role:
|
|
||||||
role = db.session.query(Role).filter(Role.rid == rid).first()
|
|
||||||
cls.set(role)
|
|
||||||
elif isinstance(rid, basestring):
|
|
||||||
role = cache.get("Role::role_name::%s" % rid)
|
|
||||||
if not role:
|
|
||||||
role = db.session.query(Role).filter(
|
|
||||||
Role.role_name == rid).first()
|
|
||||||
cls.set(role)
|
|
||||||
return role
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set(cls, role):
|
|
||||||
cache.set("Role::rid::%s" % role.rid, role)
|
|
||||||
cache.set("Role::role_name::%s" % role.role_name, role)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clean(cls, role):
|
|
||||||
cache.delete("Role::rid::%s" % role.rid, role)
|
|
||||||
cache.delete("Role::role_name::%s" % role.role_name, role)
|
|
|
@ -1,87 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
from extensions import db, cache
|
|
||||||
|
|
||||||
|
|
||||||
class CIAttribute(db.Model):
|
|
||||||
__tablename__ = "ci_attributes"
|
|
||||||
|
|
||||||
attr_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
attr_name = db.Column(db.String(32), nullable=False, unique=True)
|
|
||||||
attr_alias = db.Column(db.String(32), nullable=False, unique=True)
|
|
||||||
value_type = db.Column(
|
|
||||||
db.String(8),
|
|
||||||
db.Enum("int", "float", "text", "datetime", name='value_type'),
|
|
||||||
default="text",
|
|
||||||
nullable=False)
|
|
||||||
is_choice = db.Column(db.Boolean, default=False)
|
|
||||||
is_multivalue = db.Column(db.Boolean, default=False)
|
|
||||||
is_uniq = db.Column(db.Boolean, default=False)
|
|
||||||
is_index = db.Column(db.Boolean, default=False)
|
|
||||||
|
|
||||||
|
|
||||||
class IntegerChoice(db.Model):
|
|
||||||
__tablename__ = 'choice_integers'
|
|
||||||
|
|
||||||
choice_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
attr = db.relationship("CIAttribute", backref="choice_integers")
|
|
||||||
value = db.Column(db.Integer, nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class FloatChoice(db.Model):
|
|
||||||
__tablename__ = 'choice_floats'
|
|
||||||
|
|
||||||
choice_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
attr = db.relationship("CIAttribute", backref="choice_floats")
|
|
||||||
value = db.Column(db.Float, nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class TextChoice(db.Model):
|
|
||||||
__tablename__ = 'choice_texts'
|
|
||||||
|
|
||||||
choice_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
attr = db.relationship("CIAttribute", backref="choice_texts")
|
|
||||||
value = db.Column(db.Text, nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class CIAttributeCache(object):
|
|
||||||
@classmethod
|
|
||||||
def get(cls, key):
|
|
||||||
if key is None:
|
|
||||||
return
|
|
||||||
attr = cache.get('Field::Name::%s' % key) or \
|
|
||||||
cache.get('Field::ID::%s' % key) or \
|
|
||||||
cache.get('Field::Alias::%s' % key)
|
|
||||||
if attr is None:
|
|
||||||
attr = db.session.query(CIAttribute).filter_by(
|
|
||||||
attr_name=key).first() or \
|
|
||||||
db.session.query(CIAttribute).filter(
|
|
||||||
CIAttribute.attr_id == key).first() or \
|
|
||||||
db.session.query(CIAttribute).filter(
|
|
||||||
CIAttribute.attr_alias == key).first()
|
|
||||||
db.session.close()
|
|
||||||
if attr is not None:
|
|
||||||
CIAttributeCache.set(attr)
|
|
||||||
return attr
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set(cls, attr):
|
|
||||||
cache.set('Field::ID::%s' % attr.attr_id, attr)
|
|
||||||
cache.set('Field::Name::%s' % attr.attr_name, attr)
|
|
||||||
cache.set('Field::Alias::%s' % attr.attr_alias, attr)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clean(cls, attr):
|
|
||||||
if cache.get('Field::ID::%s' % attr.attr_id):
|
|
||||||
cache.delete('Field::ID::%s' % attr.attr_id)
|
|
||||||
cache.delete('Field::Name::%s' % attr.attr_name)
|
|
||||||
cache.delete('Field::Alias::%s' % attr.attr_alias)
|
|
|
@ -1,20 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
|
|
||||||
|
|
||||||
class CI(db.Model):
|
|
||||||
__tablename__ = "cis"
|
|
||||||
|
|
||||||
ci_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
uuid = db.Column(db.String(32), nullable=False)
|
|
||||||
type_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("ci_types.type_id"),
|
|
||||||
nullable=False)
|
|
||||||
ci_type = db.relationship("CIType", backref="cis")
|
|
||||||
status = db.Column(db.String(8),
|
|
||||||
db.Enum("review", "validate", name="stauts"))
|
|
||||||
created_time = db.Column(db.DateTime, default=datetime.datetime.now())
|
|
||||||
heartbeat = db.Column(db.DateTime, default=datetime.datetime.now())
|
|
|
@ -1,26 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
|
|
||||||
|
|
||||||
class CIRelation(db.Model):
|
|
||||||
__tablename__ = "ci_relations"
|
|
||||||
cr_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
first_ci_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("cis.ci_id"),
|
|
||||||
primary_key=True)
|
|
||||||
second_ci_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("cis.ci_id"),
|
|
||||||
primary_key=True)
|
|
||||||
first_ci = db.relationship("CI",
|
|
||||||
primaryjoin="CI.ci_id==CIRelation.first_ci_id")
|
|
||||||
second_ci = db.relationship(
|
|
||||||
"CI", primaryjoin="CI.ci_id==CIRelation.second_ci_id")
|
|
||||||
relation_type = db.Column(
|
|
||||||
db.String(8), db.Enum("connect", "deploy", "install", "contain",
|
|
||||||
name="relation_type"), nullable=False)
|
|
||||||
more = db.Column(db.Integer, db.ForeignKey("cis.ci_id"))
|
|
||||||
|
|
||||||
__table_args__ = (db.UniqueConstraint("first_ci_id", "second_ci_id",
|
|
||||||
name="first_second_uniq"), )
|
|
|
@ -1,128 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from extensions import cache
|
|
||||||
|
|
||||||
|
|
||||||
class CIType(db.Model):
|
|
||||||
__tablename__ = "ci_types"
|
|
||||||
|
|
||||||
type_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
type_name = db.Column(db.String(32))
|
|
||||||
type_alias = db.Column(db.String(32))
|
|
||||||
uniq_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("ci_attributes.attr_id"),
|
|
||||||
nullable=False)
|
|
||||||
uniq_key = db.relationship("CIAttribute", backref="ci_types")
|
|
||||||
enabled = db.Column(db.Boolean, default=True, nullable=False)
|
|
||||||
is_attached = db.Column(db.Boolean, default=False, nullable=False)
|
|
||||||
icon_url = db.Column(db.String(256))
|
|
||||||
order = db.Column(db.SmallInteger, default=0, nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeAttribute(db.Model):
|
|
||||||
__tablename__ = "type_attributes"
|
|
||||||
|
|
||||||
type_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("ci_types.type_id"),
|
|
||||||
primary_key=True)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("ci_attributes.attr_id"),
|
|
||||||
primary_key=True)
|
|
||||||
is_required = db.Column(db.Boolean, default=False)
|
|
||||||
|
|
||||||
__table_args__ = (db.UniqueConstraint("type_id", "attr_id",
|
|
||||||
name="type_attr_uniq"), )
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeCache(object):
|
|
||||||
@classmethod
|
|
||||||
def get(cls, key):
|
|
||||||
if key is None:
|
|
||||||
return
|
|
||||||
ct = cache.get("CIType::ID::%s" % key) or \
|
|
||||||
cache.get("CIType::Name::%s" % key)
|
|
||||||
if ct is None:
|
|
||||||
ct = db.session.query(CIType).filter(
|
|
||||||
CIType.type_name == key).first() or \
|
|
||||||
db.session.query(CIType).filter(CIType.type_id == key).first()
|
|
||||||
if ct is not None:
|
|
||||||
CITypeCache.set(ct)
|
|
||||||
return ct
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set(cls, ct):
|
|
||||||
cache.set("CIType::Name::%s" % ct.type_name, ct)
|
|
||||||
cache.set("CIType::ID::%d" % ct.type_id, ct)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clean(cls, key):
|
|
||||||
ct = CITypeCache.get(key)
|
|
||||||
if ct is not None:
|
|
||||||
cache.delete("CIType::Name::%s" % ct.type_name)
|
|
||||||
cache.delete("CIType::ID::%s" % ct.type_id)
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeSpecCache(object):
|
|
||||||
@classmethod
|
|
||||||
def get(cls, key):
|
|
||||||
if key is None:
|
|
||||||
return
|
|
||||||
ct = cache.get("CITypeSPEC::ID::%d" % key)
|
|
||||||
if ct is None:
|
|
||||||
ct = db.session.query(CIType).filter(CIType.type_id == key).first()
|
|
||||||
if ct is not None:
|
|
||||||
CITypeSpecCache.set(ct)
|
|
||||||
return ct
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set(cls, ct):
|
|
||||||
cache.set("CITypeSPEC::ID::%d" % ct.type_id, ct)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clean(cls, key):
|
|
||||||
ct = CITypeCache.get(key)
|
|
||||||
if ct is not None:
|
|
||||||
cache.delete("CITypeSPEC::ID::%d" % ct.type_id)
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeAttributeCache(object):
|
|
||||||
"""
|
|
||||||
key is type_id or type_name
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get(cls, key):
|
|
||||||
if key is None:
|
|
||||||
return
|
|
||||||
if isinstance(key, basestring) and isinstance(key, unicode):
|
|
||||||
key = unicode(key, 'utf8')
|
|
||||||
citypes = cache.get("CITypeAttribute::Name::%s" % key) or \
|
|
||||||
cache.get("CITypeAttribute::ID::%s" % key)
|
|
||||||
if not citypes:
|
|
||||||
citypes = db.session.query(CITypeAttribute).filter(
|
|
||||||
CITypeAttribute.type_id == key).all()
|
|
||||||
if citypes is None:
|
|
||||||
ci_type = db.session.query(CIType).filter(
|
|
||||||
CIType.type_name == key).first()
|
|
||||||
if ci_type is not None:
|
|
||||||
citypes = db.session.query(CITypeAttribute).filter_by(
|
|
||||||
type_id=ci_type.type_id).all()
|
|
||||||
if citypes is not None:
|
|
||||||
CITypeAttributeCache.set(key, citypes)
|
|
||||||
return citypes
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set(cls, key, values):
|
|
||||||
citype = CITypeCache.get(key)
|
|
||||||
if citype is not None:
|
|
||||||
cache.set("CITypeAttribute::ID::%s" % citype.type_id, values)
|
|
||||||
cache.set("CITypeAttribute::Name::%s" % citype.type_name, values)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clean(cls, key):
|
|
||||||
citype = CITypeCache.get(key)
|
|
||||||
attrs = CITypeAttributeCache.get(key)
|
|
||||||
if attrs is not None and citype:
|
|
||||||
cache.delete("CITypeAttribute::ID::%s" % citype.type_id)
|
|
||||||
cache.delete("CITypeAttribute::Name::%s" % citype.type_name)
|
|
|
@ -1,27 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
|
|
||||||
|
|
||||||
class CITypeRelation(db.Model):
|
|
||||||
__tablename__ = "ci_type_relations"
|
|
||||||
|
|
||||||
ctr_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
parent_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("ci_types.type_id"),
|
|
||||||
primary_key=True)
|
|
||||||
parent = db.relationship(
|
|
||||||
"CIType", primaryjoin="CIType.type_id==CITypeRelation.parent_id")
|
|
||||||
child_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("ci_types.type_id"),
|
|
||||||
primary_key=True)
|
|
||||||
child = db.relationship(
|
|
||||||
"CIType", primaryjoin="CIType.type_id==CITypeRelation.child_id")
|
|
||||||
relation_type = db.Column(
|
|
||||||
db.String(7),
|
|
||||||
db.Enum("contain", "connect", "deploy", "install",
|
|
||||||
name="relation_type"),
|
|
||||||
default="contain")
|
|
||||||
|
|
||||||
__table_args__ = (db.UniqueConstraint("parent_id", "child_id",
|
|
||||||
name="parent_child_uniq"), )
|
|
|
@ -1,117 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
from sqlalchemy import Index
|
|
||||||
|
|
||||||
|
|
||||||
class CIIndexValueInteger(db.Model):
|
|
||||||
__tablename__ = "index_integers"
|
|
||||||
|
|
||||||
value_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
ci_id = db.Column(db.Integer, db.ForeignKey('cis.ci_id'), nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
ci = db.relationship("CI", backref="index_integers")
|
|
||||||
attr = db.relationship("CIAttribute", backref="index_integers")
|
|
||||||
value = db.Column(db.Integer, nullable=False)
|
|
||||||
|
|
||||||
__table_args__ = (Index("attr_value_index", "attr_id", "value"), )
|
|
||||||
|
|
||||||
|
|
||||||
class CIIndexValueFloat(db.Model):
|
|
||||||
__tablename__ = "index_floats"
|
|
||||||
|
|
||||||
value_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
ci_id = db.Column(db.Integer, db.ForeignKey('cis.ci_id'), nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
ci = db.relationship("CI", backref="index_floats")
|
|
||||||
attr = db.relationship("CIAttribute", backref="index_floats")
|
|
||||||
value = db.Column(db.Float, nullable=False)
|
|
||||||
|
|
||||||
__table_args__ = (Index("attr_value_index", "attr_id", "value"), )
|
|
||||||
|
|
||||||
|
|
||||||
class CIIndexValueText(db.Model):
|
|
||||||
__tablename__ = "index_texts"
|
|
||||||
|
|
||||||
value_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
ci_id = db.Column(db.Integer, db.ForeignKey('cis.ci_id'), nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
ci = db.relationship("CI", backref="index_texts")
|
|
||||||
attr = db.relationship("CIAttribute", backref="index_texts")
|
|
||||||
value = db.Column(db.String(128), nullable=False)
|
|
||||||
|
|
||||||
__table_args__ = (Index("attr_value_index", "attr_id", "value"), )
|
|
||||||
|
|
||||||
|
|
||||||
class CIIndexValueDateTime(db.Model):
|
|
||||||
__tablename__ = "index_datetime"
|
|
||||||
|
|
||||||
value_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
ci_id = db.Column(db.Integer, db.ForeignKey('cis.ci_id'), nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
ci = db.relationship("CI", backref="index_datetime")
|
|
||||||
attr = db.relationship("CIAttribute", backref="index_datetime")
|
|
||||||
value = db.Column(db.DateTime, nullable=False)
|
|
||||||
|
|
||||||
__table_args__ = (Index("attr_value_index", "attr_id", "value"), )
|
|
||||||
|
|
||||||
|
|
||||||
class CIValueInteger(db.Model):
|
|
||||||
__tablename__ = "integers"
|
|
||||||
|
|
||||||
value_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
ci_id = db.Column(db.Integer, db.ForeignKey('cis.ci_id'), nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
ci = db.relationship("CI", backref="integers")
|
|
||||||
attr = db.relationship("CIAttribute", backref="integers")
|
|
||||||
value = db.Column(db.Integer, nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class CIValueFloat(db.Model):
|
|
||||||
__tablename__ = "floats"
|
|
||||||
|
|
||||||
value_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
ci_id = db.Column(db.Integer, db.ForeignKey('cis.ci_id'), nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
ci = db.relationship("CI", backref="floats")
|
|
||||||
attr = db.relationship("CIAttribute", backref="floats")
|
|
||||||
value = db.Column(db.Float, nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class CIValueText(db.Model):
|
|
||||||
__tablename__ = "texts"
|
|
||||||
|
|
||||||
value_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
ci_id = db.Column(db.Integer, db.ForeignKey('cis.ci_id'), nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
ci = db.relationship("CI", backref="texts")
|
|
||||||
attr = db.relationship("CIAttribute", backref="texts")
|
|
||||||
value = db.Column(db.Text, nullable=False)
|
|
||||||
|
|
||||||
|
|
||||||
class CIValueDateTime(db.Model):
|
|
||||||
__tablename__ = "datetime"
|
|
||||||
|
|
||||||
value_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
ci_id = db.Column(db.Integer, db.ForeignKey('cis.ci_id'), nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey('ci_attributes.attr_id'),
|
|
||||||
nullable=False)
|
|
||||||
ci = db.relationship("CI", backref="datetime")
|
|
||||||
attr = db.relationship("CIAttribute", backref="datetime")
|
|
||||||
value = db.Column(db.DateTime, nullable=False)
|
|
|
@ -1,51 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
|
|
||||||
|
|
||||||
class OperationRecord(db.Model):
|
|
||||||
__tablename__ = "records"
|
|
||||||
|
|
||||||
record_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
uid = db.Column(db.Integer, db.ForeignKey('users.uid'), nullable=False)
|
|
||||||
timestamp = db.Column(db.DateTime,
|
|
||||||
nullable=False,
|
|
||||||
default=datetime.datetime.now())
|
|
||||||
origin = db.Column(db.String(32))
|
|
||||||
ticket_id = db.Column(db.String(32))
|
|
||||||
reason = db.Column(db.Text)
|
|
||||||
|
|
||||||
|
|
||||||
class CIAttributeHistory(db.Model):
|
|
||||||
__tablename__ = "histories"
|
|
||||||
|
|
||||||
h_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
operate_type = db.Column(db.String(6), db.Enum("add", "delete", "update",
|
|
||||||
name="operate_type"))
|
|
||||||
record_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("records.record_id"),
|
|
||||||
nullable=False)
|
|
||||||
ci_id = db.Column(db.Integer, nullable=False)
|
|
||||||
attr_id = db.Column(db.Integer, nullable=False)
|
|
||||||
old = db.Column(db.Text)
|
|
||||||
new = db.Column(db.Text)
|
|
||||||
|
|
||||||
|
|
||||||
class CIRelationHistory(db.Model):
|
|
||||||
__tablename__ = "relation_histories"
|
|
||||||
|
|
||||||
rh_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
operate_type = db.Column(db.String(6),
|
|
||||||
db.Enum("add", "delete", name="operate_type"))
|
|
||||||
record_id = db.Column(db.Integer,
|
|
||||||
db.ForeignKey("records.record_id"),
|
|
||||||
nullable=False)
|
|
||||||
first_ci_id = db.Column(db.Integer)
|
|
||||||
second_ci_id = db.Column(db.Integer)
|
|
||||||
relation_type = db.Column(
|
|
||||||
db.String(8), db.Enum("connect", "deploy", "install", "contain",
|
|
||||||
name="relation_type"))
|
|
||||||
relation = db.Column(db.Integer, nullable=False)
|
|
|
@ -1,20 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from extensions import db
|
|
||||||
|
|
||||||
|
|
||||||
class UrlRecord(db.Model):
|
|
||||||
|
|
||||||
url_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
|
||||||
|
|
||||||
url = db.Column(db.String(64), nullable=False)
|
|
||||||
response_time = db.Column(db.Float, nullable=False)
|
|
||||||
is_ok = db.Column(db.Boolean, default=True)
|
|
||||||
source = db.Column(db.String(32))
|
|
||||||
remote_addr = db.Column(db.String(20))
|
|
||||||
hits = db.Column(db.Integer)
|
|
||||||
method = db.Column(db.String(5), default="GET")
|
|
||||||
created_at = db.Column(db.DateTime, default=datetime.datetime.now())
|
|
|
@ -1,9 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
from flask.ext.principal import RoleNeed, Permission
|
|
||||||
|
|
||||||
|
|
||||||
admin = Permission(RoleNeed('admin'))
|
|
||||||
auth = Permission(RoleNeed('authenticated'))
|
|
||||||
null = Permission(RoleNeed('null'))
|
|
|
@ -1,14 +0,0 @@
|
||||||
Flask==0.9
|
|
||||||
Flask-Script==0.5.2
|
|
||||||
Flask-Babel==0.8
|
|
||||||
Flask-principal==0.3.5
|
|
||||||
Flask-mail==0.7.4
|
|
||||||
pymysql==0.5
|
|
||||||
sqlalchemy==0.8.2
|
|
||||||
Flask-sqlalchemy==0.16
|
|
||||||
Flask-cache==0.9.2
|
|
||||||
redis==2.7.2
|
|
||||||
gunicorn==0.17.4
|
|
||||||
celery==3.0.18
|
|
||||||
flask-celery=2.4.3
|
|
||||||
Jinja2==2.7.1
|
|
|
@ -1,7 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
## CI cache
|
|
||||||
REDIS_HOST = "127.0.0.1"
|
|
||||||
REDIS_PORT = 6379
|
|
||||||
REDIS_DB = 0
|
|
||||||
REDIS_MAX_CONN = 30
|
|
|
@ -1 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
|
@ -1,30 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from extensions import celery
|
|
||||||
from extensions import db
|
|
||||||
from lib.utils import rd
|
|
||||||
import lib.ci
|
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.ci_cache", queue="cmdb_async")
|
|
||||||
def ci_cache(ci_id):
|
|
||||||
time.sleep(0.1)
|
|
||||||
db.session.close()
|
|
||||||
m = lib.ci.CIManager()
|
|
||||||
ci = m.get_ci_by_id(ci_id, need_children=False, use_master=True)
|
|
||||||
rd.delete(ci_id)
|
|
||||||
rd.add({ci_id: json.dumps(ci)})
|
|
||||||
current_app.logger.info("%d caching.........." % ci_id)
|
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.ci_delete", queue="cmdb_async")
|
|
||||||
def ci_delete(ci_id):
|
|
||||||
current_app.logger.info(ci_id)
|
|
||||||
rd.delete(ci_id)
|
|
||||||
current_app.logger.info("%d delete.........." % ci_id)
|
|
|
@ -1,21 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from extensions import celery
|
|
||||||
from extensions import db
|
|
||||||
from models.statis import UrlRecord
|
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="statis.url_record", queue="statis_async")
|
|
||||||
def url_record(url, method, remote_addr, response_time, status_code, source):
|
|
||||||
current_app.logger.info("%s add 1" % url)
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
u = UrlRecord(url=url, response_time=response_time, is_ok=1,
|
|
||||||
source="default", hits=1, method=method, created_at=now,
|
|
||||||
remote_addr=remote_addr)
|
|
||||||
db.session.add(u)
|
|
||||||
db.session.commit()
|
|
|
@ -1,27 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<responce>
|
|
||||||
<numfound>{{ numfound }}</numfound>
|
|
||||||
<page>{{ page }}</page>
|
|
||||||
<cis>
|
|
||||||
{% for ci in result %}
|
|
||||||
<ci type="{{ ci['_type'] }}" id="{{ ci['_id'] }}" type_name="{{ ci['ci_type'] }}" >
|
|
||||||
{% for k, v in ci.items() %}
|
|
||||||
{% if not k.startswith('_') %}
|
|
||||||
{% for item in v | convert_to_list %}
|
|
||||||
<attribute name="{{ k }}">{{ item }}</attribute>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</ci>
|
|
||||||
{% endfor %}
|
|
||||||
</cis>
|
|
||||||
<facets>
|
|
||||||
{% for k,v in facet.items() %}
|
|
||||||
<facet attribute="{{ k }}">
|
|
||||||
{% for item in v %}
|
|
||||||
<value name="{{ item[0] }}">{{ item[1] }}</value>
|
|
||||||
{% endfor %}
|
|
||||||
</facet>
|
|
||||||
{% endfor %}
|
|
||||||
</facets>
|
|
||||||
</responce>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<responce>
|
|
||||||
<code>{{ code }}</code>
|
|
||||||
<cis>
|
|
||||||
{% for k, v in ret.items() %}
|
|
||||||
<group by="{{ k }}">
|
|
||||||
{% for ci in v %}
|
|
||||||
|
|
||||||
<ci>
|
|
||||||
{% for item in ci|convert_to_list %}
|
|
||||||
<attribute name="{{ k }}">{{ item }}</attribute>
|
|
||||||
{% endfor %}
|
|
||||||
</ci>
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
</group>
|
|
||||||
{% endfor %}
|
|
||||||
</cis>
|
|
||||||
</responce>
|
|
Loading…
Reference in New Issue