kyutil 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kyutil-0.2.1/PKG-INFO +51 -0
- kyutil-0.2.1/README.md +9 -0
- kyutil-0.2.1/kyutil/FTPUtils.py +343 -0
- kyutil-0.2.1/kyutil/__init__.py +1 -0
- kyutil-0.2.1/kyutil/ansible.py +74 -0
- kyutil-0.2.1/kyutil/base.py +356 -0
- kyutil-0.2.1/kyutil/base64_util.py +7 -0
- kyutil-0.2.1/kyutil/build.py +293 -0
- kyutil-0.2.1/kyutil/build_base.py +1140 -0
- kyutil-0.2.1/kyutil/build_ns8.py +265 -0
- kyutil-0.2.1/kyutil/celery_util.py +58 -0
- kyutil-0.2.1/kyutil/clean_disk.py +134 -0
- kyutil-0.2.1/kyutil/cmd.py +0 -0
- kyutil-0.2.1/kyutil/comps_ks.py +446 -0
- kyutil-0.2.1/kyutil/config.py +56 -0
- kyutil-0.2.1/kyutil/constant.py +484 -0
- kyutil-0.2.1/kyutil/ctdy_koji.py +647 -0
- kyutil-0.2.1/kyutil/data.py +56 -0
- kyutil-0.2.1/kyutil/date_utils.py +15 -0
- kyutil-0.2.1/kyutil/decorators.py +72 -0
- kyutil-0.2.1/kyutil/download.py +173 -0
- kyutil-0.2.1/kyutil/enums.py +79 -0
- kyutil-0.2.1/kyutil/env.py +245 -0
- kyutil-0.2.1/kyutil/excel.py +503 -0
- kyutil-0.2.1/kyutil/exceptions.py +139 -0
- kyutil-0.2.1/kyutil/file.py +747 -0
- kyutil-0.2.1/kyutil/file_compare.py +359 -0
- kyutil-0.2.1/kyutil/git_utils.py +70 -0
- kyutil-0.2.1/kyutil/host.py +129 -0
- kyutil-0.2.1/kyutil/hostinit.py +81 -0
- kyutil-0.2.1/kyutil/http_util.py +153 -0
- kyutil-0.2.1/kyutil/inject_ks.py +78 -0
- kyutil-0.2.1/kyutil/iso_check.py +779 -0
- kyutil-0.2.1/kyutil/iso_utils.py +681 -0
- kyutil-0.2.1/kyutil/koji_tools.py +60 -0
- kyutil-0.2.1/kyutil/kyemail.py +148 -0
- kyutil-0.2.1/kyutil/kyexcel.py +314 -0
- kyutil-0.2.1/kyutil/lanxin.py +208 -0
- kyutil-0.2.1/kyutil/log.py +125 -0
- kyutil-0.2.1/kyutil/log_annotation.py +189 -0
- kyutil-0.2.1/kyutil/log_show_tools.py +80 -0
- kyutil-0.2.1/kyutil/mash.py +239 -0
- kyutil-0.2.1/kyutil/mock.py +74 -0
- kyutil-0.2.1/kyutil/model/__init__.py +13 -0
- kyutil-0.2.1/kyutil/model/vo/__init__.py +13 -0
- kyutil-0.2.1/kyutil/model/vo/log_vo.py +65 -0
- kyutil-0.2.1/kyutil/mq.py +23 -0
- kyutil-0.2.1/kyutil/paths.py +93 -0
- kyutil-0.2.1/kyutil/pungi_util.py +254 -0
- kyutil-0.2.1/kyutil/reg_exp.py +25 -0
- kyutil-0.2.1/kyutil/release_bugfix.py +95 -0
- kyutil-0.2.1/kyutil/release_dependency.py +169 -0
- kyutil-0.2.1/kyutil/repo.py +115 -0
- kyutil-0.2.1/kyutil/repo_compare.py +52 -0
- kyutil-0.2.1/kyutil/repo_utils.py +161 -0
- kyutil-0.2.1/kyutil/repomd.py +204 -0
- kyutil-0.2.1/kyutil/response_util.py +269 -0
- kyutil-0.2.1/kyutil/rpm_compare.py +276 -0
- kyutil-0.2.1/kyutil/rpm_operation.py +182 -0
- kyutil-0.2.1/kyutil/rpms.py +194 -0
- kyutil-0.2.1/kyutil/schedule.py +15 -0
- kyutil-0.2.1/kyutil/sheeter.py +93 -0
- kyutil-0.2.1/kyutil/shell.py +272 -0
- kyutil-0.2.1/kyutil/signature.py +148 -0
- kyutil-0.2.1/kyutil/source_pkg.py +227 -0
- kyutil-0.2.1/kyutil/sso_login.py +88 -0
- kyutil-0.2.1/kyutil/t.py +0 -0
- kyutil-0.2.1/kyutil/thread.py +16 -0
- kyutil-0.2.1/kyutil/url.py +56 -0
- kyutil-0.2.1/kyutil/util_repo_info.py +480 -0
- kyutil-0.2.1/kyutil/util_rpm_info.py +1044 -0
- kyutil-0.2.1/kyutil.egg-info/PKG-INFO +51 -0
- kyutil-0.2.1/kyutil.egg-info/SOURCES.txt +76 -0
- kyutil-0.2.1/kyutil.egg-info/dependency_links.txt +1 -0
- kyutil-0.2.1/kyutil.egg-info/requires.txt +35 -0
- kyutil-0.2.1/kyutil.egg-info/top_level.txt +1 -0
- kyutil-0.2.1/pyproject.toml +43 -0
- kyutil-0.2.1/setup.cfg +4 -0
kyutil-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kyutil
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Python utils for kylin python application
|
|
5
|
+
Requires-Python: >=3.7
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: apscheduler>=3.10.4
|
|
8
|
+
Requires-Dist: beautifulsoup4>=4.13.5
|
|
9
|
+
Requires-Dist: celery>=5.2.7
|
|
10
|
+
Requires-Dist: configparser>=5.3.0
|
|
11
|
+
Requires-Dist: cryptography>=45.0.7
|
|
12
|
+
Requires-Dist: defusedxml>=0.7.1
|
|
13
|
+
Requires-Dist: dotenv>=0.9.9
|
|
14
|
+
Requires-Dist: fastapi>=0.103.2
|
|
15
|
+
Requires-Dist: gitpython>=3.1.45
|
|
16
|
+
Requires-Dist: kobo>=0.40.0
|
|
17
|
+
Requires-Dist: koji>=1.35.3
|
|
18
|
+
Requires-Dist: logzero>=1.7.0
|
|
19
|
+
Requires-Dist: lxml>=5.4.0
|
|
20
|
+
Requires-Dist: msgpack>=1.0.5
|
|
21
|
+
Requires-Dist: networkx>=2.6.3
|
|
22
|
+
Requires-Dist: numpy>=1.21.6
|
|
23
|
+
Requires-Dist: openpyxl>=3.1.3
|
|
24
|
+
Requires-Dist: packaging>=24.0
|
|
25
|
+
Requires-Dist: pandas>=1.1.5
|
|
26
|
+
Requires-Dist: paramiko>=3.5.1
|
|
27
|
+
Requires-Dist: pika>=1.3.2
|
|
28
|
+
Requires-Dist: productmd>=1.48
|
|
29
|
+
Requires-Dist: pydantic>=2.5.3
|
|
30
|
+
Requires-Dist: pydot>=2.0.0
|
|
31
|
+
Requires-Dist: pytest>=7.4.4
|
|
32
|
+
Requires-Dist: python-jenkins>=1.8.3
|
|
33
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
34
|
+
Requires-Dist: redis>=5.0.8
|
|
35
|
+
Requires-Dist: requests>=2.31.0
|
|
36
|
+
Requires-Dist: retry>=0.9.2
|
|
37
|
+
Requires-Dist: six>=1.17.0
|
|
38
|
+
Requires-Dist: sqlalchemy>=2.0.43
|
|
39
|
+
Requires-Dist: starlette>=0.27.0
|
|
40
|
+
Requires-Dist: urllib3>=2.0.7
|
|
41
|
+
Requires-Dist: wget>=3.2
|
|
42
|
+
|
|
43
|
+
# kyutil
|
|
44
|
+
麒麟python工具库
|
|
45
|
+
|
|
46
|
+
Changelog:
|
|
47
|
+
### 0.1.12
|
|
48
|
+
- BUGFIX: 修复在mock环境内找不到celery.log的BUG
|
|
49
|
+
Changelog:
|
|
50
|
+
### 0.1.0
|
|
51
|
+
- initial release
|
kyutil-0.2.1/README.md
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""FTPUtils.py"""
|
|
3
|
+
import ftplib
|
|
4
|
+
import os
|
|
5
|
+
import ssl
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
from celery import states
|
|
9
|
+
from logzero import logger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ReusedSslSocket(ssl.SSLSocket):
|
|
13
|
+
def unwrap(self):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# MyFTP_TLS is derived to support TLS_RESUME(filezilla server)
|
|
18
|
+
class MyFtpTLS(ftplib.FTP_TLS):
|
|
19
|
+
"""Explicit FTPS, with shared TLS session"""
|
|
20
|
+
|
|
21
|
+
def ntransfercmd(self, cmd, rest=None):
|
|
22
|
+
conn, size = ftplib.FTP.ntransfercmd(self, cmd, rest)
|
|
23
|
+
if self._prot_p:
|
|
24
|
+
conn = self.context.wrap_socket(
|
|
25
|
+
conn,
|
|
26
|
+
server_hostname=self.host,
|
|
27
|
+
session=self.sock.session)
|
|
28
|
+
|
|
29
|
+
conn.__class__ = ReusedSslSocket
|
|
30
|
+
|
|
31
|
+
return conn, size
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
MSG_FTP_CONNECT_SUCCESS = "FTP链接成功!"
|
|
35
|
+
MSG_FTP_CONNECT_FAILED = "FTP链接失败!"
|
|
36
|
+
MSG_FTP_LOGIN_SUCCESS = "FTP登录成功!"
|
|
37
|
+
MSG_FTP_LOGIN_FAILED = "FTP登录失败!"
|
|
38
|
+
|
|
39
|
+
STOR_CMD = 'STOR '
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FTP(object):
|
|
43
|
+
"""FTPUtils(object)"""
|
|
44
|
+
# 上传过程必需参数列表
|
|
45
|
+
|
|
46
|
+
celery_task_field = [
|
|
47
|
+
'ftpaddress', # ftp连接地址
|
|
48
|
+
'ftpport', # ftp连接端口
|
|
49
|
+
'ftpuser', # ftp连接用户
|
|
50
|
+
'ftppassword', # ftp连接密码
|
|
51
|
+
'upload_path', # ftp上传远端路径
|
|
52
|
+
'isoname', # 上传的本地iso路径
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
# celery参数
|
|
56
|
+
process_rate_status = 0 # 当前处理的进度,取值从0-100
|
|
57
|
+
|
|
58
|
+
def __init__(self, update_status_func, cfg, task_id, logger=logger):
|
|
59
|
+
"""
|
|
60
|
+
:param update_status_func:
|
|
61
|
+
:param cfg:
|
|
62
|
+
cfg = {
|
|
63
|
+
'ftpaddress': "server.kylinos.cn",
|
|
64
|
+
'ftpport': "21",
|
|
65
|
+
'ftpuser': "user_name",
|
|
66
|
+
'ftppassword': "***",
|
|
67
|
+
'upload_path': "/vsftpd/os/ISO/HOSTOS/x86_64/2022/06/test",
|
|
68
|
+
'isoname': "/v10-sp1-0518-kx-x86_64-b3532/iso/Kylin-Server-10-SP1-kx.iso", # 上传的本地iso路径
|
|
69
|
+
}
|
|
70
|
+
:param task_id:
|
|
71
|
+
:param logger:
|
|
72
|
+
"""
|
|
73
|
+
self.logger = logger
|
|
74
|
+
# celery状态更新函数以及taskid
|
|
75
|
+
self.call_back_task_process = update_status_func
|
|
76
|
+
|
|
77
|
+
# 本地ftp目录 默认上传标志关闭
|
|
78
|
+
self.process_rate_status = 10
|
|
79
|
+
self.celery_task_param = cfg
|
|
80
|
+
self.task_id = task_id
|
|
81
|
+
self.ftp_instance = self.ftp_connect()
|
|
82
|
+
self.log_and_send_task_status("ftp登录成功")
|
|
83
|
+
|
|
84
|
+
# log信息输出&日志信息回传&进度设置!
|
|
85
|
+
def log_and_send_task_status(self, msg):
|
|
86
|
+
self.logger.info(f"{msg},【{self.task_id[:4]}】 目前进度{self.process_rate_status}%")
|
|
87
|
+
# 得想办法把logger.info的输出放到msg里面,这样日志信息会携带时间戳等信息
|
|
88
|
+
if self.call_back_task_process is not None:
|
|
89
|
+
if self.process_rate_status == 0:
|
|
90
|
+
self.call_back_task_process(
|
|
91
|
+
state=states.FAILURE,
|
|
92
|
+
meta={'current': 0, 'total': 100, 'status': msg, "exc_type": "RuntimeException", "exc_message": msg}
|
|
93
|
+
)
|
|
94
|
+
elif self.process_rate_status == 100:
|
|
95
|
+
self.call_back_task_process(
|
|
96
|
+
state=states.SUCCESS,
|
|
97
|
+
meta={'current': 100, 'total': 100, 'status': msg, "exc_type": "RuntimeException", "exc_message": msg}
|
|
98
|
+
)
|
|
99
|
+
else:
|
|
100
|
+
self.call_back_task_process(
|
|
101
|
+
state=states.STARTED,
|
|
102
|
+
meta={'current': self.process_rate_status, 'total': 100, 'status': msg, "exc_type": "RuntimeException", "exc_message": msg}
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def ftp_connect(self):
|
|
106
|
+
ftp_address = self.celery_task_param.get('ftpaddress')
|
|
107
|
+
ftp_port = int(self.celery_task_param.get('ftpport'))
|
|
108
|
+
ftp = MyFtpTLS(host=ftp_address, timeout=15)
|
|
109
|
+
ftp.auth()
|
|
110
|
+
ftp.port = ftp_port
|
|
111
|
+
ftp.prot_p()
|
|
112
|
+
ftp.set_pasv(1)
|
|
113
|
+
ftp.encoding = 'utf-8'
|
|
114
|
+
ftp_user = self.celery_task_param.get('ftpuser')
|
|
115
|
+
ftp_password = self.celery_task_param.get('ftppassword')
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
self.process_rate_status = 20
|
|
119
|
+
ftp.connect(ftp_address, ftp_port)
|
|
120
|
+
self.log_and_send_task_status(MSG_FTP_CONNECT_SUCCESS)
|
|
121
|
+
except Exception:
|
|
122
|
+
self.log_and_send_task_status(MSG_FTP_CONNECT_FAILED)
|
|
123
|
+
raise ConnectionError(MSG_FTP_CONNECT_FAILED)
|
|
124
|
+
try:
|
|
125
|
+
self.process_rate_status = 40
|
|
126
|
+
ftp.login(ftp_user, ftp_password) # user/passwd
|
|
127
|
+
ftp.prot_p()
|
|
128
|
+
self.log_and_send_task_status(MSG_FTP_LOGIN_SUCCESS)
|
|
129
|
+
except Exception:
|
|
130
|
+
traceback.print_exc()
|
|
131
|
+
self.log_and_send_task_status(MSG_FTP_LOGIN_FAILED)
|
|
132
|
+
raise ConnectionError(MSG_FTP_LOGIN_FAILED)
|
|
133
|
+
return ftp
|
|
134
|
+
|
|
135
|
+
def is_same_size(self, local_file, remote_file):
|
|
136
|
+
"""
|
|
137
|
+
判断远程文件和本地文件大小是否一致
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
local_file: 本地文件
|
|
141
|
+
remote_file: 远程文件
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
|
|
145
|
+
"""
|
|
146
|
+
try:
|
|
147
|
+
remote_file_size = self.ftp_instance.size(remote_file)
|
|
148
|
+
except Exception as err:
|
|
149
|
+
self.logger.debug("get remote file_size failed, Err:%s" % err)
|
|
150
|
+
remote_file_size = -1
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
local_file_size = os.path.getsize(local_file)
|
|
154
|
+
except Exception as err:
|
|
155
|
+
self.logger.debug("get local file_size failed, Err:%s" % err)
|
|
156
|
+
local_file_size = -1
|
|
157
|
+
|
|
158
|
+
result = True if (remote_file_size == local_file_size) else False
|
|
159
|
+
|
|
160
|
+
return result, remote_file_size, local_file_size
|
|
161
|
+
|
|
162
|
+
def upload_file(self, local_file, remote_file, ftp):
|
|
163
|
+
# 本地是否有此文件
|
|
164
|
+
if not os.path.exists(local_file):
|
|
165
|
+
self.logger.debug('no such file or directory %s.' % local_file)
|
|
166
|
+
return False
|
|
167
|
+
|
|
168
|
+
result, remote_file_size, local_file_size = self.is_same_size(local_file, remote_file)
|
|
169
|
+
if not result:
|
|
170
|
+
self.logger.debug('remote_file %s is not exist, now trying to upload...' % remote_file)
|
|
171
|
+
buff_size = 8192
|
|
172
|
+
try:
|
|
173
|
+
with open(local_file, 'rb') as file_handler:
|
|
174
|
+
if ftp.storbinary(STOR_CMD + remote_file, file_handler, buff_size):
|
|
175
|
+
result, remote_file_size, local_file_size = self.is_same_size(local_file, remote_file)
|
|
176
|
+
except Exception as err:
|
|
177
|
+
self.logger.debug(
|
|
178
|
+
'some error happened in storbinary file :%s. Err:%s' % (local_file, err))
|
|
179
|
+
result = False
|
|
180
|
+
|
|
181
|
+
self.logger.debug('Upload 【%s】 %s , remote_file_size = %d, local_file_size = %d.' \
|
|
182
|
+
% (
|
|
183
|
+
remote_file, 'success' if (result is True) else 'failed', remote_file_size,
|
|
184
|
+
local_file_size))
|
|
185
|
+
self.logger.info('Upload 【%s】 %s , remote_file_size = %d, local_file_size = %d.' \
|
|
186
|
+
% (
|
|
187
|
+
remote_file, 'success' if (result is True) else 'failed', remote_file_size,
|
|
188
|
+
local_file_size))
|
|
189
|
+
|
|
190
|
+
# 01-ftp-connect连接及登录
|
|
191
|
+
|
|
192
|
+
# 02-创建远端文件目录
|
|
193
|
+
def create_remote_dir(self, upload_path):
|
|
194
|
+
try:
|
|
195
|
+
self.ftp_instance.cwd(upload_path)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
self.log_and_send_task_status(f"ftp cwd异常:{e}")
|
|
198
|
+
self.ftp_instance.cwd('/')
|
|
199
|
+
# 分割目录名
|
|
200
|
+
base_dir, part_path = self.ftp_instance.pwd(), upload_path.split('/')
|
|
201
|
+
for p in part_path[1:]:
|
|
202
|
+
# 拼接子目录
|
|
203
|
+
base_dir = base_dir + p + '/'
|
|
204
|
+
try:
|
|
205
|
+
# 尝试切换子目录
|
|
206
|
+
self.ftp_instance.cwd(base_dir)
|
|
207
|
+
except Exception as e:
|
|
208
|
+
self.log_and_send_task_status(f"ftp cwd异常:{e}")
|
|
209
|
+
# 不存在创建当前子目录
|
|
210
|
+
self.ftp_instance.mkd(base_dir)
|
|
211
|
+
# 确保最后路径修改
|
|
212
|
+
self.ftp_instance.cwd(upload_path)
|
|
213
|
+
return True
|
|
214
|
+
|
|
215
|
+
# 03-上传本地指定文件
|
|
216
|
+
def ftp_upload_file(self, root_path_iso_path="/opt/integration_iso_files/") -> str:
|
|
217
|
+
iso_file = self.celery_task_param.get('isoname')
|
|
218
|
+
iso_rename = self.celery_task_param.get('filename_rename')
|
|
219
|
+
|
|
220
|
+
buffsize = 1024
|
|
221
|
+
self.ftp_instance.cwd(self.ftp_instance.pwd())
|
|
222
|
+
|
|
223
|
+
with open(root_path_iso_path + iso_file, 'rb') as f:
|
|
224
|
+
self.log_and_send_task_status('正在上传' + str(f))
|
|
225
|
+
if iso_rename is not None:
|
|
226
|
+
self.ftp_instance.storbinary(STOR_CMD + iso_rename, f, buffsize)
|
|
227
|
+
remote_file = str(self.ftp_instance.pwd()) + '/' + iso_rename
|
|
228
|
+
else:
|
|
229
|
+
self.ftp_instance.storbinary(STOR_CMD + iso_file.split('/')[-1], f, buffsize)
|
|
230
|
+
remote_file = str(self.ftp_instance.pwd()) + '/' + iso_file.split('/')[-1]
|
|
231
|
+
return remote_file
|
|
232
|
+
|
|
233
|
+
def instance_cwd(self, remote_path):
|
|
234
|
+
try:
|
|
235
|
+
self.ftp_instance.cwd(remote_path) # 切换工作路径
|
|
236
|
+
except Exception as e:
|
|
237
|
+
self.logger.error('Except INFO:', e)
|
|
238
|
+
base_dir, part_path = self.ftp_instance.pwd(), remote_path.split('/')
|
|
239
|
+
for sub_path in part_path:
|
|
240
|
+
# 针对类似 '/home/billing/scripts/zhf/send' 和 'home/billing/scripts/zhf/send' 两种格式的目录
|
|
241
|
+
# 如果第一个分解后的元素是''这种空字符,说明根目录是从/开始,如果最后一个是''这种空字符,说明目录是以/结束
|
|
242
|
+
# 例如 /home/billing/scripts/zhf/send/ 分解后得到 ['', 'home', 'billing', 'scripts', 'zhf', 'send', '']
|
|
243
|
+
# 首位和尾都不是有效名称
|
|
244
|
+
if '' == sub_path:
|
|
245
|
+
continue
|
|
246
|
+
base_dir = str(os.path.join(base_dir, sub_path)) # base_dir + subpath + '/' # 拼接子目录
|
|
247
|
+
try:
|
|
248
|
+
self.ftp_instance.cwd(base_dir) # 切换到子目录, 不存在则异常
|
|
249
|
+
except Exception as e:
|
|
250
|
+
self.logger.error('Except INFO:', e)
|
|
251
|
+
self.logger.error('remote not exist directory %s , create it.' % base_dir)
|
|
252
|
+
self.ftp_instance.mkd(base_dir) # 不存在创建当前子目录 直到创建所有
|
|
253
|
+
continue
|
|
254
|
+
|
|
255
|
+
# 03-上传目录
|
|
256
|
+
def upload_file_tree(self, local_path, remote_path, recursively):
|
|
257
|
+
# 创建服务器目录 如果服务器目录不存在 就从当前目录创建目标外层目录
|
|
258
|
+
# 打开该远程目录
|
|
259
|
+
self.instance_cwd(remote_path)
|
|
260
|
+
|
|
261
|
+
# 本地目录切换
|
|
262
|
+
try:
|
|
263
|
+
# 远端目录通过ftp对象已经切换到指定目录或创建的指定目录
|
|
264
|
+
file_list = os.listdir(local_path)
|
|
265
|
+
for file_name in file_list:
|
|
266
|
+
if os.path.isdir(os.path.join(local_path, file_name)):
|
|
267
|
+
self.logger.debug('%s is a directory...' % file_name)
|
|
268
|
+
if recursively: # 递归目录上传
|
|
269
|
+
# 创建相关的子目录 创建不成功则目录已存在
|
|
270
|
+
try:
|
|
271
|
+
cwd = self.ftp_instance.pwd()
|
|
272
|
+
self.ftp_instance.cwd(file_name) # 如果cwd成功 则表示该目录存在 退出到上一级
|
|
273
|
+
self.ftp_instance.cwd(cwd)
|
|
274
|
+
except Exception as e:
|
|
275
|
+
self.logger.error(
|
|
276
|
+
'check remote directory %s not eixst, now trying to create it! Except INFO:%s.' % (
|
|
277
|
+
file_name, e))
|
|
278
|
+
self.ftp_instance.mkd(file_name)
|
|
279
|
+
|
|
280
|
+
self.logger.debug('trying to upload directory %s --> %s ...' % (file_name, remote_path))
|
|
281
|
+
p_local_path = os.path.join(local_path, file_name)
|
|
282
|
+
p_remote_path = os.path.join(self.ftp_instance.pwd(), file_name)
|
|
283
|
+
self.upload_file_tree(p_local_path, p_remote_path, recursively)
|
|
284
|
+
# 对于递归 ftp 每次传输完成后需要切换目录到上一级
|
|
285
|
+
self.ftp_instance.cwd("..")
|
|
286
|
+
else:
|
|
287
|
+
self.logger.debug(
|
|
288
|
+
'translate mode is UnRecursively, %s is a directory, continue ...' % file_name)
|
|
289
|
+
continue
|
|
290
|
+
else:
|
|
291
|
+
# 是文件 直接上传
|
|
292
|
+
local_file = os.path.join(local_path, file_name)
|
|
293
|
+
remote_file = os.path.join(remote_path, file_name)
|
|
294
|
+
self.upload_file(local_file, remote_file, self.ftp_instance)
|
|
295
|
+
except Exception as e:
|
|
296
|
+
traceback.format_exc()
|
|
297
|
+
self.logger.debug(f'FTP 无法上传目录。 错误消息:{e}')
|
|
298
|
+
|
|
299
|
+
# 04-核实iso名称
|
|
300
|
+
def ftp_check_iso(self):
|
|
301
|
+
upload_path = self.celery_task_param.get('upload_path')
|
|
302
|
+
iso_file = self.celery_task_param.get('isoname')
|
|
303
|
+
iso_rename = self.celery_task_param.get('filename_rename')
|
|
304
|
+
|
|
305
|
+
flist = self.ftp_instance.nlst(upload_path)
|
|
306
|
+
for f in flist:
|
|
307
|
+
if f.endswith('.iso') and f.split('/')[-1] == iso_file.split('/')[-1] or (
|
|
308
|
+
f.endswith('.iso') and f.split('/')[-1] == iso_rename):
|
|
309
|
+
self.ftp_instance.quit()
|
|
310
|
+
return f
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
# 外部调用ftp主函数入口
|
|
314
|
+
def upload_iso(self, root_path_iso_path="/opt/integration_iso_files/") -> str:
|
|
315
|
+
try:
|
|
316
|
+
self.process_rate_status = 50
|
|
317
|
+
self.create_remote_dir(self.celery_task_param.get('upload_path'))
|
|
318
|
+
self.log_and_send_task_status(
|
|
319
|
+
f'ftp上传路径:{self.celery_task_param.get("ftpaddress")}:{self.ftp_instance.pwd()}')
|
|
320
|
+
|
|
321
|
+
self.process_rate_status = 60
|
|
322
|
+
|
|
323
|
+
# 是否为空
|
|
324
|
+
if self.ftp_instance.nlst():
|
|
325
|
+
raise RuntimeError("目录不为空,无法上传")
|
|
326
|
+
|
|
327
|
+
self.log_and_send_task_status("ftp开始上传本地iso文件")
|
|
328
|
+
local_dir = root_path_iso_path + self.celery_task_param.get("isoname")[
|
|
329
|
+
:self.celery_task_param.get("isoname").rfind("/")]
|
|
330
|
+
self.upload_file_tree(local_dir, self.celery_task_param.get('upload_path'), True)
|
|
331
|
+
|
|
332
|
+
self.process_rate_status = 80
|
|
333
|
+
self.log_and_send_task_status("校验ftp远端iso文件")
|
|
334
|
+
if not self.ftp_check_iso():
|
|
335
|
+
self.log_and_send_task_status("ftp上传iso失败")
|
|
336
|
+
|
|
337
|
+
self.process_rate_status = 100
|
|
338
|
+
self.log_and_send_task_status("ftp上传iso完成")
|
|
339
|
+
return self.celery_task_param.get('upload_path')
|
|
340
|
+
except Exception as e:
|
|
341
|
+
self.process_rate_status = 0
|
|
342
|
+
self.log_and_send_task_status(f"ftp上传失败!{e}")
|
|
343
|
+
return ""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: UTF-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
@Project :kyutil
|
|
5
|
+
@File :ansible.py
|
|
6
|
+
@IDE :PyCharm
|
|
7
|
+
@Author :xuyong@kylinos.cn
|
|
8
|
+
@Date :2025/5/22 下午4:39
|
|
9
|
+
@Desc :说明:
|
|
10
|
+
"""
|
|
11
|
+
import base64
|
|
12
|
+
import os
|
|
13
|
+
import random
|
|
14
|
+
import sys
|
|
15
|
+
from shutil import copyfile
|
|
16
|
+
|
|
17
|
+
import ansible_runner
|
|
18
|
+
from logzero import logger as log
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def gen_random_base_64_str() -> bytes:
|
|
22
|
+
flag = "flag{**flag**}".encode("utf-8")
|
|
23
|
+
b64list = [lambda x: base64.b16encode(x), lambda x: base64.b32encode(x), lambda x: base64.b64encode(x)]
|
|
24
|
+
for _ in range(10):
|
|
25
|
+
choice = random.choice([0, 1, 2])
|
|
26
|
+
flag = b64list[choice](flag)
|
|
27
|
+
encoded = {
|
|
28
|
+
'16': lambda x: base64.b16encode(x),
|
|
29
|
+
'32': lambda x: base64.b32encode(x),
|
|
30
|
+
'64': lambda x: base64.b64encode(x)
|
|
31
|
+
}
|
|
32
|
+
choice = random.choice(['16', '32', '64'])
|
|
33
|
+
flag = encoded[choice](flag)
|
|
34
|
+
return flag[7:32]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def execute_playbook(operation_type, host_ip, logger=log) -> bool:
|
|
38
|
+
bp_prefix = "/opt/ctdy/build_iso_server/src/app/bp/"
|
|
39
|
+
b64random = bytes.decode(gen_random_base_64_str())
|
|
40
|
+
host_file = f"{bp_prefix}ansible/hosts.{str(b64random)}"
|
|
41
|
+
random_file = f"{bp_prefix}ansible/{operation_type}.yaml." + str(b64random)
|
|
42
|
+
|
|
43
|
+
copyfile(f"{bp_prefix}ansible/{operation_type}.yaml", random_file)
|
|
44
|
+
copyfile(bp_prefix + "ansible/hosts", host_file)
|
|
45
|
+
ssh_pass_sk_raw = "RmExYzBuQEt5bGluMTAyMzYxCg=="
|
|
46
|
+
ssh_pass_sk = base64.b64decode(bytes(ssh_pass_sk_raw, 'utf-8')).decode()
|
|
47
|
+
with open(host_file, encoding="utf-8", mode="a") as f:
|
|
48
|
+
f.write("\n" + host_ip + " ansible_ssh_user=root ansible_ssh_pass=\"" + ssh_pass_sk.strip() + "\"")
|
|
49
|
+
out, err, rc = ansible_runner.run_command(
|
|
50
|
+
executable_cmd='ansible-playbook',
|
|
51
|
+
cmdline_args=[random_file, '-i', host_file, '--extra-vars', "ansible_become_pass=" + ssh_pass_sk],
|
|
52
|
+
input_fd=sys.stdin,
|
|
53
|
+
output_fd=sys.stdout,
|
|
54
|
+
error_fd=sys.stderr
|
|
55
|
+
)
|
|
56
|
+
os.remove(random_file)
|
|
57
|
+
os.remove(host_file)
|
|
58
|
+
if rc == 0:
|
|
59
|
+
logger.debug(f"Playbook执行成功:{out}. RC:{rc} ERR:{err}")
|
|
60
|
+
return True
|
|
61
|
+
else:
|
|
62
|
+
logger.error(f"Playbook执行失败:{err}. RC:{rc}")
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def hosts_restart(host_list, logger=log):
|
|
67
|
+
oks, errs = [], []
|
|
68
|
+
for host in host_list:
|
|
69
|
+
ok = execute_playbook("restart", host.hostip)
|
|
70
|
+
if ok:
|
|
71
|
+
oks.append(host.hostip)
|
|
72
|
+
else:
|
|
73
|
+
errs.append(host.hostip)
|
|
74
|
+
return oks, errs
|