class DBPrivilege(db.Model): __tablename__ = 'account_privilege' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) operation = db.Column(db.Integer, default=100) resource_type = db.Column(db.Integer, default=100) resource_id = db.Column(db.Integer, default=0, index=True) comment = db.Column(db.String(128))
class DBDNSServer(db.Model): __tablename__ = 'dns_server' id = db.Column(db.Integer, primary_key=True) host = db.Column(db.String(64), index=True) ip = db.Column(db.String(64)) env = db.Column(db.String(64)) dns_server_type = db.Column(db.String(64)) status = db.Column(db.String(64), default='INITING') zb_process_itemid = db.Column(db.String(64)) zb_port_itemid = db.Column(db.String(64)) zb_resolve_itemid = db.Column(db.String(64)) zb_resolve_rate_itemid = db.Column(db.String(64)) server_log = db.Column(db.Text()) gmt_create = db.Column(db.DateTime(), default=datetime.now) gmt_modified = db.Column(db.DateTime(), default=datetime.now) @property def can_update(self): return g.current_user.can_do( Operation.UPDATE, ResourceType.SERVER, self.id) @property def can_delete(self): return g.current_user.can_do( Operation.DELETE, ResourceType.SERVER, self.id) def to_json(self): json_server = { 'id': self.id, 'host': self.host, 'ip': self.ip, 'env': self.env, 'dns_type': self.dns_type, 'status': self.status, 'logs': self.logs } return json_server def get_server_status(self): zb = ZBapi(self) server_status = zb.get_server_status() return server_status def get_resolve_rate(self, start_time, end_time): zb = ZBapi(self) resolve_rate = zb.get_resolve_rate(start_time, end_time) # resolve_rates.append({dns_server.host: resolve_rate}) return resolve_rate def get_content_str(self, prefix=None): #'修改前内容:' content = 'id: ' + str(self.id) + '\n' \ + '主机名: ' + str(self.host) + '\n' \ + 'IP地址: ' + str(self.ip) + '\n' \ + '环境: ' + str(self.env) + '\n' \ + 'DNS类型: ' + str(self.dns_server_type) + '\n' if prefix: content = prefix + '\n' + content return content
class DBOperationLog(db.Model): __tablename__ = 'dns_operation_log' id = db.Column(db.Integer, primary_key=True) operation_time = db.Column(db.DateTime(), default=datetime.now) operation_type = db.Column(db.String(64)) operator = db.Column(db.String(64)) target_type = db.Column(db.String(64)) target_name = db.Column(db.String(64)) target_id = db.Column(db.String(64)) target_detail = db.Column(db.Text()) gmt_create = db.Column(db.DateTime(), default=datetime.now) gmt_modified = db.Column(db.DateTime(), default=datetime.now)
class DBRole(db.Model): __tablename__ = 'account_role' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) def __repr__(self): return '<DBRole %r>' % self.name @property def users(self): return db.session.query(DBUser).join( DBUserRole, and_(DBUserRole.user_id == DBUser.id)) \ .join(DBRole, and_(DBRole.id == DBUserRole.role_id)) \ .filter(DBRole.id == self.id).all() @property def privileges(self): return db.session.query(DBPrivilege).join( DBRolePrivilege, and_(DBRolePrivilege.privilege_id == DBPrivilege.id)) \ .join(DBRole, and_(DBRole.id == DBRolePrivilege.role_id)) \ .filter(DBRole.id == self.id).all() @property def privilege_ids(self): return [p.id for p in db.session.query(DBPrivilege).join( DBRolePrivilege, and_(DBRolePrivilege.privilege_id == DBPrivilege.id)) \ .join(DBRole, and_(DBRole.id == DBRolePrivilege.role_id)) \ .filter(DBRole.id == self.id).all()] @privileges.setter def privileges(self, privilege_ids): for privilege_id in privilege_ids: current_role_new_privilege = DBRolePrivilege( role_id=self.id, privilege_id=privilege_id) db.session.add(current_role_new_privilege)
class DBLocalAuth(db.Model): __tablename__ = 'account_local_auth' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), unique=True, index=True) password_hash = db.Column(db.String(128)) email = db.Column(db.String(128)) @property def password(self): raise AttributeError('password is not a readable attribute') @password.setter def password(self, password): self.password_hash = generate_password_hash(password) def verify_password(self, password): return check_password_hash(self.password_hash, password)
class DBRecord(db.Model): __tablename__ = 'dns_record' id = db.Column(db.Integer, primary_key=True) host = db.Column(db.String(64), index=True) record_type = db.Column(db.String(64)) ttl = db.Column(db.String(64)) value = db.Column(db.String(64)) view_name = db.Column(db.String(64), default='') comment = db.Column(db.String(64)) creator = db.Column(db.String(64)) status = db.Column(db.String(64), default='enabled') enabled = db.Column(db.String(64), default='1') alive = db.Column(db.String(64), default='ON') outter_record_id = db.Column(db.String(64), default='') zone_id = db.Column(db.Integer, index=True) gmt_create = db.Column(db.DateTime(), default=datetime.now) gmt_modified = db.Column(db.DateTime(), default=datetime.now) @property def can_update(self): return g.current_user.can_do(Operation.UPDATE, ResourceType.ZONE, self.zone_id) @property def can_delete(self): return g.current_user.can_do(Operation.DELETE, ResourceType.ZONE, self.zone_id) @property def can_access(self): return g.current_user.can_do(Operation.ACCESS, ResourceType.ZONE, self.zone_id) @property def zone(self): return DBZone.query.get(self.zone_id) def create(self, current_zone, args, v_name): if current_zone.zone_group in [1, 2]: record_list = db.session.query(DBRecord).filter( DBRecord.zone_id == args['zone_id'], DBRecord.view_name == v_name, DBRecord.host != '@' ).all() self._make_record(current_zone.name, record_list) else: create_url = current_app.config.get('DNSPOD_RECORD_BASE_URL') + 'Create' dnspod_data = { 'domain': current_zone.name, 'sub_domain':args['host'], 'record_type':args['record_type'], 'record_line':args['view_name'], 'value':args['value'], 'ttl':args['ttl'] } self._do_dnspod(create_url, dnspod_data) def update(self, current_zone, args): if current_zone.zone_group in [1, 2]: record_list = db.session.query(DBRecord).filter( DBRecord.zone_id == args['zone_id'], DBRecord.view_name == args['view_name'], DBRecord.host != '@' ).all() self._make_record(current_zone.name, record_list) else: modify_url = current_app.config.get('DNSPOD_RECORD_BASE_URL') + 'Modify' dnspod_data = { 'domain': current_zone.name, 'record_id': self.outter_record_id, 'sub_domain':args['host'], 'record_type':args['record_type'], 'record_line':args['view_name'], 'value':args['value'], 'ttl':args['ttl'] } self._do_dnspod(modify_url, dnspod_data) def delete(self, current_zone): if current_zone.zone_group in [1, 2]: record_list = db.session.query(DBRecord).filter( DBRecord.zone_id == self.zone_id, DBRecord.view_name == self.view_name, DBRecord.host != '@' ).all() self._make_record(current_zone.name, record_list) else: delete_url = current_app.config.get('DNSPOD_RECORD_BASE_URL') + 'Remove' dnspod_data = { 'domain': current_zone.name, 'record_id': self.outter_record_id } self._do_dnspod(delete_url, dnspod_data) def _make_record(self, zone_name, record_list): etcd_client = getETCDclient() zone_record_conf = current_app.config.get('ZONE_BASE_DIR') + \ self.view_name + '/zone.' + zone_name zone_record_conf_content = Template( current_app.config.get('RECORD_TEMPLATE')).render( zone_name=zone_name, record_list=record_list ) etcd_client.write( zone_record_conf, zone_record_conf_content, prevExist=True) time.sleep(0.2) def _do_dnspod(self, url, data): body_info = { "login_token": current_app.config.get('DNSPOD_TOKEN'), "format": current_app.config.get('DNSPOD_DATA_FORMAT') } res = requests.post(url, data=dict(body_info, **data)) res_json = res.json() print(res_json) if res.status_code != 200: raise Exception(str(res_json)) if res_json.get('status').get('code') != '1': raise Exception(str(res_json)) if current_app.config.get('DNSPOD_RECORD_BASE_URL') + 'Create' == url: self.outter_record_id = res_json.get('record').get('id') db.session.add(self) def get_content_str(self, prefix=None): #'修改前内容:' content = 'id: ' + str(self.id) + '\n' \ + '记录主机: ' + str(self.host) + '\n' \ + '记录类型: ' + str(self.record_type) + '\n' \ + '记录值: ' + str(self.value) + '\n' \ + 'TTL: ' + str(self.ttl) + '\n' \ + '线路类型: ' + str(self.view_name) + '\n' \ + '备注: ' + str(self.comment) + '\n' \ + '创建人: ' + str(self.creator) + '\n' \ + '创建时间: ' + str(self.gmt_create) if prefix: content = prefix + '\n' + content return content
class DBView(db.Model): __tablename__ = 'dns_view' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), index=True) acl = db.Column(db.Text()) gmt_create = db.Column(db.DateTime(), default=datetime.now) gmt_modified = db.Column(db.DateTime(), default=datetime.now) def __init__(self, **kwargs): super(DBView, self).__init__(**kwargs) def __repr__(self): return '<DBView %r>' % self.name @property def zones(self): related_zones = db.session.query(DBZone).join( DBViewZone, and_(DBViewZone.zone_id == DBZone.id)) \ .join(DBView, and_(DBView.id == DBViewZone.view_id)) \ .filter(DBView.id == self.id).all() if not related_zones: return [] return related_zones @property def zone_name_list(self): return [v.name for v in self.zones] def get_content_str(self, prefix=None): content = 'id: ' + str(self.id) + '\n' \ + 'View名称: ' + str(self.name) + '\n' \ + 'ACL: ' + str(self.acl) + '\n' if prefix: content = prefix + '\n' + content return content @property def can_update(self): return g.current_user.can_do( Operation.UPDATE, ResourceType.VIEW, self.id) @property def can_delete(self): return g.current_user.can_do( Operation.DELETE, ResourceType.VIEW, self.id) def make_view(self, action, view_list): prevExist = True if action == 'create': prevExist = False etcd_client = getETCDclient() if action == 'del': view_base_dir = current_app.config.get('ETCD_BASE_DIR') + self.name etcd_client.delete(view_base_dir, recursive=True) time.sleep(0.2) return if action == 'create': view_zone_conf = current_app.config.get('ETCD_BASE_DIR') + self.name + '/view.conf' view_zone_conf_content = Template( current_app.config.get('VIEW_TEMPLATE')).render(view_name=self.name) etcd_client.write(view_zone_conf, view_zone_conf_content, prevExist=prevExist) time.sleep(0.2) #连续几个提交速度过快,etcd server检测不到提交 view_define_conf_content = Template( current_app.config.get('VIEW_DEFINE_TEMPLATE')).render(view_list=view_list) # print(current_app.config.get('VIEW_DEFINE_CONF')) # print(view_define_conf_content) etcd_client.write( current_app.config.get('VIEW_DEFINE_CONF'), view_define_conf_content, prevExist=True) time.sleep(0.2) acl_conf = current_app.config.get('ETCD_BASE_DIR') + self.name + '/acl.conf' acl_conf_content = Template(current_app.config.get('ACL_TEMPLATE')).render( view_name=self.name, ip_list=self.acl.split()) etcd_client.write(acl_conf, acl_conf_content, prevExist=prevExist) time.sleep(0.2)
class DBZone(db.Model): __tablename__ = 'dns_zone' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), index=True) zone_group = db.Column(db.Integer, default=1) zone_type = db.Column(db.String(64), default='') forwarders = db.Column(db.String(64), default='') gmt_create = db.Column(db.DateTime(), default=datetime.now) gmt_modified = db.Column(db.DateTime(), default=datetime.now) def create(self): if self.zone_group in [1, 2]: self._create_inner() else: self._create_outter() def modify(self, pre_views): if self.zone_group in [1, 2]: self._modify_inner(pre_views) else: self._modify_outter() def delete(self): if self.zone_group in [1, 2]: self._del_inner() else: self._del_outter() @property def can_update(self): return g.current_user.can_do(Operation.UPDATE, ResourceType.ZONE, self.id) @property def can_delete(self): return g.current_user.can_do(Operation.DELETE, ResourceType.ZONE, self.id) @property def can_access(self): return g.current_user.can_do(Operation.ACCESS, ResourceType.ZONE, self.id) @property def views(self): related_views = db.session.query(DBView).join( DBViewZone, and_(DBViewZone.view_id == DBView.id)) \ .join(DBZone, and_(DBZone.id == DBViewZone.zone_id)) \ .filter(DBZone.id == self.id).all() if not related_views: return [] return related_views @property def view_name_list(self): return [v.name for v in self.views] @property def view_ids(self): return [v.id for v in self.views] def get_content_str(self, prefix=None): if self.zone_group == 0: related_view_list = '' if self.zone_group in [1, 2]: related_view_list = str(self.view_name_list) content = 'id: ' + str(self.id) + '\n' \ + 'Zone名称: ' + str(self.name) + '\n' \ + 'Zone归属: ' + ZONE_GROUP_MAPPING.get(self.zone_group) + '\n' \ + 'Zone类型: ' + str(self.zone_type) + '\n' \ + '关联View: ' + related_view_list + '\n' if prefix: content = prefix + '\n' + content return content def _create_inner(self): for related_view in self.view_name_list: ns_record = DBRecord(host='@', record_type='NS', value='master.'+self.name+'.' , ttl='86400', view_name=related_view, creator=g.current_user.username, zone_id=self.id) db.session.add(ns_record) zone_list = db.session.query(DBZone).filter( or_(DBZone.zone_group == 1, DBZone.zone_group == 2)).all() for z_view in self.view_name_list: self._make_zone('create', z_view, zone_list, []) time.sleep(0.1) def _create_outter(self): create_url = current_app.config.get('DNSPOD_DOMAIN_BASE_URL') + 'Create' res = requests.post(create_url, data=dict(login_token=current_app.config.get('DNSPOD_TOKEN'), domain=self.name, format=current_app.config.get('DNSPOD_DATA_FORMAT'))) if res.status_code == 200: res_json = res.json() if res_json.get('status').get('code') == '1': return raise Exception(str(res_json)) def _modify_inner(self, pre_views): zone_list = db.session.query(DBZone).filter(or_(DBZone.zone_group == 1, DBZone.zone_group == 2)).all() current_views = set(self.view_name_list) pre_views = set(pre_views) # 清除当前zone 解除绑定view所对应的record del_views = pre_views - current_views del_records = db.session.query(DBRecord).filter( DBRecord.zone_id==self.id, DBRecord.view_name.in_(tuple(del_views)) ).all() for del_record in del_records: db.session.delete(del_record) # 添加当前zone新增绑定view时候,所对应的默认record (默认host为@的record) add_views = current_views - pre_views for add_view in add_views: ns_record = DBRecord(host='@', record_type='NS', value='master.'+self.name+'.' , ttl='86400', view_name=add_view, creator=g.current_user.username, zone_id=self.id) db.session.add(ns_record) for del_view in del_views: self._make_zone('del', del_view, zone_list, []) for z_view in current_views: record_list = db.session.query(DBRecord).filter( DBRecord.zone_id == self.id, DBRecord.view_name == z_view.strip(), DBRecord.host != '@' ).all() self._make_zone('modify', z_view, zone_list, record_list) def _modify_outter(self): raise Exception('外部域名不支持修改!') def _del_inner(self): zone_list = db.session.query(DBZone).filter( or_(DBZone.zone_group == 1, DBZone.zone_group == 2)).all() for z_view in self.view_name_list: self._make_zone('del', z_view, zone_list, []) def _del_outter(self): delete_url = current_app.config.get('DNSPOD_DOMAIN_BASE_URL') + 'Remove' res = requests.post(delete_url, data=dict( login_token=current_app.config.get('DNSPOD_TOKEN'), domain=self.name, format=current_app.config.get('DNSPOD_DATA_FORMAT')) ) if res.status_code == 200: res_json = res.json() if res_json.get('status').get('code') == '1': return raise Exception(str(res_json)) def _make_zone(self, action, view_name, zone_list, record_list): etcd_client = getETCDclient() view_zone_conf = current_app.config.get('ETCD_BASE_DIR') + view_name + '/view.conf' if action == 'del': bind_zones = [] for zz in zone_list: if view_name in self.view_name_list and zz.name != self.name : bind_zones.append(zz) view_zone_conf_content = Template( current_app.config.get('ZONE_TEMPLATE')).render( view_name=view_name, zone_list=bind_zones) else: bind_zones = [] for zz in zone_list: if view_name in self.view_name_list: bind_zones.append(zz) view_zone_conf_content = Template(current_app.config.get('ZONE_TEMPLATE')).render( view_name=view_name, zone_list=bind_zones) etcd_client.write(view_zone_conf, view_zone_conf_content, prevExist=True) time.sleep(0.2) # view_zone_confiig 文件操作 # forward only类型的zone,不生成 zone.xx.xx 文件 # 修改zone不需要更改zone.xx.xx 文件 if self.zone_type != 'forward only': zone_record_conf = current_app.config.get('ZONE_BASE_DIR') + \ view_name + '/zone.' + self.name if action == 'create' or action == 'modify': zone_record_conf_content = Template( current_app.config.get('RECORD_TEMPLATE')).render( zone_name=self.name, record_list=record_list) try: etcd_client.write( zone_record_conf, zone_record_conf_content, prevExist=True) except etcd.EtcdKeyNotFound: zone_record_conf_content = Template( current_app.config.get('RECORD_TEMPLATE')).render( zone_name=self.name, record_list=[]) etcd_client.write( zone_record_conf, zone_record_conf_content, prevExist=False) time.sleep(0.2) if action == 'del': etcd_client.delete(zone_record_conf) time.sleep(0.2)
class DBUser(db.Model): __tablename__ = 'account_user' id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(64), default='') username = db.Column(db.String(64), unique=True, index=True) chinese_name = db.Column(db.String(64), default='') cellphone = db.Column(db.String(64), default='') actived = db.Column(db.Integer, default=1) position = db.Column(db.String(64), default='') location = db.Column(db.String(64), default='') member_since = db.Column(db.DateTime(), default=datetime.now) last_seen = db.Column(db.DateTime(), default=datetime.now) def to_json(self): json_user = { 'id': self.id, 'username': self.username, 'email': self.email, 'chinese_name': self.chinese_name, 'cellphone': self.cellphone, 'position': self.position, 'location': self.location, 'can_add_server': self.can_add_server, 'can_add_view': self.can_add_view, 'can_add_zone': self.can_add_zone, 'member_since': str(self.member_since) } return json_user def can(self, privilege): current_user_privileges = db.session.query(DBPrivilege) \ .join(DBRolePrivilege, and_( DBPrivilege.id == DBRolePrivilege.privilege_id, DBPrivilege.name == privilege)) \ .join(DBRole, and_(DBRole.id == DBRolePrivilege.role_id)) \ .join(DBUserRole, and_(DBUserRole.role_id == DBRole.id)) \ .join(DBUser, and_(DBUser.id == DBUserRole.user_id)) \ .filter(DBUser.id == self.id).all() for current_user_privilege in current_user_privileges: if privilege == current_user_privilege.name: return True return False def is_admin(self): admins = db.session.query(DBRole).join( DBUserRole, and_(DBUserRole.role_id == DBRole.id, DBRole.name == "admin")) \ .join(DBUser, and_(DBUser.id == DBUserRole.user_id)) \ .filter(DBUser.id == self.id).all() if admins: return True return False @property def can_add_server(self): return self.can(DefaultPrivilege.SERVER_ADD) @property def can_add_view(self): return self.can(DefaultPrivilege.VIEW_ADD) @property def can_add_zone(self): return self.can(DefaultPrivilege.ZONE_ADD) @property def can_edit_bind_conf(self): return self.can(DefaultPrivilege.BIND_CONF_EDIT) def can_access_log(self): return self.can(DefaultPrivilege.LOG_PAGE_ACCESS) @property def roles(self): return db.session.query(DBRole).join( DBUserRole, and_(DBUserRole.role_id == DBRole.id)) \ .join(DBUser, and_(DBUser.id == DBUserRole.user_id)) \ .filter(DBUser.id == self.id).all() @property def role_ids(self): return [r.id for r in db.session.query(DBRole).join( DBUserRole, and_(DBUserRole.role_id == DBRole.id)) \ .join(DBUser, and_(DBUser.id == DBUserRole.user_id)) \ .filter(DBUser.id == self.id).all()] @roles.setter def roles(self, role_ids): for role_id in role_ids: current_user_new_role = DBUserRole(user_id=self.id, role_id=role_id) db.session.add(current_user_new_role) def can_do(self, operation, resource_type, resource_id): r = RESOURCE_TYPE_MAPPING.get(resource_type) current_user_resources = db.session.query(r) \ .join(DBPrivilege, and_( r.id == resource_id, r.id == DBPrivilege.resource_id, DBPrivilege.resource_type == resource_type, DBPrivilege.operation == operation)) \ .join(DBRolePrivilege, and_(DBPrivilege.id == DBRolePrivilege.privilege_id)) \ .join(DBRole, and_(DBRole.id == DBRolePrivilege.role_id)) \ .join(DBUserRole, and_(DBUserRole.role_id == DBRole.id)) \ .join(DBUser, and_(DBUser.id == DBUserRole.user_id)) \ .filter(DBUser.id == self.id).all() if current_user_resources: return True return False