From 58b2bb92a0058ddcd4aac7910fe651ffd4dfa304 Mon Sep 17 00:00:00 2001 From: Sindweller Date: Mon, 15 Jun 2020 16:41:19 +0800 Subject: [PATCH 1/3] add resource api --- common_utils.py | 3 +- resource.py | 208 ++++++++++++++++++++++++++++++++++++++++++++++-- run.py | 28 +++++++ 3 files changed, 232 insertions(+), 7 deletions(-) diff --git a/common_utils.py b/common_utils.py index 1377192..128afb8 100644 --- a/common_utils.py +++ b/common_utils.py @@ -21,7 +21,6 @@ import settings import xml.parsers as parsers import abc import reports -import subprocess import pam from errors import ReportItemSeverity @@ -419,4 +418,4 @@ def check_auth(username, password): S_PASS = password return True else: - return False + return False \ No newline at end of file diff --git a/resource.py b/resource.py index e1486e7..8eff52e 100644 --- a/resource.py +++ b/resource.py @@ -1,11 +1,209 @@ -''' +# -*-coding:utf-8-*- +# !/usr/bin/python +# coding: iso-8859-15 import common_utils -def get_resouce_add(): - status, output = common_utils.run_cmd("crm_resource --list-standards") - print(output) -''' +def update_resource_attributes(rsc_id, data): + """ + Example data: + { + "category": "primitive", + "actions":[ + { + "interval":"100", + "name":"start" + } + ], + "meta_attributes":{ + "resource-stickiness":"104", + "is-managed":"true", + "target-role":"Started" + }, + "type":"Filesystem", + "id":"iscisi", + "provider":"heartbeat", + "instance_attributes":{ + "device":"/dev/sda1", + "directory":"/var/lib/mysql", + "fstype":"ext4" + }, + "class":"ocf" + } + """ + if data == {} or data is None: + return {'action': False, 'error': _('No input data')} + if "meta_attributes" in data: + meta = data["meta_attributes"] + for key in meta.keys(): + value = meta[key] + cmd_str = "crm_resource -m -r " + rsc_id + " -p " + str(key) + " -v " + str(value) + status, output = common_utils.run_cmd(cmd_str) + if status != 0: + return {'action': False, 'error': _(output)} + if "instance_attributes" in data: + inst = data["instance_attributes"] + cmd_str = "crm_resource --resource" + rsc_id + "--set-parameter " + for key in inst.keys(): + value = inst[key] + cmd = cmd_str + str(key) + " --parameter-value " + str(value) + status, output = common_utils.run_cmd(cmd) + if status != 0: + return {'action': False, 'error': _(output)} + if data["category"] == "group": #组资源 + """ + { + "id":"group1", + "category":"group", + "rscs":["iscisi", "test1" ], + "meta_attributes":{ + "target-role":"Started" + } + } + """ + rscs = data["rscs"] + cmd_str = "pcs resource group add" + for r in rscs: + cmd_str = cmd_str + " " + r + common_utils.run_cmd(cmd_str) + if status != 0: + return {'action': False, 'error': _(output)} + return {'action': True, 'info': _('Update resource attributes Success')} + +def create_resource(data): #创建资源 + """ + { + "category": "primitive", //资源范畴 + "meta_attributes":{ + "target-role":"Stopped" + }, + "type":"CTDB", //资源类型 + "class":"ocf", + "provider":"heartbeat", + "instance_attributes":{ + "ctdb_recovery_lock":"lock" + }, + "id":"test1" + } + """ + if data == {} or data is None: + return {'action': False, 'error': _('No input data')} + rsc_id = data["id"] + cate = str(data["category"]) + #判断category + if cate == "primitive": + rsc_type = "\" type=\"" + str(data["type"]) + rsc_class = "\" class=\"" + str(data["class"]) + provider = "\" provider=\"" + str(data["provider"]) + cmd = "cibadmin --create -o resources --xml-text '<" + cmd_str = cmd + cate + " id=\"" + rsc_id + rsc_class + rsc_type + provider + "\">'" + + if cate == "group": + """ + { + "category": "group", + "id":"tomcat_group", + "rscs":[ + "tomcat6", + "tomcat7" + ], + "meta_attributes":{ + "target-role":"Stopped" + } + } + """ + rscs = data["rscs"] + cmd_str = "pcs resource group add" + for r in rscs: + cmd_str = cmd_str + " " + r + + if cate == "clone": #无法指定克隆资源的名字,自动生成为xx-clone + """ + { + "category": "clone", + "id":"test5", + "rsc_id":"test4", + "meta_attributes":{ + "target-role":"Stopped" + } + } + """ + ori_id = data["rsc_id"] + cmd_str = "pcs resource clone " + ori_id + + #通用步骤 + status, output = common_utils.run_cmd(cmd_str) + if status != 0: + return {'action': False, 'error': _(output)} + update_resource_attributes(rsc_id, data) + return {"action":True, "info":"Add " + str(cate) + " resource success"} + + +def resource_action(rsc_id, action, data): + """ + operations: start stop delete cleanup + Example input: + {} + """ + cmd = "crm_resource --resource " + if str(action) == "start": + cmd_str = cmd + str(rsc_id) + " --set-parameter target-role --meta --parameter-value started" + if str(action) == "stop": + cmd_str = cmd + str(rsc_id) + " --set-parameter target-role --meta --parameter-value stopped" + if str(action) == "delete": + cmd_str = "cibadmin --delete --xml-text ''" + cmd_first = cmd + str(rsc_id) + " -delete -t primitive\n" + status, output = common_utils.run_cmd(cmd_first) + if status != 0: + return {'action': False, 'error': _(output)} + if str(action) == "cleanup": + cmd_str = cmd + str(rsc_id) + " --clear" + """ + Example input: + migrate(迁移): + {"is_force": True, "to_node": "ns187", "period": "PYMDTHM3S"} + """ + if str(action) == "migrate": #迁移节点 + to_node = str(data["to_node"]) + cmd_str = "crm_resource --resource " + str(rsc_id) + " --move --node " + to_node #period没有用到 + """ + unmigrate: + {"rsc_id": "kk1", "is_all_rscs": False} + """ + if str(action) == "unmigrate":#迁离当前节点 + cmd_str = "crm_resource --resource " + str(data["rsc_id"]) + " --move" + #is_all_rscs命令中没有找到相应选项 + """ + location: + {"node_level": [{"node": "ns187", "level": "Master Node"}, {"node": "ns188", "level": "Slave 1"}]} + """ + if str(action) == "location": + + #cmd_str = "pcs constraint location " + + return + + """ + colocation: + {"same_node": ["test1234"],"diff_node": ["group_tomcat"]} + """ + if str(action) == "colocation": + sn = str(data["same_node"]) + dn = str(data["diff_node"]) + cmd_str = "pcs constraint colocation add " + dn + " with " + sn + + """ + {"before_rscs": ["test1234"],"after_rscs": ["group-fs-ps"]} + """ + if str(action) == "order": + br = str(data["before_rscs"]) + ar = str(data["after_rscs"]) + cmd_str = "pcs constraint order start " + br + " then " + ar + + #相同操作 + status, output = common_utils.run_cmd(cmd_str) + if status != 0: + return {'action': False, 'error': _(output)} + return {"action":True, "info":"Action on resource success"} diff --git a/run.py b/run.py index 20a5832..2a48e33 100644 --- a/run.py +++ b/run.py @@ -10,6 +10,7 @@ import common_utils import re from flask import jsonify import node +import resource API_VERSION = "v1" app_name = "ha-api" @@ -90,6 +91,33 @@ def create_app(test_config=None): if request.method == 'GET': return json.dumps(node.get_node_info()) + #资源 + @app.route('/api/' + API_VERSION + '/haclusters/1/resources', methods=['GET', 'POST', 'PUT']) + def resources_ops(): + if request.method == 'POST': #添加资源 primitive group clone + data = request.json + ret = resource.create_resource(data) + return ret + else: + return + #修改资源 + @app.route('/api/' + API_VERSION + '/haclusters/1/resources/', methods=['GET', 'POST', 'PUT']) + def resource_ops_by_id(rsc_id): + if request.method == 'GET': + return json.dumps(resource.get_resource_info_by_rsc_id(rsc_id)) + if request.method == 'PUT': + data = request.json + ret = resource.update_resource_attributes(rsc_id, data) + return json.dumps(ret) + + #单个资源操作start、stop、delete、cleanup、migrate、unmigrate、location、order、colocation + @app.route('/api/' + API_VERSION + '/haclusters/1/resources//', methods=['GET', 'POST', 'PUT']) + def resource_ops(rsc_id, action): + if request.method == 'PUT': #按id修改资源 + data = request.json + ret = resource.resource_action(rsc_id, action, data) + return json.dumps(ret) + #根据id取单个node @app.route('/api/' + API_VERSION + '/haclusters/1/nodes/', methods=['GET', 'POST', 'PUT']) def get_nodeid(nodeid): -- Gitee From 1327d51370e0ddcd2a6b86ffd1e89bd3e400d6d7 Mon Sep 17 00:00:00 2001 From: Sindweller Date: Tue, 16 Jun 2020 17:36:10 +0800 Subject: [PATCH 2/3] append --- common_utils.py | 36 ++++++++++++++++++ resource.py | 99 +++++++++++++++++++++++++++++++++++-------------- run.py | 4 +- 3 files changed, 110 insertions(+), 29 deletions(-) diff --git a/common_utils.py b/common_utils.py index 128afb8..4b654fa 100644 --- a/common_utils.py +++ b/common_utils.py @@ -66,6 +66,23 @@ def get_cluster_property_from_xml(etree_el): prop["longdesc"] = "" return prop +def get_resource_info_from_xml(cl, et):#从xml格式中获取单个资源信息 + if cl == "primitive": + prop = { + "id": et.get("id", ""), + "category": et.get("category", ""), + "class": et.get("class", ""), + "type": et.get("type", ""), + "provider": et.get("provider", "") + } + if cl == "meta" or cl == "inst": + prop = {} + for item in et.findall("./nvpair"): + name = item.get("name") + value = item.get("value") + prop[name] = value + return prop + def shell_quote(s): """Return a shell-escaped version of the string *s*.""" if not s: @@ -405,6 +422,25 @@ def get_cluster_properties_definition(): )) return data +def get_resource_info(xml_data): + etree = ET.fromstring(xml_data) + try: + data = {} + #获取primitive属性 + data = get_resource_info_from_xml("primitive", etree) + #获取meta_attributes + for e in etree.findall("./meta_attributes"): + prop = get_resource_info_from_xml("meta", e) + data["meta_attributes"] = prop + #获取instance_attributes + for e in etree.findall("./instance_attributes"): + prop = get_resource_info_from_xml("inst", e) + data["instance_attributes"] = prop + + except ET.ParseError as e: + err("Unable to parse resource information: " + e) + return data + def run_cmd(cmd): try: status, output = subprocess.getstatusoutput(cmd) diff --git a/resource.py b/resource.py index 8eff52e..c1ff88c 100644 --- a/resource.py +++ b/resource.py @@ -1,7 +1,7 @@ # -*-coding:utf-8-*- # !/usr/bin/python # coding: iso-8859-15 -import common_utils +import common_utils def update_resource_attributes(rsc_id, data): """ @@ -37,7 +37,7 @@ def update_resource_attributes(rsc_id, data): meta = data["meta_attributes"] for key in meta.keys(): value = meta[key] - cmd_str = "crm_resource -m -r " + rsc_id + " -p " + str(key) + " -v " + str(value) + cmd_str = "crm_resource -m -r " + str(rsc_id) + " -p " + str(key) + " -v " + str(value) status, output = common_utils.run_cmd(cmd_str) if status != 0: return {'action': False, 'error': _(output)} @@ -91,7 +91,7 @@ def create_resource(data): #创建资源 """ if data == {} or data is None: return {'action': False, 'error': _('No input data')} - rsc_id = data["id"] + rsc_id = str(data["id"]) cate = str(data["category"]) #判断category if cate == "primitive": @@ -100,8 +100,8 @@ def create_resource(data): #创建资源 provider = "\" provider=\"" + str(data["provider"]) cmd = "cibadmin --create -o resources --xml-text '<" cmd_str = cmd + cate + " id=\"" + rsc_id + rsc_class + rsc_type + provider + "\">'" - - if cate == "group": + print(cmd_str) + elif cate == "group": """ { "category": "group", @@ -116,11 +116,11 @@ def create_resource(data): #创建资源 } """ rscs = data["rscs"] - cmd_str = "pcs resource group add" + cmd_str = "pcs resource group add " + rsc_id for r in rscs: - cmd_str = cmd_str + " " + r + cmd_str = cmd_str + " " + str(r) - if cate == "clone": #无法指定克隆资源的名字,自动生成为xx-clone + elif cate == "clone": """ { "category": "clone", @@ -130,15 +130,17 @@ def create_resource(data): #创建资源 "target-role":"Stopped" } } + *id is unused """ - ori_id = data["rsc_id"] + ori_id = str(data["rsc_id"]) cmd_str = "pcs resource clone " + ori_id - #通用步骤 - status, output = common_utils.run_cmd(cmd_str) - if status != 0: - return {'action': False, 'error': _(output)} - update_resource_attributes(rsc_id, data) + #通用步骤 + status, output = common_utils.run_cmd(cmd_str) + if status != 0: + return {'action': False, 'error': _(output)} + #更新属性 + update_resource_attributes(rsc_id, data) return {"action":True, "info":"Add " + str(cate) + " resource success"} @@ -154,11 +156,10 @@ def resource_action(rsc_id, action, data): if str(action) == "stop": cmd_str = cmd + str(rsc_id) + " --set-parameter target-role --meta --parameter-value stopped" if str(action) == "delete": - cmd_str = "cibadmin --delete --xml-text ''" - cmd_first = cmd + str(rsc_id) + " -delete -t primitive\n" - status, output = common_utils.run_cmd(cmd_first) - if status != 0: - return {'action': False, 'error': _(output)} + if get_resource_category(rsc_id) == "group": + cmd_str = "crm_resource -D -r " + str(rsc_id) + " -t group" + else: + cmd_str = "crm_resource --resource " + str(rsc_id) + " --delete -t primitive" if str(action) == "cleanup": cmd_str = cmd + str(rsc_id) + " --clear" """ @@ -166,24 +167,40 @@ def resource_action(rsc_id, action, data): migrate(迁移): {"is_force": True, "to_node": "ns187", "period": "PYMDTHM3S"} """ - if str(action) == "migrate": #迁移节点 - to_node = str(data["to_node"]) - cmd_str = "crm_resource --resource " + str(rsc_id) + " --move --node " + to_node #period没有用到 + if str(action) == "migrate": + #迁移节点 + duration = "" + if "period" in data.keys() and data["period"] != "PYMDT0HMS": + duration = str(data["period"]) #持续时间 + to_node = str(data["to_node"]) #迁移到指定节点 + #提取duration + duration = duration.replace('THMS', '').replace('MS', 'M').replace('HM', 'H').replace('TH', 'T') + duration = duration.replace('PYMD', 'P').replace('MD', 'M').replace('YM', 'Y').replace('PY', 'P') + cmd_head = "crm_resource --resource " + cmd_str = cmd_head + str(rsc_id) + " --move -N " + to_node + " -u " + duration + """ unmigrate: {"rsc_id": "kk1", "is_all_rscs": False} """ - if str(action) == "unmigrate":#迁离当前节点 + if str(action) == "unmigrate": + #迁离当前节点 + #is_all_rscs = str(data["is_all_rscs"]) cmd_str = "crm_resource --resource " + str(data["rsc_id"]) + " --move" - #is_all_rscs命令中没有找到相应选项 + """ location: {"node_level": [{"node": "ns187", "level": "Master Node"}, {"node": "ns188", "level": "Slave 1"}]} """ if str(action) == "location": - - #cmd_str = "pcs constraint location " + - return + mn = "" + sn = "" + for item in data["node_level"]: + if item["level"] == "Master Node": + mn = str(item["node"]) + else: + sn = str(item["node"]) + cmd_str = "pcs constraint location " + sn + " prefers " + mn """ colocation: @@ -206,4 +223,30 @@ def resource_action(rsc_id, action, data): status, output = common_utils.run_cmd(cmd_str) if status != 0: return {'action': False, 'error': _(output)} - return {"action":True, "info":"Action on resource success"} + return {"action": True, "info":"Action on resource success"} + +def get_resource_info_by_rsc_id(rsc_id): + cmd_str = "crm_resource --resource " + str(rsc_id) + " --query-xml" + status, output = common_utils.run_cmd(cmd_str) #输出为xml格式 + if status != 0: + return {'action': False, 'error': _(output)} + #format + xml_data = output.split(":\n") + xml = xml_data[1] + data = common_utils.get_resource_info(xml) #获取资源信息并按格式输出 + data["id"] = str(rsc_id) + ret = {} + if data is not None: + ret['data'] = data + ret['action'] = True + else: + ret['action'] = False + ret['error'] = data + return ret + +def get_all_resource_info(): + cmd_str = "crm_resource --list" + status, output = common_utils.run_cmd(cmd_str) + if status != 0: + return "false" + return output \ No newline at end of file diff --git a/run.py b/run.py index 2a48e33..2c33852 100644 --- a/run.py +++ b/run.py @@ -17,7 +17,7 @@ app_name = "ha-api" gettext.bindtextdomain(app_name) gettext.textdomain(app_name) gettext.install(app_name, "/usr/share/locale") -gettext.translation(app_name, "/usr/share/locale", languages=["zh_CN"]).install(True) +#gettext.translation(app_name, "/usr/share/locale", languages=["zh_CN"]).install(True) def create_app(test_config=None): @@ -94,6 +94,8 @@ def create_app(test_config=None): #资源 @app.route('/api/' + API_VERSION + '/haclusters/1/resources', methods=['GET', 'POST', 'PUT']) def resources_ops(): + if request.method == 'GET': + return resource.get_all_resource_info() if request.method == 'POST': #添加资源 primitive group clone data = request.json ret = resource.create_resource(data) -- Gitee From accc2982edf23ddde13f9ff0d552c702bd9e3ed0 Mon Sep 17 00:00:00 2001 From: Sindweller Date: Wed, 17 Jun 2020 09:54:41 +0800 Subject: [PATCH 3/3] finish single resource PUT, add GET --- common_utils.py | 28 +++++++++++++++++++++------- resource.py | 26 ++++++++++++++++++-------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/common_utils.py b/common_utils.py index 4b654fa..67ba738 100644 --- a/common_utils.py +++ b/common_utils.py @@ -66,7 +66,12 @@ def get_cluster_property_from_xml(etree_el): prop["longdesc"] = "" return prop -def get_resource_info_from_xml(cl, et):#从xml格式中获取单个资源信息 +def get_resource_info_from_xml(cl, et): + #从xml格式中获取单个资源信息 + if cl == "group": + prop = [] + for e in et.findall("primitive"): + prop.append(e.get("id", "")) if cl == "primitive": prop = { "id": et.get("id", ""), @@ -75,6 +80,9 @@ def get_resource_info_from_xml(cl, et):#从xml格式中获取单个资源信息 "type": et.get("type", ""), "provider": et.get("provider", "") } + if cl == "clone": + et = et.find("primitive") + prop = et.get("id", "") if cl == "meta" or cl == "inst": prop = {} for item in et.findall("./nvpair"): @@ -422,21 +430,27 @@ def get_cluster_properties_definition(): )) return data -def get_resource_info(xml_data): +def get_resource_info(ct, xml_data): etree = ET.fromstring(xml_data) try: data = {} - #获取primitive属性 - data = get_resource_info_from_xml("primitive", etree) + #根据分类 + if ct == "primitive": + data = get_resource_info_from_xml("primitive", etree) + if ct == "group": + data["rscs"] = get_resource_info_from_xml("group", etree) + if ct == "clone": + data["rsc_id"] = get_resource_info_from_xml("clone", etree) #获取meta_attributes - for e in etree.findall("./meta_attributes"): + e = etree.find("meta_attributes") + if e: prop = get_resource_info_from_xml("meta", e) data["meta_attributes"] = prop #获取instance_attributes - for e in etree.findall("./instance_attributes"): + e = etree.find("instance_attributes") + if e: prop = get_resource_info_from_xml("inst", e) data["instance_attributes"] = prop - except ET.ParseError as e: err("Unable to parse resource information: " + e) return data diff --git a/resource.py b/resource.py index c1ff88c..2067ba5 100644 --- a/resource.py +++ b/resource.py @@ -1,9 +1,11 @@ # -*-coding:utf-8-*- # !/usr/bin/python # coding: iso-8859-15 -import common_utils +import common_utils +import xml.etree.ElementTree as ET def update_resource_attributes(rsc_id, data): + #修改资源meta_attributes和instance_attributes属性 """ Example data: { @@ -51,7 +53,8 @@ def update_resource_attributes(rsc_id, data): if status != 0: return {'action': False, 'error': _(output)} - if data["category"] == "group": #组资源 + if data["category"] == "group": + #组资源加入新资源 """ { "id":"group1", @@ -63,9 +66,9 @@ def update_resource_attributes(rsc_id, data): } """ rscs = data["rscs"] - cmd_str = "pcs resource group add" + cmd_str = "pcs resource group add " + str(data["id"]) for r in rscs: - cmd_str = cmd_str + " " + r + cmd_str = cmd_str + " " + str(r) common_utils.run_cmd(cmd_str) if status != 0: return {'action': False, 'error': _(output)} @@ -73,7 +76,8 @@ def update_resource_attributes(rsc_id, data): return {'action': True, 'info': _('Update resource attributes Success')} -def create_resource(data): #创建资源 +def create_resource(data): + #创建新资源,分三种:primitive, group, clone """ { "category": "primitive", //资源范畴 @@ -100,7 +104,7 @@ def create_resource(data): #创建资源 provider = "\" provider=\"" + str(data["provider"]) cmd = "cibadmin --create -o resources --xml-text '<" cmd_str = cmd + cate + " id=\"" + rsc_id + rsc_class + rsc_type + provider + "\">'" - print(cmd_str) + elif cate == "group": """ { @@ -145,6 +149,7 @@ def create_resource(data): #创建资源 def resource_action(rsc_id, action, data): + #单个资源操作 """ operations: start stop delete cleanup Example input: @@ -156,15 +161,17 @@ def resource_action(rsc_id, action, data): if str(action) == "stop": cmd_str = cmd + str(rsc_id) + " --set-parameter target-role --meta --parameter-value stopped" if str(action) == "delete": + #通过id查询category if get_resource_category(rsc_id) == "group": cmd_str = "crm_resource -D -r " + str(rsc_id) + " -t group" else: cmd_str = "crm_resource --resource " + str(rsc_id) + " --delete -t primitive" if str(action) == "cleanup": cmd_str = cmd + str(rsc_id) + " --clear" + """ Example input: - migrate(迁移): + migrate: {"is_force": True, "to_node": "ns187", "period": "PYMDTHM3S"} """ if str(action) == "migrate": @@ -233,8 +240,11 @@ def get_resource_info_by_rsc_id(rsc_id): #format xml_data = output.split(":\n") xml = xml_data[1] - data = common_utils.get_resource_info(xml) #获取资源信息并按格式输出 + etree = ET.fromstring(xml) + ct = etree.tag + data = common_utils.get_resource_info(ct, xml) #获取资源信息并按格式输出 data["id"] = str(rsc_id) + data["category"] = str(ct) ret = {} if data is not None: ret['data'] = data -- Gitee