class ZdZnodeImportTreeHandler(CommonBaseHandler): """batch edit, 批量修改 """ args_list = [ ArgsMap('path', required=True), ArgsMap('cluster_name', required=True), ] @authenticated def response(self): '''batch edit ''' return self.render('config/znode/batchimport.html', action='/config/znode/importsave', cluster_name=self.cluster_name, parent_path=self.path, uploadfile='', child_znodes=[])
class WsZnodeAddSnapshotHandler(CommonBaseHandler): """批量生成快照 """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', default='/'), ] @authenticated def response(self): """根据父节点路径批量生成快照 """ try: SnapshotService.make_snapshots_from_path(self.cluster_name, self.path) return self.ajax_ok(close_current=False) except MakeSnapshotError: return self.ajax_popup(code=300, msg="父节点快照尚未生成,无法创建快照!")
class ZdZnodeExportHandler(CommonBaseHandler): """export,导出数据到文件 """ args_list = [ ArgsMap('path', required=True), ArgsMap('cluster_name', required=True), ] @authenticated def response(self): '''导出数据到文件中 ''' data = ZookeeperService.get(self.cluster_name, self.path) filename = "{}".format(self.path.rsplit('/')[-1]) self.set_header('Content-Type', 'application/octet-stream') self.set_header('Content-Disposition', 'attachment; filename={}'.format(filename)) self.finish(data)
class ZdZnodeMetadataHandler(CommonBaseHandler): '''metainfo ''' args_list = [ ArgsMap('path', required=True), ArgsMap('cluster_name', required=True) ] @authenticated def response(self): """获取zookeeper节点的元数据信息 """ metainfo = dict() zk_node = ZdZnode.one(path=self.path, cluster_name=self.cluster_name) if zk_node: metainfo['type'] = zk_node.type metainfo['business'] = zk_node.business return json.dumps(metainfo)
class WsSnapshotDeleteNodesHandler(CommonBaseHandler): """删除快照节点树形结构信息 """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('node_path', required=True), ArgsMap('recursive', default='0') ] @authenticated def response(self): """删除树节点节点 """ err_msg = SnapshotService.delete_snapshot_nodes( self.cluster_name, self.node_path, self.recursive) if err_msg: return self.ajax_popup(code=300, msg=err_msg) else: return self.ajax_ok(close_current=False)
class ZdQconfFeedbackSaveHandler(ApiBaseHandler): """save """ args_list = [ ArgsMap('id', default=''), ArgsMap('hostname', default=''), ArgsMap('ip', default=''), ArgsMap('node_whole', default=''), ArgsMap('value_md5', default=''), ArgsMap('idc', default=''), ArgsMap('update_time', default=''), ArgsMap('data_type', default=''), ArgsMap('deleted', default=''), ] def response(self): '''add ''' feedback = ZdQconfFeedback.one(idc=self.idc, ip=self.ip, path=self.node_whole) if feedback is None: # create new feedback record feedback = ZdQconfFeedback() # 填充字段 if self.idc: feedback.idc = self.idc if self.ip: feedback.ip = self.ip if self.hostname: feedback.hostname = self.hostname if self.node_whole: feedback.path = self.node_whole if self.value_md5: feedback.md5_value = self.value_md5 if self.update_time: # convert unix timestamp to datetime update_time = datetime.fromtimestamp(int( self.update_time)).strftime('%Y-%m-%d %H:%M:%S') feedback.update_time = update_time if self.data_type: feedback.data_type = self.data_type # 自定义字段 if self.deleted: feedback.deleted = self.deleted feedback.save() # qconf protocol, return '0' means ok self.finish('0')
class ZdZnodeEditTreeHandler(CommonBaseHandler): """batch edit, 批量修改 """ args_list = [ ArgsMap('path', required=True), ArgsMap('cluster_name', required=True), ] @authenticated def response(self): '''batch edit ''' child_znodes = ZnodeService.get_child_znodes(self.cluster_name, self.path) return self.render('config/znode/batchedit.html', action='/config/znode/batchsave', cluster_name=self.cluster_name, parent_path=self.path, child_znodes=child_znodes)
class ZdZnodeChildHandler(CommonBaseHandler): """tree """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', default="/"), ArgsMap('pid', default=0), ] @authenticated def response(self): """返回指定zookeeper集群的znode信息, 响应ajax请求 """ nodes = [] normalized_path = normalize_path(self.path) if USE_QCONF: ZnodeService.get_znode_tree_from_qconf(self.cluster_name, normalized_path, nodes) else: zoo_client = ZookeeperService.get_zoo_client(self.cluster_name) if not zoo_client: return self.ajax_popup(code=300, msg="连接zookeeper出错!") ZnodeService.get_znode_tree(zoo_client, normalized_path, nodes, current_id=self.pid + 1, parent_id=self.pid) if normalized_path != "/" and len(nodes) <= 1: return self.ajax_popup(code=300, msg="对不起,该节点路径下(%s)无数据!" % self.path) for node in nodes: zk_node = ZdZnode.one(path=node["path"], cluster_name=self.cluster_name) if zk_node: node['type'] = zk_node.type node['business'] = zk_node.business node['data'] = ZookeeperService.get(self.cluster_name, node["path"]) znodes_data = json.dumps(nodes) return znodes_data
class PassPortHandler(CommonBaseHandler): '''search,搜索 ''' args_list = [ ArgsMap('token', required=True), ArgsMap('fromPath', default='/'), ] def response(self): self.set_secure_cookie('token', self.token) url = PASSPORT['publicUrl'] + 'api/findAdminByToken?token=' + quote( self.token) + '&projectKey=' + quote(PASSPORT['projectKey']) resp = json.load(urlopen(url)) self.set_secure_cookie('email', quote(resp['email'].encode('utf-8'))) self.set_secure_cookie('name', quote(resp['name'].encode('utf-8'))) # self.set_cookie('email', (resp['email']).encode('utf-8')) # self.set_cookie('name', (resp['name']).encode('utf-8')) redirecturl = self.request.protocol + '://' + self.request.host + unquote( self.fromPath) self.redirect(redirecturl.encode('utf-8'))
class ZdZnodeSyncstatusHandler(CommonBaseHandler): '''syncstatus ''' args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', required=True) ] @authenticated def response(self): '''客户端同步状况查看 ''' # md5 value in zookeeper znode_value = ZookeeperService.get(self.cluster_name, self.path) #znode_md5_value = hashlib.md5(znode_value).hexdigest() # agent value, idc转换为zookeeper集群名称,方便统一管理 feedbacks = ZdFeedback.select().where( (ZdFeedback.cluster == self.cluster_name) & (ZdFeedback.path == self.path) & (ZdFeedback.deleted == '0')) count = feedbacks.count() equal_counts = 0 not_equal_counts = 0 # check sync_status for feedback in feedbacks: feedback.znode_value = znode_value if znode_value == feedback.value: feedback.sync_status = "相等" equal_counts = equal_counts + 1 else: feedback.sync_status = "不等" not_equal_counts = not_equal_counts + 1 return self.render('config/znode/syncstatus.html', path=self.path, count=count, equal_counts=equal_counts, not_equal_counts=not_equal_counts, cluster=self.cluster_name, feedbacks=feedbacks)
class ZdZookeeperStatHandler(CommonBaseHandler): """stat """ args_list = [ArgsMap('hosts', required=True)] @authenticated def response(self): """stat """ cluster_info = ZookeeperService.get_stat(self.hosts) self.render('config/zookeeper/statdetail.html', cluster_info=cluster_info)
class ZdSnapshotViewHandler(CommonBaseHandler): '''view ''' args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', required=True) ] @authenticated def response(self): '''zookeeper上znode快照的查看 ''' status_mapping = {"0": "备份中", "1": "最近使用"} snapshots = ZdSnapshot.select().where( (ZdSnapshot.cluster_name == self.cluster_name) & (ZdSnapshot.path == self.path) & (ZdSnapshot.deleted == "0")).order_by(ZdSnapshot.create_time) return self.render('config/snapshot/view.html', status_mapping=status_mapping, path=self.path, snapshots=snapshots)
class WsAgentCheckAgentsHandler(CommonBaseHandler): '''check agents ''' args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('agent_register_prefix', default="/qconf/__qconf_register_hosts") ] @authenticated def response(self): '''watch ''' zoo_client = ZookeeperService.get_zoo_client(self.cluster_name) if not zoo_client: return self.ajax_popup(code=300, msg="连接zookeeper出错!") try: zk_agents = zoo_client.get_children(self.agent_register_prefix) except NoNodeError: return self.ajax_popup(code=300, msg="节点路径不存在!") records = ZdQconfAgent.select().where( (ZdQconfAgent.cluster_name == self.cluster_name) & (ZdQconfAgent.deleted == '0') ) mysql_agents = [record.hostname for record in records] # agent在mysql上的统计信息和在zookeeper上注册信息的对比 agents_stat = [] for diff_info in Differ().compare(mysql_agents, zk_agents): agent_name = diff_info[2:] if diff_info[0] == "+": cmp_res = ['无', agent_name] elif diff_info[0] == "-": cmp_res = [agent_name, '无'] else: cmp_res = [agent_name, agent_name] agents_stat.append(cmp_res) return agents_stat
class ZdZnodeEditHandler(CommonBaseHandler): """edit, 修改 """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', required=True), ] @authenticated def response(self): '''edit ''' node_type = data = download_link = "" normalized_path = normalize_path(self.path) znode = ZdZnode.one(path=normalized_path, cluster_name=self.cluster_name, deleted='0') description = "" if znode: #node_type = znode.type description = znode.description # "0"代表普通节点, "1"代表文本节点 #if node_type == "1": # # 文件节点提供下载路径 # download_link = "/config/znode/download?path={0}&cluster_name={1}".format( # self.path, self.cluster_name) #else: # data = ZookeeperService.get(self.cluster_name, self.path) data = ZookeeperService.get(self.cluster_name, self.path) return self.render('config/znode/edit.html', action='/config/znode/save', cluster_name=self.cluster_name, path=normalized_path, data=data, description=description, download_link=download_link)
class ZdZnodeBatchImportHandler(CommonBaseHandler): """fileimport """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('parent_path', required='/'), ArgsMap('uploadfile', required=True), ] @authenticated def response(self): '''fileimport ''' child_znodes = [] keyvalue = [] for line in self.uploadfile.splitlines(): if line.strip() and not line.startswith('#'): keyvalue = line.split('=', 1) node = {"name": keyvalue[0], "value": keyvalue[1]} node["path"] = os.path.join(self.parent_path, keyvalue[0]) child_znodes.append(node) return json.dumps(child_znodes)
class ZdZnodeCopyHandler(CommonBaseHandler): '''copy,复制节点,含子节点 ''' args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('parent_path', required=True), ] @authenticated def response(self): '''copytree ''' index = self.parent_path.rfind('/') path = self.parent_path[index+1:] parent_path = self.parent_path[:index] return self.render('config/znode/copytree.html', action='config/znode/savecopy', cluster_name=self.cluster_name, parent_path=parent_path, path_name=path, path=self.parent_path)
class PassportClearCookie(CommonBaseHandler): '''clear cookie ''' args_list = [ ArgsMap('cookieName', default='token'), ] def response(self): self.clear_cookie('token') self.clear_cookie(self.cookieName) self.clear_cookie('email') self.clear_cookie('name') self.clear_cookie('current_user')
class ZdZnodeShowHandler(CommonBaseHandler): """tree """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('service_name', required=True), ArgsMap('path', default="/"), ] @authenticated def response(self): """返回指定zookeeper集群的znode信息, 响应ajax请求 """ nodes = [] normalized_path = normalize_path(self.path) zoo_client = ZookeeperService.get_zoo_client(self.cluster_name) if not zoo_client: return self.ajax_popup(code=300, msg="连接zookeeper出错!") ZnodeService.get_znode_tree(zoo_client, normalized_path, nodes) #if normalized_path != "/" and len(nodes) <= 1: # return self.ajax_popup( # code=300, msg="对不起,该节点路径下(%s)无数据!" % self.path) for node in nodes: zk_node = ZdZnode.one(path=node["path"], cluster_name=self.cluster_name) if zk_node: node['description'] = zk_node.description node['data'] = ZookeeperService.get(self.cluster_name, node["path"]) znodes_data = json.dumps(nodes) return self.render('config/znode/displaytree.html', cluster_name=self.cluster_name, service_name=self.service_name, znodes_data=znodes_data)
class ZdUserSaveHandler(CommonBaseHandler): '''add, 新增 ''' args_list = [ ArgsMap('id', default=''), ArgsMap('username', default=''), ArgsMap('password', default=''), ] @authenticated def response(self): '''save ''' if self.id: user = ZdUser.one(id=self.id, deleted='0') else: user = ZdUser.one(username=self.username, deleted='0') if user: return self.ajax_popup(code=300, msg="用户名重复!") else: user = ZdUser() if self.id: user.id = self.id if self.username: user.username = self.username if self.password: user.password= self.password service_ids = self.get_arguments("service_id") permission = '' for service_id in service_ids: permission = permission + service_id + ':' user.permission= permission user.save() return self.ajax_ok(forward="/config/user/index")
class ZdZnodeDeleteHandler(CommonBaseHandler): """delete, 删除 """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', required=True), ArgsMap('recursive', default="0"), ] @authenticated def response(self): '''delete ''' # recursive or not recursive = self.recursive == "1" try: # 删除节点在mysql上的数据信息 ZnodeService.delete_znodes(self.cluster_name, self.path, recursive, del_snapshots=False) ZookeeperService.delete(self.cluster_name, self.path, recursive) return self.ajax_ok(close_current=False) except (NotEmptyError, BadArgumentsError) as exc: return self.ajax_popup(code=300, msg="无法删除节点!")
class ZdSnapshotTreeHandler(CommonBaseHandler): """tree """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', default="/"), ] @authenticated def response(self): """返回指定zookeeper集群的znode信息 """ normalized_path = normalize_path(self.path) nodes = SnapshotService.get_snapshot_tree(self.cluster_name, normalized_path) if not nodes: return self.ajax_popup(code=300, msg="对不起,该节点路径下(%s)无快照数据!" % self.path) znodes_data = json.dumps(nodes) return self.render('config/snapshot/displaytree.html', cluster_name=self.cluster_name, znodes_data=znodes_data)
class ZdZnodeBatchSaveHandler(CommonBaseHandler): """save """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('parent_path', required='/'), ArgsMap('business', default=''), ArgsMap('deleted', default=''), ] @authenticated def response(self): '''save ''' keys = self.get_arguments("key") values = self.get_arguments("value") batch_data = [] for node_name, node_value in zip(keys, values): # 过滤掉所有key为空字符串的项 if node_name == '': continue # 检验node_name中是否包含`/`特殊字符 if not ZnodeService.is_node_name_ok(node_name): return self.ajax_popup(code=300, msg="节点名不允许包含特殊字符'/'!") batch_data.append((node_name, node_value)) # 更新字典,需要删除旧字典与新字典的差集项 ZnodeService.delete_znodes_diff_with_keys(self.cluster_name, self.parent_path, keys) # 更新在zookeeper和mysql上存储的配置信息, 同时进行快照备份 ZnodeService.set_batch_znodes(cluster_name=self.cluster_name, parent_path=self.parent_path, batch_data=batch_data, business=self.business) return self.ajax_ok(close_current=True)
class ZdZnodeSyncstatusHandler(CommonBaseHandler): '''syncstatus ''' args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', required=True) ] @authenticated def response(self): '''客户端同步状况查看 ''' # md5 value in zookeeper znode_value = ZookeeperService.get(self.cluster_name, self.path) znode_md5_value = hashlib.md5(znode_value).hexdigest() # agent value, idc转换为zookeeper集群名称,方便统一管理 qconf_feedbacks = ZdQconfFeedback.select().where( (ZdQconfFeedback.idc == self.cluster_name) & (ZdQconfFeedback.path == self.path) & (ZdQconfFeedback.deleted == '0') ) # check sync_status for feedback in qconf_feedbacks: # 只检查agent反馈记录中get_conf命令获取的值, 2代表get_conf命令的反馈记录 if feedback.data_type != '2': continue if znode_md5_value == feedback.md5_value: feedback.sync_status = "已同步" else: feedback.sync_status = "未同步" return self.render('config/znode/syncstatus.html', path=self.path, idc=self.cluster_name, feedbacks=qconf_feedbacks)
class ZdZookeeperSaveHandler(CommonBaseHandler): """save """ args_list = [ ArgsMap('id', default=''), ArgsMap('cluster_name', default=''), ArgsMap('hosts', default=''), ArgsMap('business', default=''), ] @authenticated def response(self): '''add ''' if self.id: # 修改记录 tb_inst = ZdZookeeper.one(id=self.id) else: # 新增记录 zookeeper = ZdZookeeper.one(cluster_name=self.cluster_name, deleted='0') # 检验集群名称是否重复 if zookeeper: return self.ajax_popup(code=300, msg="zookeeper集群名称重复!") else: tb_inst = ZdZookeeper() if self.id: tb_inst.id = self.id if self.cluster_name: tb_inst.cluster_name = self.cluster_name if self.hosts: tb_inst.hosts = self.hosts if self.business: tb_inst.business = self.business tb_inst.save() return self.ajax_ok(forward="/config/zookeeper/index")
class ZdZookeeperDeleteHandler(CommonBaseHandler): """delete, 删除 """ args_list = [ ArgsMap('info_ids', default=''), ] def response(self): '''delete ''' if self.info_ids: id_li = self.info_ids.split(',') for user_id in id_li: ZdZookeeper.one(id=user_id).delete_instance() return self.ajax_ok(close_current=False)
class ZdSnapshotDeleteHandler(CommonBaseHandler): """delete, 删除 """ args_list = [ ArgsMap('info_ids', default=''), ] @authenticated def response(self): '''delete ''' if self.info_ids: id_li = self.info_ids.split(',') for user_id in id_li: ZdSnapshot.one(id=user_id).delete_instance() return self.ajax_ok(close_current=True)
class WsAgentWatchHandler(CommonBaseHandler): '''watch, 观察 ''' args_list = [ ArgsMap('agent_register_prefix', default="/qconf/__qconf_register_hosts") ] @authenticated def response(self): '''watch ''' clusters = ZdZookeeper.select().where(ZdZookeeper.deleted == "0") self.render('config/agent/watch.html', clusters=clusters, agent_register_prefix=self.agent_register_prefix)
class ZdZnodeSaveHandler(CommonBaseHandler): """save """ args_list = [ ArgsMap('cluster_name', required=True), ArgsMap('path', default=''), ArgsMap('parent_path', default=''), ArgsMap('node_name', default=''), ArgsMap('znode_type', default='0'), ArgsMap('data', default=''), ArgsMap('business', default=''), ] @authenticated def response(self): '''save ''' # node_name中不可包含`/`特殊字符 if self.node_name and not ZnodeService.is_node_name_ok(self.node_name): return self.ajax_popup(code=300, msg="节点名不允许包含特殊字符'/'!") zk_path = "" if not self.path: # 新增节点需要进行存在检验 zk_path = os.path.join(self.parent_path, self.node_name) if ZookeeperService.exists(self.cluster_name, zk_path): return self.ajax_popup(code=300, msg="节点已经存在!") else: zk_path = self.path # znode_type, 0代表普通节点, 1代表文件节点 zk_data = "" if self.znode_type == "1": if 'uploadfile' not in self.request.files: return self.ajax_popup(code=300, msg="请选择上传文件!") upload_file = self.request.files['uploadfile'][0] zk_data = upload_file['body'] else: zk_data = self.data # 更新在zookeeper和mysql上存储的配置信息, 同时进行快照备份 ZnodeService.set_znode(cluster_name=self.cluster_name, path=zk_path, data=zk_data, znode_type=self.znode_type, business=self.business) return self.ajax_ok(close_current=True)
class ZdSnapshotDeleteHandler(CommonBaseHandler): """delete, 删除 """ args_list = [ ArgsMap('id', required=True), ] @authenticated def response(self): '''delete ''' try: ZdSnapshot.one(id=self.id).delete_instance() return self.ajax_ok(close_current=True) except Exception as exc: log.error("error occurred while delete snapshot, id: %s\n%s", self.id, str(exc)) return self.ajax_popup(code=300, msg="删除快照出错啦!")
class ZdZookeeperEditHandler(CommonBaseHandler): """edit, 修改 """ args_list = [ ArgsMap('info_ids', default=''), ] def response(self): '''edit ''' if self.info_ids: id_li = self.info_ids.split(',') if len(id_li) != 1: return self.ajax_popup(close_current=False, code=300, msg="请选择单条记录进行修改") record = ZdZookeeper.one(id=id_li[0]) return self.render('config/zookeeper/edit.html', action='/config/zookeeper/save', record=record) else: return self.ajax_popup(close_current=False, code=300, msg="请选择某条记录进行修改")