399 lines
18 KiB
Python
399 lines
18 KiB
Python
|
|
#coding: utf-8
|
|||
|
|
#-------------------------------------------------------------------
|
|||
|
|
# YakPanel
|
|||
|
|
#-------------------------------------------------------------------
|
|||
|
|
# Copyright (c) 2015-2018 YakPanel(www.yakpanel.com) All rights reserved.
|
|||
|
|
#-------------------------------------------------------------------
|
|||
|
|
# Author: hwliang <hwl@yakpanel.com>
|
|||
|
|
#-------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
#------------------------------
|
|||
|
|
# Apache管理模块
|
|||
|
|
#------------------------------
|
|||
|
|
import public,os,re,shutil,math,psutil,time
|
|||
|
|
from json import loads
|
|||
|
|
os.chdir("/www/server/panel")
|
|||
|
|
|
|||
|
|
class apache:
|
|||
|
|
setupPath = '/www/server'
|
|||
|
|
apachedefaultfile = "%s/apache/conf/extra/httpd-default.conf" % (setupPath)
|
|||
|
|
apachempmfile = "%s/apache/conf/extra/httpd-mpm.conf" % (setupPath)
|
|||
|
|
httpdconf = "%s/apache/conf/httpd.conf" % (setupPath)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def GetProcessCpuPercent(self,i,process_cpu):
|
|||
|
|
try:
|
|||
|
|
pp = psutil.Process(i)
|
|||
|
|
if pp.name() not in process_cpu.keys():
|
|||
|
|
process_cpu[pp.name()] = float(pp.cpu_percent(interval=0.1))
|
|||
|
|
process_cpu[pp.name()] += float(pp.cpu_percent(interval=0.1))
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def GetApacheStatus(self):
|
|||
|
|
data = {}
|
|||
|
|
try:
|
|||
|
|
process_cpu = {}
|
|||
|
|
apacheconf = "%s/apache/conf/httpd.conf" % (self.setupPath)
|
|||
|
|
confcontent = public.readFile(apacheconf)
|
|||
|
|
rep = "#Include conf/extra/httpd-info.conf"
|
|||
|
|
if re.search(rep,confcontent):
|
|||
|
|
confcontent = re.sub(rep,"Include conf/extra/httpd-info.conf",confcontent)
|
|||
|
|
public.writeFile(apacheconf,confcontent)
|
|||
|
|
public.serviceReload()
|
|||
|
|
# 多服务修改获取端口
|
|||
|
|
if public.get_multi_webservice_status():
|
|||
|
|
result = public.HttpGet('http://127.0.0.1:8288/server-status?auto')
|
|||
|
|
else:
|
|||
|
|
result = public.HttpGet('http://127.0.0.1/server-status?auto')
|
|||
|
|
try:
|
|||
|
|
workermen = int(public.ExecShell("ps aux|grep httpd|grep 'start'|awk '{memsum+=$6};END {print memsum}'")[0]) / 1024
|
|||
|
|
except:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get RAM False"))
|
|||
|
|
for proc in psutil.process_iter():
|
|||
|
|
if proc.name() == "httpd":
|
|||
|
|
self.GetProcessCpuPercent(proc.pid,process_cpu)
|
|||
|
|
time.sleep(0.5)
|
|||
|
|
|
|||
|
|
if not result:
|
|||
|
|
raise Exception("Get Apache Status False, Please Check Apache Status")
|
|||
|
|
# 计算启动时间
|
|||
|
|
Uptime = re.search(r"ServerUptimeSeconds:\s+(.*)",result)
|
|||
|
|
if not Uptime:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get Uptime False"))
|
|||
|
|
Uptime = int(Uptime.group(1))
|
|||
|
|
min = Uptime / 60
|
|||
|
|
hours = min / 60
|
|||
|
|
days = math.floor(hours / 24)
|
|||
|
|
hours = math.floor(hours - (days * 24))
|
|||
|
|
min = math.floor(min - (days * 60 * 24) - (hours * 60))
|
|||
|
|
|
|||
|
|
#格式化重启时间
|
|||
|
|
restarttime = re.search(r"RestartTime:\s+(.*)",result)
|
|||
|
|
if not restarttime:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get Restart Time False"))
|
|||
|
|
restarttime = restarttime.group(1)
|
|||
|
|
rep = r"\w+,\s([\w-]+)\s([\d\:]+)\s\w+"
|
|||
|
|
date = re.search(rep,restarttime)
|
|||
|
|
if not date:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get date False"))
|
|||
|
|
date = date.group(1)
|
|||
|
|
timedetail = re.search(rep,restarttime)
|
|||
|
|
if not timedetail:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get time detail False"))
|
|||
|
|
timedetail=timedetail.group(2)
|
|||
|
|
monthen = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
|
|||
|
|
n = 0
|
|||
|
|
for m in monthen:
|
|||
|
|
if m in date:
|
|||
|
|
date = re.sub(m,str(n+1),date)
|
|||
|
|
n+=1
|
|||
|
|
date = date.split("-")
|
|||
|
|
date = "%s-%s-%s" % (date[2],date[1],date[0])
|
|||
|
|
|
|||
|
|
reqpersec = re.search(r"ReqPerSec:\s+(.*)", result)
|
|||
|
|
if not reqpersec:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get reqpersec False"))
|
|||
|
|
reqpersec = reqpersec.group(1)
|
|||
|
|
if re.match(r"^\.", reqpersec):
|
|||
|
|
reqpersec = "%s%s" % (0,reqpersec)
|
|||
|
|
data["RestartTime"] = "%s %s" % (date,timedetail)
|
|||
|
|
data["UpTime"] = "%s day %s hour %s minute" % (str(int(days)),str(int(hours)),str(int(min)))
|
|||
|
|
total_acc = re.search(r"Total Accesses:\s+(\d+)",result)
|
|||
|
|
if not total_acc:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get TotalAccesses False"))
|
|||
|
|
data["TotalAccesses"] = total_acc.group(1)
|
|||
|
|
total_kb = re.search(r"Total kBytes:\s+(\d+)",result)
|
|||
|
|
if not total_kb:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get TotalKBytes False"))
|
|||
|
|
data["TotalKBytes"] = total_kb.group(1)
|
|||
|
|
data["ReqPerSec"] = round(float(reqpersec), 2)
|
|||
|
|
busywork = re.search(r"BusyWorkers:\s+(\d+)",result)
|
|||
|
|
if not busywork:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get BusyWorkers False"))
|
|||
|
|
data["BusyWorkers"] = busywork.group(1)
|
|||
|
|
idlework = re.search(r"IdleWorkers:\s+(\d+)",result)
|
|||
|
|
if not idlework:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Get IdleWorkers False"))
|
|||
|
|
data["IdleWorkers"] = idlework.group(1)
|
|||
|
|
data["workercpu"] = round(float(process_cpu["httpd"]),2)
|
|||
|
|
data["workermem"] = "%s%s" % (int(workermen),"MB")
|
|||
|
|
except Exception as e:
|
|||
|
|
public.print_log("error, %s" % e)
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
def GetApacheValue(self):
|
|||
|
|
apachedefaultcontent = public.readFile(self.apachedefaultfile)
|
|||
|
|
apachempmcontent = public.readFile(self.apachempmfile)
|
|||
|
|
if not isinstance(apachempmcontent, str) or "mpm_event_module" not in apachempmcontent:
|
|||
|
|
return public.returnMsg(False, public.lang("mpm_event_module conf not found or /www/server/apache/conf/extra/httpd-mpm.conf is empty"))
|
|||
|
|
apachempmcontent = re.search(r"\<IfModule mpm_event_module\>(\n|.)+?\</IfModule\>",apachempmcontent).group()
|
|||
|
|
ps = ["%s,%s" % (public.lang("Second"),public.lang("Request timeout")),
|
|||
|
|
public.lang("Keep alive"),
|
|||
|
|
"%s,%s" % (public.lang("Second"), public.lang("Connection timeout")),
|
|||
|
|
public.lang("Max keep-alive requests per connection")]
|
|||
|
|
gets = ["Timeout","KeepAlive","KeepAliveTimeout","MaxKeepAliveRequests"]
|
|||
|
|
if public.get_webserver() == 'apache':
|
|||
|
|
shutil.copyfile(self.apachedefaultfile, '/tmp/apdefault_file_bk.conf')
|
|||
|
|
shutil.copyfile(self.apachempmfile, '/tmp/apmpm_file_bk.conf')
|
|||
|
|
conflist = []
|
|||
|
|
n = 0
|
|||
|
|
for i in gets:
|
|||
|
|
rep = r"(%s)\s+(\w+)" % i
|
|||
|
|
k = re.search(rep, apachedefaultcontent)
|
|||
|
|
if not k:
|
|||
|
|
return public.return_msg_gettext(False, "Get Key {} False",(i,))
|
|||
|
|
k = k.group(1)
|
|||
|
|
v = re.search(rep, apachedefaultcontent)
|
|||
|
|
if not v:
|
|||
|
|
return public.return_msg_gettext(False, "Get Value {} False",(v,))
|
|||
|
|
v = v.group(2)
|
|||
|
|
if k not in ["KeepAlive"]:
|
|||
|
|
try:
|
|||
|
|
v = int(v)
|
|||
|
|
except Exception as e:
|
|||
|
|
public.print_log(f"Value {v} False %s", e)
|
|||
|
|
ps_str = ps[n]
|
|||
|
|
kv = {"name":k,"value":v,"ps":ps_str}
|
|||
|
|
conflist.append(kv)
|
|||
|
|
n += 1
|
|||
|
|
|
|||
|
|
ps = [public.lang("Default processes"),
|
|||
|
|
public.lang("Maximum number of idle threads"),
|
|||
|
|
public.lang("Minimum number of idle threads available to handle request spikes"),
|
|||
|
|
public.lang("Number of threads created by each child process"),
|
|||
|
|
public.lang("Maximum number of connections that will be processed simultaneously"),
|
|||
|
|
public.lang("Limit on the number of connections that an individual child server will handle during its life")]
|
|||
|
|
gets = ["StartServers","MaxSpareThreads","MinSpareThreads","ThreadsPerChild","MaxRequestWorkers","MaxConnectionsPerChild"]
|
|||
|
|
n = 0
|
|||
|
|
for i in gets:
|
|||
|
|
rep = r"(%s)\s+(\w+)" % i
|
|||
|
|
k = re.search(rep, apachempmcontent)
|
|||
|
|
if not k:
|
|||
|
|
return public.return_msg_gettext(False, "Get Key {} False",(i,))
|
|||
|
|
k = k.group(1)
|
|||
|
|
v = re.search(rep, apachempmcontent)
|
|||
|
|
if not v:
|
|||
|
|
return public.return_msg_gettext(False, "Get Value {} False",(v,))
|
|||
|
|
v = v.group(2)
|
|||
|
|
if k not in ["KeepAlive"]:
|
|||
|
|
try:
|
|||
|
|
v = int(v)
|
|||
|
|
except Exception as e:
|
|||
|
|
public.print_log(f"Value {v} False %s", e)
|
|||
|
|
ps_str = ps[n]
|
|||
|
|
kv = {"name": k, "value": v, "ps": ps_str}
|
|||
|
|
conflist.append(kv)
|
|||
|
|
n += 1
|
|||
|
|
return conflist
|
|||
|
|
|
|||
|
|
def SetApacheValue(self, get):
|
|||
|
|
apachedefaultcontent = public.readFile(self.apachedefaultfile)
|
|||
|
|
apachempmcontent = public.readFile(self.apachempmfile)
|
|||
|
|
if not "mpm_event_module" in apachempmcontent:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("mpm_event_module conf not found or /www/server/apache/conf/extra/httpd-mpm.conf is empty"))
|
|||
|
|
conflist = []
|
|||
|
|
getdict = get.get_items()
|
|||
|
|
for i in getdict.keys():
|
|||
|
|
if i != "__module__" and i != "__doc__" and i != "data" and i != "args" and i != "action":
|
|||
|
|
getpost = {
|
|||
|
|
"name": i,
|
|||
|
|
"value": str(getdict[i])
|
|||
|
|
}
|
|||
|
|
conflist.append(getpost)
|
|||
|
|
for c in conflist:
|
|||
|
|
if c["name"] == "KeepAlive":
|
|||
|
|
if not re.search("On|Off", c["value"]):
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Parameter ERROR!"))
|
|||
|
|
else:
|
|||
|
|
print(c["value"])
|
|||
|
|
if not re.search(r"\d+", c["value"]):
|
|||
|
|
print(c["name"],c["value"])
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Parameter ERROR!"))
|
|||
|
|
|
|||
|
|
rep = r"%s\s+\w+" % c["name"]
|
|||
|
|
if re.search(rep,apachedefaultcontent):
|
|||
|
|
newconf = "%s %s" % (c["name"],c["value"])
|
|||
|
|
apachedefaultcontent = re.sub(rep,newconf,apachedefaultcontent)
|
|||
|
|
elif re.search(rep,apachempmcontent):
|
|||
|
|
newconf = "%s\t\t\t%s" % (c["name"], c["value"])
|
|||
|
|
apachempmcontent = re.sub(rep, newconf , apachempmcontent)
|
|||
|
|
public.writeFile(self.apachedefaultfile,apachedefaultcontent)
|
|||
|
|
public.writeFile(self.apachempmfile, apachempmcontent)
|
|||
|
|
isError = public.checkWebConfig()
|
|||
|
|
if (isError != True):
|
|||
|
|
shutil.copyfile('/tmp/_file_bk.conf', self.apachedefaultfile)
|
|||
|
|
shutil.copyfile('/tmp/proxyfile_bk.conf', self.apachempmfile)
|
|||
|
|
return public.returnMsg(False, 'ERROR: %s<br><a style="color:red;">' % public.lang("Configuration ERROR") + isError.replace("\n",
|
|||
|
|
'<br>') + '</a>')
|
|||
|
|
public.serviceReload()
|
|||
|
|
return public.return_msg_gettext(True, public.lang("Setup successfully!"))
|
|||
|
|
|
|||
|
|
def add_httpd_access_log_format(self,args):
|
|||
|
|
'''
|
|||
|
|
@name 添加httpd日志格式
|
|||
|
|
@author zhwen<zhw@yakpanel.com>
|
|||
|
|
@param log_format 需要设置的日志格式["$server_name","$remote_addr","-"....]
|
|||
|
|
@param log_format_name
|
|||
|
|
@param act 操作方式 add/edit
|
|||
|
|
'''
|
|||
|
|
try:
|
|||
|
|
log_format = loads(args.log_format)
|
|||
|
|
data = """
|
|||
|
|
#LOG_FORMAT_BEGIN_{n}
|
|||
|
|
LogFormat '{c}' {n}
|
|||
|
|
#LOG_FORMAT_END_{n}
|
|||
|
|
""".format(n=args.log_format_name,c=' '.join(log_format))
|
|||
|
|
data = data.replace('%{User-agent}i','"%{User-agent}i"')
|
|||
|
|
data = data.replace('%{Referer}i', '"%{Referer}i"')
|
|||
|
|
if args.act == 'edit':
|
|||
|
|
self.del_httpd_access_log_format(args)
|
|||
|
|
conf = public.readFile(self.httpdconf)
|
|||
|
|
if not conf:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Configuration file not exist"))
|
|||
|
|
reg = '<IfModule log_config_module>'
|
|||
|
|
conf = re.sub(reg,'<IfModule log_config_module>'+data,conf)
|
|||
|
|
public.writeFile(self.httpdconf,conf)
|
|||
|
|
public.serviceReload()
|
|||
|
|
return public.return_msg_gettext(True, public.lang("Setup successfully!"))
|
|||
|
|
except:
|
|||
|
|
return public.returnMsg(False, str(public.get_error_info()))
|
|||
|
|
|
|||
|
|
def del_httpd_access_log_format(self,args):
|
|||
|
|
'''
|
|||
|
|
@name 删除日志格式
|
|||
|
|
@author zhwen<zhw@yakpanel.com>
|
|||
|
|
@param log_format_name
|
|||
|
|
'''
|
|||
|
|
conf = public.readFile(self.httpdconf)
|
|||
|
|
if not conf:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Configuration file not exist"))
|
|||
|
|
reg = r'\s*#LOG_FORMAT_BEGIN_{n}(\n|.)+#LOG_FORMAT_END_{n}\n?'.format(n=args.log_format_name)
|
|||
|
|
conf = re.sub(reg,'',conf)
|
|||
|
|
self._del_format_log_of_website(args.log_format_name)
|
|||
|
|
public.writeFile(self.httpdconf,conf)
|
|||
|
|
public.serviceReload()
|
|||
|
|
return public.return_msg_gettext(True, public.lang("Setup successfully!"))
|
|||
|
|
|
|||
|
|
def del_all_log_format(self,args):
|
|||
|
|
all_format = self.get_httpd_access_log_format(args)
|
|||
|
|
for i in all_format:
|
|||
|
|
args.log_format_name = i
|
|||
|
|
self.del_httpd_access_log_format(args)
|
|||
|
|
|
|||
|
|
def _del_format_log_of_website(self,log_format_name):
|
|||
|
|
site_format_log_status = self._get_format_log_to_website(log_format_name)
|
|||
|
|
try:
|
|||
|
|
for s in site_format_log_status.keys():
|
|||
|
|
if not site_format_log_status[s]:
|
|||
|
|
continue
|
|||
|
|
website_conf_file = '/www/server/panel/vhost/apache/{}.conf'.format(s)
|
|||
|
|
format_exist_reg = r'CustomLog\s+"/www.*"\s+{}'.format(log_format_name)
|
|||
|
|
conf = public.readFile(website_conf_file)
|
|||
|
|
if not conf:continue
|
|||
|
|
if not re.search(format_exist_reg,conf):continue
|
|||
|
|
access_log = re.search(format_exist_reg,conf).group().split()
|
|||
|
|
access_log = access_log[0] + ' ' +access_log[1] + 'combined'
|
|||
|
|
conf = re.sub(format_exist_reg,access_log,conf)
|
|||
|
|
public.writeFile(website_conf_file,conf)
|
|||
|
|
return True
|
|||
|
|
except:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def get_httpd_access_log_format_parameter(self,args=None):
|
|||
|
|
data = {
|
|||
|
|
"%h":"Client's IP address",
|
|||
|
|
"%r":"Request agreement",
|
|||
|
|
"%t":"Request time",
|
|||
|
|
"%>s":"http status code",
|
|||
|
|
"%b":"Send data size",
|
|||
|
|
"%{Referer}i":"http referer",
|
|||
|
|
"%{User-agent}i":"http user agent",
|
|||
|
|
"%{X-Forwarded-For}i":"The real ip of the client",
|
|||
|
|
"%l":"Remote login name",
|
|||
|
|
"%u":"Remote user",
|
|||
|
|
"-":"-"
|
|||
|
|
}
|
|||
|
|
if hasattr(args,'log_format_name'):
|
|||
|
|
site_list = self._get_format_log_to_website(args.log_format_name)
|
|||
|
|
return {'site_list':site_list,'format_log':data}
|
|||
|
|
else:
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
def _process_log_format(self,tmp):
|
|||
|
|
log_tips = self.get_httpd_access_log_format_parameter()
|
|||
|
|
data = []
|
|||
|
|
for t in tmp:
|
|||
|
|
t = t.replace('\"','')
|
|||
|
|
t = t.replace("'", "")
|
|||
|
|
if t not in log_tips:
|
|||
|
|
continue
|
|||
|
|
data.append({t:log_tips[t]})
|
|||
|
|
return data
|
|||
|
|
|
|||
|
|
def get_httpd_access_log_format(self,args=None):
|
|||
|
|
try:
|
|||
|
|
reg = "#LOG_FORMAT_BEGIN.*"
|
|||
|
|
conf = public.readFile(self.httpdconf)
|
|||
|
|
if not conf:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Configuration file not exist"))
|
|||
|
|
data = re.findall(reg,conf)
|
|||
|
|
format_name = [i.split('LOG_FORMAT_BEGIN_')[-1] for i in data]
|
|||
|
|
format_log = {}
|
|||
|
|
for i in format_name:
|
|||
|
|
format_reg = r"#LOG_FORMAT_BEGIN_{n}(\n|.)+LogFormat\s+\'(.*)\'\s+{n}".format(n=i)
|
|||
|
|
tmp = re.search(format_reg,conf)
|
|||
|
|
if not tmp:
|
|||
|
|
continue
|
|||
|
|
tmp = tmp.groups()[1].split()
|
|||
|
|
format_log[i] = self._process_log_format(tmp)
|
|||
|
|
return format_log
|
|||
|
|
except:
|
|||
|
|
return public.returnMsg(False,public.get_error_info())
|
|||
|
|
|
|||
|
|
def set_httpd_format_log_to_website(self,args):
|
|||
|
|
'''
|
|||
|
|
@name 设置网站日志格式
|
|||
|
|
@author zhwen<zhw@yakpanel.com>
|
|||
|
|
@param sites aaa.com,bbb.com
|
|||
|
|
@param log_format_name
|
|||
|
|
'''
|
|||
|
|
# sites = args.sites.split(',')
|
|||
|
|
sites = loads(args.sites)
|
|||
|
|
try:
|
|||
|
|
all_site = public.M('sites').field('name').select()
|
|||
|
|
reg = r'CustomLog\s+"/www.*{}\s*'.format(args.log_format_name)
|
|||
|
|
for site in all_site:
|
|||
|
|
website_conf_file = '/www/server/panel/vhost/apache/{}.conf'.format(site['name'])
|
|||
|
|
conf = public.readFile(website_conf_file)
|
|||
|
|
if not conf:
|
|||
|
|
return public.return_msg_gettext(False, public.lang("Configuration file not exist"))
|
|||
|
|
format_exist_reg = r'(CustomLog\s+"/www.*\_log).*'
|
|||
|
|
access_log = re.search(format_exist_reg, conf).groups()[0] + '" ' + args.log_format_name
|
|||
|
|
if site['name'] not in sites and re.search(format_exist_reg,conf):
|
|||
|
|
access_log = ' '.join(access_log.split()[:-1])
|
|||
|
|
conf = re.sub(reg, access_log, conf)
|
|||
|
|
public.writeFile(website_conf_file,conf)
|
|||
|
|
continue
|
|||
|
|
conf = re.sub(format_exist_reg,access_log,conf)
|
|||
|
|
public.writeFile(website_conf_file,conf)
|
|||
|
|
public.serviceReload()
|
|||
|
|
return public.return_msg_gettext(True, public.lang("Setup successfully!"))
|
|||
|
|
except:
|
|||
|
|
return public.returnMsg(False, str(public.get_error_info()))
|
|||
|
|
|
|||
|
|
def _get_format_log_to_website(self,log_format_name):
|
|||
|
|
tmp = public.M('sites').field('name').select()
|
|||
|
|
reg = 'CustomLog.*{}'.format(log_format_name)
|
|||
|
|
data = {}
|
|||
|
|
for i in tmp:
|
|||
|
|
website_conf_file = '/www/server/panel/vhost/apache/{}.conf'.format(i['name'])
|
|||
|
|
conf = public.readFile(website_conf_file)
|
|||
|
|
if not conf:
|
|||
|
|
data[i['name']] = False
|
|||
|
|
continue
|
|||
|
|
if re.search(reg,conf):
|
|||
|
|
data[i['name']] = True
|
|||
|
|
else:
|
|||
|
|
data[i['name']] = False
|
|||
|
|
return data
|