613 lines
23 KiB
Python
613 lines
23 KiB
Python
#!/usr/bin/python3 -u
|
|
|
|
import sys
|
|
import argparse
|
|
import os
|
|
#from pastafariutils.unix import add_user, del_user, change_password
|
|
from pathlib import Path
|
|
from subprocess import call, DEVNULL
|
|
import json
|
|
import time
|
|
import shutil
|
|
import pwd
|
|
import distro
|
|
import subprocess
|
|
import pwd
|
|
import grp
|
|
|
|
php_version={'php74': 'php7.4', 'php80': 'php8.0', 'php81': 'php8.1', 'php82': 'php8.2'}
|
|
|
|
def manage():
|
|
|
|
parser=argparse.ArgumentParser(prog='manage_apache.py', description='A tool for admin an apache server')
|
|
|
|
parser.add_argument('--operation', help='The operation', required=True)
|
|
|
|
parser.add_argument('--domain', help='The domain for this operation', required=True)
|
|
|
|
parser.add_argument('--aliases', help='The Domain aliases for the domain')
|
|
|
|
parser.add_argument('--port', help='The http port where petitions are listened')
|
|
|
|
parser.add_argument('--ssl_port', help='The https port where petitions are listened')
|
|
|
|
parser.add_argument('--ip', help='The IP where http petitions are listened')
|
|
|
|
parser.add_argument('--email', help='If add an domain, you can use an email for apache errors')
|
|
|
|
parser.add_argument('--user', help='You need an user for the virtualhost')
|
|
|
|
parser.add_argument('--root_dir', help='If add an domain, you need define where the virtualhost is installed')
|
|
|
|
parser.add_argument("--indexes", action='store_true')
|
|
|
|
parser.add_argument("--allow_override", action='store_true')
|
|
|
|
parser.add_argument("--ssl", help='SSL Type: 0, no SSL')
|
|
|
|
parser.add_argument("--redirect_ssl", action='store_true')
|
|
|
|
parser.add_argument("--debug", action='store_true')
|
|
|
|
parser.add_argument('--type_cgi', help='The CGI type for this virtualhost')
|
|
|
|
parser.add_argument('--password', help='The password of the unix user')
|
|
|
|
#parser.add_argument('--ftp_user', help='The CGI type for this virtualhost', required=True)
|
|
|
|
args=parser.parse_args()
|
|
|
|
apache_cmd='apache2'
|
|
|
|
apachectl='apache2ctl'
|
|
|
|
apache_group='www-data'
|
|
|
|
linux_distro=distro.id()
|
|
|
|
if linux_distro!='debian' and linux_distro!='ubuntu':
|
|
apache_cmd='httpd'
|
|
apachectl='apachectl'
|
|
if linux_distro=='rocky' or linux_distro=='fedora' or linux_distro=='almalinux':
|
|
apache_group='apache'
|
|
|
|
if linux_distro=='arch':
|
|
apache_group='http'
|
|
|
|
|
|
port=80
|
|
ssl_port=443
|
|
ip='*'
|
|
yes_ssl=False
|
|
|
|
debug=False
|
|
|
|
if args.debug:
|
|
debug=True
|
|
|
|
json_return={'error':0, 'status': 0, 'progress': 0, 'no_progress':0, 'message': ''}
|
|
|
|
if args.operation=='add' or args.operation=='edit':
|
|
|
|
if not args.email:
|
|
parser.error('You need --email option if you add a new virtual host.')
|
|
|
|
if not args.root_dir:
|
|
parser.error('You need --root_dir option if you add a new virtual host.')
|
|
|
|
|
|
if not args.user:
|
|
parser.error('You need --user option if you add a new virtual host.')
|
|
|
|
if args.port:
|
|
port=args.port
|
|
|
|
if args.ip:
|
|
ip=args.ip
|
|
|
|
if ip.find(':')!=-1:
|
|
ip='['+ip+']'
|
|
|
|
real_root_dir=args.root_dir+'/htdocs'
|
|
|
|
#aliases='ServerAlias www.'+args.domain
|
|
aliases=''
|
|
|
|
if args.aliases:
|
|
aliases='ServerAlias '+args.aliases.replace(',', ' ')
|
|
|
|
indexes=''
|
|
|
|
if args.indexes:
|
|
indexes='Indexes'
|
|
|
|
allow_override=''
|
|
|
|
if args.allow_override:
|
|
allow_override='AllowOverride all'
|
|
|
|
type_ssl=0
|
|
|
|
if args.ssl:
|
|
try:
|
|
|
|
type_ssl=int(args.ssl)
|
|
except:
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: bad SSL configuration type'
|
|
|
|
print(json.dumps(json_return))
|
|
|
|
exit(1)
|
|
|
|
#print( os.path.basename(__file__)+'/files/vhost.tpl')
|
|
|
|
vhost_file=os.path.dirname(os.path.abspath(__file__))+'/files/vhost.tpl'
|
|
|
|
with open(vhost_file) as f_vhost:
|
|
vhost=f_vhost.read()
|
|
vhost_ssl=vhost
|
|
|
|
vhost=vhost.replace('$port', args.port)
|
|
vhost=vhost.replace('$ip', args.ip)
|
|
vhost=vhost.replace('$email', args.email)
|
|
vhost=vhost.replace('$domain', args.domain)
|
|
#ServerAlias www.$domain
|
|
vhost=vhost.replace('$ServerAlias', aliases)
|
|
vhost=vhost.replace('$rootDir', real_root_dir)
|
|
vhost=vhost.replace('$Indexes', indexes)
|
|
vhost=vhost.replace('$AllowOverride', allow_override)
|
|
vhost=vhost.replace('$apache_cmd', apache_cmd)
|
|
|
|
if type_ssl>0:
|
|
if type_ssl==1:
|
|
|
|
json_return['error']=0
|
|
json_return['status']=0
|
|
json_return['progress']=0
|
|
json_return['message']='Preparing SSL virtualhost'
|
|
|
|
#print(json.dumps(json_return))
|
|
|
|
#Make letsencrypt
|
|
|
|
"""
|
|
MDContactEmail antonio.delarosa@salirdelhoyo.com
|
|
MDCertificateAgreement accepted
|
|
MDomain foro.desiertoslejanos.org
|
|
|
|
SSLEngine on
|
|
|
|
"""
|
|
|
|
vhost_ssl=vhost_ssl.replace('$port', args.ssl_port)
|
|
vhost_ssl=vhost_ssl.replace('$ip', args.ip)
|
|
vhost_ssl=vhost_ssl.replace('$email', args.email)
|
|
vhost_ssl=vhost_ssl.replace('$domain', args.domain)
|
|
#ServerAlias www.$domain
|
|
vhost_ssl=vhost_ssl.replace('$ServerAlias', aliases)
|
|
vhost_ssl=vhost_ssl.replace('$rootDir', real_root_dir)
|
|
vhost_ssl=vhost_ssl.replace('$Indexes', indexes)
|
|
vhost_ssl=vhost_ssl.replace('$AllowOverride', allow_override)
|
|
vhost_ssl=vhost_ssl.replace('$apache_cmd', apache_cmd)
|
|
|
|
vhost_ssl_md=[]
|
|
|
|
vhost_ssl_md.append("MDContactEmail {}".format(args.email))
|
|
vhost_ssl_md.append("MDCertificateAgreement accepted")
|
|
vhost_ssl_md.append("MDomain {}".format(args.domain))
|
|
|
|
if debug:
|
|
#vhost_ssl_md.append("LE Test Setup")
|
|
vhost_ssl_md.append("MDCertificateAuthority https://acme-staging-v02.api.letsencrypt.org/directory")
|
|
|
|
vhost_ssl="\n".join(vhost_ssl_md)+"\n"+vhost_ssl
|
|
|
|
vhost_ssl=vhost_ssl.replace('# SSL Options', "SSLEngine on")
|
|
|
|
if args.redirect_ssl:
|
|
print("Adding redirect http to https...")
|
|
ssl_options_http=("RewriteEngine On",
|
|
"RewriteCond %{HTTPS} !=on",
|
|
"RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]")
|
|
vhost=vhost.replace('# SSL Options', "\n".join(ssl_options_http))
|
|
|
|
vhost=vhost_ssl+"\n\n"+vhost
|
|
|
|
json_return={'error':0, 'status': 0, 'progress': 100, 'no_progress':0, 'message': 'Creating SSL Cert with Letsencrypt using mod_md...'}
|
|
print(json.dumps(json_return))
|
|
|
|
"""
|
|
ssl_debug=''
|
|
|
|
if debug:
|
|
ssl_debug='--test-cert'
|
|
|
|
#certbot certonly --test-cert --webroot -w /var/www/sites/prueba.cuchulu.com/htdocs/ -d prueba.cuchulu.com
|
|
|
|
final_aliases=''
|
|
|
|
if args.aliases:
|
|
#arr_aliases=[alias.strip() for args.aliases.split(',')]
|
|
final_aliases=" ".join(['-d '+alias.strip() for alias in args.aliases.split(',')])
|
|
|
|
expand=''
|
|
|
|
if os.path.isfile('/etc/letsencrypt/live/{}/fullchain.pem'.format(args.domain)):
|
|
expand='--expand'
|
|
|
|
|
|
certbot_cmd='certbot certonly --noninteractive --agree-tos -m {} {} --webroot -w /home/{}/sites/{}/htdocs/ -d {} {} {}'.format(args.email, ssl_debug, args.user, args.domain, args.domain, final_aliases, expand)
|
|
|
|
json_return={'error':0, 'status': 0, 'progress': 0, 'no_progress':0, 'message': 'Creating SSL Cert with Letsencrypt...'}
|
|
print(json.dumps(json_return))
|
|
|
|
if call("sudo "+certbot_cmd, shell=True) > 0:
|
|
|
|
#If error, not stop the virtualhost, simply not creater ssl.
|
|
|
|
json_return['error']=0
|
|
json_return['status']=0
|
|
json_return['progress']=100
|
|
json_return['message']='Error: the ssl command is wrong '+certbot_cmd
|
|
print(json.dumps(json_return))
|
|
|
|
else:
|
|
|
|
json_return={'error':0, 'status': 0, 'progress': 100, 'no_progress':0, 'message': 'Created SSL Cert with Letsencrypt...'}
|
|
print(json.dumps(json_return))
|
|
|
|
# /etc/letsencrypt/live/prueba.cuchulu.com/fullchain.pem
|
|
# /etc/letsencrypt/live/prueba.cuchulu.com/privkey.pem
|
|
|
|
vhost_ssl=vhost_ssl.replace('$port', args.ssl_port)
|
|
vhost_ssl=vhost_ssl.replace('$ip', args.ip)
|
|
vhost_ssl=vhost_ssl.replace('$email', args.email)
|
|
vhost_ssl=vhost_ssl.replace('$domain', args.domain)
|
|
#ServerAlias www.$domain
|
|
vhost_ssl=vhost_ssl.replace('$ServerAlias', aliases)
|
|
vhost_ssl=vhost_ssl.replace('$rootDir', real_root_dir)
|
|
vhost_ssl=vhost_ssl.replace('$Indexes', indexes)
|
|
vhost_ssl=vhost_ssl.replace('$AllowOverride', allow_override)
|
|
vhost_ssl=vhost_ssl.replace('$apache_cmd', apache_cmd)
|
|
|
|
ssl_cert='/etc/letsencrypt/live/{}/fullchain.pem'.format(args.domain)
|
|
ssl_key='/etc/letsencrypt/live/{}/privkey.pem'.format(args.domain)
|
|
|
|
ssl_options=("SSLEngine on",
|
|
"SSLCertificateFile %s" % ssl_cert,
|
|
"SSLCertificateKeyFile %s" % ssl_key)
|
|
|
|
vhost_ssl=vhost_ssl.replace('# SSL Options', "\n".join(ssl_options))
|
|
|
|
if args.redirect_ssl:
|
|
print("Adding redirect http to https...")
|
|
ssl_options_http=("RewriteEngine On",
|
|
"RewriteCond %{HTTPS} !=on",
|
|
"RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]")
|
|
vhost=vhost.replace('# SSL Options', "\n".join(ssl_options_http))
|
|
|
|
vhost+="\n\n"+vhost_ssl
|
|
|
|
pass
|
|
"""
|
|
if type_ssl==2:
|
|
vhost_ssl=vhost_ssl.replace('$port', args.ssl_port)
|
|
vhost_ssl=vhost_ssl.replace('$ip', args.ip)
|
|
vhost_ssl=vhost_ssl.replace('$email', args.email)
|
|
vhost_ssl=vhost_ssl.replace('$domain', args.domain)
|
|
#ServerAlias www.$domain
|
|
vhost_ssl=vhost_ssl.replace('$ServerAlias', aliases)
|
|
vhost_ssl=vhost_ssl.replace('$rootDir', real_root_dir)
|
|
vhost_ssl=vhost_ssl.replace('$Indexes', indexes)
|
|
vhost_ssl=vhost_ssl.replace('$AllowOverride', allow_override)
|
|
vhost_ssl=vhost_ssl.replace('$apache_cmd', apache_cmd)
|
|
|
|
#Create dir for ssl
|
|
|
|
if call("sudo mkdir -p /etc/"+apache_cmd+"/ssl/", shell=True) > 0:
|
|
print('Error, cannot create SSL Cert directory if not exists...')
|
|
exit(1)
|
|
|
|
ssl_cert='/etc/'+apache_cmd+'/ssl/'+args.domain+'-ssl.crt'
|
|
ssl_key='/etc/'+apache_cmd+'/ssl/'+args.domain+'-ssl.key'
|
|
|
|
ssl_orig_cert=os.path.dirname(os.path.abspath(__file__))+'/files/'+args.domain+'-ssl.crt'
|
|
ssl_orig_key=os.path.dirname(os.path.abspath(__file__))+'/files/'+args.domain+'-ssl.key'
|
|
|
|
if os.path.isfile(ssl_orig_cert):
|
|
shutil.copy(ssl_orig_cert, ssl_cert)
|
|
shutil.copy(ssl_orig_key, ssl_key)
|
|
|
|
if not os.path.isfile(ssl_cert):
|
|
print('Error, you have not uploaded crt files for ssl vhost...')
|
|
exit(1)
|
|
|
|
ssl_options=("SSLEngine on",
|
|
"SSLCertificateFile %s" % ssl_cert,
|
|
"SSLCertificateKeyFile %s" % ssl_key)
|
|
|
|
vhost_ssl=vhost_ssl.replace('# SSL Options', "\n".join(ssl_options))
|
|
|
|
if args.redirect_ssl:
|
|
print("Adding redirect http to https...")
|
|
ssl_options_http=("RewriteEngine On",
|
|
"RewriteCond %{HTTPS} !=on",
|
|
"RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]")
|
|
vhost=vhost.replace('# SSL Options', "\n".join(ssl_options_http))
|
|
|
|
vhost+="\n\n"+vhost_ssl
|
|
|
|
pass
|
|
|
|
|
|
|
|
if args.operation=='add':
|
|
|
|
# Add site directory
|
|
|
|
# Add user
|
|
|
|
if args.user=='root':
|
|
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: you cannot use user root for sites'
|
|
|
|
print(json.dumps(json_return))
|
|
sys.exit(1)
|
|
|
|
#ret_user=add_user(args.user, '', '', user_directory='', shell='/bin/bash')
|
|
|
|
|
|
func_user="sudo useradd -m -s %s %s" % ('/bin/bash', args.user)
|
|
|
|
ret_user=True
|
|
|
|
if call(func_user, shell=True, stdout=DEVNULL) > 0:
|
|
ret_user=False
|
|
|
|
if not ret_user:
|
|
|
|
print("User %s exists, used for the new site" % args.user)
|
|
|
|
#Need add to group apache if not. Need for red hat and derivatives.
|
|
|
|
|
|
g=grp.getgrnam(apache_group)
|
|
#grp.struct_group(gr_name='apache', gr_passwd='x', gr_gid=48, gr_mem=['absurdo'])
|
|
|
|
if args.user not in g.gr_mem:
|
|
if call("sudo usermod -a -G %s %s" % (apache_group, args.user), shell=True) > 0:
|
|
print('Error, cannot add group to user')
|
|
exit(1)
|
|
else:
|
|
print('Added user to apache group')
|
|
else:
|
|
|
|
|
|
if call("sudo usermod -a -G %s %s" % (apache_group, args.user), shell=True) > 0:
|
|
print('Error, cannot add group to user')
|
|
exit(1)
|
|
|
|
|
|
# Add ssh key for diverses uses
|
|
print("Adding ssh key for user...")
|
|
|
|
if call('sudo su - %s -s /bin/bash -c \'ssh-keygen -f /home/%s/.ssh/id_rsa.pub -N \\"\\"\'' % (args.user, args.user), shell=True) > 0:
|
|
print('Error, cannot add ssh key for user...')
|
|
exit(1)
|
|
|
|
if call("sudo chmod +x %s" % '/home/'+args.user, shell=True) > 0:
|
|
print('Error, cannot set permissions for folder %s...' % real_root_dir)
|
|
exit(1)
|
|
|
|
if call("sudo su - %s -s /bin/bash -c 'mkdir -p %s'" % (args.user, real_root_dir), shell=True) > 0:
|
|
print('Error, cannot create Virtualhost folder %s...' % real_root_dir)
|
|
exit(1)
|
|
|
|
password_user=''
|
|
|
|
if args.password:
|
|
password_user=args.password
|
|
|
|
ret_pass=change_password(args.user, password_user)
|
|
|
|
if not ret_pass[0]:
|
|
print('Error, cannot change password for %s' % args.user )
|
|
exit(1)
|
|
|
|
if call("sudo getenforce", shell=True)==0:
|
|
|
|
# If selinux enabled, set permissions to /home/hosting/sites for sites.
|
|
|
|
#if call("sudo setsebool -P httpd_enable_homedirs true && sudo chcon -R -t httpd_sys_content_t /var/www/sites && sudo setsebool -P httpd_can_network_connect 1 && sudo semanage fcontext -a -t httpd_sys_rw_content_t \"/var/www/sites(/.*)?\"", shell=True) > 0:
|
|
print('Setting selinux permissions for the http folder %s...' % real_root_dir)
|
|
if call("sudo setsebool -P httpd_enable_homedirs true && sudo chcon -R -t httpd_sys_content_t %s && sudo setsebool -P httpd_can_network_connect 1 && chcon -R -t httpd_sys_rw_content_t %s" % (real_root_dir, real_root_dir), shell=True) > 0:
|
|
print('Error, cannot set selinux permissions...')
|
|
exit(1)
|
|
|
|
# Save virtualhost
|
|
|
|
virtualhost_base='/etc/'+apache_cmd+'/vhosts.d'
|
|
|
|
virtualhost_path=virtualhost_base+'/%s.conf' % (args.domain)
|
|
#virtualhost_enabled_path='/etc/'+apache_cmd+'/vhosts.d/%s.conf' % args.domain
|
|
|
|
with open(virtualhost_path, 'w') as f:
|
|
f.write(vhost)
|
|
|
|
json_return['progress']=60
|
|
json_return['message']='Created or edited virtualhost'
|
|
print(json.dumps(json_return))
|
|
#time.sleep(1)
|
|
|
|
# Activate virtualhost
|
|
|
|
#os.symlink(virtualhost_path, virtualhost_enabled_path)
|
|
|
|
# Check apache configuration, if not delete user and virtualhost.
|
|
|
|
if call("sudo "+apachectl+" configtest", shell=True) > 0:
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: the config is wrong \n'+vhost
|
|
|
|
# Delete user
|
|
|
|
#os.unlink(virtualhost_enabled_path)
|
|
os.remove(virtualhost_path)
|
|
|
|
#err, txt=del_user(args.user)
|
|
|
|
print(json.dumps(json_return))
|
|
|
|
exit(1)
|
|
|
|
json_return['progress']=75
|
|
json_return['message']='Finished virtualhost configuration'
|
|
print(json.dumps(json_return))
|
|
#time.sleep(1)
|
|
|
|
# Restart config
|
|
|
|
if call("sudo systemctl restart "+apache_cmd, shell=True) > 0:
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: I cannot reload the httpd server'
|
|
|
|
# Delete user
|
|
|
|
#os.unlink(virtualhost_enabled_path)
|
|
os.remove(virtualhost_path)
|
|
|
|
#err, txt=del_user(args.user)
|
|
|
|
print(json.dumps(json_return))
|
|
|
|
exit(1)
|
|
|
|
json_return['progress']=85
|
|
json_return['message']='Apache2 restarted...'
|
|
print(json.dumps(json_return))
|
|
#time.sleep(1)
|
|
|
|
# Finish
|
|
|
|
json_return['progress']=100
|
|
json_return['message']='Virtualhost done'
|
|
json_return['status']=0
|
|
print(json.dumps(json_return))
|
|
#time.sleep(1)
|
|
|
|
elif args.operation=='remove':
|
|
|
|
# Change uid to user id of the
|
|
|
|
if args.root_dir=='/':
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: I cannot delete this place...'
|
|
|
|
|
|
#if not args.user:
|
|
# parser.error('You need --user option if you want to remove a virtual host.')
|
|
|
|
virtualhost_path='/etc/'+apache_cmd+'/vhosts.d/%s.conf' % (args.domain)
|
|
|
|
virtualhost_php_path='/etc/'+apache_cmd+'/vhosts.d/php/%s-php.conf' % (args.domain)
|
|
|
|
#virtualhost_enabled_path='/etc/'+apache_cmd+'/vhosts.d/%s.conf' % args.domain
|
|
|
|
# Delete first apache configuration
|
|
|
|
try:
|
|
os.remove(virtualhost_path)
|
|
except:
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: I cannot remove symbolic link of apache file config'
|
|
print(json.dumps(json_return))
|
|
|
|
exit(1)
|
|
|
|
if os.path.isfile(virtualhost_php_path):
|
|
try:
|
|
os.remove(virtualhost_php_path)
|
|
except:
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: I cannot remove symbolic link of apache file config'
|
|
print(json.dumps(json_return))
|
|
|
|
exit(1)
|
|
|
|
|
|
json_return['progress']=25
|
|
json_return['message']='Deleted virtualhost in Apache configuration'
|
|
print(json.dumps(json_return))
|
|
#time.sleep(1)
|
|
|
|
# Test apache config
|
|
|
|
if call("sudo "+apachectl+" configtest", shell=True) > 0:
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: the config is wrong'
|
|
|
|
exit(1)
|
|
|
|
json_return['progress']=50
|
|
json_return['message']='Apache configuration checked correctly...'
|
|
print(json.dumps(json_return))
|
|
#time.sleep(1)
|
|
|
|
|
|
# Restart apache
|
|
|
|
if call("sudo systemctl restart "+apache_cmd, shell=True) > 0:
|
|
json_return['error']=1
|
|
json_return['status']=1
|
|
json_return['progress']=100
|
|
json_return['message']='Error: I cannot reload the httpd server'
|
|
|
|
print(json.dumps(json_return))
|
|
|
|
exit(1)
|
|
|
|
json_return['progress']=75
|
|
json_return['message']='Apache restarted successfully...'
|
|
print(json.dumps(json_return))
|
|
#time.sleep(1)
|
|
|
|
# Delete root_dir if exists
|
|
# Change effective uid
|
|
|
|
pw=pwd.getpwnam(args.user)
|
|
os.setuid(pw[2])
|
|
|
|
if args.root_dir:
|
|
shutil.rmtree(args.root_dir)
|
|
|
|
# Delete user of virtualhost
|
|
|
|
# Done
|
|
|
|
json_return['progress']=100
|
|
json_return['message']='VHost removed successfully. Done all.'
|
|
json_return['status']=0
|
|
print(json.dumps(json_return))
|
|
#time.sleep(1)
|
|
|
|
|
|
if __name__=='__main__':
|
|
manage()
|