python平台无关_Python实现跨平台运维小神器(一)

#!/bin/env python3

# coding:utf-8

"""

Usage:

auto_task [options] cmd [--skip-err] [--parallel]

auto_task [options] put [--parallel]

auto_task [options] get

Options:

-h --help Show this screen.

-u Remote username [default: root]

-p User's password

--pkey Local private key [default: /root/.ssh/id_rsa]

--server

File include the remote server's information,

With the format of 'name-ip:port', such as 'web1-192.168.1.100:22',one server one line.

--skip-err Use with cmd, if sikp any server's error and continue process the other servers [default: False].

--parallel Parallel execution, only use with cmd or put. This option implies the --skip-err [default: False].

cmd Run command on remote server(s),multiple commands sperate by ';'

put Transfer from local to remote. Transport mechanism similar to rsync.

get Transfer from remote to local. Transport mechanism similar to rsync.

Notice: cmd, get, put can only use one at once

For Windows: always use double quotes for quote something;

it's highly recommend that with get or put in Windows,always use '/' instead of ''

"""

"""

by ljk 20160704

update at 2017011,20170320

"""

fromdocoptimportdocopt

fromparamikoimportSSHClient, AutoAddPolicy

fromosimportpath, walk, makedirs, stat, utime

fromreimportsplit, match, search

fromsysimportexit, stdout

importplatform

frommathimportfloor

importthreading

"""

因为涉及了(多)线程,所以我们将串行也归为单线程,这样可以统一用线程的一些思路,而不必编写一套多线程模型一套串行模型。

也因为多线程,所以输出用print()的话,各server的输出会对不上号,所以引入了OutputText类,将每个server的输出统一保存起来,最后打印出来

但是这样依然无法避免多个线程同时完成了,同时打印各自的最终结果。也就是说多线程任务最终需要输出时,输出这个动作必须要串行

"""

classOutputText:

"""该类的对象具有write()方法,用来存储每台server的执行结果.

因为引入了多线程异步执行才需要这么做,以保证异步执行多台server的输出不会乱.

为了简洁,并行与串行的输出就都用这一套东西了"""

def__init__(self):

self.buffer = []

defwrite(self, *args, color=None):

ifcolor:

ifplatform.uname().system =='Windows':

self.buffer.extend(args)

else:

self.buffer.extend('033[0;{}m'.format(color))

self.buffer.extend(args)

self.buffer.extend('033[0m')

else:

self.buffer.extend(args)

defprint_lock(self):

"""并发模式下,所有的输出动作都要加锁"""

global_lock.acquire()

forlineinself.buffer:

print(line, end='')

global_lock.release()

defprint_color(text, color=31, sep=' ', end='n', file=stdout, flush=False):

"""打印彩色字体,color默认为红色

该方法只针对Linux有效"""

ifplatform.uname().system =='Windows':

print(text, sep=sep, end=end, file=file, flush=flush)

else:

print('033[0;{}m'.format(color), end='')

print(text, sep=sep, end=end, file=file, flush=flush)

print('033[0m', end='')

defget_ip_port(fname):

"""从制定文件(特定格式)中,取得主机名/主机ip/端口

output:存储输出的对象"""

try:

with open(fname, 'r') as fobj:

forlineinfobj.readlines():

ifline !='n'andnotmatch('#', line): # 过滤空行和注释行

list_tmp = split('[-:]', line)

server_name = list_tmp[0]

server_ip = list_tmp[1]

port = int(list_tmp[2])

yield(server_name, server_ip, port)

exceptException as err:

print_color('{}n'.format(err))

exit(10)

defcreate_sshclient(server_ip, port, output):

"""根据命令行提供的参数,建立到远程server的ssh链接.这段本应在run_command()函数内部。

摘出来的目的是为了让sftp功能也通过sshclient对象来创建sftp对象,因为初步观察t.connect()方法在使用key时有问题

output:存储输出的对象"""

local_client = threading.local() # 多线程中每个线程要在函数内某些保持自己特定值

local_client.client = SSHClient()

local_client.client.set_missing_host_key_policy(AutoAddPolicy())

try:

local_client.client.connect(server_ip, port=port, username=arguments['-u'], password=arguments['-p'], key_filename=arguments['--pkey'])

exceptException as err:# 有异常,打印异常,并返回'error'

output.write('{}----{} ssh connect error: {}n'.format(' '*4, server_ip, err), color=31)

return'error'

else:

returnlocal_client.client# 返回的client对象在每个线程内是不同的

# ----------

# run_command()执行远程命令

# ----------

defrun_command(client, output):

"""

执行远程命令的主函数

client: paramiko.client.SSHClient object

output: 存储输出的对象

"""

# stdout 假如通过分号提供单行的多条命令,所有命令的输出(在linux终端会输出的内容)都会存储于stdout

# 据观察,下面三个变量的特点是无论"如何引用过一次"之后,其内容就会清空

# 有readlines()的地方都是流,用过之后就没有了

stdin, stdout, stderr = client.exec_command(arguments[''])

copy_out, copy_err = stdout.readlines(), stderr.readlines()

iflen(copy_out)andlen(copy_err):

output.write('%s----result:n'% (' '*8))

foriincopy_out:

output.write('%s%s'% (' '*12, i))

foriincopy_err:

output.write('%s%s'% (' '*12, i), color=31)

ifnotarguments['--skip-err']:# 忽略命令执行错误的情况

output.print_lock()

exit(10)

eliflen(copy_out):

output.write('%s----result:n'% (' '*8))

foriincopy_out:

output.write('%s%s'% (' '*12, i))

eliflen(copy_err):

output.write('%s----error:n'% (' '*8), color=31)

foriincopy_err:

output.write('%s%s'% (' '*12, i), color=31)

ifnotarguments['--skip-err']:

client.close()

output.print_lock()

exit(10)

client.close()

# ----------

# sftp_transfer() 远程传输文件的主函数

# ----------

defsftp_transfer(source_path, destination_path, method, client, output):

"""

文件传输的 主函数

paramiko的sftp client传输,只能单个文件作为参数,并且不会保留文件的时间信息,这两点都需要代码里额外处理

client: paramiko.client.SSHClient object

output:存储输出的对象

"""

sftp = client.open_sftp()

ifplatform.system() =='Windows':

'''''根据put或get,将windows路径中的 分隔符替换为 / '''

ifarguments["put"]:

source_path = source_path.replace('', '/')

elifarguments["get"]:

destination_path = destination_path.replace('', '/')

# -----下面定义sftp_transfer()函数所需的一些子函数-----

defprocess_arg_dir(target):

"""处理目录时,检查用户输入,在路径后面加上/"""

ifnottarget.endswith('/'):

target = target + '/'

returntarget

defsftp_put(src, dst, space):

"""封装put,增加相应输出,并依据m_time和size判断两端文件一致性,决定是否传输该文件"""

ifcheck_remote_path(dst) =='file':

src_stat = stat(src)

dst_stat = sftp.stat(dst)

else:

src_stat = ''

dst_stat = ''

if(src_stat ==''anddst_stat =='')ornot(floor(src_stat.st_mtime) == dst_stat.st_mtimeandsrc_stat.st_size == dst_stat.st_size):

try:

sftp.put(src, dst)

output.write('%s%sn'% (' '* space, src))

exceptException as err:

output.write('%s----Uploading %s Failedn'% (' '* (space-4), src), color=31)

output.write('{}----{}n'.format(' '* (space-4), err), color=31)

client.close()

output.print_lock()

exit(10)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值