def sse_worker(product): # job_pattern = re.compile('salt/job/\d+/ret/') mine_pattern = re.compile(r'"fun": "mine.update"') saltutil_pattern = re.compile(r'"fun": "saltutil.find_job"') running_pattern = re.compile(r'"fun": "saltutil.running"') lookup_pattern = re.compile(r'"fun": "runner.jobs.lookup_jid"') event_pattern = re.compile(r'"tag": "salt/event/new_client"') salt_api = salt_api_for_product(product) event_response = salt_api.events() client = sseclient.SSEClient(event_response) for event in client.events(): if mine_pattern.search(event.data): pass elif saltutil_pattern.search(event.data): pass elif running_pattern.search(event.data): pass elif lookup_pattern.search(event.data): pass elif event_pattern.search(event.data): pass else: print(event.data) event_dict = ast.literal_eval(event.data.replace('true', 'True').replace('false', 'False'). replace('null', '""')) event_dict['data']['_stamp'] = utc_to_local(event_dict['data']['_stamp'] + "Z") event_dict['data']['product_id'] = product db = DB() db.insert("event", json.dumps(event_dict, ensure_ascii=False)) db.close_mysql()
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: if args["minion"]: if args["item"]: result = salt_api.grain(args["minion"], args["item"]) if result: result.update({"status": True, "message": ""}) return result return { "status": False, "message": "The specified minion does not exist" }, 404 else: result = salt_api.grains(args["minion"]) if result: # create_grains(args["product_id"]) data = {"data": result, "status": True, "message": ""} return data return { "status": False, "message": "The specified minion does not exist" }, 404 else: return { "status": False, "message": "The specified minion parameter does not exist" }, 400
def post(self): logger.info("SinglePing") args = parser.parse_args() target_id = args['target_id'] cipher = args['cipher'] # 获得所需参数minion_id、product_id、target_ip db = DB() state, result = db.select_by_id('target', target_id) target_ip = result['IP'] host_id = result['host_id'] state, result = db.select_by_id('host', host_id) minion_id = result['minion_id'] product_id = result['product_id'] salt_api = salt_api_for_product(product_id) command = 'snmpwalk -v 2c -t 0.5 -c \'' + cipher + '\' ' + target_ip + ' 1.3.6.1.2.1.1.1' logger.info('command:' + command) sysDescr = salt_api.shell_remote_execution([minion_id], command) response_data = {} if str(sysDescr[minion_id]).__contains__("Timeout") | str( sysDescr[minion_id]).__contains__("Unknown"): response_data['status'] = '设备网络不通' else: response_data['status'] = "设备正常" response_data['sysDescr'] = str(sysDescr[minion_id]) return {"status": True, "message": '成功', "data": response_data}, 200
def delete(self): args = parser.parse_args() db = DB() status, result = db.select_by_id("product", args["product_id"]) db.close_mysql() if status is True: if result: product = eval(result[0][0]) else: return {"status": False, "message": "%s does not exist" % args["product_id"]} else: return {"status": False, "message": result} salt_api = salt_api_for_product(args["product_id"]) user = g.user_info["username"] if isinstance(salt_api, dict): return salt_api, 500 else: if args["action"] == "kill" and args["jid"]: kill = "salt %s saltutil.kill_job %s" % (product.get("salt_master_id"), args["jid"]) try: result = salt_api.shell_remote_execution(product.get("salt_master_id"), kill) audit_log(user, args["jid"], args["product_id"], "job id", "kill") return {"status": True, "message": result}, 200 except Exception as e: return {"status": False, "message": str(e)}, 500 else: return {"status": False, "message": "The specified job id does not exist or arguments error"}, 400
def post(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) user = g.user_info["username"] if isinstance(salt_api, dict): return salt_api else: result_list = [] if args["action"] and args["minion_id"]: if args["action"] == "accept": for minion in args["minion_id"]: result = salt_api.accept_key(minion) result_list.append({minion: result}) audit_log(user, minion, args["product_id"], "minion", "accept") return {"status": result_list, "message": ""}, 200 if args["action"] == "reject": for minion in args["minion_id"]: result = salt_api.reject_key(minion) result_list.append({minion: result}) audit_log(user, minion, args["product_id"], "minion", "reject") return {"status": result_list, "message": ""}, 200 if args["action"] == "delete": for minion in args["minion_id"]: result = salt_api.delete_key(minion) result_list.append({minion: result}) audit_log(user, minion, args["product_id"], "minion", "delete") return {"status": result_list, "message": ""}, 200 else: return {"status": False, "message": "Missing required parameter in the JSON body or " "the post body or the query string"}, 400
def get(self): status = [] args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: cherry_result = salt_api.stats() if isinstance(cherry_result, dict): if cherry_result.get("CherryPy Applications").get( "Enabled") is True: status.append({"name": "Cherry", "status": "UP"}) else: status.append({"name": "Cherry", "status": "Down"}) master_status = salt_api.list_all_key() if master_status.get("status") is not False: status.append({"name": "Master", "status": "UP"}) else: status.append({"name": "Master", "status": "Down"}) echo = os.popen("ps aux|grep app.celery|grep -v grep |wc -l") try: num = int(echo.readline().split('\n')[0]) if num == 0: status.append({"name": "Celery", "status": "Down"}) else: status.append({"name": "Celery", "status": "Up"}) except Exception as e: status.append({"name": "Celery", "status": "Down"}) logger.error("Get celery error: %s" % e) return {"data": status, "status": True, "message": ""}, 200
def post(self): logger.info("PingList") args = parser.parse_args() db = DB() host_id = args['host_id'] cipher = args['cipher'] state, result = db.select('host', "where data -> '$.id'='%s'" % host_id) minion_id = result[0]['minion_id'] logger.info('minion_id:' + minion_id) product_id = result[0]['product_id'] salt_api = salt_api_for_product(product_id) state, targets = db.select('target', "where data -> '$.host_id'='%s'" % host_id) targets_not = [] thread_pool = ThreadPoolExecutor(max_workers=10, thread_name_prefix="target_") futures = [] for target in targets: future = thread_pool.submit(pingTarget, target, minion_id, salt_api, cipher) futures.append(future) thread_pool.shutdown(wait=True) for future in futures: result = future.result() logger.info(str(result['status'])) if str(result['status']).__contains__("Timeout") | str( result['status']).__contains__("Unknown"): targets_not.append(result["target"]) return {"status": True, "message": '配置发送成功', "data": targets_not}, 200
def get(self): user = g.user_info["username"] args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) minions = [] minions_mysql = [] if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.list_all_key() if result: if result.get("status") is False: return result, 500 for minion in result.get("minions"): minions.append(minion) # 同步产品线下的Grains Grains.create_grains(minions, args["product_id"], user) db = DB() status, result = db.select("grains", "where data -> '$.product_id'='%s'" % args["product_id"]) db.close_mysql() if status is True and result: for i in result: minions_mysql.append(i.get("id")) # 对比数据库中的minion与已经同意的minion的不同,删掉数据库中多余的minion diff = list(set(minions_mysql).difference(minions)) Grains.delete_grains(diff, args["product_id"], user) return {"status": True, "message": ""}, 200
def create_grains(minion_list, product_id, user): salt_api = salt_api_for_product(product_id) if isinstance(salt_api, dict): return salt_api, 500 db = DB() for minion in minion_list: select_status, select_result = db.select( "grains", "where data -> '$.id'='%s' and data -> " "'$.product_id'='%s'" % (minion, product_id)) grains = salt_api.grains(minion) grains[minion].update({"product_id": product_id}) if select_status is True: if len(select_result) > 1: for m in select_result: db.delete_by_id("grains", m["id"]) insert_status, insert_result = db.insert( "grains", json.dumps(grains[minion], ensure_ascii=False)) if insert_status is not True: logger.error("Add Grains error: %s" % insert_result) elif len(select_result) == 1: update_status, update_result = db.update_by_id( "grains", json.dumps(grains[minion], ensure_ascii=False), select_result[0]["id"]) if update_status is not True: logger.error("Update Grains error: %s" % update_result) else: insert_status, insert_result = db.insert( "grains", json.dumps(grains[minion], ensure_ascii=False)) if insert_status is not True: logger.error("Add Grains error: %s" % insert_result) db.close_mysql()
def post(self): args = parser.parse_args() command = args["command"] if not command: return { "status": False, "message": "Missing required parameter in the JSON body or " "the post body or the query string" }, 400 minion_id = args["minion_id"] salt_api = salt_api_for_product(args["product_id"]) user_info = g.user_info if isinstance(salt_api, dict): return salt_api, 500 acl_list = user_info["acl"] # 验证 acl status = verify_acl(acl_list, command) # acl deny 验证完成后执行命令 if status["status"]: host = ",".join(minion_id) result = salt_api.shell_remote_execution(host, command) # 记录历史命令 db = DB() cmd_history = { "user_id": user_info["id"], "command": command, "minion_id": minion_id, "result": result, "time": time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) } db.insert("cmd_history", json.dumps(cmd_history, ensure_ascii=False)) db.close_mysql() audit_log(user_info["username"], minion_id, args["product_id"], "minion", "shell") minion_count = 'Total: ' + str(len(minion_id)) cmd_succeed = 'Succeed: ' + str(len(result)) cmd_failure = 'Failure: ' + str(len(minion_id) - len(result)) command = 'Command: ' + command succeed_minion = [] for i in result: succeed_minion.append(i) failure_minion = 'Failure_Minion: ' + ','.join( list(set(minion_id).difference(set(succeed_minion)))) return { 'result': result, 'command': command, 'line': "#" * 50, 'minion_count': minion_count, 'cmd_succeed': cmd_succeed, 'cmd_failure': cmd_failure, 'failure_minion': failure_minion, "status": True, "message": "" }, 200 else: return status, 500
def post(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) user = g.user_info["username"] if isinstance(salt_api, dict): return salt_api, 500 else: if args["action"] == "kill" and args["jid"] and args["minion"]: for minion in args["minion"]: for minion_id, ppid in minion.items(): # 获取pgid 并杀掉 kill_ppid_pid = r'''ps -eo pid,pgid,ppid,comm |grep %s |grep salt-minion | awk '{print "kill -- -"$2}'|sh''' % ppid try: # kill_job = "salt %s saltutil.kill_job %s" % (minion_id, args["jid"]) # result = salt_api.shell_remote_execution(product.get("salt_master_id"), kill_job) # audit_log(user, args["jid"], args["product_id"], "job id", "kill") # logger.info("kill %s %s return: %s" % (minion, args["jid"], result)) audit_log(user, args["jid"], args["product_id"], "job id", "kill") # 通过kill -- -pgid 删除salt 相关的父进程及子进程 pid_result = salt_api.shell_remote_execution(minion_id, kill_ppid_pid) logger.info("kill %s %s return: %s" % (minion, kill_ppid_pid, pid_result)) except Exception as e: logger.info("kill %s %s error: %s" % (minion, args["jid"], e)) return {"status": True, "message": ""}, 200 else: return {"status": False, "message": "The specified jid or action or minion_id " "parameter does not exist"}, 400
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) minion_key = [] if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.list_all_key() if isinstance(result, dict): if result.get("status") is False: return result, 500 for minions_rejected in result.get("minions_rejected"): minion_key.append({ "minions_status": "Rejected", "minions_id": minions_rejected }) for minions_denied in result.get("minions_denied"): minion_key.append({ "minions_status": "Denied", "minions_id": minions_denied }) for minions in result.get("minions"): minion_key.append({ "minions_status": "Accepted", "minions_id": minions }) for minions_pre in result.get("minions_pre"): minion_key.append({ "minions_status": "Unaccepted", "minions_id": minions_pre }) else: logger.error("Get minion key error: %s" % result) return {"data": minion_key, "status": True, "message": ""}, 200
def post(self): args = parser.parse_args() sls = args["sls"] if not sls: return { "status": False, "message": "The specified sls parameter does not exist" }, 400 # 去掉后缀 sls = sls.replace(".sls", "") minion_id = args["minion_id"] salt_api = salt_api_for_product(args["product_id"]) user_info = g.user_info audit_log(user_info["username"], minion_id, args["product_id"], "minion", "sls") if isinstance(salt_api, dict): return salt_api, 500 result = salt_api.target_deploy(minion_id, sls) db = DB() cmd_history = { "user_id": user_info["id"], "product_id": args["product_id"], "command": args["sls"], "type": "sls", "minion_id": minion_id, "result": result, "time": time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) } db.insert("cmd_history", json.dumps(cmd_history, ensure_ascii=False)) db.close_mysql() audit_log(user_info["username"], minion_id, args["product_id"], "minion", "sls") minion_count = str(len(minion_id)) cmd_succeed = str(len(result)) cmd_failure = str(len(minion_id) - len(result)) succeed_minion = [] for i in result: succeed_minion.append(i) failure_minion = ','.join( list(set(minion_id).difference(set(succeed_minion)))) if result.get("status") is False: status = False message = result.get("message") else: status = True message = "" return { "data": { "result": result, "command": args["sls"], "total": minion_count, "succeed": cmd_succeed, "failure": cmd_failure, "failure_minion": failure_minion }, "status": status, "message": message }, 200
def get(self, job_id): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.jobs_info(job_id) return result, 200
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.runner_status("status") return result, 200
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.list_all_key() return result, 200
def post(self): args = parser.parse_args() command = args["command"] if not command: return {"status": False, "message": "The specified command parameter does not exist"}, 400 minion_id = args["minion_id"] salt_api = salt_api_for_product(args["product_id"]) user_info = g.user_info if isinstance(salt_api, dict): return salt_api, 500 acl_list = user_info["acl"] # 验证 acl status = verify_acl(acl_list, command) # acl deny 验证完成后执行命令 if status["status"]: result = salt_api.shell_remote_execution(minion_id, command) # 记录历史命令 db = DB() cmd_history = { "user_id": user_info["id"], "product_id": args["product_id"], "command": command, "type": "shell", "minion_id": minion_id, "result": result, "time": time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) } db.insert("cmd_history", json.dumps(cmd_history, ensure_ascii=False)) db.close_mysql() audit_log(user_info["username"], minion_id, args["product_id"], "minion", "shell") minion_count = str(len(minion_id)) result_len = len(result) for k, v in result.items(): if not v: result_len -= 1 cmd_succeed = str(result_len) cmd_failure = str(len(minion_id) - result_len) succeed_minion = [] for i in result: succeed_minion.append(i) failure_minion = ','.join( list(set(minion_id).difference(set(succeed_minion)))) if result.get("status") is False: status = False message = result.get("message") else: status = True message = "" return {"data": {"result": result, "command": command, "total": minion_count, "succeed": cmd_succeed, "failure": cmd_failure, "failure_minion": failure_minion}, "status": status, "message": message}, 200 else: return status, 500
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.stats() result.update({"status": True, "message": ""}) return result, 200
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.list_all_key() result_dict = eval(result) result_dict.get("minions").extend(result_dict.get("minions_rejected")) all_minion = result_dict.get("minions")
def post(self): args = parser.parse_args() desc_path = args["desc_path"] target = args["target"] file_path = args["file_path"] db = DB() target_minion_list = [] for group_id in target: state, group = db.select_by_id('groups', group_id) if state: target_minion_list = target_minion_list + group["minion"] else: return {"status": False, "message": 'select group error'}, 500 logger.info("target_minion_list:" + str(target_minion_list)) state, product_result = db.select_by_id('product', args["product_id"]) master_id = product_result['salt_master_id'] salt_api = salt_api_for_product(args['product_id']) state, project_result = db.select_by_id('projects', args["project_id"]) project_name = project_result["gitlab_name"] logger.info("project_name:" + project_name) file_path_list = str(file_path).rsplit('/', 1) source_path = '/tmp/' + project_name + '/' + file_path source_path_tmp = '/tmp/' + project_name + '/' + file_path_list[ 0] + '/tmp_file' no_success_minion_list = [] for minion_id in target_minion_list: command_path = 'mkdir -p ' + desc_path salt_api.shell_remote_execution(minion_id, command_path) command_distribute = 'salt-cp ' + minion_id + ' ' + source_path_tmp + ' ' + desc_path + '/' + \ file_path_list[1] command_list = [] command_list.append('cd /tmp/' + project_name + ' \n ') command_list.append('git pull \n ') command_list.append('cp ' + source_path + ' ' + source_path_tmp + ' \n ') command_list.append(command_distribute + ' \n ') command_list.append('rm -f ' + source_path_tmp + ' \n ') command_final = ''.join(command_list) logger.info("command_final:" + command_final) result = salt_api.shell_remote_execution([master_id], command_final) if not str(result).__contains__('True'): no_success_minion_list.append(minion_id) db.close_mysql() if len(no_success_minion_list) == 0: return {"status": True, "message": 'success'}, 200 else: return { "status": False, "message": '没有成功发送的节点有:' + str(no_success_minion_list) }, 500
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: key_result = salt_api.list_all_key() if key_result: if key_result.get("status") is False: return key_result, 500 # status_result = salt_api.runner_status("status") # if status_result: # if status_result.get("status") is False: # return status_result, 500 data = { "title": ["Accepted", "Rejected", "Unaccepted"], "series": [ { "value": len(key_result.get("minions")), "name": 'Accepted', "itemStyle": { "normal": { "color": '#f0e334' } } }, # {"value": 40, "name": 'Up', "itemStyle": # {"normal": {"color": '#64d572'}}}, # {"value": 0, "name": 'Down', "itemStyle": # {"normal": {"color": '#f25e43'}}}, { "value": len(key_result.get("minions_rejected")), "name": 'Rejected', "itemStyle": { "normal": { "color": '#ffd572' } } }, { "value": len(key_result.get("minions_pre")), "name": 'Unaccepted', "itemStyle": { "normal": { "color": '#2d8cf0' } } } ], } return {"data": data, "status": True, "message": ""}, 200
def post(self): args = parser.parse_args() desc_path = args["desc_path"] target = args["target"] db = DB() state, result = db.select('product', "where data -> '$.name'='%s'" % 'config') product_config_id = result[0]['id'] salt_api = salt_api_for_product(product_config_id) command = 'cat /home/111' result = salt_api.shell_remote_execution(target, command) logger.info('result:' + str(result)) db.close_mysql() return {"status": True, "message": 'success'}, 200
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) job_active_list = [] if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.runner("jobs.active") if result: if result.get("status") is False: return result, 500 for jid, info in result.items(): # 不能直接把info放到append中 info.update({"Jid": jid}) job_active_list.append(info) return {"data": job_active_list, "status": True, "message": ""}, 200
def git_clone(product_id, project_name): db = DB() status, product = db.select_by_id('product', product_id) gitlab_url = product['gitlab_url'] master = product['salt_master_id'] gitlab_url = gitlab_url.replace('http://', 'git@') gitlab_project_url = re.split( ':', gitlab_url)[0] + ':root/' + project_name + '.git' #gitlab_project_url = gitlab_url.replace(':80', '/root/') + project_name + '.git' command_list = [] command_list.append('cd /tmp/' + ' \n ') command_list.append('git clone ' + gitlab_project_url + ' \n ') command_final = ''.join(command_list) logger.info('command' + command_final) salt_api = salt_api_for_product(product_id) exec_result = salt_api.shell_remote_execution([master], command_final)
def delete(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) user = g.user_info["username"] if isinstance(salt_api, dict): return salt_api, 500 else: if args["action"] == "kill" and args["jid"]: kill = "salt '*' saltutil.kill_job" + " " + args["jid"] try: result = os.popen(kill).read() audit_log(user, args["jid"], args["product_id"], "job id", "kill") return {"status": True, "message": result}, 200 except Exception as e: return {"status": False, "message": str(e)}, 500 else: return {"status": False, "message": "The specified job id does not exist or arguments error"}, 400
def post(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: result = salt_api.hook(args["tag"]) if isinstance(result, dict): if result.get("success") is True: return {"data": result, "status": True, "message": ""}, 200 else: return { "data": result, "status": False, "message": result }, 500 else: return {"status": False, "message": result}, 500
def post(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) user = g.user_info["username"] if isinstance(salt_api, dict): return salt_api else: result_list = [] if args["action"] and args["minion_id"]: if args["action"] == "accept": for minion in args["minion_id"]: result = salt_api.accept_key(minion) result_list.append({minion: result}) audit_log(user, minion, args["product_id"], "minion", "accept") # 添加host Hosts.add_host(args["minion_id"], args["product_id"], user) return {"status": True, "message": result_list}, 200 if args["action"] == "reject": for minion in args["minion_id"]: result = salt_api.reject_key(minion) result_list.append({minion: result}) audit_log(user, minion, args["product_id"], "minion", "reject") # 拒绝host # Hosts.reject_host(args["minion_id"], args["product_id"], user) return {"status": True, "message": result_list}, 200 if args["action"] == "delete": for minion in args["minion_id"]: result = salt_api.delete_key(minion) result_list.append({minion: result}) audit_log(user, minion, args["product_id"], "minion", "delete") # 删除host Hosts.delete_host(args["minion_id"], args["product_id"], user) return {"status": True, "message": result_list}, 200 else: return { "status": False, "message": "The specified action or minion_id parameter does not exist" }, 400
def get(self): args = parser.parse_args() salt_api = salt_api_for_product(args["product_id"]) if isinstance(salt_api, dict): return salt_api, 500 else: if args["minion"]: if args["item"]: result = salt_api.grain(args["minion"], args["item"]) if result: return result return {"status": False, "message": "The specified minion does not exist"}, 404 else: result = salt_api.grains(args["minion"]) if result: return result return {"status": False, "message": "The specified minion does not exist"}, 404 else: return {"status": False, "message": "The specified minion arguments error"}, 400
def get_period(period_id, product_id): db = DB() status, period_result = db.select_by_id("period_task", period_id) if status is True and period_result: minions = [] for group in period_result.get("target"): status, result = db.select_by_id("groups", group) if status is True and result: minions.extend(result.get("minion")) minion_list = list(set(minions)) salt_api = salt_api_for_product(product_id) if isinstance(salt_api, dict): period_result["status"] = {"id": 4, "name": period_status.get(4)} db.update_by_id("period_task", json.dumps(period_result, ensure_ascii=False), period_id) return salt_api, 500 return period_result, minion_list, salt_api else: logger.error("Get period and minion error: %s" % period_result)
def post(self): args = parser.parse_args() sls = args["sls"] if not sls: return { "status": False, "message": "Missing required parameter in the JSON body or " "the post body or the query string" }, 400 minion_id = args["minion_id"] salt_api = salt_api_for_product(args["product_id"]) user_info = g.user_info audit_log(user_info["username"], minion_id, args["product_id"], "minion", "sls") if isinstance(salt_api, dict): return salt_api, 500 host = ",".join(minion_id) result = salt_api.target_deploy(host, sls) result.update({"status": True, "message": ""}) return result, 200