目录同步

同步示例

upload successful

目录流程

upload successful

初次安装
进入虚拟环境
source ../../DeviceOnline/bin/activate
进入run.py所在目录
cd ../DeviceOnline
查看帮助
(venv) [root@localhost DeviceOnline]# ./run.py -h
usage: run.py [-h] [--force] [--configfile CONFIGFILE] folder_name

positional arguments:
folder_name The Name of Device

optional arguments:
-h, --help show this help message and exit
--force Force installation
--configfile CONFIGFILE
Configuration file to use
创建目录
./run.py folder_name

upload successful

使用场景:
本地Samba未安装lsyncd同步软件
本地Samba & Azrue Samba服务器均未创建SyncUser用户
本地Samba SyncUser公钥尚未同步至Azrue Samba

非初次安装

upload successful

配置文件

../DeviceOnline/install.cfg

upload successful

异常处理
Lsyncd未安装

upload successful

SyncUser未创建

upload successful

公钥未同步

upload successful

目录重名

upload successful

邮件通知

upload successful

CODE
#!/usr/bin/env python
# coding:utf8
"""
An installer for GENEWIZ Device Online.

Create Device Folder Name IN Local & Azrue Samba Servers.
Config Lsyncd Service IN Local Samba Server.
"""

import argparse
try:
import configparser
except ImportError:
import ConfigParser as configparser
import sys
import os
import platform
import paramiko


from scripts import package
from scripts import system
from scripts import utils
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from datetime import datetime


def deploy_key(key, host, username, password, sync_user):
"""Deploy Public key to target machines"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(host, username=username, password=password)
client.exec_command('mkdir -p /home/{}/.ssh/'.format(sync_user))
client.exec_command('chown {}.{} /home/{}/.ssh/'.format(sync_user, sync_user, sync_user))
client.exec_command('echo "{}" > /home/{}/.ssh/authorized_keys'.format(key, sync_user))
client.exec_command('chmod 600 /home/{}/.ssh/authorized_keys'.format(sync_user))
client.exec_command('chown {}.{} /home/{}/.ssh/authorized_keys'.format(sync_user, sync_user, sync_user))
client.exec_command('chmod 700 /home/{}/.ssh/'.format(sync_user))
client.close()


def mkdirs(host, username, password, folder_path, sync_user):
"""Make dirs"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(host, username=username, password=password)
for path in folder_path:
client.exec_command('mkdir -p {}'.format(path))
client.exec_command('chmod -R 2776 {}'.format(path))
client.exec_command('chown {}."{}" {}'.format(sync_user, 'domain users', path))
client.close()


def SentMail(msg_data, mail_to, mail_cc):
"""Send Email"""
mail_host = '10.xx.xx.xx'
mail_user = 'xx@genewiz.com.cn'
# mail_pass = '********'
mail_sender = 'xx@genewiz.com.cn'
mail_to = mail_to
mail_cc = mail_cc
subject = '【GENEWIZ-IT】设备目录上线通知'
# message = MIMEMultipart()
tr_messages_list = []
li_messages_list = []
for smb_tag, smb_info in msg_data.items():
for host in smb_info['hosts']:
hostname = host['hostname']
folder_auth = host['folder_auth']
create_time = host['create_time']
folder_group = host['folder_group']
folder_name = host['folder_name']
if 'local' in smb_tag:
smb_path_internal_in = os.path.join(host['smb_path_internal_in'], folder_name).replace('/', '\\')
smb_path_external_in = os.path.join(host['smb_path_external_in'], folder_name).replace('/', '\\')
smb_path_out = os.path.join(host['smb_path_out'], folder_name).replace('/', '\\')

for service in smb_info['services']:
li_messages = '''
<li><strong>服务: {}</strong></li>
<li>日志目录: {}</li>
<li>配置文件: {}</li>
<li style="margin-bottom:15px">系统服务: {}【自启动】</li>
'''.format(service['name'], service['log_file_path'], service['config_path'], service['init_path'])
li_messages_list.append(li_messages)

for path in host['path']:
tr_messages = '''
<tr>
<td style="border-top: 1px solid #c4cacd;border-right: 1px solid #c4cacd;height: 27px;line-height: 27px;background: #fff;padding:0 10px;color:#4285F4" >{0}</td>
<td style="border-top: 1px solid #c4cacd;border-right: 1px solid #c4cacd;height: 27px;line-height: 27px;background: #fff;padding:0 10px">{1}</td>
<td style="border-top: 1px solid #c4cacd;border-right: 1px solid #c4cacd;line-height: 27px;background: #fff;padding:0 20px">{2}</td>
<td style="border-top: 1px solid #c4cacd;border-right: 1px solid #c4cacd;line-height: 27px;background: #fff;padding:0 10px">{3}</td>
<td style="border-top: 1px solid #c4cacd;border-right: 1px solid #c4cacd;line-height: 27px;background: #fff;padding:0 10px">{4}</td>
<td style="border-top: 1px solid #c4cacd;border-right: 1px solid #c4cacd;line-height: 27px;background: #fff;padding:0 10px">{5}</td>
<td style="border-top: 1px solid #c4cacd;line-height: 27px;background: #fff;padding:0 20px;color: red">{6}</td>
</tr>
'''.format(smb_tag, hostname, folder_name, path, folder_auth, folder_group, create_time)
tr_messages_list.append(tr_messages)

mail_msg = '''

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<p style="margin-right:20px;margin-bottom:15px; padding-bottom: 5px;font-size:14px;font-family:'Microsoft YaHei';color:#333;font-weight: bold;">您的设备目录已创建,详情如下:</p>

<table cellspacing="0" cellpadding="0" style="border: 1px solid #c4cacd;font-size:13px;font-family:'Microsoft YaHei';color:#333;">
<tbody>
<tr>
<th style="border-top: 1px solid #fff;border-left: 1px solid #fff;border-right: 1px solid #c4cacd;color: white;background: #4285F4; line-height: 21px;font-weight: bold;padding: 2px 0">类型</th>
<th style="border-top: 1px solid #fff;border-right: 1px solid #c4cacd;color: white;background: #4285F4; line-height: 21px;font-weight: bold;padding: 2px 0">IP</th>
<th style="border-top: 1px solid #fff;border-right: 1px solid #c4cacd;color: white;background: #4285F4; line-height: 21px;font-weight: bold;padding: 2px 0">目录</th>
<th style="border-top: 1px solid #fff;border-right: 1px solid #c4cacd;color: white;background: #4285F4; line-height: 21px;font-weight: bold;padding: 2px 0">目录路径</th>
<th style="border-top: 1px solid #fff;border-right: 1px solid #c4cacd;color: white;background: #4285F4;line-height: 21px;font-weight: bold;padding: 2px 0">权限</th>
<th style="border-top: 1px solid #fff;border-right: 1px solid #c4cacd;color: white;background: #4285F4;line-height: 21px;font-weight: bold;padding: 2px 0">目录属组</th>
<th style="border-top: 1px solid #fff;border-right: 1px solid #c4cacd;color: white;background: #4285F4;line-height: 21px;font-weight: bold;padding: 2px 0">创建时间</th>
</tr>
%s
</tbody>
</table>

<p style="margin-right:20px;margin-top:15px;margin-bottom:15px; padding-bottom: 5px;font-size:14px;font-family:'Microsoft YaHei';font-weight:bold;color:#333;border-bottom: 1px solid #eee">本地下机路径</p>
<ul style="list-style: none;margin: 0;padding: 0;font-size:13px;font-family:'Microsoft YaHei';color:#333;">
<li>本地下机路径-内单【IN】: %s</li>
<li>本地下机路径-外单【IN】: %s</li>
<li>本地下机路径【OUT】: %s</li>
</ul>

<p style="margin-right:20px;margin-top:15px;margin-bottom:15px; padding-bottom: 5px;font-size:14px;font-family:'Microsoft YaHei';font-weight:bold;color:#333;border-bottom: 1px solid #eee">目录同步服务</p>
<ul style="list-style: none;margin: 0;padding: 0;font-size:13px;font-family:'Microsoft YaHei';color:#333;">
%s
</ul>


<p style="margin-right:20px;margin-top:15px;margin-bottom:15px; padding-bottom: 5px;font-size:12px;font-family:'Microsoft YaHei';color:red;">本邮件是由系统自动发出的,请不要回复本邮件!</p>
''' % ("".join(tr_messages_list), smb_path_internal_in, smb_path_external_in, smb_path_out, "".join(li_messages_list))

message = MIMEText(mail_msg, 'html', 'utf-8')
message['From'] = Header(mail_sender)
message['To'] = Header(','.join(mail_to))
message['Cc'] = Header(','.join(mail_cc))
message['Subject'] = Header(subject, 'utf-8')
try:
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 25)
# SZ SMTP Server do not require login
# smtpObj.login(mail_user,mail_pass)
smtpObj.sendmail(mail_sender, mail_to + mail_cc, message.as_string())
return True
except Exception as e:
print(e)
return False


def main(input_args):
"""Install process."""
parser = argparse.ArgumentParser()
parser.add_argument("--force", action="store_true", default=False,help="Force installation")
parser.add_argument("--configfile", default="installer.cfg",help="Configuration file to use")
parser.add_argument("folder_name", type=str,help="The Name of Device")
args = parser.parse_args(input_args)

folder_name = args.folder_name
basedir = os.path.dirname(os.path.abspath(__file__))

utils.printcolor("Welcome to Genewiz installer for Device Online!\n", utils.GREEN)
config = configparser.SafeConfigParser()
with open(args.configfile) as fp:
config.readfp(fp)

mail_to = config.get('notify', 'mail_to').split(',')
mail_cc= config.get('notify', 'mail_cc').split(',')

# Check if Lsyncd is installed?
utils.printcolor("1.Check if Lsyncd is installed?", utils.BLUE)
utils.printcolor("-"*80, utils.BLUE)
code,res = utils.exec_cmd('which lsyncd')
if code:
utils.printcolor("[Error]: lsyncd is not installed.", utils.RED)
name, version, _id = platform.linux_distribution()
if version.startswith('6'):
lsyncd_rpm = os.path.join(basedir, 'scripts/files/lsyncd-2.1.5-0.el6.x86_64.rpm')
elif version.startswith('7'):
lsyncd_rpm = os.path.join(basedir, 'scripts/files/lsyncd-2.1.5-6.el7.x86_64.rpm')
utils.printcolor("[Info]: Installing lsyncd for you, please wait...", utils.BLUE)
package.backend.install_many(config.get('requirement', 'package').split(','))
utils.exec_cmd('rpm -ivh {}'.format(lsyncd_rpm))
code, res = utils.exec_cmd('which lsyncd')
utils.printcolor("[Success]: Lsyncd installation is complete. {}".format([res.strip()]), utils.GREEN)
else:
utils.printcolor("[Success]: Lsyncd is installed {}".format([res.strip()]), utils.GREEN)

# Check if SyncUser is Created?
utils.printcolor('', utils.BLACK)
utils.printcolor("2.Check if SyncUser is Created?", utils.BLUE)
utils.printcolor("-"*80, utils.BLUE)

# Get all samba servers
hosts = {}
for section in config.sections():
if section.startswith('smb-local'):
hosts[section] = {i[0]:i[1] for i in config.items(section)}
elif section.startswith('smb-azure'):
hosts[section] = {i[0]:i[1] for i in config.items(section)}

# Get local and azure servers
azure_hosts = []
local_hosts = []
for key in hosts.keys():
if key.startswith('smb-local'):
local_hosts.append(hosts[key])
else:
azure_hosts.append(hosts[key])

for host in hosts.values():
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(host['hostname'], 22, username=host['username'], password=host['password'])
cmd = 'id SyncUser'
stdin, stdout, stderr = client.exec_command(cmd)
stdout = stdout.read()

if not stdout:
utils.printcolor("[Error]: the SyncUser was not detected for {}".format(host['hostname']), utils.RED)
utils.printcolor("[Info]: Creating SyncUser for {}, please wait...".format(host['hostname']), utils.BLUE)
cmd = 'useradd SyncUser'
client.exec_command(cmd)
utils.printcolor("[Success]: SyncUser is created", utils.GREEN)
utils.printcolor('', utils.BLACK)
else:
utils.printcolor("[Success]: detected SyncUser in {}\n".format(host['hostname']), utils.GREEN)
client.close()


# Check if can ssh pass the key?
utils.printcolor("3.Check if can ssh pass the key?", utils.BLUE)
utils.printcolor("-"*80, utils.BLUE)
for host in azure_hosts:
key = os.path.join(basedir, 'scripts/files/SyncUser_id_rsa')
private_key = paramiko.RSAKey.from_private_key_file(key)
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(host['hostname'], 22, username='SyncUser', pkey=private_key)
utils.printcolor("[Success]: login {} with private key\n".format(host['hostname']), utils.GREEN)
except Exception as e:
utils.printcolor("[Error]: {} for {}".format(str(e), host['hostname']), utils.RED)
utils.printcolor("[Info]: Initializing user permissions for {}, please wait...".format(host['hostname']), utils.BLUE)
key = open(os.path.expanduser(os.path.join(basedir,'scripts/files/SyncUser_id_rsa.pub'))).read()
deploy_key(key, host['hostname'], host['username'], host['password'], host['sync_user'])
utils.printcolor("[Success]: Permission initialization succeeded in {}\n".format(host['hostname']), utils.GREEN)
client.close()

# Check if the directory name conflicts?
utils.printcolor("4.Check if the directory name conflicts?", utils.BLUE)
utils.printcolor("-"*80, utils.BLUE)

has_created_folders = []
for host in local_hosts:
path_internal_in = host['path_internal_in']
path_external_in = host['path_external_in']
path_out = host['path_out']

for folder in os.listdir(path_internal_in):
if os.path.isdir(os.path.join(path_internal_in, folder)):
has_created_folders.append(folder)

for folder in os.listdir(path_external_in):
if os.path.isdir(os.path.join(path_external_in, folder)):
has_created_folders.append(folder)

for folder in os.listdir(path_out):
if os.path.isdir(os.path.join(path_out, folder)):
has_created_folders.append(folder)

# Directory deduplication
has_created_folders_unique = list(set(has_created_folders))
if folder_name in has_created_folders_unique:
utils.printcolor("Error: Directory name conflict - {}, please change the directory name.".format(folder_name),utils.RED)
return
else:
utils.printcolor("[Success]: No directory conflict.".format(folder_name),utils.GREEN)
utils.printcolor("", utils.GREEN)

# Ask for confirm
if not args.force:
answer = utils.user_input("Will create a directory and synchronization service,do you confirm? (Y/n) ")
if answer.lower().startswith("n"):
return

# mail dict
msg_data = {
'local_smb': {'hosts': [], 'services': []},
'azrue_smb': {'hosts': []},
}

# Create device folder in Local Samba Servers
for host in local_hosts:
folder_path_internal_in = os.path.join(host['path_internal_in'], folder_name)
folder_path_external_in = os.path.join(host['path_external_in'], folder_name)
folder_path_out = os.path.join(host['path_out'], folder_name)
folder_path = [folder_path_internal_in, folder_path_external_in, folder_path_out]
mkdirs(host['hostname'],host['username'],host['password'],folder_path,host['sync_user'])
tmp_data = {}
tmp_data['hostname'] = host['hostname']
tmp_data['path'] = folder_path
tmp_data['folder_name'] = folder_name
tmp_data['folder_auth'] = '2766'
tmp_data['folder_group'] = 'SyncUser:Domain User'
tmp_data['smb_path_internal_in'] = host['smb_path_internal_in']
tmp_data['smb_path_external_in'] = host['smb_path_external_in']
tmp_data['smb_path_out'] = host['smb_path_out']
tmp_data['create_time'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg_data['local_smb']['hosts'].append(tmp_data)
utils.printcolor("[Success-LocalSamba]: {} is created in {}".format(folder_path, host['hostname']), utils.GREEN)

# Create device folder in Azrue Samba Servers
for host in azure_hosts:
folder_path_out = os.path.join(host['path_out'], folder_name)
folder_path = [folder_path_out]
mkdirs(host['hostname'], host['username'], host['password'], folder_path, host['sync_user'])
tmp_data = {}
tmp_data['hostname'] = host['hostname']
tmp_data['path'] = folder_path
tmp_data['folder_name'] = folder_name
tmp_data['folder_auth'] = '2766'
tmp_data['folder_group'] = 'SyncUser:Domain User'
tmp_data['create_time'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg_data['azrue_smb']['hosts'].append(tmp_data)
utils.printcolor("[Success-AzureSamba]: {} is created in {}".format(folder_path, host['hostname']), utils.GREEN)


# Create Lsyncd services in Local Samba Servers
for local_host in local_hosts:
key = os.path.join(basedir, 'scripts/files/SyncUser_id_rsa')
for azure_host in azure_hosts:
if azure_host.has_key('tag'):
logfile = 'Sync_' + folder_name + '_DR'
else:
logfile = 'Sync_' + folder_name
statusfile = logfile
source = os.path.join(local_host['path_out'],folder_name)
target = azure_host['hostname'] + ':' + os.path.join(azure_host['path_out'],folder_name)
rsh = azure_host['sync_user']
sync_cfg_tpl = open(os.path.join(basedir, 'scripts/files/sync-cfg.tpl')).read() % (logfile,logfile,statusfile,statusfile,source,target,key,rsh)
sync_init_tpl = open(os.path.join(basedir, 'scripts/files/sync-init.tpl')).read() % (logfile,res.strip(),logfile,logfile,logfile)
f = open(os.path.join('/etc',logfile + '.lua'), 'wb')
f.write(sync_cfg_tpl)
f.flush()
f.close()

f = open(os.path.join('/etc/init.d',logfile), 'wb')
f.write(sync_init_tpl)
f.flush()
f.close()

os.chmod(os.path.join('/etc/init.d',logfile), 0755)
try:
os.makedirs(os.path.join('/var/log', logfile))
except OSError as e:
pass
system.enable_and_start_service(logfile)

tmp_data = {}
tmp_data['name'] = logfile
tmp_data['log_file_path'] = os.path.join('/var/log', logfile)
tmp_data['config_path'] = os.path.join('/etc', logfile + '.lua')
tmp_data['init_path'] = os.path.join('/etc/init.d', logfile)
msg_data['local_smb']['services'].append(tmp_data)

utils.printcolor("Congratulations!",utils.GREEN)
SentMail(msg_data, mail_to, mail_cc)


if __name__ == "__main__":
main(sys.argv[1:])
0%