init
This commit is contained in:
0
common/__init__.py
Normal file
0
common/__init__.py
Normal file
96
common/config.py
Normal file
96
common/config.py
Normal file
@@ -0,0 +1,96 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 配置管理模块
|
||||
|
||||
import json
|
||||
import os
|
||||
import yaml
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
class ConfigManager:
|
||||
"""配置管理类,负责加载和管理各种配置"""
|
||||
|
||||
@staticmethod
|
||||
def load_json_config(config_path: str) -> Dict:
|
||||
"""
|
||||
加载JSON格式的配置文件
|
||||
|
||||
Args:
|
||||
config_path: JSON配置文件路径
|
||||
|
||||
Returns:
|
||||
加载的配置字典
|
||||
"""
|
||||
try:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(f"加载配置文件 {config_path} 失败: {str(e)}")
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def load_yaml_config(config_path: str) -> Dict:
|
||||
"""
|
||||
加载YAML格式的配置文件
|
||||
|
||||
Args:
|
||||
config_path: YAML配置文件路径
|
||||
|
||||
Returns:
|
||||
加载的配置字典
|
||||
"""
|
||||
try:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
return yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
print(f"加载配置文件 {config_path} 失败: {str(e)}")
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def save_json_config(config_path: str, config_data: Dict) -> bool:
|
||||
"""
|
||||
保存配置到JSON文件
|
||||
|
||||
Args:
|
||||
config_path: 目标JSON文件路径
|
||||
config_data: 要保存的配置数据
|
||||
|
||||
Returns:
|
||||
是否保存成功
|
||||
"""
|
||||
try:
|
||||
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(config_data, f, indent=4, ensure_ascii=False)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"保存配置到 {config_path} 失败: {str(e)}")
|
||||
return False
|
||||
|
||||
# 默认仓库配置
|
||||
DEFAULT_REPO_CONFIGS = [
|
||||
{
|
||||
"product": "V10-SP3-2403",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP3",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP2",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP1.1",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "mips64el", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
# 其他仓库配置可以通过配置文件加载...
|
||||
]
|
||||
155
common/file_utils.py
Normal file
155
common/file_utils.py
Normal file
@@ -0,0 +1,155 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 文件处理工具模块
|
||||
|
||||
import os
|
||||
import bz2
|
||||
import gzip
|
||||
import lzma
|
||||
import shutil
|
||||
import logging
|
||||
from typing import Optional, List, Tuple, Union, IO
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger("file_utils")
|
||||
|
||||
class FileUtils:
|
||||
"""文件处理工具类,处理文件操作和压缩文件"""
|
||||
|
||||
@staticmethod
|
||||
def ensure_dir_exists(directory: str) -> bool:
|
||||
"""
|
||||
确保目录存在,如果不存在则创建
|
||||
|
||||
Args:
|
||||
directory: 目录路径
|
||||
|
||||
Returns:
|
||||
是否成功创建或已存在
|
||||
"""
|
||||
try:
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"创建目录失败: {directory}, 错误: {str(e)}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def extract_bz2(source_path: str, target_path: str) -> bool:
|
||||
"""
|
||||
解压BZ2文件
|
||||
|
||||
Args:
|
||||
source_path: BZ2文件路径
|
||||
target_path: 解压后文件路径
|
||||
|
||||
Returns:
|
||||
是否解压成功
|
||||
"""
|
||||
try:
|
||||
logger.info(f"解压BZ2文件: {source_path} -> {target_path}")
|
||||
with bz2.BZ2File(source_path) as bz2_file:
|
||||
data = bz2_file.read()
|
||||
|
||||
with open(target_path, 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"解压BZ2文件失败: {source_path}, 错误: {str(e)}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def extract_xz(source_path: str, target_path: str) -> bool:
|
||||
"""
|
||||
解压XZ文件
|
||||
|
||||
Args:
|
||||
source_path: XZ文件路径
|
||||
target_path: 解压后文件路径
|
||||
|
||||
Returns:
|
||||
是否解压成功
|
||||
"""
|
||||
try:
|
||||
logger.info(f"解压XZ文件: {source_path} -> {target_path}")
|
||||
with lzma.open(source_path, 'rb') as input_file:
|
||||
with open(target_path, 'wb') as output_file:
|
||||
shutil.copyfileobj(input_file, output_file)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"解压XZ文件失败: {source_path}, 错误: {str(e)}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def extract_gz(source_path: str, target_path: str) -> bool:
|
||||
"""
|
||||
解压GZ文件
|
||||
|
||||
Args:
|
||||
source_path: GZ文件路径
|
||||
target_path: 解压后文件路径
|
||||
|
||||
Returns:
|
||||
是否解压成功
|
||||
"""
|
||||
try:
|
||||
logger.info(f"解压GZ文件: {source_path} -> {target_path}")
|
||||
with gzip.open(source_path, 'rb') as input_file:
|
||||
with open(target_path, 'wb') as output_file:
|
||||
shutil.copyfileobj(input_file, output_file)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"解压GZ文件失败: {source_path}, 错误: {str(e)}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def write_to_file(file_path: str, content: str, mode: str = 'w', encoding: str = 'utf-8') -> bool:
|
||||
"""
|
||||
写入内容到文件
|
||||
|
||||
Args:
|
||||
file_path: 文件路径
|
||||
content: 文件内容
|
||||
mode: 文件打开模式
|
||||
encoding: 编码方式
|
||||
|
||||
Returns:
|
||||
是否写入成功
|
||||
"""
|
||||
try:
|
||||
# 确保目录存在
|
||||
directory = os.path.dirname(file_path)
|
||||
if directory and not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
with open(file_path, mode, encoding=encoding) as f:
|
||||
f.write(content)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"写入文件失败: {file_path}, 错误: {str(e)}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def read_file(file_path: str, mode: str = 'r', encoding: Optional[str] = 'utf-8') -> Optional[Union[str, bytes]]:
|
||||
"""
|
||||
读取文件内容
|
||||
|
||||
Args:
|
||||
file_path: 文件路径
|
||||
mode: 文件打开模式
|
||||
encoding: 编码方式(二进制模式下为None)
|
||||
|
||||
Returns:
|
||||
文件内容,失败返回None
|
||||
"""
|
||||
try:
|
||||
with open(file_path, mode, encoding=encoding) as f:
|
||||
return f.read()
|
||||
except Exception as e:
|
||||
logger.error(f"读取文件失败: {file_path}, 错误: {str(e)}")
|
||||
return None
|
||||
103
common/http_utils.py
Normal file
103
common/http_utils.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# HTTP工具模块
|
||||
|
||||
import requests
|
||||
import logging
|
||||
from typing import Dict, Optional, Union, Any
|
||||
from urllib.parse import urljoin
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger("http_utils")
|
||||
|
||||
class HttpClient:
|
||||
"""HTTP客户端工具类,处理常见的HTTP请求操作"""
|
||||
|
||||
DEFAULT_HEADERS = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get(url: str, headers: Optional[Dict[str, str]] = None,
|
||||
params: Optional[Dict[str, Any]] = None, timeout: int = 30,
|
||||
verify: bool = False) -> Optional[requests.Response]:
|
||||
"""
|
||||
发送GET请求
|
||||
|
||||
Args:
|
||||
url: 请求的URL
|
||||
headers: 请求头
|
||||
params: 查询参数
|
||||
timeout: 超时时间(秒)
|
||||
verify: 是否验证SSL证书
|
||||
|
||||
Returns:
|
||||
Response对象,请求失败返回None
|
||||
"""
|
||||
_headers = headers or HttpClient.DEFAULT_HEADERS
|
||||
|
||||
try:
|
||||
logger.debug(f"发送GET请求: {url}")
|
||||
response = requests.get(url, headers=_headers, params=params,
|
||||
timeout=timeout, verify=verify)
|
||||
|
||||
if response.status_code != 200:
|
||||
logger.warning(f"请求失败,状态码:{response.status_code}, URL: {url}")
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
logger.error(f"请求异常: {url}, 错误: {str(e)}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def download_file(url: str, save_path: str, headers: Optional[Dict[str, str]] = None,
|
||||
timeout: int = 60, verify: bool = False, chunk_size: int = 8192) -> bool:
|
||||
"""
|
||||
下载文件
|
||||
|
||||
Args:
|
||||
url: 文件URL
|
||||
save_path: 保存路径
|
||||
headers: 请求头
|
||||
timeout: 超时时间(秒)
|
||||
verify: 是否验证SSL证书
|
||||
chunk_size: 每次读取的块大小
|
||||
|
||||
Returns:
|
||||
是否下载成功
|
||||
"""
|
||||
_headers = headers or HttpClient.DEFAULT_HEADERS
|
||||
|
||||
try:
|
||||
logger.info(f"正在下载: {url} -> {save_path}")
|
||||
response = requests.get(url, headers=_headers, timeout=timeout,
|
||||
verify=verify, stream=True)
|
||||
|
||||
if response.status_code != 200:
|
||||
logger.error(f"下载失败,状态码: {response.status_code}")
|
||||
return False
|
||||
|
||||
with open(save_path, 'wb') as f:
|
||||
for chunk in response.iter_content(chunk_size=chunk_size):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
|
||||
logger.info(f"下载完成: {save_path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"下载文件异常: {url}, 错误: {str(e)}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def join_url(base_url: str, path: str) -> str:
|
||||
"""
|
||||
拼接URL
|
||||
|
||||
Args:
|
||||
base_url: 基础URL
|
||||
path: 路径
|
||||
|
||||
Returns:
|
||||
完整URL
|
||||
"""
|
||||
return urljoin(base_url.rstrip('/') + '/', path.lstrip('/'))
|
||||
100
common/logger.py
Normal file
100
common/logger.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 日志管理模块
|
||||
|
||||
import os
|
||||
import logging
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
class LoggerManager:
|
||||
"""日志管理类,用于配置和创建日志器"""
|
||||
|
||||
@staticmethod
|
||||
def setup_logger(name: str, log_file: Optional[str] = None,
|
||||
level: int = logging.INFO,
|
||||
format_str: str = '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
console_output: bool = True) -> logging.Logger:
|
||||
"""
|
||||
设置并返回一个日志器
|
||||
|
||||
Args:
|
||||
name: 日志器名称
|
||||
log_file: 日志文件路径,None表示不输出到文件
|
||||
level: 日志级别
|
||||
format_str: 日志格式
|
||||
console_output: 是否输出到控制台
|
||||
|
||||
Returns:
|
||||
配置好的日志器
|
||||
"""
|
||||
# 创建日志器
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(level)
|
||||
|
||||
# 创建格式化器
|
||||
formatter = logging.Formatter(format_str)
|
||||
|
||||
# 如果指定了日志文件
|
||||
if log_file:
|
||||
# 确保日志目录存在
|
||||
log_dir = os.path.dirname(log_file)
|
||||
if log_dir and not os.path.exists(log_dir):
|
||||
os.makedirs(log_dir)
|
||||
|
||||
# 创建文件处理器
|
||||
file_handler = logging.FileHandler(log_file)
|
||||
file_handler.setFormatter(formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
# 如果需要输出到控制台
|
||||
if console_output:
|
||||
# 创建控制台处理器
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setFormatter(formatter)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
return logger
|
||||
|
||||
@staticmethod
|
||||
def get_timestamped_log_path(base_dir: str, prefix: str, ext: str = '.log') -> str:
|
||||
"""
|
||||
生成带时间戳的日志文件路径
|
||||
|
||||
Args:
|
||||
base_dir: 基础目录
|
||||
prefix: 文件名前缀
|
||||
ext: 文件扩展名
|
||||
|
||||
Returns:
|
||||
完整的日志文件路径
|
||||
"""
|
||||
# 确保基础目录存在
|
||||
if not os.path.exists(base_dir):
|
||||
os.makedirs(base_dir)
|
||||
|
||||
# 生成时间戳
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
|
||||
# 构建文件名
|
||||
filename = f"{prefix}_{timestamp}{ext}"
|
||||
|
||||
return os.path.join(base_dir, filename)
|
||||
|
||||
# 创建一个全局日志器
|
||||
def get_logger(name: str = 'repo_test', log_file: Optional[str] = None,
|
||||
level: int = logging.INFO) -> logging.Logger:
|
||||
"""
|
||||
获取全局日志器
|
||||
|
||||
Args:
|
||||
name: 日志器名称
|
||||
log_file: 日志文件路径
|
||||
level: 日志级别
|
||||
|
||||
Returns:
|
||||
日志器
|
||||
"""
|
||||
return LoggerManager.setup_logger(name, log_file, level)
|
||||
|
||||
# 默认日志器
|
||||
default_logger = get_logger()
|
||||
95
common/rpm.py
Normal file
95
common/rpm.py
Normal file
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import os
|
||||
import glob
|
||||
import xml.etree.ElementTree as ET
|
||||
import tempfile
|
||||
import requests
|
||||
|
||||
class YumRepoParser:
|
||||
def __init__(self, config_dir='/etc/yum.repo.d/', config_files=None):
|
||||
self.config_dir = config_dir
|
||||
self.config_files = config_files or self._discover_config_files()
|
||||
|
||||
def _discover_config_files(self):
|
||||
"""
|
||||
自动发现配置文件(*.repo)
|
||||
"""
|
||||
pattern = os.path.join(self.config_dir, '*.repo')
|
||||
return glob.glob(pattern)
|
||||
|
||||
def parse_repo_metadata(self, repo_path_or_url):
|
||||
"""
|
||||
解析指定 yum 仓库的 repodata/primary.xml,支持本地路径和远程 http(s) 地址
|
||||
返回 src 包和二进制包列表
|
||||
"""
|
||||
if repo_path_or_url.startswith('http://') or repo_path_or_url.startswith('https://'):
|
||||
primary_xml_url = repo_path_or_url.rstrip('/') + '/repodata/primary.xml'
|
||||
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
||||
try:
|
||||
resp = requests.get(primary_xml_url, timeout=30)
|
||||
resp.raise_for_status()
|
||||
tmp.write(resp.content)
|
||||
tmp_path = tmp.name
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"下载远程 primary.xml 失败: {primary_xml_url}, 原因: {e}")
|
||||
else:
|
||||
tmp_path = os.path.join(repo_path_or_url, 'repodata', 'primary.xml')
|
||||
if not os.path.exists(tmp_path):
|
||||
raise FileNotFoundError(f"未找到 primary.xml: {repo_path_or_url}")
|
||||
|
||||
src_packages = []
|
||||
bin_packages = []
|
||||
|
||||
tree = ET.parse(tmp_path)
|
||||
root = tree.getroot()
|
||||
|
||||
for package in root.findall('.//package'):
|
||||
pkg_type = package.get('type')
|
||||
name = package.findtext('name')
|
||||
location = package.find('location').get('href')
|
||||
if pkg_type == 'src':
|
||||
src_packages.append({'name': name, 'location': location})
|
||||
else:
|
||||
bin_packages.append({'name': name, 'location': location})
|
||||
|
||||
# 清理临时文件(远程下载时)
|
||||
if repo_path_or_url.startswith('http://') or repo_path_or_url.startswith('https://'):
|
||||
os.remove(tmp_path)
|
||||
|
||||
return src_packages, bin_packages
|
||||
|
||||
def get_all_repos(self):
|
||||
"""
|
||||
解析所有配置文件,返回所有仓库地址列表,支持 baseurl/mirrorlist/metalink
|
||||
"""
|
||||
repos = []
|
||||
for cfg in self.config_files:
|
||||
with open(cfg, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line.startswith('baseurl='):
|
||||
url = line.split('=', 1)[1].strip()
|
||||
repos.append(url)
|
||||
elif line.startswith('mirrorlist='):
|
||||
url = line.split('=', 1)[1].strip()
|
||||
repos.append(url)
|
||||
elif line.startswith('metalink='):
|
||||
url = line.split('=', 1)[1].strip()
|
||||
repos.append(url)
|
||||
return repos
|
||||
|
||||
# 示例用法
|
||||
if __name__ == "__main__":
|
||||
parser = YumRepoParser()
|
||||
repo_urls = parser.get_all_repos()
|
||||
for repo_url in repo_urls:
|
||||
# 假设 repo_url 是本地路径,实际可根据需求下载 repodata/primary.xml
|
||||
try:
|
||||
src_pkgs, bin_pkgs = parser.parse_repo_metadata(repo_url)
|
||||
print(f"仓库: {repo_url}")
|
||||
print(f"源码包: {len(src_pkgs)},二进制包: {len(bin_pkgs)}")
|
||||
except Exception as e:
|
||||
print(f"解析失败: {repo_url},原因: {e}")
|
||||
36
config/fedora/fedora.repo
Normal file
36
config/fedora/fedora.repo
Normal file
@@ -0,0 +1,36 @@
|
||||
[fedora]
|
||||
name=Fedora $releasever - $basearch
|
||||
baseurl=https://mirrors.tuna.tsinghua.edu.cn/fedora/releases/$releasever/Everything/$basearch/os/
|
||||
#metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch
|
||||
enabled=1
|
||||
countme=1
|
||||
metadata_expire=7d
|
||||
repo_gpgcheck=0
|
||||
type=rpm
|
||||
gpgcheck=1
|
||||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch
|
||||
skip_if_unavailable=False
|
||||
|
||||
[fedora-debuginfo]
|
||||
name=Fedora $releasever - $basearch - Debug
|
||||
baseurl=https://mirrors.tuna.tsinghua.edu.cn/fedora/updates/$releasever/Everything/$basearch/
|
||||
#metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-$releasever&arch=$basearch
|
||||
enabled=0
|
||||
metadata_expire=7d
|
||||
repo_gpgcheck=0
|
||||
type=rpm
|
||||
gpgcheck=1
|
||||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch
|
||||
skip_if_unavailable=False
|
||||
|
||||
[fedora-source]
|
||||
name=Fedora $releasever - Source
|
||||
#baseurl=https://mirrors.tuna.tsinghua.edu.cn/fedora/updates/$releasever/Everything/$basearch/
|
||||
metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-source-$releasever&arch=$basearch
|
||||
enabled=0
|
||||
metadata_expire=7d
|
||||
repo_gpgcheck=0
|
||||
type=rpm
|
||||
gpgcheck=1
|
||||
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch
|
||||
skip_if_unavailable=False
|
||||
94
config/repo_configs.json
Normal file
94
config/repo_configs.json
Normal file
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"repo_configs": [
|
||||
{
|
||||
"product": "V10-SP3-2403",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP3",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP2",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP1.1",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "mips64el", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-HPC",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-ZJ",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-2309A",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/2309A/os/adv/lic/",
|
||||
"architectures": ["aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-2309B",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/2309B/os/adv/lic/",
|
||||
"architectures": ["aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "HostOS",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "Host-2309",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "Host-2406",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/HOST/2406/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V7.9",
|
||||
"base_url": "https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V6.10",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V6/V6.10/os/lic/",
|
||||
"architectures": ["x86_64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "8U2",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/8U2/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["BaseOS-updates", "AppStream-updates", "PowerTools-updates", "Plus-updates"]
|
||||
},
|
||||
{
|
||||
"product": "8U8",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/8U8/os/adv/lic/",
|
||||
"architectures": ["x86_64"],
|
||||
"repo_types": ["BaseOS-updates", "AppStream-updates", "BaseOS", "AppStream", "kernel418", "kernel419"]
|
||||
}
|
||||
]
|
||||
}
|
||||
145
main.py
Normal file
145
main.py
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding:utf-8 -*-
|
||||
# 主执行文件
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
from typing import Dict, List, Any
|
||||
|
||||
from common.logger import get_logger, LoggerManager
|
||||
from common.config import ConfigManager, DEFAULT_REPO_CONFIGS
|
||||
from repodata.checker import RepoChecker
|
||||
|
||||
# 设置基本日志级别
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = get_logger("main")
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(description='仓库测试检查工具')
|
||||
|
||||
parser.add_argument('--config', type=str, help='配置文件路径')
|
||||
parser.add_argument('--output-dir', type=str, default='./results', help='输出目录')
|
||||
parser.add_argument('--max-workers', type=int, default=5, help='最大并行检查线程数')
|
||||
parser.add_argument('--product', type=str, help='仅检查指定产品')
|
||||
parser.add_argument('--arch', type=str, help='仅检查指定架构')
|
||||
parser.add_argument('--verbose', action='store_true', help='显示详细日志')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
def load_config(config_path: str) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
加载配置文件
|
||||
|
||||
Args:
|
||||
config_path: 配置文件路径
|
||||
|
||||
Returns:
|
||||
仓库配置列表
|
||||
"""
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(config_path):
|
||||
logger.error(f"配置文件不存在: {config_path}")
|
||||
return DEFAULT_REPO_CONFIGS
|
||||
|
||||
# 根据文件扩展名决定加载方式
|
||||
ext = os.path.splitext(config_path)[1].lower()
|
||||
|
||||
if ext == '.json':
|
||||
config = ConfigManager.load_json_config(config_path)
|
||||
elif ext in ['.yaml', '.yml']:
|
||||
config = ConfigManager.load_yaml_config(config_path)
|
||||
else:
|
||||
logger.error(f"不支持的配置文件格式: {ext}")
|
||||
return DEFAULT_REPO_CONFIGS
|
||||
|
||||
# 检查配置是否有效
|
||||
if not config or not isinstance(config, list):
|
||||
logger.error("无效的配置格式,使用默认配置")
|
||||
return DEFAULT_REPO_CONFIGS
|
||||
|
||||
return config
|
||||
|
||||
def filter_configs(configs: List[Dict[str, Any]], product: str = None, arch: str = None) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
根据条件过滤仓库配置
|
||||
|
||||
Args:
|
||||
configs: 原始配置列表
|
||||
product: 产品名称过滤
|
||||
arch: 架构过滤
|
||||
|
||||
Returns:
|
||||
过滤后的配置列表
|
||||
"""
|
||||
if not product and not arch:
|
||||
return configs
|
||||
|
||||
filtered_configs = []
|
||||
|
||||
for config in configs:
|
||||
# 如果指定了产品,但不匹配,则跳过
|
||||
if product and config["product"] != product:
|
||||
continue
|
||||
|
||||
# 如果指定了架构,需要检查
|
||||
if arch:
|
||||
# 克隆配置但只保留指定架构
|
||||
if arch in config["architectures"]:
|
||||
new_config = config.copy()
|
||||
new_config["architectures"] = [arch]
|
||||
filtered_configs.append(new_config)
|
||||
else:
|
||||
# 没有指定架构,保留所有
|
||||
filtered_configs.append(config)
|
||||
|
||||
return filtered_configs
|
||||
|
||||
def setup_logging(verbose: bool):
|
||||
"""设置日志级别"""
|
||||
level = logging.DEBUG if verbose else logging.INFO
|
||||
logging.getLogger().setLevel(level)
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 解析命令行参数
|
||||
args = parse_arguments()
|
||||
|
||||
# 设置日志级别
|
||||
setup_logging(args.verbose)
|
||||
|
||||
# 加载配置
|
||||
if args.config:
|
||||
logger.info(f"加载配置文件: {args.config}")
|
||||
repo_configs = load_config(args.config)
|
||||
else:
|
||||
logger.info("使用默认配置")
|
||||
repo_configs = DEFAULT_REPO_CONFIGS
|
||||
|
||||
# 过滤配置
|
||||
filtered_configs = filter_configs(repo_configs, args.product, args.arch)
|
||||
|
||||
if not filtered_configs:
|
||||
logger.error("没有匹配的仓库配置")
|
||||
return 1
|
||||
|
||||
logger.info(f"将检查 {len(filtered_configs)} 个仓库配置")
|
||||
|
||||
# 创建输出目录
|
||||
if not os.path.exists(args.output_dir):
|
||||
os.makedirs(args.output_dir)
|
||||
|
||||
# 创建检查器并执行检查
|
||||
checker = RepoChecker(args.output_dir)
|
||||
results = checker.check_all_repositories(filtered_configs, args.max_workers)
|
||||
|
||||
# 检查是否有失败
|
||||
has_failures = any(not result.get("passed", False) for result in results.values())
|
||||
|
||||
# 返回状态码
|
||||
return 1 if has_failures else 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
75
main_test.py
Normal file
75
main_test.py
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding:utf-8 -*-
|
||||
# 主执行文件
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import platform
|
||||
import logging
|
||||
|
||||
from common.logger import get_logger
|
||||
from common.config import ConfigManager, DEFAULT_REPO_CONFIGS
|
||||
from repodata.checker import RepoChecker
|
||||
|
||||
logger = get_logger("main")
|
||||
|
||||
def detect_system_version():
|
||||
# 示例:根据实际需求完善
|
||||
sys_info = platform.uname()
|
||||
# 假设根据发行版和版本号拼接配置文件名
|
||||
distro = getattr(sys_info, "system", "unknown").lower()
|
||||
version = getattr(sys_info, "release", "unknown")
|
||||
config_path = f"config/{distro}_{version}.json"
|
||||
if os.path.exists(config_path):
|
||||
return config_path
|
||||
# 默认配置
|
||||
return "config/repo_configs.json"
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(description='仓库测试检查工具')
|
||||
parser.add_argument('--config', type=str, help='配置文件路径')
|
||||
parser.add_argument('--output-dir', type=str, default='./results', help='输出目录')
|
||||
parser.add_argument('--max-workers', type=int, default=5, help='最大并行检查线程数')
|
||||
parser.add_argument('--product', type=str, help='仅检查指定产品')
|
||||
parser.add_argument('--arch', type=str, help='仅检查指定架构')
|
||||
parser.add_argument('--diff', nargs=2, metavar=('A', 'B'), help='对比两个仓库(名称或链接)')
|
||||
parser.add_argument('--verbose', action='store_true', help='显示详细日志')
|
||||
return parser.parse_args()
|
||||
|
||||
def main():
|
||||
args = parse_arguments()
|
||||
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
|
||||
|
||||
# 配置文件选择逻辑
|
||||
if args.config:
|
||||
config_path = args.config
|
||||
else:
|
||||
config_path = detect_system_version()
|
||||
logger.info(f"自动检测配置文件: {config_path}")
|
||||
|
||||
# 加载配置
|
||||
if os.path.exists(config_path):
|
||||
repo_configs = ConfigManager.load_json_config(config_path)
|
||||
else:
|
||||
logger.warning("未找到配置文件,使用默认配置")
|
||||
repo_configs = DEFAULT_REPO_CONFIGS
|
||||
|
||||
# 过滤配置
|
||||
# ...根据 args.product 和 args.arch 过滤 repo_configs...
|
||||
|
||||
# diff 模式
|
||||
if args.diff:
|
||||
repo_a, repo_b = args.diff
|
||||
# ...实现仓库对比逻辑...
|
||||
logger.info(f"对比仓库: {repo_a} vs {repo_b}")
|
||||
# diff 结果输出
|
||||
return 0
|
||||
|
||||
# 常规检查模式
|
||||
checker = RepoChecker(args.output_dir)
|
||||
# ...执行检查逻辑...
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
114
readme.md
Normal file
114
readme.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# 仓库测试工具套件
|
||||
|
||||
## 项目概述
|
||||
|
||||
本项目是一个综合性的仓库测试工具套件,用于测试和验证软件仓库的各种特性和功能。它提供了一系列工具来检查仓库数据的一致性、软件包签名、哈希值、依赖关系等。
|
||||
|
||||
## 架构设计
|
||||
|
||||
项目已重构为模块化架构,将功能划分为不同的模块,便于维护和扩展。
|
||||
|
||||
### 目录结构
|
||||
|
||||
```
|
||||
repo_test/
|
||||
├── script/
|
||||
│ └── 测试项 #测试脚本
|
||||
│
|
||||
├── common/ # 通用工具模块
|
||||
│ ├── __init__.py
|
||||
│ ├── config.py # 配置管理
|
||||
│ ├── file_utils.py # 文件处理工具
|
||||
│ ├── http_utils.py # HTTP请求工具
|
||||
│ └── logger.py # 日志管理
|
||||
├── config/ # 配置文件目录
|
||||
│ └── repo_configs.json # 仓库配置
|
||||
├── repodata/ # 仓库数据检查模块
|
||||
│ ├── __init__.py
|
||||
│ ├── checker.py # 仓库检查器
|
||||
│ └── parser.py # 仓库数据解析
|
||||
├── main.py # 主执行文件
|
||||
├── requirements.txt # 依赖列表
|
||||
└── README.md # 项目说明
|
||||
```
|
||||
|
||||
## 测试项
|
||||
|
||||
- 仓库repodata数据校验
|
||||
- 软件包签名
|
||||
- 软件包哈希值
|
||||
- 黑名单二次校验
|
||||
- 单个软件包安装、卸载
|
||||
- 源码包下所有二进制包升降级
|
||||
- 软件包依赖检查
|
||||
- 软件包兼容性对比
|
||||
- v10spx 产品升级
|
||||
- v10spx 仓库软件包对比
|
||||
|
||||
## 功能模块
|
||||
|
||||
### 1. 仓库数据检查 (repodata)
|
||||
|
||||
检查仓库元数据的一致性,确保repodata中的软件包列表与实际可下载的软件包一致。
|
||||
|
||||
**用法**:
|
||||
|
||||
```bash
|
||||
python main.py --config config/repo_configs.json
|
||||
```
|
||||
|
||||
### 2. 配置管理 (common.config)
|
||||
|
||||
用于加载和管理各种配置文件,支持JSON、YAML、和yum仓库原生格式。
|
||||
|
||||
### 3. HTTP工具 (common.http_utils)
|
||||
|
||||
处理HTTP请求和下载功能的工具类。
|
||||
|
||||
### 4. 文件工具 (common.file_utils)
|
||||
|
||||
处理文件操作和压缩文件的工具类。
|
||||
|
||||
### 5. 日志管理 (common.logger)
|
||||
|
||||
提供统一的日志记录功能。
|
||||
|
||||
## 安装依赖
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## 命令行参数
|
||||
|
||||
主程序支持以下命令行参数:
|
||||
|
||||
- `--config`: 配置文件路径
|
||||
- `--output-dir`: 输出目录(默认: ./results)
|
||||
- `--max-workers`: 最大并行检查线程数(默认: 5)
|
||||
- `--product`: 仅检查指定产品
|
||||
- `--arch`: 仅检查指定架构
|
||||
- `--verbose`: 显示详细日志
|
||||
|
||||
## 配置文件格式
|
||||
|
||||
配置文件支持JSON和YAML格式,示例如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"repo_configs": [
|
||||
{
|
||||
"product": "V10-SP3",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 输出文件
|
||||
|
||||
- `repo_rpmlist_check_results.log`: 完整测试结果
|
||||
- `repo_rpmlist_check_fail.log`: 仅包含失败的测试结果
|
||||
- `error_*.log`: 各个仓库的详细错误信息
|
||||
0
repodata/__init__.py
Normal file
0
repodata/__init__.py
Normal file
251
repodata/checker.py
Normal file
251
repodata/checker.py
Normal file
@@ -0,0 +1,251 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 仓库检查器模块
|
||||
|
||||
import os
|
||||
import logging
|
||||
from typing import List, Dict, Any, Optional, Tuple
|
||||
import concurrent.futures
|
||||
import time
|
||||
|
||||
from common.logger import get_logger
|
||||
from common.config import ConfigManager
|
||||
from repodata.parser import RepoDataParser
|
||||
|
||||
# 获取日志记录器
|
||||
logger = get_logger(__name__)
|
||||
|
||||
class RepoChecker:
|
||||
"""仓库检查器,用于检查仓库数据的一致性"""
|
||||
|
||||
def __init__(self, output_dir: str = "./results"):
|
||||
"""
|
||||
初始化检查器
|
||||
|
||||
Args:
|
||||
output_dir: 输出目录
|
||||
"""
|
||||
self.output_dir = output_dir
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# 初始化日志文件
|
||||
self.overall_log_file = os.path.join(output_dir, "repo_rpmlist_check_results.log")
|
||||
self.fail_log_file = os.path.join(output_dir, "repo_rpmlist_check_fail.log")
|
||||
|
||||
# 初始化日志文件
|
||||
self._init_log_files()
|
||||
|
||||
def _init_log_files(self):
|
||||
"""初始化日志文件"""
|
||||
# 清空或创建结果文件
|
||||
with open(self.overall_log_file, 'w', encoding='utf-8') as f:
|
||||
f.write("=== 整体测试结果 ===
|
||||
|
||||
")
|
||||
|
||||
with open(self.fail_log_file, 'w', encoding='utf-8') as f:
|
||||
f.write("=== 失败记录 ===
|
||||
|
||||
")
|
||||
|
||||
def check_repository(self, repo_config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
检查单个仓库配置
|
||||
|
||||
Args:
|
||||
repo_config: 仓库配置
|
||||
|
||||
Returns:
|
||||
检查结果
|
||||
"""
|
||||
product = repo_config["product"]
|
||||
base_url = repo_config["base_url"]
|
||||
|
||||
results = {}
|
||||
|
||||
for arch in repo_config["architectures"]:
|
||||
for repo_type in repo_config["repo_types"]:
|
||||
# 构建完整的仓库URL
|
||||
if "8U8" not in product:
|
||||
repo_url = f"{base_url}{repo_type}/{arch}/"
|
||||
else:
|
||||
repo_url = f"{base_url}{repo_type}/{arch}/os/"
|
||||
|
||||
# 生成输出文件名
|
||||
output_file = os.path.join(self.output_dir, f"rpm_check_{product}_{arch}_{repo_type}.txt")
|
||||
|
||||
logger.info(f"开始测试: {product} {arch} {repo_type}")
|
||||
logger.info(f"测试仓库地址: {repo_url}")
|
||||
|
||||
try:
|
||||
# 创建解析器
|
||||
parser = RepoDataParser(repo_url)
|
||||
|
||||
# 获取HTML中的RPM列表
|
||||
html = parser.get_html_content()
|
||||
if not html:
|
||||
raise Exception("获取HTML内容失败")
|
||||
|
||||
rpm_list_html = parser.parse_rpm_list_from_html(html)
|
||||
|
||||
# 获取数据库中的RPM列表
|
||||
sqlite_path = parser.download_and_extract_sqlite()
|
||||
if not sqlite_path:
|
||||
raise Exception("获取数据库文件失败")
|
||||
|
||||
rpm_list_sqlite = parser.get_rpm_list_from_sqlite(sqlite_path)
|
||||
|
||||
# 比较两个列表
|
||||
comparison_result = parser.compare_rpm_lists(rpm_list_html, rpm_list_sqlite)
|
||||
formatted_result = parser.format_comparison_result(comparison_result)
|
||||
|
||||
# 记录结果
|
||||
test_passed = comparison_result["is_identical"]
|
||||
test_key = f"{product}_{arch}_{repo_type}"
|
||||
results[test_key] = {
|
||||
"product": product,
|
||||
"arch": arch,
|
||||
"repo_type": repo_type,
|
||||
"repo_url": repo_url,
|
||||
"passed": test_passed,
|
||||
"result": formatted_result,
|
||||
"html_count": comparison_result["list1_count"],
|
||||
"sqlite_count": comparison_result["list2_count"]
|
||||
}
|
||||
|
||||
# 输出到日志文件
|
||||
self._write_to_overall_log(product, arch, repo_type, repo_url, formatted_result)
|
||||
|
||||
# 如果测试失败,记录到失败日志
|
||||
if not test_passed:
|
||||
self._write_to_fail_log(product, arch, repo_type, repo_url, formatted_result)
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"处理仓库出错: {str(e)}"
|
||||
logger.error(error_msg)
|
||||
|
||||
# 记录错误信息
|
||||
test_key = f"{product}_{arch}_{repo_type}"
|
||||
results[test_key] = {
|
||||
"product": product,
|
||||
"arch": arch,
|
||||
"repo_type": repo_type,
|
||||
"repo_url": repo_url,
|
||||
"passed": False,
|
||||
"error": error_msg
|
||||
}
|
||||
|
||||
# 输出到日志文件
|
||||
self._write_error_to_logs(product, arch, repo_type, repo_url, error_msg)
|
||||
|
||||
return results
|
||||
|
||||
def check_all_repositories(self, repo_configs: List[Dict[str, Any]], max_workers: int = 5) -> Dict[str, Any]:
|
||||
"""
|
||||
并行检查所有仓库
|
||||
|
||||
Args:
|
||||
repo_configs: 仓库配置列表
|
||||
max_workers: 最大并行工作线程数
|
||||
|
||||
Returns:
|
||||
所有检查结果
|
||||
"""
|
||||
all_results = {}
|
||||
start_time = time.time()
|
||||
|
||||
# 使用线程池并行执行检查
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||
# 提交所有检查任务
|
||||
future_to_config = {executor.submit(self.check_repository, config): config for config in repo_configs}
|
||||
|
||||
# 收集结果
|
||||
for future in concurrent.futures.as_completed(future_to_config):
|
||||
config = future_to_config[future]
|
||||
try:
|
||||
result = future.result()
|
||||
all_results.update(result)
|
||||
except Exception as e:
|
||||
logger.error(f"检查仓库 {config['product']} 时出错: {str(e)}")
|
||||
|
||||
end_time = time.time()
|
||||
duration = end_time - start_time
|
||||
|
||||
# 记录总结信息
|
||||
total_tests = len(all_results)
|
||||
passed_tests = sum(1 for result in all_results.values() if result.get("passed", False))
|
||||
|
||||
summary = f"""
|
||||
====== 测试总结 ======
|
||||
总测试数: {total_tests}
|
||||
通过测试: {passed_tests}
|
||||
失败测试: {total_tests - passed_tests}
|
||||
测试耗时: {duration:.2f} 秒
|
||||
"""
|
||||
|
||||
logger.info(summary)
|
||||
|
||||
# 将总结写入整体日志
|
||||
with open(self.overall_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(summary)
|
||||
|
||||
return all_results
|
||||
|
||||
def _write_to_overall_log(self, product: str, arch: str, repo_type: str, repo_url: str, result_lines: List[str]):
|
||||
"""写入结果到整体日志"""
|
||||
with open(self.overall_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(f"
|
||||
=== {product} {arch} {repo_type} ===
|
||||
")
|
||||
f.write(f"Repository URL: {repo_url}
|
||||
")
|
||||
for line in result_lines:
|
||||
f.write(line + '
|
||||
')
|
||||
f.write("
|
||||
")
|
||||
|
||||
def _write_to_fail_log(self, product: str, arch: str, repo_type: str, repo_url: str, result_lines: List[str]):
|
||||
"""写入结果到失败日志"""
|
||||
with open(self.fail_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(f"
|
||||
=== {product} {arch} {repo_type} ===
|
||||
")
|
||||
f.write(f"Repository URL: {repo_url}
|
||||
")
|
||||
for line in result_lines:
|
||||
if "测试通过" not in line:
|
||||
f.write(line + '
|
||||
')
|
||||
f.write("
|
||||
")
|
||||
|
||||
def _write_error_to_logs(self, product: str, arch: str, repo_type: str, repo_url: str, error_msg: str):
|
||||
"""写入错误信息到日志"""
|
||||
# 写入到整体日志
|
||||
with open(self.overall_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(f"
|
||||
=== {product} {arch} {repo_type} ===
|
||||
")
|
||||
f.write(f"Repository URL: {repo_url}
|
||||
")
|
||||
f.write(f"ERROR: {error_msg}
|
||||
|
||||
")
|
||||
|
||||
# 写入到失败日志
|
||||
with open(self.fail_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(f"
|
||||
=== {product} {arch} {repo_type} ===
|
||||
")
|
||||
f.write(f"Repository URL: {repo_url}
|
||||
")
|
||||
f.write(f"ERROR: {error_msg}
|
||||
|
||||
")
|
||||
|
||||
# 写入到单独的错误日志
|
||||
error_log_file = os.path.join(self.output_dir, f"error_{product}_{arch}_{repo_type}.log")
|
||||
with open(error_log_file, 'w', encoding='utf-8') as f:
|
||||
f.write(f"Error processing {repo_url}:
|
||||
{error_msg}")
|
||||
519
repodata/parser.py
Normal file
519
repodata/parser.py
Normal file
@@ -0,0 +1,519 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# 仓库数据解析模块
|
||||
|
||||
import os
|
||||
import sqlite3
|
||||
import logging
|
||||
import xml.etree.ElementTree as ET
|
||||
import time
|
||||
from typing import List, Optional, Dict, Any, Tuple, Set
|
||||
from bs4 import BeautifulSoup
|
||||
from urllib.parse import unquote
|
||||
|
||||
from common.http_utils import HttpClient
|
||||
from common.file_utils import FileUtils
|
||||
|
||||
# 获取日志记录器
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# XML命名空间
|
||||
NAMESPACES = {
|
||||
'common': 'http://linux.duke.edu/metadata/common',
|
||||
'rpm': 'http://linux.duke.edu/metadata/rpm',
|
||||
'repo': 'http://linux.duke.edu/metadata/repo'
|
||||
}
|
||||
|
||||
class RepoDataParser:
|
||||
"""
|
||||
仓库数据解析器,用于解析RPM仓库数据
|
||||
|
||||
支持从HTML页面、SQLite数据库和repodata.xml文件中获取软件包列表
|
||||
"""
|
||||
|
||||
def __init__(self, base_url: str):
|
||||
"""
|
||||
初始化解析器
|
||||
|
||||
Args:
|
||||
base_url: 仓库基础URL
|
||||
"""
|
||||
self.base_url = base_url
|
||||
self.http_client = HttpClient()
|
||||
self.file_utils = FileUtils()
|
||||
self.cache_dir = os.path.join(os.getcwd(), ".cache")
|
||||
|
||||
# 确保缓存目录存在
|
||||
if not os.path.exists(self.cache_dir):
|
||||
os.makedirs(self.cache_dir)
|
||||
|
||||
def get_html_content(self, path: str = "Packages/") -> Optional[str]:
|
||||
"""
|
||||
获取HTML内容
|
||||
|
||||
Args:
|
||||
path: 相对路径,默认为"Packages/"
|
||||
|
||||
Returns:
|
||||
HTML内容,失败返回None
|
||||
"""
|
||||
url = HttpClient.join_url(self.base_url, path)
|
||||
logger.debug(f"获取HTML内容: {url}")
|
||||
|
||||
response = self.http_client.get(url)
|
||||
if not response:
|
||||
logger.warning(f"获取HTML内容失败: {url}")
|
||||
return None
|
||||
|
||||
return response.text
|
||||
|
||||
def parse_rpm_list_from_html(self, html: str) -> List[str]:
|
||||
"""
|
||||
从HTML中解析RPM列表
|
||||
|
||||
Args:
|
||||
html: HTML内容
|
||||
|
||||
Returns:
|
||||
RPM列表
|
||||
"""
|
||||
if not html:
|
||||
logger.warning("HTML内容为空,无法解析")
|
||||
return []
|
||||
|
||||
rpmlist = []
|
||||
try:
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
raw_links = [item.get('href') for item in soup.find_all('a')]
|
||||
|
||||
# 筛选出rpm文件并修正URL编码问题
|
||||
for link in raw_links:
|
||||
if link and link.lower().endswith(".rpm"):
|
||||
# 修正URL编码问题
|
||||
link = unquote(link)
|
||||
rpmlist.append(link)
|
||||
|
||||
logger.info(f"从HTML解析到{len(rpmlist)}个RPM文件")
|
||||
return rpmlist
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"解析HTML内容失败: {str(e)}")
|
||||
return []
|
||||
|
||||
def download_and_extract_sqlite(self) -> Optional[str]:
|
||||
"""
|
||||
下载并解压数据库文件
|
||||
|
||||
Returns:
|
||||
解压后的数据库文件路径,失败返回None
|
||||
"""
|
||||
repodata_url = HttpClient.join_url(self.base_url, "repodata/")
|
||||
response = self.http_client.get(repodata_url)
|
||||
|
||||
if not response:
|
||||
logger.error(f"获取repodata目录失败: {repodata_url}")
|
||||
return None
|
||||
|
||||
html = response.text
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
links = [item.get('href') for item in soup.find_all('a')]
|
||||
|
||||
# 生成缓存文件名
|
||||
cache_key = self.base_url.replace('://', '_').replace('/', '_').replace(':', '_')
|
||||
sqlite_file_path = os.path.join(self.cache_dir, f"{cache_key}_primary.sqlite")
|
||||
|
||||
# 如果缓存文件存在且不超过24小时,直接返回
|
||||
if os.path.exists(sqlite_file_path):
|
||||
file_age = time.time() - os.path.getmtime(sqlite_file_path)
|
||||
if file_age < 86400: # 24小时 = 86400秒
|
||||
logger.info(f"使用缓存的SQLite文件: {sqlite_file_path}")
|
||||
return sqlite_file_path
|
||||
|
||||
# 尝试各种数据库文件格式
|
||||
for link in links:
|
||||
if not link:
|
||||
continue
|
||||
|
||||
# 处理BZ2格式
|
||||
if link.endswith("primary.sqlite.bz2"):
|
||||
sqlite_url = HttpClient.join_url(repodata_url, link)
|
||||
logger.info(f"发现BZ2数据库文件: {sqlite_url}")
|
||||
|
||||
# 下载文件
|
||||
sqlite_bz2_path = os.path.join(self.cache_dir, f"{cache_key}_primary.sqlite.bz2")
|
||||
if not self.http_client.download_file(sqlite_url, sqlite_bz2_path):
|
||||
continue
|
||||
|
||||
# 解压文件
|
||||
if not self.file_utils.extract_bz2(sqlite_bz2_path, sqlite_file_path):
|
||||
continue
|
||||
|
||||
return sqlite_file_path
|
||||
|
||||
# 处理XZ格式
|
||||
elif link.endswith("primary.sqlite.xz"):
|
||||
sqlite_url = HttpClient.join_url(repodata_url, link)
|
||||
logger.info(f"发现XZ数据库文件: {sqlite_url}")
|
||||
|
||||
# 下载文件
|
||||
sqlite_xz_path = os.path.join(self.cache_dir, f"{cache_key}_primary.sqlite.xz")
|
||||
if not self.http_client.download_file(sqlite_url, sqlite_xz_path):
|
||||
continue
|
||||
|
||||
# 解压文件
|
||||
if not self.file_utils.extract_xz(sqlite_xz_path, sqlite_file_path):
|
||||
continue
|
||||
|
||||
return sqlite_file_path
|
||||
|
||||
# 处理GZ格式
|
||||
elif link.endswith("primary.sqlite.gz"):
|
||||
sqlite_url = HttpClient.join_url(repodata_url, link)
|
||||
logger.info(f"发现GZ数据库文件: {sqlite_url}")
|
||||
|
||||
# 下载文件
|
||||
sqlite_gz_path = os.path.join(self.cache_dir, f"{cache_key}_primary.sqlite.gz")
|
||||
if not self.http_client.download_file(sqlite_url, sqlite_gz_path):
|
||||
continue
|
||||
|
||||
# 解压文件
|
||||
if not self.file_utils.extract_gz(sqlite_gz_path, sqlite_file_path):
|
||||
continue
|
||||
|
||||
return sqlite_file_path
|
||||
|
||||
logger.error(f"未找到支持的数据库文件: {repodata_url}")
|
||||
return None
|
||||
|
||||
def get_rpm_list_from_sqlite(self, sqlite_path: str) -> List[str]:
|
||||
"""
|
||||
从SQLite数据库获取RPM列表
|
||||
|
||||
Args:
|
||||
sqlite_path: SQLite数据库文件路径
|
||||
|
||||
Returns:
|
||||
RPM列表
|
||||
"""
|
||||
if not os.path.exists(sqlite_path):
|
||||
logger.error(f"SQLite数据库文件不存在: {sqlite_path}")
|
||||
return []
|
||||
|
||||
rpmlist = []
|
||||
try:
|
||||
# 连接数据库
|
||||
conn = sqlite3.connect(sqlite_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 查询软件包信息
|
||||
cursor.execute("""
|
||||
SELECT name, version, release, epoch, arch
|
||||
FROM packages
|
||||
ORDER BY name
|
||||
""")
|
||||
|
||||
# 生成RPM文件名
|
||||
for row in cursor:
|
||||
name, version, release, epoch, arch = row
|
||||
|
||||
# 处理epoch
|
||||
epoch_str = "" if not epoch or epoch == "0" else f"{epoch}:"
|
||||
|
||||
rpm = f"{name}-{epoch_str}{version}-{release}.{arch}.rpm"
|
||||
rpmlist.append(rpm)
|
||||
|
||||
logger.info(f"从SQLite获取到{len(rpmlist)}个RPM文件")
|
||||
|
||||
# 关闭连接
|
||||
cursor.close()
|
||||
conn.close()
|
||||
|
||||
return rpmlist
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"从SQLite获取RPM列表失败: {str(e)}")
|
||||
return []
|
||||
|
||||
def download_repodata_xml(self) -> Optional[str]:
|
||||
"""
|
||||
下载repodata.xml文件
|
||||
|
||||
Returns:
|
||||
下载的文件路径,失败返回None
|
||||
"""
|
||||
# 首先尝试获取repomd.xml
|
||||
repomd_url = HttpClient.join_url(self.base_url, "repodata/repomd.xml")
|
||||
logger.info(f"尝试下载repomd.xml: {repomd_url}")
|
||||
|
||||
# 生成缓存文件名
|
||||
cache_key = self.base_url.replace('://', '_').replace('/', '_').replace(':', '_')
|
||||
repomd_path = os.path.join(self.cache_dir, f"{cache_key}_repomd.xml")
|
||||
|
||||
# 下载repomd.xml
|
||||
if not self.http_client.download_file(repomd_url, repomd_path):
|
||||
logger.error(f"下载repomd.xml失败: {repomd_url}")
|
||||
return None
|
||||
|
||||
# 解析repomd.xml以找到primary.xml的位置
|
||||
try:
|
||||
tree = ET.parse(repomd_path)
|
||||
root = tree.getroot()
|
||||
|
||||
# 查找primary.xml的位置
|
||||
primary_location = None
|
||||
for data_element in root.findall('.//{http://linux.duke.edu/metadata/repo}data'):
|
||||
if data_element.get('type') == 'primary':
|
||||
location_element = data_element.find('.//{http://linux.duke.edu/metadata/repo}location')
|
||||
if location_element is not None:
|
||||
primary_location = location_element.get('href')
|
||||
break
|
||||
|
||||
if not primary_location:
|
||||
logger.error("在repomd.xml中未找到primary.xml的位置")
|
||||
return None
|
||||
|
||||
# 下载primary.xml
|
||||
primary_url = HttpClient.join_url(self.base_url, primary_location)
|
||||
logger.info(f"找到primary.xml位置: {primary_url}")
|
||||
|
||||
# 检查是否是压缩文件
|
||||
primary_path = os.path.join(self.cache_dir, f"{cache_key}_primary.xml")
|
||||
compressed_path = None
|
||||
|
||||
if primary_location.endswith('.gz'):
|
||||
compressed_path = os.path.join(self.cache_dir, f"{cache_key}_primary.xml.gz")
|
||||
if not self.http_client.download_file(primary_url, compressed_path):
|
||||
logger.error(f"下载primary.xml.gz失败: {primary_url}")
|
||||
return None
|
||||
if not self.file_utils.extract_gz(compressed_path, primary_path):
|
||||
logger.error(f"解压primary.xml.gz失败")
|
||||
return None
|
||||
elif primary_location.endswith('.bz2'):
|
||||
compressed_path = os.path.join(self.cache_dir, f"{cache_key}_primary.xml.bz2")
|
||||
if not self.http_client.download_file(primary_url, compressed_path):
|
||||
logger.error(f"下载primary.xml.bz2失败: {primary_url}")
|
||||
return None
|
||||
if not self.file_utils.extract_bz2(compressed_path, primary_path):
|
||||
logger.error(f"解压primary.xml.bz2失败")
|
||||
return None
|
||||
elif primary_location.endswith('.xz'):
|
||||
compressed_path = os.path.join(self.cache_dir, f"{cache_key}_primary.xml.xz")
|
||||
if not self.http_client.download_file(primary_url, compressed_path):
|
||||
logger.error(f"下载primary.xml.xz失败: {primary_url}")
|
||||
return None
|
||||
if not self.file_utils.extract_xz(compressed_path, primary_path):
|
||||
logger.error(f"解压primary.xml.xz失败")
|
||||
return None
|
||||
else:
|
||||
# 直接下载未压缩的XML
|
||||
if not self.http_client.download_file(primary_url, primary_path):
|
||||
logger.error(f"下载primary.xml失败: {primary_url}")
|
||||
return None
|
||||
|
||||
return primary_path
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理repomd.xml时出错: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_rpm_list_from_xml(self, xml_path: str) -> List[str]:
|
||||
"""
|
||||
从primary.xml文件获取RPM列表
|
||||
|
||||
Args:
|
||||
xml_path: primary.xml文件路径
|
||||
|
||||
Returns:
|
||||
RPM列表
|
||||
"""
|
||||
if not os.path.exists(xml_path):
|
||||
logger.error(f"XML文件不存在: {xml_path}")
|
||||
return []
|
||||
|
||||
rpmlist = []
|
||||
try:
|
||||
# 解析XML文件
|
||||
tree = ET.parse(xml_path)
|
||||
root = tree.getroot()
|
||||
|
||||
# 查找所有包元素
|
||||
for pkg_element in root.findall('.//{http://linux.duke.edu/metadata/common}package'):
|
||||
if pkg_element.get('type') != 'rpm':
|
||||
continue
|
||||
|
||||
# 获取包名
|
||||
name_element = pkg_element.find('.//{http://linux.duke.edu/metadata/common}name')
|
||||
if name_element is None:
|
||||
continue
|
||||
name = name_element.text
|
||||
|
||||
# 获取版本信息
|
||||
version_element = pkg_element.find('.//{http://linux.duke.edu/metadata/common}version')
|
||||
if version_element is None:
|
||||
continue
|
||||
|
||||
epoch = version_element.get('epoch', '0')
|
||||
version = version_element.get('ver', '')
|
||||
release = version_element.get('rel', '')
|
||||
|
||||
# 获取架构
|
||||
arch_element = pkg_element.find('.//{http://linux.duke.edu/metadata/common}arch')
|
||||
if arch_element is None:
|
||||
continue
|
||||
arch = arch_element.text
|
||||
|
||||
# 处理epoch
|
||||
epoch_str = "" if not epoch or epoch == "0" else f"{epoch}:"
|
||||
|
||||
# 生成RPM文件名
|
||||
rpm = f"{name}-{epoch_str}{version}-{release}.{arch}.rpm"
|
||||
rpmlist.append(rpm)
|
||||
|
||||
logger.info(f"从XML获取到{len(rpmlist)}个RPM文件")
|
||||
return rpmlist
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"从XML获取RPM列表失败: {str(e)}")
|
||||
return []
|
||||
|
||||
def compare_rpm_lists(self, list1: List[str], list2: List[str]) -> Dict[str, Any]:
|
||||
"""
|
||||
比较两个RPM列表的差异
|
||||
|
||||
Args:
|
||||
list1: 第一个RPM列表
|
||||
list2: 第二个RPM列表
|
||||
|
||||
Returns:
|
||||
比较结果
|
||||
"""
|
||||
# 标准化RPM名称(去除版本信息,只保留包名)
|
||||
def normalize_rpm_name(rpm: str) -> str:
|
||||
# 移除.rpm后缀
|
||||
if rpm.lower().endswith('.rpm'):
|
||||
rpm = rpm[:-4]
|
||||
return rpm
|
||||
|
||||
# 转换为集合以便比较
|
||||
set1 = set(list1)
|
||||
set2 = set(list2)
|
||||
|
||||
# 计算差异
|
||||
only_in_list1 = sorted(set1 - set2)
|
||||
only_in_list2 = sorted(set2 - set1)
|
||||
|
||||
# 检查是否完全相同
|
||||
is_identical = len(only_in_list1) == 0 and len(only_in_list2) == 0
|
||||
|
||||
# 构建结果
|
||||
result = {
|
||||
"is_identical": is_identical,
|
||||
"only_in_list1": only_in_list1,
|
||||
"only_in_list2": only_in_list2,
|
||||
"list1_count": len(list1),
|
||||
"list2_count": len(list2),
|
||||
"common_count": len(set1.intersection(set2))
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
def format_comparison_result(self, result: Dict[str, Any]) -> List[str]:
|
||||
"""
|
||||
格式化比较结果
|
||||
|
||||
Args:
|
||||
result: 比较结果字典
|
||||
|
||||
Returns:
|
||||
格式化后的文本行列表
|
||||
"""
|
||||
output_lines = []
|
||||
|
||||
# 添加统计信息
|
||||
output_lines.append(f"列表1软件包数量: {result['list1_count']}")
|
||||
output_lines.append(f"列表2软件包数量: {result['list2_count']}")
|
||||
output_lines.append(f"共同软件包数量: {result.get('common_count', 0)}")
|
||||
output_lines.append("")
|
||||
|
||||
if result["is_identical"]:
|
||||
output_lines.append("两个软件包列表完全相同")
|
||||
output_lines.append("#####【测试通过】#####")
|
||||
return output_lines
|
||||
|
||||
# 报告第一个列表独有的项目
|
||||
if result["only_in_list1"]:
|
||||
output_lines.append(f"仅在第一个列表中存在的软件包({len(result['only_in_list1'])}个):")
|
||||
output_lines.extend(result["only_in_list1"])
|
||||
else:
|
||||
output_lines.append("没有仅在第一个列表中存在的软件包。")
|
||||
|
||||
output_lines.append("")
|
||||
|
||||
# 报告第二个列表独有的项目
|
||||
if result["only_in_list2"]:
|
||||
output_lines.append(f"仅在第二个列表中存在的软件包({len(result['only_in_list2'])}个):")
|
||||
output_lines.extend(result["only_in_list2"])
|
||||
else:
|
||||
output_lines.append("没有仅在第二个列表中存在的软件包。")
|
||||
|
||||
return output_lines
|
||||
|
||||
def get_all_rpm_sources(self) -> Dict[str, List[str]]:
|
||||
"""
|
||||
获取所有可用的RPM列表来源
|
||||
|
||||
Returns:
|
||||
包含不同来源RPM列表的字典
|
||||
"""
|
||||
results = {}
|
||||
|
||||
# 1. 从HTML获取RPM列表
|
||||
html = self.get_html_content()
|
||||
if html:
|
||||
rpm_list_html = self.parse_rpm_list_from_html(html)
|
||||
results["html"] = rpm_list_html
|
||||
|
||||
# 2. 从SQLite获取RPM列表
|
||||
sqlite_path = self.download_and_extract_sqlite()
|
||||
if sqlite_path:
|
||||
rpm_list_sqlite = self.get_rpm_list_from_sqlite(sqlite_path)
|
||||
results["sqlite"] = rpm_list_sqlite
|
||||
|
||||
# 3. 从XML获取RPM列表
|
||||
xml_path = self.download_repodata_xml()
|
||||
if xml_path:
|
||||
rpm_list_xml = self.get_rpm_list_from_xml(xml_path)
|
||||
results["xml"] = rpm_list_xml
|
||||
|
||||
return results
|
||||
|
||||
def compare_all_sources(self) -> Dict[str, Any]:
|
||||
"""
|
||||
比较所有来源的RPM列表
|
||||
|
||||
Returns:
|
||||
比较结果
|
||||
"""
|
||||
sources = self.get_all_rpm_sources()
|
||||
|
||||
if not sources:
|
||||
logger.error("没有找到任何RPM列表来源")
|
||||
return {"error": "没有找到任何RPM列表来源"}
|
||||
|
||||
results = {}
|
||||
|
||||
# 如果有HTML和SQLite来源,比较它们
|
||||
if "html" in sources and "sqlite" in sources:
|
||||
html_vs_sqlite = self.compare_rpm_lists(sources["html"], sources["sqlite"])
|
||||
results["html_vs_sqlite"] = html_vs_sqlite
|
||||
|
||||
# 如果有HTML和XML来源,比较它们
|
||||
if "html" in sources and "xml" in sources:
|
||||
html_vs_xml = self.compare_rpm_lists(sources["html"], sources["xml"])
|
||||
results["html_vs_xml"] = html_vs_xml
|
||||
|
||||
# 如果有SQLite和XML来源,比较它们
|
||||
if "sqlite" in sources and "xml" in sources:
|
||||
sqlite_vs_xml = self.compare_rpm_lists(sources["sqlite"], sources["xml"])
|
||||
results["sqlite_vs_xml"] = sqlite_vs_xml
|
||||
|
||||
return results
|
||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
# 项目依赖
|
||||
requests>=2.25.0
|
||||
beautifulsoup4>=4.9.3
|
||||
lxml>=4.6.3
|
||||
PyYAML>=5.4.1
|
||||
27
script/01_repodata_check/README.md
Normal file
27
script/01_repodata_check/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# 测试项1 - repodata 检查
|
||||
|
||||
## 脚本简介
|
||||
|
||||
本脚本用于批量测试每周更新仓库的repodata是否正常。
|
||||
|
||||
## 使用方法
|
||||
|
||||
python3 repo_rpmlist_check_all.py
|
||||
|
||||
## 输入
|
||||
|
||||
repo_configs1
|
||||
|
||||
1.每周仓库测试仓库地址 一般情况下不需要变更,可直接检查所有更新仓库;
|
||||
2.可修改reo_config1中仓库为待其他测试仓库地址;
|
||||
3.需注意仓库地址链接(eg:8U8)与其他仓库地址链接不一致;
|
||||
|
||||
## 输出文件
|
||||
|
||||
完整日志:repo_rpmlist_check_results.log
|
||||
失败记录:repo_rpmlist_check_fail.log
|
||||
|
||||
## 脚本流程说明
|
||||
|
||||
脚本通过获取repodata内的数据库文件得到rpmlist 与网页解析获得rpmlist进行对比;
|
||||
内网python3 环境,即可运行;
|
||||
354
script/01_repodata_check/repodata_rpmlist_check_all.py
Normal file
354
script/01_repodata_check/repodata_rpmlist_check_all.py
Normal file
@@ -0,0 +1,354 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# Time:2025/4/21 17:46
|
||||
# Author:GuoChao
|
||||
# FileName:repo_rpmlist_check.py
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
import bz2
|
||||
import lzma
|
||||
import shutil
|
||||
import sqlite3
|
||||
import requests
|
||||
from lxml import etree
|
||||
import logging
|
||||
logging.captureWarnings(True)
|
||||
import gzip
|
||||
|
||||
def Gethtml(baseurl):
|
||||
path = f"{baseurl}/Packages/"
|
||||
head = {
|
||||
"User-Agent": "Mozilla / 5.0(Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 80.0.3987.122 Safari / 537.36"
|
||||
}
|
||||
response = requests.get(path, headers=head, verify=False)
|
||||
html = response.text
|
||||
return html
|
||||
|
||||
#使用bs4 进行网页解析,获取软件包列表
|
||||
def Getrpmlist(html):
|
||||
rpmlist1=[]
|
||||
rpmlist=[]
|
||||
soup=BeautifulSoup(html,"html.parser")
|
||||
for item in soup.find_all('a'):
|
||||
rpmlist1.append(item['href'])
|
||||
for i in rpmlist1: # 对url不能识别”+“进行修正
|
||||
if ".rpm" in i:
|
||||
i = i.replace("%2B", "+")
|
||||
rpmlist.append(i)
|
||||
print('\n' +"通过html页面获取软件包: ",len(rpmlist))
|
||||
return rpmlist
|
||||
|
||||
|
||||
def Getsqlite(baseurl):
|
||||
"""
|
||||
从repodata中获取数据库压缩文件,下载并解压
|
||||
url 格式 例如 baseurl=https://update.cs2c.com.cn/NS/V10/8U6/os/adv/lic/AppStream/x86_64/os/
|
||||
"""
|
||||
head = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||
}
|
||||
|
||||
path = f"{baseurl}/repodata/"
|
||||
|
||||
try:
|
||||
response = requests.get(path, headers=head, verify=False, timeout=10)
|
||||
if response.status_code != 200:
|
||||
print(f"请求失败,状态码:{response.status_code}")
|
||||
return None
|
||||
|
||||
html = response.text
|
||||
html = etree.HTML(html)
|
||||
a_links = html.xpath("//a")
|
||||
|
||||
for item in a_links:
|
||||
item1 = item.get('href')
|
||||
if item1.endswith("primary.sqlite.bz2"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
print(f"正在下载:{sqlite_url}")
|
||||
response = requests.get(sqlite_url, headers=head, verify=False, timeout=10)
|
||||
if response.status_code != 200:
|
||||
print(f"下载失败,状态码:{response.status_code}")
|
||||
return None
|
||||
un_path = "repotest.sqlite.bz2"
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
with bz2.BZ2File(un_path) as bz2file:
|
||||
data = bz2file.read()
|
||||
newfilepath = "repotest.sqlite"
|
||||
with open(newfilepath, 'wb') as f:
|
||||
f.write(data)
|
||||
return newfilepath
|
||||
|
||||
elif item1.endswith("primary.sqlite.xz"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
print(f"正在下载:{sqlite_url}")
|
||||
response = requests.get(sqlite_url, headers=head, verify=False, timeout=100)
|
||||
if response.status_code != 200:
|
||||
print(f"下载失败,状态码:{response.status_code}")
|
||||
return None
|
||||
un_path = "repotest.sqlite.xz"
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
with lzma.open(un_path, 'rb') as input:
|
||||
with open("repotest.sqlite", 'wb') as output:
|
||||
shutil.copyfileobj(input, output)
|
||||
return "repotest.sqlite"
|
||||
|
||||
elif item1.endswith("primary.sqlite.gz"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
print(f"正在下载:{sqlite_url}")
|
||||
response = requests.get(sqlite_url, headers=head, verify=False, timeout=10)
|
||||
if response.status_code != 200:
|
||||
print(f"下载失败,状态码:{response.status_code}")
|
||||
return None
|
||||
un_path = "repotest.sqlite.gz"
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
with gzip.open(un_path, 'rb') as input:
|
||||
with open("repotest.sqlite", 'wb') as output:
|
||||
shutil.copyfileobj(input, output)
|
||||
return "repotest.sqlite"
|
||||
|
||||
print("获取数据库文件失败,请检查!")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"发生异常:{e}")
|
||||
return None
|
||||
|
||||
|
||||
def Getrpminfo(database):
|
||||
"""
|
||||
连接sqlite数据库获取软件包信息
|
||||
"""
|
||||
if database is None:
|
||||
print("database 错误")
|
||||
con = sqlite3.connect(database)
|
||||
##读取sqlite数据
|
||||
cursor = con.cursor()
|
||||
##创建游标cursor来执行executeSQL语句
|
||||
cursor.execute("SELECT * from packages")
|
||||
content = cursor.execute("SELECT name, version, epoch, release, arch, rpm_sourcerpm, pkgId from packages")
|
||||
rpmlist=[]
|
||||
for row in cursor:
|
||||
rpm=row[0] + "-" + row[1] + "-" + row[3] + "." + row[4] + ".rpm"
|
||||
rpmlist.append(rpm)
|
||||
print('\n' + '通过sqlite获取软件包:', len(rpmlist))
|
||||
return rpmlist
|
||||
cursor.close()
|
||||
con.close()
|
||||
|
||||
|
||||
def compare_package_lists(package_list1, package_list2):
|
||||
set1 = set(package_list1)
|
||||
set2 = set(package_list2)
|
||||
|
||||
only_in_list1 = set1 - set2
|
||||
only_in_list2 = set2 - set1
|
||||
|
||||
result = []
|
||||
if only_in_list1:
|
||||
result.append("仅在第一个列表中存在的软件包:")
|
||||
result.extend(sorted(only_in_list1))
|
||||
else:
|
||||
result.append("没有仅在第一个列表中存在的软件包。")
|
||||
|
||||
if only_in_list2:
|
||||
result.append("仅在第二个列表中存在的软件包:")
|
||||
result.extend(sorted(only_in_list2))
|
||||
else:
|
||||
result.append("没有仅在第二个列表中存在的软件包。")
|
||||
|
||||
if not only_in_list1 and not only_in_list2:
|
||||
result.append('\n' +"两个软件包列表完全相同"+'\n'+"#####【测试通过】#####")
|
||||
|
||||
for line in result:
|
||||
print(line)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
def save_comparison_result_to_file(result, file_path):
|
||||
try:
|
||||
with open(file_path, 'w', encoding='utf-8') as file:
|
||||
for line in result:
|
||||
file.write(line + '\n')
|
||||
print(f"对比结果已成功保存到 {file_path}")
|
||||
except Exception as e:
|
||||
print(f"保存结果到文件时出现错误:{e}")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
|
||||
repo_configs1 = [
|
||||
|
||||
{
|
||||
"product": "V10-SP3-2403",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP3",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP2",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-SP1.1",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64", "mips64el", "loongarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-HPC",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-ZJ",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-2309A",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/2309A/os/adv/lic/",
|
||||
"architectures": ["aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V10-2309B",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/2309B/os/adv/lic/",
|
||||
"architectures": ["aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "HostOS",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "Host-2309",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "Host-2406",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/HOST/2406/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V7.9",
|
||||
"base_url": "https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "V6.10",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V6/V6.10/os/lic/",
|
||||
"architectures": ["x86_64"],
|
||||
"repo_types": ["updates"]
|
||||
},
|
||||
{
|
||||
"product": "8U2",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/8U2/os/adv/lic/",
|
||||
"architectures": ["x86_64", "aarch64"],
|
||||
"repo_types": ["BaseOS-updates", "AppStream-updates", "PowerTools-updates", "Plus-updates"]
|
||||
},
|
||||
{
|
||||
"product": "8U8",
|
||||
"base_url": "https://update.cs2c.com.cn/private_test/repo/V10/8U8/os/adv/lic/",
|
||||
"architectures": ["x86_64"],
|
||||
"repo_types": ["BaseOS-updates", "AppStream-updates", "BaseOS", "AppStream","/kernel418","/kernel419"]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 初始化结果记录文件
|
||||
overall_result_file = "./repo_rpmlist_check_results.log"
|
||||
fail_log_file = "./repo_rpmlist_check_fail.log"
|
||||
|
||||
# 清空或创建结果文件
|
||||
with open(overall_result_file, 'w', encoding='utf-8') as f:
|
||||
f.write("=== 整体测试结果 ===\n\n")
|
||||
with open(fail_log_file, 'w', encoding='utf-8') as f:
|
||||
f.write("=== 失败记录 ===\n\n")
|
||||
|
||||
for config in repo_configs1:
|
||||
product = config["product"]
|
||||
base_url = config["base_url"]
|
||||
|
||||
for arch in config["architectures"]:
|
||||
for repo_type in config["repo_types"]:
|
||||
# Construct full repository URL
|
||||
#repo_url = f"{base_url}{repo_type}/{arch}/" # 通用
|
||||
# repo_url = f"{base_url}{repo_type}/{arch}/os/" # ns8.8 使用
|
||||
if "8U8" not in product:
|
||||
repo_url = f"{base_url}{repo_type}/{arch}/"
|
||||
else:
|
||||
repo_url = f"{base_url}{repo_type}/{arch}/os/"
|
||||
# Generate output filename
|
||||
output_file = f"./rpm_check_{product}_{arch}_{repo_type}.txt"
|
||||
|
||||
print(f"\n#####开始测试: {product} {arch} {repo_type}#####")
|
||||
print(f"测试仓库地址: {repo_url}")
|
||||
|
||||
try:
|
||||
# Get RPM lists and compare
|
||||
rpmlist1 = Getrpmlist(Gethtml(repo_url))
|
||||
rpmlist2 = Getrpminfo(Getsqlite(repo_url))
|
||||
result = compare_package_lists(rpmlist1, rpmlist2)
|
||||
# Save full results to individual file
|
||||
# save_comparison_result_to_file(result, output_file)
|
||||
# 记录到整体结果文件
|
||||
with open(overall_result_file, 'a', encoding='utf-8') as f:
|
||||
f.write(f"\n=== {product} {arch} {repo_type} ===\n")
|
||||
f.write(f"Repository URL: {repo_url}\n")
|
||||
for line in result:
|
||||
f.write(line + '\n')
|
||||
f.write("\n")
|
||||
|
||||
# 检查是否有失败,如果有则记录到fail.log
|
||||
if "测试通过" not in '\n'.join(result):
|
||||
with open(fail_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(f"\n=== {product} {arch} {repo_type} ===\n")
|
||||
f.write(f"Repository URL: {repo_url}\n")
|
||||
for line in result:
|
||||
if "测试通过" not in line:
|
||||
f.write(line + '\n')
|
||||
f.write("\n")
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"Error processing {product} {arch} {repo_type}: {str(e)}"
|
||||
print(error_msg)
|
||||
|
||||
# 记录错误到整体结果文件和fail.log
|
||||
with open(overall_result_file, 'a', encoding='utf-8') as f:
|
||||
f.write(f"\n=== {product} {arch} {repo_type} ===\n")
|
||||
f.write(f"Repository URL: {repo_url}\n")
|
||||
f.write(f"ERROR: {error_msg}\n\n")
|
||||
|
||||
with open(fail_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(f"\n=== {product} {arch} {repo_type} ===\n")
|
||||
f.write(f"Repository URL: {repo_url}\n")
|
||||
f.write(f"ERROR: {error_msg}\n\n")
|
||||
|
||||
# 仍然保存错误到单独文件
|
||||
with open(f"./error_{product}_{arch}_{repo_type}.log", "w") as f:
|
||||
f.write(f"Error processing {repo_url}:\n{error_msg}")
|
||||
|
||||
print(f"\n所有测试完成,整体结果已保存到 {overall_result_file}")
|
||||
print(f"失败记录已保存到 {fail_log_file}")
|
||||
37
script/02_package_signatures/README.md
Normal file
37
script/02_package_signatures/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
测试项1 - 安装包签名检查
|
||||
|
||||
## 脚本简介
|
||||
|
||||
本脚本用于批量测试软件包签名的方式
|
||||
|
||||
## 使用方法
|
||||
# ./check_pgpsig.sh
|
||||
|
||||
---
|
||||
|
||||
## 输入文件
|
||||
- `pkg`:每行一个 RPM 包名,。
|
||||
---
|
||||
|
||||
## 输出文件
|
||||
|
||||
- 没有签名的软件包名
|
||||
---
|
||||
|
||||
## 脚本流程说明
|
||||
|
||||
1. 使用 find 命令递归查找当前目录下的所有 RPM 文件
|
||||
2. 使用 rpm -qp 命令查询 RPM 包的头信息
|
||||
3. --qf 选项指定输出格式,包含包名 (%{NAME}) 和 GPG 签名信息 (%{RSAHEADER:pgpsig})
|
||||
4. 如果签名信息中包含 "none",表示未签名,将结果同时输出到标准输出和 nogpg.list 文件。
|
||||
5. 否则,表示已签名,将结果追加到 gpg.list 文件。
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 需要 root 权限运行。
|
||||
- 系统需启用 YUM 并配置好可用仓库。
|
||||
- 安装失败或卸载失败的错误日志会保存在 `Error` 字段中,方便后续排查。
|
||||
|
||||
---
|
||||
11
script/02_package_signatures/check_pgpsig.sh
Normal file
11
script/02_package_signatures/check_pgpsig.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
LANG=C
|
||||
for pkg in `find . -type f -name '*.rpm'`
|
||||
do
|
||||
RES=`rpm -qp --qf '%{NAME} %{RSAHEADER:pgpsig}\n' $pkg`
|
||||
if echo $RES | grep 'none';then
|
||||
echo $RES | tee -a nogpg.list
|
||||
else
|
||||
echo $RES >> gpg.list
|
||||
fi
|
||||
done
|
||||
38
script/03_package_hash/README.md
Normal file
38
script/03_package_hash/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
测试项1 - 安装包哈希值
|
||||
|
||||
## 脚本简介
|
||||
|
||||
这个 Bash 脚本的主要功能是递归查找当前目录下的所有 RPM 包文件,然后比较每个 RPM 包的本地 MD5 签名与 Koji 构建系统中记录的 MD5 签名是否一致
|
||||
|
||||
## 使用方法
|
||||
# ./check_pgpsig.sh
|
||||
|
||||
---
|
||||
|
||||
## 输入文件
|
||||
- `pkg`:每行一个 RPM 包名,。
|
||||
-使用 find 命令递归查找当前目录下的所有 RPM 文件
|
||||
---
|
||||
|
||||
## 输出文件
|
||||
-:MD5 值匹配的和不匹配的,并分别将结果输出到 equal.txt 和 noquual.txt 文件中
|
||||
|
||||
---
|
||||
|
||||
## 脚本流程说明
|
||||
|
||||
1. 使用 find 命令递归查找当前目录下的所有 RPM 文件
|
||||
2. 使用 rpm -qp 命令查询 RPM 包的头信息
|
||||
3. --qf 选项指定输出格式,包含包名 (%{NAME}) 和 GPG 签名信息 (%{RSAHEADER:pgpsig})
|
||||
4. 如果签名信息中包含 "none",表示未签名,将结果同时输出到标准输出和 nogpg.list 文件。
|
||||
5. 否则,表示已签名,将结果追加到 gpg.list 文件。
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 需要 root 权限运行。
|
||||
- 系统需启用 YUM 并配置好可用仓库。
|
||||
- 安装失败或卸载失败的错误日志会保存在 `Error` 字段中,方便后续排查。
|
||||
|
||||
---
|
||||
31
script/03_package_hash/compare_file_hash.sh
Normal file
31
script/03_package_hash/compare_file_hash.sh
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 定义要处理的文件夹路径
|
||||
for rpm_file in $(find . -type f -name '*.rpm'); do
|
||||
# 去除 ./ 前缀
|
||||
clean_rpm_file="${rpm_file#./}"
|
||||
|
||||
# 检查文件是否存在
|
||||
if [ -f "$clean_rpm_file" ]; then
|
||||
# 执行 rpm 命令获取 MD5 值
|
||||
rpm_md5=$(rpm -qp --queryformat '%{SIGMD5}\n' "$clean_rpm_file" 2>&1 | awk '/^[0-9a-fA-F]{32}$/ {print $0}')
|
||||
|
||||
echo $rpm_md5
|
||||
# 执行 koji 命令获取 MD5 值
|
||||
koji_md5=$(koji rpminfo "$clean_rpm_file" | grep 'SIGMD5:' | awk '{print $2}')
|
||||
echo $koji_md5
|
||||
|
||||
# 比较两个 MD5 值
|
||||
if [ "$rpm_md5" = "$koji_md5" ]; then
|
||||
echo "File: $clean_rpm_file" >> equal.txt
|
||||
echo "rpm MD5: $rpm_md5" >> equal.txt
|
||||
echo "koji MD5: $koji_md5" >> equal.txt
|
||||
echo "------------------------" >> equal.txt
|
||||
else
|
||||
echo "File: $clean_rpm_file" >> noquual.txt
|
||||
echo "rpm MD5: $rpm_md5" >> noquual.txt
|
||||
echo "koji MD5: $koji_md5" >> noquual.txt
|
||||
echo "------------------------" >> noquual.txt
|
||||
fi
|
||||
fi
|
||||
done
|
||||
62
script/04_package_tps_test/README.md
Normal file
62
script/04_package_tps_test/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# 测试项2 - 安装卸载测试脚本说明
|
||||
|
||||
## 脚本简介
|
||||
|
||||
本脚本用于批量测试指定架构和产品下的软件包是否能被 **成功安装** 并 **成功卸载**。
|
||||
读取 `Pkglist` 中的包名,分别进行安装和卸载,并记录详细日志与结果。
|
||||
|
||||
## 使用方法
|
||||
# sh test.sh <PRODUCT_NAME> <ARCH>
|
||||
示例:
|
||||
# sh test.sh V10 x86_64
|
||||
---
|
||||
|
||||
## 输入文件
|
||||
- `Pkglist`:文本文件,每行一个 RPM 包名,作为测试目标(不包含 `.rpm` 后缀)。
|
||||
---
|
||||
|
||||
## 输出文件
|
||||
|
||||
- `测试项2-<PRODUCT_NAME>_<ARCH>_install_results.csv`
|
||||
安装卸载测试结果表格,包含四列:
|
||||
- `Package`:软件包名
|
||||
- `Install Status`:安装是否成功(成功/失败)
|
||||
- `Remove Status`:卸载是否成功(成功/失败)
|
||||
- `Error`:失败时的错误信息
|
||||
|
||||
- `测试项2-<PRODUCT_NAME>_<ARCH>_install_test.log`
|
||||
完整安装卸载过程日志。
|
||||
---
|
||||
|
||||
## 脚本流程说明
|
||||
|
||||
1. 读取参数 `<PRODUCT_NAME>` 与 `<ARCH>`。
|
||||
2. 逐行读取 `Pkglist` 中的软件包名。
|
||||
3. 对每个包执行:
|
||||
- `yum install -y <pkg>`(记录安装状态)
|
||||
- 安装成功后再执行 `yum remove -y <pkg>`(记录卸载状态)
|
||||
4. 写入最终 CSV 与日志文件。
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 需要 root 权限运行。
|
||||
- 系统需启用 YUM 并配置好可用仓库。
|
||||
- 安装失败或卸载失败的错误日志会保存在 `Error` 字段中,方便后续排查。
|
||||
|
||||
---
|
||||
compare-0522.py
|
||||
|
||||
## 脚本简介
|
||||
|
||||
本脚本生成仓库测试的多个脚本,安装本周更新的的软件包以及多版本升降级脚本,把每个产品都生成一个文件夹供测试使用
|
||||
## 使用方法
|
||||
# python3 compare-0522.py
|
||||
生成文件内容:
|
||||
install_remove_test.sh (安装测试脚本)
|
||||
Pkglist-V10SP4-x86 (本周仓库更新的二进制包列表)
|
||||
V10SP3-2403-x86分析报告-分析报告-20250527-1406.xlsx (仓库分析报告)
|
||||
test_upgrade_downgrade_V10SP3-2403-x86--20250527-1406.sh (本周更新包升降级测试脚本)
|
||||
|
||||
|
||||
91
script/04_package_tps_test/README_compare_ldy.md
Normal file
91
script/04_package_tps_test/README_compare_ldy.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# compare.py 使用说明文档
|
||||
|
||||
## 脚本简介
|
||||
|
||||
该脚本用于比较测试仓库与发布仓库新增/减少的软件包的构建差异:
|
||||
|
||||
- 自动下载并解压 `primary.sqlite` 数据库文件
|
||||
- 提取 SRPM 与 Binary NVR 信息
|
||||
- 分析多个仓库中同一 SRPM 的二进制构建差异
|
||||
- 输出本周重点分析的 SRPM 差异(新增/减少)
|
||||
- 输出多仓库整体 SRPM 构建分布情况到 Excel 文件
|
||||
---
|
||||
|
||||
## 使用方法
|
||||
|
||||
python3 compare.py
|
||||
|
||||
脚本默认读取当前目录下的 `config.ini` 配置文件。
|
||||
---
|
||||
|
||||
## 配置说明
|
||||
|
||||
`config.ini` 配置格式如下:
|
||||
|
||||
[V10sp4-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/x86_64/
|
||||
filename = V10SP3-2403-x86分析报告
|
||||
|
||||
[V10sp3-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/x86_64/
|
||||
filename = V10SP3-x86分析报告
|
||||
|
||||
支持多个产品段落,每个段落会分别输出一个分析结果。
|
||||
|
||||
---
|
||||
|
||||
## 输出内容说明
|
||||
|
||||
脚本会输出一个 `.xlsx` 文件,包含以下 Sheet:
|
||||
|
||||
1. `仓库所有SRPM分析`:列出所有仓库中 SRPM → Binary 包映射与差异信息
|
||||
2. `外网仓库本周重点分析的SRPM`:筛选重点 SRPM 在 base 与 updates 仓库中的构建差异
|
||||
3. `测试仓库本周重点分析的SRPM`:筛选重点 SRPM 在 base 与 updates_test 仓库中的构建差异
|
||||
4. `测试仓库对比`:展示 updates 与 updates_test 仓库中新增/减少的 SRPM 与 Binary 包
|
||||
5. `仓库原始数据`(可选):包含各仓库原始包信息
|
||||
---
|
||||
|
||||
## 分析字段说明
|
||||
|
||||
输出的主要字段如下:
|
||||
|
||||
- `Repo`:仓库名
|
||||
- `SRPM Name`:源码包名(无版本)
|
||||
- `SRPM Version`:源码包版本号(version-release)
|
||||
- `Binary Package List`:构建出的二进制包名(不含版本)
|
||||
- `Binary NVR List`:完整二进制包 NVR
|
||||
- `Binary Count`:该 SRPM 构建的二进制包数量
|
||||
- `Binary Count Changed`:是否存在包数量变动(Yes/No)
|
||||
- `Added Binaries` / `Removed Binaries`:相对于其他版本新增/减少的二进制包名
|
||||
|
||||
---
|
||||
|
||||
## 依赖组件
|
||||
|
||||
- Python 3.x
|
||||
- `requests`
|
||||
- `lxml`
|
||||
- `pandas`
|
||||
- `openpyxl`
|
||||
|
||||
安装依赖示例:
|
||||
|
||||
```bash
|
||||
pip install requests lxml pandas openpyxl
|
||||
```
|
||||
---
|
||||
|
||||
## 示例输出文件
|
||||
|
||||
输出文件名格式如下:
|
||||
|
||||
```
|
||||
<ProductName>-差异分析-YYYYMMDD-HHMM.xlsx
|
||||
```
|
||||
|
||||
371
script/04_package_tps_test/compare-0522.py
Normal file
371
script/04_package_tps_test/compare-0522.py
Normal file
@@ -0,0 +1,371 @@
|
||||
from collections import defaultdict
|
||||
import bz2
|
||||
import lzma
|
||||
import shutil
|
||||
import sqlite3
|
||||
import requests
|
||||
from lxml import etree
|
||||
import configparser
|
||||
import datetime
|
||||
import pandas as pd
|
||||
import openpyxl
|
||||
import gzip
|
||||
import os
|
||||
from openpyxl.styles import Font, PatternFill
|
||||
import urllib3
|
||||
from test_srpm_up_down import gen_updown_test
|
||||
#from generate_test_summary import generate_test_summary_excel
|
||||
|
||||
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
# ---------------------
|
||||
# 下载 primary.sqlite 并解压
|
||||
# ---------------------
|
||||
def Getsqlite_primary(baseurl):
|
||||
head = {
|
||||
"User-Agent": "Mozilla/5.0"
|
||||
}
|
||||
path = f"{baseurl}repodata/"
|
||||
try:
|
||||
response = requests.get(path, headers=head, verify=False)
|
||||
html = response.text
|
||||
html = etree.HTML(html)
|
||||
a_links = html.xpath("//a")
|
||||
|
||||
for item in a_links:
|
||||
item1 = item.get('href')
|
||||
if item1.endswith("primary.sqlite.bz2"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
print(sqlite_url)
|
||||
response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
un_path = "repotest.sqlite.bz2"
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
bz2file = bz2.BZ2File(un_path)
|
||||
data = bz2file.read()
|
||||
newfilepath = "repotest.sqlite"
|
||||
open(newfilepath, 'wb').write(data)
|
||||
return newfilepath
|
||||
|
||||
elif item1.endswith("primary.sqlite.xz"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
print(sqlite_url)
|
||||
response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
un_path = "repotest.sqlite.xz"
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
with lzma.open(un_path, 'rb') as input:
|
||||
with open("repotest.sqlite", 'wb') as output:
|
||||
shutil.copyfileobj(input, output)
|
||||
return "repotest.sqlite"
|
||||
|
||||
elif item1.endswith("primary.sqlite.gz"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
un_path = "repotest.sqlite.gz"
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
with gzip.open(un_path, 'rb') as input:
|
||||
with open("repotest.sqlite", 'wb') as output:
|
||||
shutil.copyfileobj(input, output)
|
||||
return "repotest.sqlite"
|
||||
|
||||
print("获取数据库文件失败,请检查!")
|
||||
return None
|
||||
except Exception as e:
|
||||
print("发生异常:", e)
|
||||
return None
|
||||
|
||||
# ---------------------
|
||||
# 获取包信息
|
||||
# ---------------------
|
||||
def get_package_info_from_db1(baseurl):
|
||||
db_path = Getsqlite_primary(baseurl)
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT pkgkey, name, arch, version, release, rpm_sourcerpm FROM packages")
|
||||
result = cursor.fetchall()
|
||||
conn.close()
|
||||
pkg_info = {}
|
||||
for pkgkey, name, arch, version, release, rpm_sourcerpm in result:
|
||||
pkg_info[pkgkey] = {
|
||||
"pkgkey": pkgkey,
|
||||
"name": name,
|
||||
"arch": arch,
|
||||
"version": version,
|
||||
"release": release,
|
||||
"source": rpm_sourcerpm,
|
||||
}
|
||||
return pkg_info
|
||||
|
||||
def extract_srpm_nvr(pkg_info):
|
||||
return set(info['source'].replace('.src.rpm', '').replace('.nosrc.rpm', '') for info in pkg_info.values() if info['source'])
|
||||
|
||||
def extract_binary_nvr(pkg_info):
|
||||
return set(f"{info['name']}-{info['version']}-{info['release']}.{info['arch']}" for info in pkg_info.values())
|
||||
|
||||
def extract_srpm_names_from_nvr_list(nvr_list):
|
||||
srpm_names = set()
|
||||
for nvr in nvr_list:
|
||||
parts = nvr.rsplit('-', 2)
|
||||
if len(parts) == 3:
|
||||
srpm_names.add(parts[0])
|
||||
return sorted(srpm_names)
|
||||
|
||||
def analyze_binary_distribution_all_repos(all_pkg_info_by_repo):
|
||||
srpm_versions = defaultdict(lambda: defaultdict(lambda: defaultdict(set)))
|
||||
for repo_name, pkg_info in all_pkg_info_by_repo.items():
|
||||
for info in pkg_info.values():
|
||||
source_rpm = info['source']
|
||||
binary_name = info['name']
|
||||
try:
|
||||
srpm_base = source_rpm.replace('.src.rpm', '').replace('.nosrc.rpm', '')
|
||||
srpm_name, srpm_version, srpm_release = srpm_base.rsplit('-', 2)
|
||||
srpm_fullver = f"{srpm_version}-{srpm_release}"
|
||||
except Exception:
|
||||
continue
|
||||
srpm_versions[srpm_name][repo_name][srpm_fullver].add(binary_name)
|
||||
|
||||
srpm_change_flags = {}
|
||||
srpm_all_binaries = {}
|
||||
for srpm_name, repo_versions in srpm_versions.items():
|
||||
all_binary_counts = []
|
||||
all_binaries = set()
|
||||
for version_map in repo_versions.values():
|
||||
for binaries in version_map.values():
|
||||
all_binary_counts.append(len(binaries))
|
||||
all_binaries.update(binaries)
|
||||
srpm_change_flags[srpm_name] = len(set(all_binary_counts)) > 1
|
||||
srpm_all_binaries[srpm_name] = all_binaries
|
||||
|
||||
records = []
|
||||
for srpm_name, repo_versions in srpm_versions.items():
|
||||
changed = srpm_change_flags[srpm_name]
|
||||
all_binaries = srpm_all_binaries[srpm_name]
|
||||
for repo_name, versions in repo_versions.items():
|
||||
pkg_info_map = all_pkg_info_by_repo[repo_name]
|
||||
for version, binaries in versions.items():
|
||||
added = sorted(binaries - all_binaries)
|
||||
removed = sorted(all_binaries - binaries)
|
||||
binary_nvrs = sorted([
|
||||
f"{info['name']}-{info['version']}-{info['release']}.{info['arch']}"
|
||||
for info in pkg_info_map.values()
|
||||
if info["name"] in binaries and
|
||||
info["source"].replace('.src.rpm', '').replace('.nosrc.rpm', '').endswith(version)
|
||||
])
|
||||
records.append({
|
||||
"Repo": repo_name,
|
||||
"SRPM Name": srpm_name,
|
||||
"SRPM Version": version,
|
||||
"Binary Count": len(binaries),
|
||||
"Binary Package List": ' '.join(sorted(binaries)),
|
||||
"Binary NVR List": ' '.join(binary_nvrs),
|
||||
"Binary Count Changed": "Yes" if changed else "No",
|
||||
"Added Binaries": ' '.join(added),
|
||||
"Removed Binaries": ' '.join(removed),
|
||||
})
|
||||
df = pd.DataFrame(records)
|
||||
df.sort_values(by=["SRPM Name", "SRPM Version", "Repo"], inplace=True)
|
||||
return df
|
||||
|
||||
def write_test_comparison_sheet(wb, added_srpms, removed_srpms, added_bins, removed_bins):
|
||||
sheet = wb.create_sheet("测试仓库对比")
|
||||
sheet.append(["新增 SRPM(仅测试仓库对比)", "新增 Binary NVR(仅测试仓库对比)"])
|
||||
for i in range(max(len(added_srpms), len(added_bins))):
|
||||
sheet.append([added_srpms[i] if i < len(added_srpms) else "", added_bins[i] if i < len(added_bins) else ""])
|
||||
sheet.append([])
|
||||
sheet.append(["减少 SRPM(仅测试仓库对比)", "减少 Binary NVR(仅测试仓库对比)"])
|
||||
for i in range(max(len(removed_srpms), len(removed_bins))):
|
||||
sheet.append([removed_srpms[i] if i < len(removed_srpms) else "", removed_bins[i] if i < len(removed_bins) else ""])
|
||||
|
||||
def write_filtered_analysis_sheet(analysis_df, target_srpms, worksheet):
|
||||
filtered_df = analysis_df[analysis_df["SRPM Name"].isin(target_srpms)]
|
||||
worksheet.append(list(filtered_df.columns))
|
||||
for _, row in filtered_df.iterrows():
|
||||
worksheet.append(list(row.values))
|
||||
|
||||
def write_analysis_to_excel(analysis_df, repo_name, worksheet, is_first=False):
|
||||
if is_first:
|
||||
worksheet.append(["Repo"] + list(analysis_df.columns))
|
||||
for _, row in analysis_df.iterrows():
|
||||
worksheet.append([repo_name] + list(row.values))
|
||||
|
||||
def load_product_config():
|
||||
config = configparser.ConfigParser()
|
||||
config.read('config.ini', encoding='utf-8')
|
||||
all_products = {}
|
||||
for section in config.sections():
|
||||
if not config.has_option(section, 'baseurls'):
|
||||
continue
|
||||
baseurls_raw = config.get(section, 'baseurls', fallback='')
|
||||
baseurl_lines = [line.strip() for line in baseurls_raw.splitlines() if line.strip()]
|
||||
baseurl_dict = {}
|
||||
for line in baseurl_lines:
|
||||
if '=' in line:
|
||||
name, url = line.split('=', 1)
|
||||
baseurl_dict[name.strip()] = url.strip()
|
||||
filename = config.get(section, 'filename', fallback=section)
|
||||
updates_test = config.get(section, 'updates_test', fallback=None)
|
||||
all_products[section] = {
|
||||
"baseurls": baseurl_dict,
|
||||
"filename": filename,
|
||||
"updates_test": updates_test
|
||||
}
|
||||
return all_products
|
||||
|
||||
def compare_updates_and_test(updates_url, test_url):
|
||||
new_info = get_package_info_from_db1(updates_url)
|
||||
test_info = get_package_info_from_db1(test_url)
|
||||
new_srpms = extract_srpm_nvr(new_info)
|
||||
test_srpms = extract_srpm_nvr(test_info)
|
||||
new_bins = extract_binary_nvr(new_info)
|
||||
test_bins = extract_binary_nvr(test_info)
|
||||
added_srpms = sorted(test_srpms - new_srpms)
|
||||
removed_srpms = sorted(new_srpms - test_srpms)
|
||||
added_bins = sorted(test_bins - new_bins)
|
||||
removed_bins = sorted(new_bins - test_bins)
|
||||
focus_srpms = extract_srpm_names_from_nvr_list(added_srpms + removed_srpms)
|
||||
return added_srpms, removed_srpms, added_bins, removed_bins, focus_srpms
|
||||
|
||||
def run_for_product(product_name, baseurl_map, filename_prefix, updates_test_url, show_raw=False):
|
||||
print(f"\n🔍 正在分析产品:{product_name}")
|
||||
|
||||
output_dir = os.path.join("output", product_name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M")
|
||||
excel_path = os.path.join(output_dir, f"{filename_prefix}-分析报告-{timestamp}.xlsx")
|
||||
pkglist_path = os.path.join(output_dir, f"Pkglist-{product_name}")
|
||||
|
||||
wb = openpyxl.Workbook()
|
||||
if show_raw:
|
||||
raw_sheet = wb.create_sheet("仓库原始数据")
|
||||
analysis_sheet = wb.create_sheet("仓库所有SRPM分析")
|
||||
focus_sheet_external = wb.create_sheet("外网仓库本周重点分析的SRPM")
|
||||
focus_sheet_test = wb.create_sheet("测试仓库本周重点分析的SRPM")
|
||||
wb.remove(wb.active)
|
||||
|
||||
all_pkg_info_by_repo = {}
|
||||
if show_raw:
|
||||
is_first_raw = True
|
||||
for repo_name, baseurl in baseurl_map.items():
|
||||
info = get_package_info_from_db1(baseurl)
|
||||
all_pkg_info_by_repo[repo_name] = info
|
||||
write_Excel(info, repo_name, raw_sheet, is_first=is_first_raw)
|
||||
is_first_raw = False
|
||||
else:
|
||||
for repo_name, baseurl in baseurl_map.items():
|
||||
all_pkg_info_by_repo[repo_name] = get_package_info_from_db1(baseurl)
|
||||
|
||||
analysis_df = analyze_binary_distribution_all_repos(all_pkg_info_by_repo)
|
||||
write_analysis_to_excel(analysis_df, "Merged", analysis_sheet, is_first=True)
|
||||
|
||||
updates_url = baseurl_map.get("updates")
|
||||
base_url = baseurl_map.get("base")
|
||||
if updates_url and updates_test_url:
|
||||
added_srpms, removed_srpms, added_bins, removed_bins, focus_srpms = compare_updates_and_test(updates_url, updates_test_url)
|
||||
write_test_comparison_sheet(wb, added_srpms, removed_srpms, added_bins, removed_bins)
|
||||
with open(pkglist_path, 'w') as f:
|
||||
for nvr in added_bins:
|
||||
f.write(nvr + '\n')
|
||||
|
||||
ext_info = {
|
||||
'base': get_package_info_from_db1(base_url),
|
||||
'updates': get_package_info_from_db1(updates_url)
|
||||
}
|
||||
ext_df = analyze_binary_distribution_all_repos(ext_info)
|
||||
write_filtered_analysis_sheet(ext_df, focus_srpms, focus_sheet_external)
|
||||
|
||||
test_info = {
|
||||
'base': get_package_info_from_db1(base_url),
|
||||
'updates_test': get_package_info_from_db1(updates_test_url)
|
||||
}
|
||||
test_df = analyze_binary_distribution_all_repos(test_info)
|
||||
write_filtered_analysis_sheet(test_df, focus_srpms, focus_sheet_test)
|
||||
|
||||
wb.save(excel_path)
|
||||
print(f"✅ {product_name} 分析完成,输出文件:{excel_path}")
|
||||
|
||||
# ✅ 生成安装卸载测试脚本(不需要传参)
|
||||
install_test_script_path = os.path.join(output_dir, "install_remove_test.sh")
|
||||
with open(install_test_script_path, "w") as f:
|
||||
f.write(f"""#!/bin/bash
|
||||
|
||||
PACKAGE_LIST=Pkglist-{product_name}
|
||||
OUTPUT_FILE={product_name}_install_results.csv
|
||||
LOG_FILE={product_name}_install_test.log
|
||||
|
||||
echo \"Package,Install Status,Remove Status,Error\" > "$OUTPUT_FILE"
|
||||
echo \"===== 安装测试日志开始 =====\" > "$LOG_FILE"
|
||||
|
||||
while IFS= read -r package
|
||||
do
|
||||
if [ -z "$package" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "正在安装: $package" | tee -a "$LOG_FILE"
|
||||
install_output=$(yum --setopt=timeout=300 --setopt=retries=10 install -y "$package" 2>&1)
|
||||
install_status=$?
|
||||
|
||||
if [ $install_status -eq 0 ]; then
|
||||
install_result=成功
|
||||
else
|
||||
install_result=失败
|
||||
fi
|
||||
|
||||
echo "$install_output" >> "$LOG_FILE"
|
||||
echo "正在卸载: $package" | tee -a "$LOG_FILE"
|
||||
remove_output=$(yum remove -y "$package" 2>&1)
|
||||
remove_status=$?
|
||||
|
||||
if [ $remove_status -eq 0 ]; then
|
||||
remove_result=成功
|
||||
else
|
||||
remove_result=失败
|
||||
fi
|
||||
|
||||
echo "$remove_output" >> "$LOG_FILE"
|
||||
|
||||
if [ "$install_result" = "失败" ]; then
|
||||
error_msg="$install_output"
|
||||
elif [ "$remove_result" = "失败" ]; then
|
||||
error_msg="$remove_output"
|
||||
else
|
||||
error_msg=""
|
||||
fi
|
||||
|
||||
echo "$package,$install_result,$remove_result,\"$error_msg\"" >> "$OUTPUT_FILE"
|
||||
echo "--------------------------------" >> "$LOG_FILE"
|
||||
done < "$PACKAGE_LIST"
|
||||
|
||||
echo \"===== 安装测试日志结束 =====\" >> "$LOG_FILE"
|
||||
echo \"测试完成,结果保存到 $OUTPUT_FILE,过程日志保存到 $LOG_FILE\"
|
||||
""")
|
||||
os.chmod(install_test_script_path, 0o755)
|
||||
|
||||
# 🔧 生成 SRPM 升降级测试脚本
|
||||
success = gen_updown_test([excel_path], filter_choice="All", sheet_name="测试仓库本周重点分析的SRPM")
|
||||
if success:
|
||||
for fname in os.listdir("."):
|
||||
if fname.startswith("test_upgrade_downgrade_") and fname.endswith(".sh"):
|
||||
shutil.move(fname, os.path.join(output_dir, fname))
|
||||
elif fname.startswith("test_results_") or fname.startswith("test_process_"):
|
||||
shutil.move(fname, os.path.join(output_dir, fname))
|
||||
# # ✅ 汇总测试结果(安装卸载 + 升级降级)
|
||||
# generate_test_summary_excel(output_dir, product_name)
|
||||
|
||||
|
||||
def main():
|
||||
products = load_product_config()
|
||||
for name, conf in products.items():
|
||||
run_for_product(
|
||||
product_name=name,
|
||||
baseurl_map=conf['baseurls'],
|
||||
filename_prefix=conf['filename'],
|
||||
updates_test_url=conf.get('updates_test'),
|
||||
show_raw=False
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
319
script/04_package_tps_test/compare.py
Normal file
319
script/04_package_tps_test/compare.py
Normal file
@@ -0,0 +1,319 @@
|
||||
from collections import defaultdict
|
||||
import bz2
|
||||
import lzma
|
||||
import shutil
|
||||
import sqlite3
|
||||
import requests
|
||||
from lxml import etree
|
||||
import configparser
|
||||
import datetime
|
||||
import pandas as pd
|
||||
import openpyxl
|
||||
import gzip
|
||||
from openpyxl.styles import Font, PatternFill
|
||||
import urllib3
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
# ---------------------
|
||||
# 下载 primary.sqlite 并解压
|
||||
# ---------------------
|
||||
def Getsqlite_primary(baseurl):
|
||||
"""
|
||||
从repodata中获取数据库压缩文件,下载并解压
|
||||
url 格式 例如 basrutl=https://update.cs2c.com.cn/NS/V10/8U6/os/adv/lic/AppStream/x86_64/os/
|
||||
"""
|
||||
head = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||
}
|
||||
path = f"{baseurl}repodata/"
|
||||
try:
|
||||
response = requests.get(path, headers=head, verify=False)
|
||||
html = response.text
|
||||
html = etree.HTML(html)
|
||||
a_links = html.xpath("//a")
|
||||
|
||||
for item in a_links:
|
||||
item1 = item.get('href')
|
||||
if item1.endswith("primary.sqlite.bz2"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
print(sqlite_url)
|
||||
response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
un_path = "repotest.sqlite.bz2"
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
bz2file = bz2.BZ2File(un_path)
|
||||
data = bz2file.read()
|
||||
newfilepath = "repotest.sqlite"
|
||||
open(newfilepath, 'wb').write(data)
|
||||
return newfilepath
|
||||
|
||||
elif item1.endswith("primary.sqlite.xz"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
un_path = "repotest.sqlite.xz"
|
||||
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
|
||||
with lzma.open(un_path, 'rb') as input:
|
||||
with open("repotest.sqlite", 'wb') as output:
|
||||
shutil.copyfileobj(input, output)
|
||||
return "repotest.sqlite"
|
||||
|
||||
elif item1.endswith("primary.sqlite.gz"):
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
print(sqlite_url)
|
||||
response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
un_path = "repotest.sqlite.gz"
|
||||
|
||||
with open(un_path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
|
||||
with gzip.open(un_path, 'rb') as input:
|
||||
with open("repotest.sqlite", 'wb') as output:
|
||||
shutil.copyfileobj(input, output)
|
||||
return "repotest.sqlite"
|
||||
|
||||
print("获取数据库文件失败,请检查!")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print("发生异常:", e)
|
||||
return None
|
||||
|
||||
|
||||
# ---------------------
|
||||
# 获取包信息
|
||||
# ---------------------
|
||||
def get_package_info_from_db1(baseurl):
|
||||
db_path = Getsqlite_primary(baseurl)
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT pkgkey, name, arch, version, release, rpm_sourcerpm FROM packages")
|
||||
result = cursor.fetchall()
|
||||
conn.close()
|
||||
pkg_info = {}
|
||||
for pkgkey, name, arch, version, release, rpm_sourcerpm in result:
|
||||
pkg_info[pkgkey] = {
|
||||
"pkgkey": pkgkey,
|
||||
"name": name,
|
||||
"arch": arch,
|
||||
"version": version,
|
||||
"release": release,
|
||||
"source": rpm_sourcerpm,
|
||||
}
|
||||
return pkg_info
|
||||
|
||||
# ---------------------
|
||||
# 提取 SRPM 名称/NVR
|
||||
# ---------------------
|
||||
def extract_srpm_nvr(pkg_info):
|
||||
return set(info['source'].replace('.src.rpm', '').replace('.nosrc.rpm', '') for info in pkg_info.values() if info['source'])
|
||||
|
||||
def extract_binary_nvr(pkg_info):
|
||||
return set(f"{info['name']}-{info['version']}-{info['release']}.{info['arch']}" for info in pkg_info.values())
|
||||
|
||||
def extract_srpm_names_from_nvr_list(nvr_list):
|
||||
srpm_names = set()
|
||||
for nvr in nvr_list:
|
||||
parts = nvr.rsplit('-', 2)
|
||||
if len(parts) == 3:
|
||||
srpm_names.add(parts[0])
|
||||
return sorted(srpm_names)
|
||||
|
||||
# ---------------------
|
||||
# 差异分析(多仓库)
|
||||
# ---------------------
|
||||
def analyze_binary_distribution_all_repos(all_pkg_info_by_repo):
|
||||
srpm_versions = defaultdict(lambda: defaultdict(lambda: defaultdict(set)))
|
||||
for repo_name, pkg_info in all_pkg_info_by_repo.items():
|
||||
for info in pkg_info.values():
|
||||
source_rpm = info['source']
|
||||
binary_name = info['name']
|
||||
try:
|
||||
srpm_base = source_rpm.replace('.src.rpm', '').replace('.nosrc.rpm', '')
|
||||
srpm_name, srpm_version, srpm_release = srpm_base.rsplit('-', 2)
|
||||
srpm_fullver = f"{srpm_version}-{srpm_release}"
|
||||
except Exception:
|
||||
continue
|
||||
srpm_versions[srpm_name][repo_name][srpm_fullver].add(binary_name)
|
||||
|
||||
srpm_change_flags = {}
|
||||
srpm_all_binaries = {}
|
||||
for srpm_name, repo_versions in srpm_versions.items():
|
||||
all_binary_counts = []
|
||||
all_binaries = set()
|
||||
for version_map in repo_versions.values():
|
||||
for binaries in version_map.values():
|
||||
all_binary_counts.append(len(binaries))
|
||||
all_binaries.update(binaries)
|
||||
srpm_change_flags[srpm_name] = len(set(all_binary_counts)) > 1
|
||||
srpm_all_binaries[srpm_name] = all_binaries
|
||||
|
||||
records = []
|
||||
for srpm_name, repo_versions in srpm_versions.items():
|
||||
changed = srpm_change_flags[srpm_name]
|
||||
all_binaries = srpm_all_binaries[srpm_name]
|
||||
for repo_name, versions in repo_versions.items():
|
||||
pkg_info_map = all_pkg_info_by_repo[repo_name]
|
||||
for version, binaries in versions.items():
|
||||
added = sorted(binaries - all_binaries)
|
||||
removed = sorted(all_binaries - binaries)
|
||||
binary_nvrs = sorted([
|
||||
f"{info['name']}-{info['version']}-{info['release']}.{info['arch']}"
|
||||
for info in pkg_info_map.values()
|
||||
if info["name"] in binaries and
|
||||
info["source"].replace('.src.rpm', '').replace('.nosrc.rpm', '').endswith(version)
|
||||
])
|
||||
records.append({
|
||||
"Repo": repo_name,
|
||||
"SRPM Name": srpm_name,
|
||||
"SRPM Version": version,
|
||||
"Binary Count": len(binaries),
|
||||
"Binary Package List": ' '.join(sorted(binaries)),
|
||||
"Binary NVR List": ' '.join(binary_nvrs),
|
||||
"Binary Count Changed": "Yes" if changed else "No",
|
||||
"Added Binaries": ' '.join(added),
|
||||
"Removed Binaries": ' '.join(removed),
|
||||
})
|
||||
df = pd.DataFrame(records)
|
||||
df.sort_values(by=["SRPM Name", "SRPM Version", "Repo"], inplace=True)
|
||||
return df
|
||||
|
||||
# ---------------------
|
||||
# 对比测试仓库并返回重点 SRPM
|
||||
# ---------------------
|
||||
def compare_updates_and_test(updates_url, test_url):
|
||||
new_info = get_package_info_from_db1(updates_url)
|
||||
test_info = get_package_info_from_db1(test_url)
|
||||
new_srpms = extract_srpm_nvr(new_info)
|
||||
test_srpms = extract_srpm_nvr(test_info)
|
||||
new_bins = extract_binary_nvr(new_info)
|
||||
test_bins = extract_binary_nvr(test_info)
|
||||
added_srpms = sorted(test_srpms - new_srpms)
|
||||
removed_srpms = sorted(new_srpms - test_srpms)
|
||||
added_bins = sorted(test_bins - new_bins)
|
||||
removed_bins = sorted(new_bins - test_bins)
|
||||
focus_srpms = extract_srpm_names_from_nvr_list(added_srpms + removed_srpms)
|
||||
return added_srpms, removed_srpms, added_bins, removed_bins, focus_srpms
|
||||
|
||||
def write_test_comparison_sheet(wb, added_srpms, removed_srpms, added_bins, removed_bins):
|
||||
sheet = wb.create_sheet("测试仓库对比")
|
||||
sheet.append(["新增 SRPM(仅测试仓库对比)", "新增 Binary NVR(仅测试仓库对比)"])
|
||||
for i in range(max(len(added_srpms), len(added_bins))):
|
||||
sheet.append([added_srpms[i] if i < len(added_srpms) else "", added_bins[i] if i < len(added_bins) else ""])
|
||||
sheet.append([])
|
||||
sheet.append(["减少 SRPM(仅测试仓库对比)", "减少 Binary NVR(仅测试仓库对比)"])
|
||||
for i in range(max(len(removed_srpms), len(removed_bins))):
|
||||
sheet.append([removed_srpms[i] if i < len(removed_srpms) else "", removed_bins[i] if i < len(removed_bins) else ""])
|
||||
|
||||
# ---------------------
|
||||
# 写入分析表
|
||||
# ---------------------
|
||||
def write_filtered_analysis_sheet(analysis_df, target_srpms, worksheet):
|
||||
filtered_df = analysis_df[analysis_df["SRPM Name"].isin(target_srpms)]
|
||||
worksheet.append(list(filtered_df.columns))
|
||||
for _, row in filtered_df.iterrows():
|
||||
worksheet.append(list(row.values))
|
||||
|
||||
def write_analysis_to_excel(analysis_df, repo_name, worksheet, is_first=False):
|
||||
if is_first:
|
||||
worksheet.append(["Repo"] + list(analysis_df.columns))
|
||||
for _, row in analysis_df.iterrows():
|
||||
worksheet.append([repo_name] + list(row.values))
|
||||
|
||||
# ---------------------
|
||||
# 配置读取
|
||||
# ---------------------
|
||||
def load_product_config():
|
||||
config = configparser.ConfigParser()
|
||||
config.read('config.ini', encoding='utf-8')
|
||||
all_products = {}
|
||||
for section in config.sections():
|
||||
if not config.has_option(section, 'baseurls'):
|
||||
continue
|
||||
baseurls_raw = config.get(section, 'baseurls', fallback='')
|
||||
baseurl_lines = [line.strip() for line in baseurls_raw.splitlines() if line.strip()]
|
||||
baseurl_dict = {}
|
||||
for line in baseurl_lines:
|
||||
if '=' in line:
|
||||
name, url = line.split('=', 1)
|
||||
baseurl_dict[name.strip()] = url.strip()
|
||||
filename = config.get(section, 'filename', fallback=section)
|
||||
updates_test = config.get(section, 'updates_test', fallback=None)
|
||||
all_products[section] = {
|
||||
"baseurls": baseurl_dict,
|
||||
"filename": filename,
|
||||
"updates_test": updates_test
|
||||
}
|
||||
return all_products
|
||||
|
||||
# ---------------------
|
||||
# 主执行逻辑
|
||||
# ---------------------
|
||||
def run_for_product(product_name, baseurl_map, filename_prefix, updates_test_url, show_raw=False):
|
||||
print(f"\n🔍 正在分析产品:{product_name}")
|
||||
wb = openpyxl.Workbook()
|
||||
if show_raw:
|
||||
raw_sheet = wb.create_sheet("仓库原始数据")
|
||||
analysis_sheet = wb.create_sheet("仓库所有SRPM分析")
|
||||
focus_sheet_external = wb.create_sheet("外网仓库本周重点分析的SRPM")
|
||||
focus_sheet_test = wb.create_sheet("测试仓库本周重点分析的SRPM")
|
||||
wb.remove(wb.active)
|
||||
|
||||
all_pkg_info_by_repo = {}
|
||||
if show_raw:
|
||||
is_first_raw = True
|
||||
for repo_name, baseurl in baseurl_map.items():
|
||||
info = get_package_info_from_db1(baseurl)
|
||||
all_pkg_info_by_repo[repo_name] = info
|
||||
write_Excel(info, repo_name, raw_sheet, is_first=is_first_raw)
|
||||
is_first_raw = False
|
||||
else:
|
||||
for repo_name, baseurl in baseurl_map.items():
|
||||
all_pkg_info_by_repo[repo_name] = get_package_info_from_db1(baseurl)
|
||||
|
||||
analysis_df = analyze_binary_distribution_all_repos(all_pkg_info_by_repo)
|
||||
write_analysis_to_excel(analysis_df, "Merged", analysis_sheet, is_first=True)
|
||||
|
||||
updates_url = baseurl_map.get("updates")
|
||||
base_url = baseurl_map.get("base")
|
||||
if updates_url and updates_test_url:
|
||||
added_srpms, removed_srpms, added_bins, removed_bins, focus_srpms = compare_updates_and_test(updates_url, updates_test_url)
|
||||
write_test_comparison_sheet(wb, added_srpms, removed_srpms, added_bins, removed_bins)
|
||||
# 外网重点分析:base + updates
|
||||
ext_info = {
|
||||
'base': get_package_info_from_db1(base_url),
|
||||
'updates': get_package_info_from_db1(updates_url)
|
||||
}
|
||||
ext_df = analyze_binary_distribution_all_repos(ext_info)
|
||||
write_filtered_analysis_sheet(ext_df, focus_srpms, focus_sheet_external)
|
||||
# 测试重点分析:base + updates_test
|
||||
test_info = {
|
||||
'base': get_package_info_from_db1(base_url),
|
||||
'updates_test': get_package_info_from_db1(updates_test_url)
|
||||
}
|
||||
test_df = analyze_binary_distribution_all_repos(test_info)
|
||||
write_filtered_analysis_sheet(test_df, focus_srpms, focus_sheet_test)
|
||||
|
||||
timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M")
|
||||
output_file = f"{filename_prefix}-{timestamp}.xlsx"
|
||||
wb.save(output_file)
|
||||
print(f"✅ {product_name} 分析完成,输出文件:{output_file}")
|
||||
|
||||
# ---------------------
|
||||
# 主函数
|
||||
# ---------------------
|
||||
def main():
|
||||
products = load_product_config()
|
||||
for name, conf in products.items():
|
||||
run_for_product(
|
||||
product_name=name,
|
||||
baseurl_map=conf['baseurls'],
|
||||
filename_prefix=conf['filename'],
|
||||
updates_test_url=conf.get('updates_test'),
|
||||
show_raw=False
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
170
script/04_package_tps_test/config.ini
Normal file
170
script/04_package_tps_test/config.ini
Normal file
@@ -0,0 +1,170 @@
|
||||
[V10SP4-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/x86_64/
|
||||
filename = V10SP3-2403-x86分析报告
|
||||
|
||||
[V10SP3-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/x86_64/
|
||||
filename = V10SP3-x86分析报告
|
||||
|
||||
[V10SP2-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/updates/x86_64/
|
||||
filename = V10SP2-x86分析报告
|
||||
|
||||
[V10SP1-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/x86_64/
|
||||
filename = V10SP1-x86分析报告
|
||||
|
||||
[HPC-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/updates/x86_64/
|
||||
filename = HPC-x86分析报告
|
||||
|
||||
[HOST-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/updates/x86_64/
|
||||
filename = HOST-x86分析报告
|
||||
|
||||
|
||||
[yundizuoV10-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/updates/x86_64/
|
||||
filename = yundizuoV10-x86分析报告
|
||||
|
||||
|
||||
[V10-ZJ-x86]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/base/x86_64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/updates/x86_64/
|
||||
filename = V10-ZJ-x86分析报告
|
||||
|
||||
[2309a]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/2309A/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/2309A/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/2309A/os/adv/lic/updates/aarch64/
|
||||
filename = 2309A分析报告
|
||||
|
||||
|
||||
[2309b]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/2309B/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/2309B/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/2309B/os/adv/lic/updates/aarch64/
|
||||
filename = 2309B分析报告
|
||||
|
||||
|
||||
[V10SP4-aarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/aarch64/
|
||||
filename = V10SP3-2403-aarch64分析报告
|
||||
|
||||
[V10SP3-aarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/aarch64/
|
||||
filename = V10SP3-aarch64分析报告
|
||||
|
||||
[V10SP2-aarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/updates/aarch64/
|
||||
filename = V10SP2-aarch64分析报告
|
||||
|
||||
[V10SP1-aarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/aarch64/
|
||||
filename = V10SP1-aarch64分析报告
|
||||
|
||||
[HPC-aarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/updates/aarch64/
|
||||
filename = HPC-aarch64分析报告
|
||||
|
||||
[HOST-aarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/updates/aarch64/
|
||||
filename = HOST-aarch64分析报告
|
||||
|
||||
|
||||
[yundizuoV10-aarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/updates/aarch64/
|
||||
filename = yundizuoV10-aarch64分析报告
|
||||
|
||||
|
||||
[V10-ZJ-aarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/base/aarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/updates/aarch64/
|
||||
filename = V10-ZJ-aarch64分析报告
|
||||
|
||||
|
||||
[V10SP4-loongarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/loongarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/loongarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/loongarch64/
|
||||
filename = V10SP3-2403-loongarch64分析报告
|
||||
|
||||
[V10SP3-loongarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/loongarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/updates/loongarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/loongarch64/
|
||||
filename = V10SP3-loongarch64分析报告
|
||||
|
||||
[V10SP1-loongarch64]
|
||||
baseurls =
|
||||
base=https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/loongarch64/
|
||||
updates=https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/loongarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/loongarch64/
|
||||
filename = V10SP1-loongarch64分析报告
|
||||
|
||||
[V7-x86]
|
||||
baseurls =
|
||||
base= https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/base/x86_64/
|
||||
updates= https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/x86_64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V7/V7Update9/os/adv/lic/updates/x86_64/
|
||||
filename = V7-x86分析报告
|
||||
|
||||
[V7-arm]
|
||||
baseurls =
|
||||
base= https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/base/aarch64/
|
||||
updates= https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/aarch64/
|
||||
updates_test = https://update.cs2c.com.cn/private_test/repo/V7/V7Update9/os/adv/lic/updates/aarch64/
|
||||
filename = V7-arm分析报告
|
||||
|
||||
|
||||
|
||||
137
script/04_package_tps_test/test_srpm_up_down.py
Normal file
137
script/04_package_tps_test/test_srpm_up_down.py
Normal file
@@ -0,0 +1,137 @@
|
||||
# test_srpm_up_down.py
|
||||
import os
|
||||
from openpyxl import load_workbook
|
||||
from rpm_vercmp import vercmp
|
||||
from functools import cmp_to_key
|
||||
import re
|
||||
|
||||
def process_excel_to_dict(file_path, filter_choice, sheet_name):
|
||||
wb = load_workbook(filename=file_path)
|
||||
sheet = wb[sheet_name]
|
||||
headers = [cell.value for cell in sheet[1]]
|
||||
try:
|
||||
name_idx = headers.index("SRPM Name")
|
||||
version_idx = headers.index("SRPM Version")
|
||||
binary_idx = headers.index("Binary NVR List")
|
||||
changed_idx = headers.index("Binary Count Changed")
|
||||
except ValueError as e:
|
||||
print(f"Error: 缺少必要的列 - {e}")
|
||||
return None
|
||||
|
||||
result_dict = {}
|
||||
for row in sheet.iter_rows(min_row=2, values_only=True):
|
||||
if filter_choice == "Yes" and row[changed_idx] != "Yes":
|
||||
continue
|
||||
if filter_choice == "No" and row[changed_idx] != "No":
|
||||
continue
|
||||
name = row[name_idx]
|
||||
version = row[version_idx]
|
||||
binary_pkgs = row[binary_idx]
|
||||
if name and version:
|
||||
key = f"{name}-{version}"
|
||||
result_dict[key] = binary_pkgs
|
||||
return result_dict
|
||||
|
||||
def process_single_file(file_path, filter_choice, sheet_name):
|
||||
result = process_excel_to_dict(file_path, filter_choice, sheet_name)
|
||||
if not result:
|
||||
print(f"警告: 文件 {file_path} 处理失败,跳过")
|
||||
return
|
||||
|
||||
srpm_groups = {}
|
||||
for key, value in result.items():
|
||||
parts = key.split('-')
|
||||
name = '-'.join(parts[:-2])
|
||||
srpm_groups.setdefault(name, []).append((key, value))
|
||||
|
||||
for name in srpm_groups:
|
||||
srpm_groups[name] = sorted(srpm_groups[name], key=cmp_to_key(lambda a, b: vercmp(a[0], b[0])))
|
||||
|
||||
base_name = re.sub(r'[^\x00-\x7F]+', '', os.path.basename(file_path)).split('.')[0]
|
||||
output_dir = os.path.dirname(file_path)
|
||||
script_name = os.path.join(output_dir, f"test_upgrade_downgrade_{base_name}.sh")
|
||||
results_log = f"test_results_{base_name}.log"
|
||||
process_log = f"test_process_{base_name}.log"
|
||||
|
||||
try:
|
||||
with open(script_name, 'w') as f:
|
||||
f.write("#!/bin/bash\n\n")
|
||||
f.write(f"# Auto-generated from {base_name}\n")
|
||||
f.write(f"rm -f {results_log} {process_log}\n")
|
||||
f.write(f"touch {process_log}\n")
|
||||
f.write(f"exec > >(tee -a {process_log}) 2>&1\n")
|
||||
f.write("echo 'Logging started at $(date)'\n")
|
||||
f.write("echo '========================================'\n\n")
|
||||
|
||||
for name, versions in srpm_groups.items():
|
||||
if len(versions) < 2:
|
||||
continue
|
||||
f.write(f"echo '\n=== Testing SRPM: {name} ==='\n")
|
||||
|
||||
if sheet_name == "测试仓库本周重点分析的SRPM":
|
||||
highest_idx = len(versions) - 1
|
||||
highest_ver = versions[highest_idx][0]
|
||||
highest_pkgs = ' '.join(versions[highest_idx][1].split())
|
||||
for i in range(highest_idx):
|
||||
from_ver = versions[i][0]
|
||||
from_pkgs = ' '.join(versions[i][1].split())
|
||||
f.write(f"echo 'Installing base version: {from_ver}'\n")
|
||||
f.write(f"if ! yum install -y {from_pkgs}; then\n")
|
||||
f.write(f" echo 'BASE INSTALL FAILED: {from_ver}' >> {results_log}\n")
|
||||
f.write(f"else\n")
|
||||
f.write(f" echo 'BASE INSTALL SUCCESS: {from_ver}' >> {results_log}\n")
|
||||
f.write(f" echo 'Testing upgrade: {from_ver} -> {highest_ver}'\n")
|
||||
f.write(f" if yum upgrade -y {highest_pkgs}; then\n")
|
||||
f.write(f" echo 'UPGRADE SUCCESS' >> {results_log}\n")
|
||||
f.write(f" else\n")
|
||||
f.write(f" echo 'UPGRADE FAILED' >> {results_log}\n")
|
||||
f.write(f" fi\n")
|
||||
f.write(f" echo 'Testing downgrade: {highest_ver} -> {from_ver}'\n")
|
||||
f.write(f" if yum downgrade -y {from_pkgs}; then\n")
|
||||
f.write(f" echo 'DOWNGRADE SUCCESS' >> {results_log}\n")
|
||||
f.write(f" else\n")
|
||||
f.write(f" echo 'DOWNGRADE FAILED' >> {results_log}\n")
|
||||
f.write(f" fi\n")
|
||||
f.write(f"fi\n")
|
||||
f.write(f"echo '=== TEST END ===' >> {results_log}\n\n")
|
||||
else:
|
||||
for i in range(len(versions)):
|
||||
for j in range(i+1, len(versions)):
|
||||
from_ver = versions[i][0]
|
||||
to_ver = versions[j][0]
|
||||
from_pkgs = ' '.join(versions[i][1].split())
|
||||
to_pkgs = ' '.join(versions[j][1].split())
|
||||
f.write(f"echo 'Installing base version: {from_ver}'\n")
|
||||
f.write(f"if ! yum install -y {from_pkgs}; then\n")
|
||||
f.write(f" echo 'BASE INSTALL FAILED: {from_ver}' >> {results_log}\n")
|
||||
f.write(f"else\n")
|
||||
f.write(f" echo 'Testing upgrade: {from_ver} -> {to_ver}'\n")
|
||||
f.write(f" if yum upgrade -y {to_pkgs}; then\n")
|
||||
f.write(f" echo 'UPGRADE SUCCESS' >> {results_log}\n")
|
||||
f.write(f" else\n")
|
||||
f.write(f" echo 'UPGRADE FAILED' >> {results_log}\n")
|
||||
f.write(f" fi\n")
|
||||
f.write(f" echo 'Testing downgrade: {to_ver} -> {from_ver}'\n")
|
||||
f.write(f" if yum downgrade -y {from_pkgs}; then\n")
|
||||
f.write(f" echo 'DOWNGRADE SUCCESS' >> {results_log}\n")
|
||||
f.write(f" else\n")
|
||||
f.write(f" echo 'DOWNGRADE FAILED' >> {results_log}\n")
|
||||
f.write(f" fi\n")
|
||||
f.write(f"fi\n")
|
||||
f.write(f"echo '=== TEST END ===' >> {results_log}\n\n")
|
||||
print(f"✅ 成功生成测试脚本: {script_name}")
|
||||
except Exception as e:
|
||||
print(f"❌ 生成脚本失败: {e}")
|
||||
|
||||
def gen_updown_test(excel_files, filter_choice="All", sheet_name="测试仓库本周重点分析的SRPM"):
|
||||
if not excel_files:
|
||||
print("错误: 未提供任何Excel文件")
|
||||
return False
|
||||
for file_path in excel_files:
|
||||
if not os.path.exists(file_path):
|
||||
print(f"警告: 文件不存在 {file_path}")
|
||||
continue
|
||||
print(f"\n处理文件: {os.path.basename(file_path)}")
|
||||
process_single_file(file_path, filter_choice, sheet_name)
|
||||
return True
|
||||
|
||||
76
script/04_package_tps_test/test_测试项2-ldy.sh
Normal file
76
script/04_package_tps_test/test_测试项2-ldy.sh
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
|
||||
# -------------------------
|
||||
# 读取命令行传入的产品名和架构
|
||||
# -------------------------
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "用法: $0 <PRODUCT_NAME> <ARCH>"
|
||||
echo "示例: $0 V10 x86_64"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PRODUCT_NAME="$1"
|
||||
ARCH="$2"
|
||||
|
||||
# 软件包列表文件
|
||||
PACKAGE_LIST="Pkglist"
|
||||
|
||||
# 自动生成输出文件名
|
||||
OUTPUT_FILE="测试项2-${PRODUCT_NAME}_${ARCH}_install_results-.csv"
|
||||
LOG_FILE="测试项2-${PRODUCT_NAME}_${ARCH}_install_test-.log"
|
||||
|
||||
# 创建或清空输出文件
|
||||
echo "Package,Install Status,Remove Status,Error" > "$OUTPUT_FILE"
|
||||
echo "===== 安装测试日志开始 =====" > "$LOG_FILE"
|
||||
|
||||
# 逐行读取软件包名称并安装
|
||||
while IFS= read -r package
|
||||
do
|
||||
if [ -z "$package" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "正在安装: $package" | tee -a "$LOG_FILE"
|
||||
|
||||
# 真正安装软件包
|
||||
install_output=$(yum --setopt=timeout=300 --setopt=retries=10 install -y "$package" 2>&1)
|
||||
install_status=$?
|
||||
|
||||
if [ $install_status -eq 0 ]; then
|
||||
install_result="成功"
|
||||
else
|
||||
install_result="失败"
|
||||
fi
|
||||
|
||||
echo "$install_output" >> "$LOG_FILE"
|
||||
|
||||
# 如果安装成功,执行卸载
|
||||
echo "正在卸载: $package" | tee -a "$LOG_FILE"
|
||||
remove_output=$(yum remove -y "$package" 2>&1)
|
||||
remove_status=$?
|
||||
|
||||
if [ $remove_status -eq 0 ]; then
|
||||
remove_result="成功"
|
||||
else
|
||||
remove_result="失败"
|
||||
fi
|
||||
|
||||
echo "$remove_output" >> "$LOG_FILE"
|
||||
|
||||
# 写入 CSV(错误信息用双引号包裹防止格式错乱)
|
||||
if [ "$install_result" = "失败" ]; then
|
||||
error_msg="$install_output"
|
||||
elif [ "$remove_result" = "失败" ]; then
|
||||
error_msg="$remove_output"
|
||||
else
|
||||
error_msg=""
|
||||
fi
|
||||
|
||||
echo "$package,$install_result,$remove_result,\"$error_msg\"" >> "$OUTPUT_FILE"
|
||||
|
||||
echo "--------------------------------" >> "$LOG_FILE"
|
||||
|
||||
done < "$PACKAGE_LIST"
|
||||
|
||||
echo "===== 安装测试日志结束 =====" >> "$LOG_FILE"
|
||||
echo "测试完成,结果保存到 $OUTPUT_FILE,过程日志保存到 $LOG_FILE"
|
||||
36
script/05_src_multi_version_test/README.md
Normal file
36
script/05_src_multi_version_test/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 源码包多版本升降级测试
|
||||
|
||||
## 前置条件
|
||||
- 需要基于丁瑶脚本生成的表格文件
|
||||
- 确保执行环境已安装所需依赖库
|
||||
|
||||
## 使用步骤
|
||||
1. 执行测试脚本:
|
||||
```bash
|
||||
python3 test_srpm_up_down_0512.py
|
||||
```
|
||||
|
||||
2. 根据提示进行选择:
|
||||
- **第一步** 输入文件路径:
|
||||
- 1) 多个表格文件:输入文件夹绝对路径
|
||||
- 2) 单个表格文件:输入文件绝对路径
|
||||
- **第二步** 筛选条件(Binary Count Changed列):
|
||||
- 1) 仅筛选"No":多版本但包个数一致
|
||||
- 2) 仅筛选"Yes":多版本但包个数不一致
|
||||
- 3) 不筛选:处理全部数据
|
||||
- **第三步** 选择工作表:
|
||||
- 1) 测试仓库本周重点分析的SRPM
|
||||
- 生成测试顺序:A→D, D→A, B→D, D→B, C→D, D→C
|
||||
- 2) 仓库所有SRPM分析
|
||||
- 生成所有版本排列组合的升降级测试
|
||||
|
||||
## 后续操作
|
||||
- 执行完成后会生成测试脚本
|
||||
- 将脚本传输到测试机执行:
|
||||
```bash
|
||||
bash 测试脚本
|
||||
```
|
||||
|
||||
> **注意**
|
||||
> 当前生成的测试脚本存在缺陷:
|
||||
> 如果base install版本安装失败,后续升降级测试需手动确认
|
||||
232
script/05_src_multi_version_test/test_srpm_up_down_0512.py
Normal file
232
script/05_src_multi_version_test/test_srpm_up_down_0512.py
Normal file
@@ -0,0 +1,232 @@
|
||||
import os
|
||||
import openpyxl
|
||||
from openpyxl import load_workbook
|
||||
from rpm_vercmp import vercmp
|
||||
from functools import cmp_to_key
|
||||
|
||||
def process_excel_to_dict(file_path, filter_choice, sheet_name):
|
||||
# 加载工作簿并选择指定工作表
|
||||
wb = load_workbook(filename=file_path)
|
||||
sheet = wb[sheet_name]
|
||||
|
||||
# 获取表头行,确定列索引
|
||||
headers = [cell.value for cell in sheet[1]]
|
||||
try:
|
||||
name_idx = headers.index("SRPM Name")
|
||||
version_idx = headers.index("SRPM Version")
|
||||
binary_idx = headers.index("Binary NVR List")
|
||||
changed_idx = headers.index("Binary Count Changed")
|
||||
except ValueError as e:
|
||||
print(f"Error: 缺少必要的列 - {e}")
|
||||
return None
|
||||
|
||||
result_dict = {}
|
||||
|
||||
# 从第二行开始处理数据
|
||||
for row in sheet.iter_rows(min_row=2, values_only=True):
|
||||
if filter_choice == "Yes" and row[changed_idx] != "Yes":
|
||||
continue
|
||||
if filter_choice == "No" and row[changed_idx] != "No":
|
||||
continue
|
||||
|
||||
name = row[name_idx]
|
||||
version = row[version_idx]
|
||||
binary_pkgs = row[binary_idx]
|
||||
|
||||
if name and version: # 确保关键字段不为空
|
||||
key = f"{name}-{version}"
|
||||
result_dict[key] = binary_pkgs
|
||||
|
||||
return result_dict
|
||||
|
||||
def process_single_file(file_path, filter_choice, sheet_name):
|
||||
result = process_excel_to_dict(file_path, filter_choice, sheet_name)
|
||||
# 按SRPM名称分组
|
||||
srpm_groups = {}
|
||||
for key, value in result.items():
|
||||
parts = key.split('-')
|
||||
name = '-'.join(parts[:-2])
|
||||
if name not in srpm_groups:
|
||||
srpm_groups[name] = []
|
||||
srpm_groups[name].append((key, value))
|
||||
|
||||
|
||||
# 在每个分组内按版本号排序
|
||||
for name in srpm_groups:
|
||||
srpm_groups[name] = sorted(srpm_groups[name],
|
||||
key=cmp_to_key(lambda a,b: vercmp(a[0], b[0])))
|
||||
|
||||
if not result:
|
||||
print(f"警告: 文件 {file_path} 处理失败,跳过")
|
||||
return
|
||||
|
||||
# 从Excel文件名生成基础名(不含路径、扩展名和中文)
|
||||
import re
|
||||
base_name = re.sub(r'[^\x00-\x7F]+', '', os.path.basename(file_path)).split('.')[0]
|
||||
|
||||
# 生成测试脚本
|
||||
script_name = f"test_upgrade_downgrade_{base_name}.sh"
|
||||
results_log = f"test_results_{base_name}.log"
|
||||
process_log = f"test_process_{base_name}.log"
|
||||
|
||||
try:
|
||||
with open(script_name, 'w') as f:
|
||||
f.write("#!/bin/bash\n\n")
|
||||
f.write(f"# Auto-generated SRPM upgrade/downgrade test script from {base_name}\n")
|
||||
f.write("# Tests all version combinations for each SRPM\n")
|
||||
f.write(f"# Results will be saved to {results_log}\n")
|
||||
f.write(f"# Process details will be saved to {process_log}\n\n")
|
||||
f.write(f"rm -f {results_log} {process_log}\n")
|
||||
f.write(f"touch {process_log} \n")
|
||||
f.write(f"exec > >(tee -a {process_log}) 2>&1\n")
|
||||
f.write("echo 'Logging started at $(date)'\n")
|
||||
f.write("echo '========================================'\n\n")
|
||||
|
||||
|
||||
for name, versions in srpm_groups.items():
|
||||
if len(versions) < 2:
|
||||
continue
|
||||
|
||||
f.write(f"echo '\n=== Testing SRPM: {name} ==='\n")
|
||||
|
||||
if sheet_name == "测试仓库本周重点分析的SRPM":
|
||||
# 本周重点分析的特殊测试顺序:每个中间版本与最高版本测试
|
||||
highest_idx = len(versions) - 1
|
||||
highest_ver = versions[highest_idx][0]
|
||||
highest_pkgs = ' '.join(versions[highest_idx][1].split())
|
||||
|
||||
for i in range(highest_idx):
|
||||
from_ver = versions[i][0]
|
||||
from_pkgs = ' '.join(versions[i][1].split())
|
||||
|
||||
# 1. 安装基本版本
|
||||
f.write(f"echo 'Installing base version: {from_ver}'\n")
|
||||
f.write(f"if ! yum install -y {from_pkgs}; then\n")
|
||||
f.write(f" echo 'BASE INSTALL FAILED: {from_ver} - VERSION: {from_ver} upgrade/downgrade test requires manual handling' >> {results_log}\n")
|
||||
f.write(f" echo '=== TEST END ===' >> {results_log}\n\n")
|
||||
f.write(f"else\n")
|
||||
f.write(f" echo 'BASE INSTALL SUCCESS: {from_ver}' >> {results_log}\n")
|
||||
|
||||
# 2. 升级到最高版本
|
||||
f.write(f"echo 'Testing upgrade: {from_ver} -> {highest_ver}'\n")
|
||||
f.write(f"if yum upgrade -y {highest_pkgs}; then\n")
|
||||
f.write(f" echo 'UPGRADE SUCCESS: {name} from {from_ver} to {highest_ver}' >> {results_log}\n")
|
||||
f.write(f"else\n")
|
||||
f.write(f" echo 'UPGRADE FAILED: {name} from {from_ver} to {highest_ver}' >> {results_log}\n")
|
||||
f.write(f"fi\n")
|
||||
|
||||
# 3. 降级回基本版本
|
||||
f.write(f"echo 'Testing downgrade: {highest_ver} -> {from_ver}'\n")
|
||||
f.write(f"if yum downgrade -y {from_pkgs}; then\n")
|
||||
f.write(f" echo 'DOWNGRADE SUCCESS: {name} from {highest_ver} to {from_ver}' >> {results_log}\n")
|
||||
f.write(f"else\n")
|
||||
f.write(f" echo 'DOWNGRADE FAILED: {name} from {highest_ver} to {from_ver}' >> {results_log}\n")
|
||||
f.write(f"fi\n")
|
||||
|
||||
f.write(f"echo '=== TEST END ===' >> {results_log}\n\n")
|
||||
|
||||
f.write(f"fi\n")
|
||||
|
||||
|
||||
else:
|
||||
# 原有测试逻辑:所有版本组合测试
|
||||
for i in range(len(versions)):
|
||||
for j in range(i+1, len(versions)):
|
||||
from_ver = versions[i][0]
|
||||
to_ver = versions[j][0]
|
||||
from_pkgs = ' '.join(versions[i][1].split())
|
||||
pkgs = ' '.join(versions[j][1].split())
|
||||
|
||||
# 1. 安装基本版本
|
||||
f.write(f"echo 'Installing base version: {from_ver}'\n")
|
||||
f.write(f"if ! yum install -y {from_pkgs}; then\n")
|
||||
f.write(f" echo 'BASE INSTALL FAILED: {from_ver} - VERSION: {from_ver} upgrade/downgrade test requires manual handling' >> {results_log}\n")
|
||||
f.write(f" echo '=== TEST END ===' >> {results_log}\n\n")
|
||||
f.write(f"else\n")
|
||||
f.write(f" echo 'BASE INSTALL SUCCESS: {from_ver}' >> {results_log}\n")
|
||||
# 2. 升级测试
|
||||
f.write(f"echo 'Testing upgrade: {from_ver} -> {to_ver}'\n")
|
||||
f.write(f"if yum upgrade -y {pkgs}; then\n")
|
||||
f.write(f" echo 'UPGRADE SUCCESS: {name} from {from_ver} to {to_ver}' >> {results_log}\n")
|
||||
f.write(f"else\n")
|
||||
f.write(f" echo 'UPGRADE FAILED: {name} from {from_ver} to {to_ver}' >> {results_log}\n")
|
||||
f.write(f"fi\n")
|
||||
|
||||
# 3. 降级测试
|
||||
f.write(f"echo 'Testing downgrade: {to_ver} -> {from_ver}'\n")
|
||||
f.write(f"if yum downgrade -y {' '.join(versions[i][1].split())}; then\n")
|
||||
f.write(f" echo 'DOWNGRADE SUCCESS: {name} from {to_ver} to {from_ver}' >> {results_log}\n")
|
||||
f.write(f"else\n")
|
||||
f.write(f" echo 'DOWNGRADE FAILED: {name} from {to_ver} to {from_ver}' >> {results_log}\n")
|
||||
f.write(f"fi\n")
|
||||
|
||||
f.write(f"echo '=== TEST END ===' >> {results_log}\n\n")
|
||||
f.write(f"fi\n")
|
||||
|
||||
|
||||
print(f"成功生成测试脚本: {script_name}")
|
||||
except Exception as e:
|
||||
print(f"生成脚本失败: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("请选择模式:")
|
||||
print("1. 手动输入单个Excel文件路径")
|
||||
print("2. 批量处理文件夹下所有Excel文件")
|
||||
|
||||
while True:
|
||||
choice = input("请输入模式编号(1/2): ").strip()
|
||||
if choice in ('1', '2'):
|
||||
break
|
||||
print("错误: 请输入1或2")
|
||||
|
||||
print("\n请选择Binary Count Changed筛选条件:")
|
||||
print("1. 仅筛选Yes")
|
||||
print("2. 仅筛选No")
|
||||
print("3. 不筛选(全部处理)")
|
||||
|
||||
while True:
|
||||
filter_choice = input("请输入筛选条件编号(1/2/3): ").strip()
|
||||
if filter_choice == '1':
|
||||
filter_choice = "Yes"
|
||||
break
|
||||
elif filter_choice == '2':
|
||||
filter_choice = "No"
|
||||
break
|
||||
elif filter_choice == '3':
|
||||
filter_choice = "All"
|
||||
break
|
||||
print("错误: 请输入1、2或3")
|
||||
|
||||
print("\n请选择工作表:")
|
||||
print("1. 测试仓库本周重点分析的SRPM")
|
||||
print("2. 仓库所有SRPM分析")
|
||||
|
||||
while True:
|
||||
sheet_choice = input("请输入工作表编号(1/2): ").strip()
|
||||
if sheet_choice == '1':
|
||||
sheet_name = "测试仓库本周重点分析的SRPM"
|
||||
break
|
||||
elif sheet_choice == '2':
|
||||
sheet_name = "仓库所有SRPM分析"
|
||||
break
|
||||
print("错误: 请输入1或2")
|
||||
|
||||
if choice == '1':
|
||||
while True:
|
||||
file_path = input("请输入Excel文件的绝对路径: ").strip()
|
||||
if os.path.exists(file_path):
|
||||
break
|
||||
print(f"错误: 文件 '{file_path}' 不存在,请重新输入")
|
||||
process_single_file(file_path, filter_choice, sheet_name)
|
||||
else:
|
||||
while True:
|
||||
dir_path = input("请输入包含Excel文件的文件夹路径: ").strip()
|
||||
if os.path.isdir(dir_path):
|
||||
break
|
||||
print(f"错误: 文件夹 '{dir_path}' 不存在,请重新输入")
|
||||
|
||||
for filename in os.listdir(dir_path):
|
||||
if filename.endswith('.xlsx') or filename.endswith('.xls'):
|
||||
file_path = os.path.join(dir_path, filename)
|
||||
print(f"\n处理文件: {filename}")
|
||||
process_single_file(file_path, filter_choice, sheet_name)
|
||||
67
script/06_dnf_repoclosure/README.md
Normal file
67
script/06_dnf_repoclosure/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# 测试项9 - 仓库中软件依赖检测脚本说明
|
||||
|
||||
## 脚本简介
|
||||
|
||||
本脚本用于测试指定产品的仓库中软件包的依赖异常,使用dnf repoclosure工具检测仓库软件包的依赖。
|
||||
脚本执行的终端环境架构需要和测试仓库的架构一致,否则会有很多误报。所以按照架构(aarch64,x86_64,loongarch64,mips64el)有对应的测试脚本。
|
||||
## 使用方法
|
||||
1. 在x86_64的服务器终端执行:
|
||||
`python3 test_pkg_repoclosure_x86_64.py`
|
||||
|
||||
2. 在aarch64的服务器终端执行:
|
||||
`python3 test_pkg_repoclosure_aarch64.py`
|
||||
|
||||
3. 在loongarch64的服务器终端执行:
|
||||
`python3 test_pkg_repoclosure_loongarch64.py`
|
||||
|
||||
4. 在mips64el的服务器终端执行:
|
||||
`python3 test_pkg_repoclosure_mips64.py`
|
||||
|
||||
---
|
||||
|
||||
## 输入文件
|
||||
测试脚本中的变量command_pairs 变量中包含当前测试产品版本对应的命令内容。该变量的每项由三部分组成:
|
||||
1. 描述信息(如产品版本及架构)
|
||||
|
||||
2. 检查已发布仓库中软件包依赖情况的 Shell 命令
|
||||
|
||||
3. 检查本次更新仓库中软件包依赖情况的 Shell 命令
|
||||
|
||||
以 V10SP1.1 x86_64 为例,其 command_pairs 项如下:
|
||||
```python
|
||||
command_pairs = [
|
||||
(
|
||||
"V10SP1.1 x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/x86_64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
)
|
||||
]
|
||||
```
|
||||
---
|
||||
|
||||
## 输出文件
|
||||
执行脚本执行时,会输出对应架构的执行结果,输出文件分别为:
|
||||
- `package_comparison_aarch64.md`
|
||||
- `package_comparison_x86_64.md`
|
||||
- `package_comparison_loongarch64.md`
|
||||
- `package_comparison_mips64el.md`
|
||||
|
||||
每个文件中:
|
||||
- 结果1:为已发布仓库中依赖报错软件包列表,
|
||||
- 结果2:为测试仓库中依赖报错软件包列表
|
||||
- 结果3:结果2中列表比结果1中列表多的软件包的包名,即测试仓库新增的报错包列表。结果为“无”,说明本周更新的包没有新增依赖报错,如果有包名,则需要确认是否为误报及查看有报错的原因。
|
||||
---
|
||||
|
||||
## 脚本流程说明
|
||||
|
||||
1. 执行dnf repoclosure 检查已发布的updates仓库是否有依赖报错,将结果输出到结果1;
|
||||
2. 执行dnf repoclosure 检查本周更新的updates仓库是否有依赖报错,将结果输出到结果2;
|
||||
3. 对比结果1和2,输出结果3 = 结果2依赖报错列表 - 结果2依赖报错列表;结果3是测试仓库新增的报错包列表;如不是‘无’,表示更新的包有依赖报错,需要排查是否为误报;
|
||||
4. 将步骤1-3的结果输出到md文档中。
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 需要在对应架构的服务器上执行对应的脚本,不同架构上也可以执行,但会有很多误报。
|
||||
---
|
||||
143
script/06_dnf_repoclosure/test_pkg_repoclosure_aarch64.py
Normal file
143
script/06_dnf_repoclosure/test_pkg_repoclosure_aarch64.py
Normal file
@@ -0,0 +1,143 @@
|
||||
import subprocess
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# 功能:检查某个产品的仓库中软件包的依赖报错
|
||||
# 输入:依赖检测的bash命令
|
||||
# 返回值: 依赖报错软件包列表
|
||||
def get_package_list(command):
|
||||
try:
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True, check=True)
|
||||
output = result.stdout
|
||||
pattern = r'package: (\S+)'
|
||||
packages = re.findall(pattern, output)
|
||||
return set(packages)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"执行命令时出错: {e.stderr}")
|
||||
return set()
|
||||
|
||||
|
||||
# 功能:将软件包的nvr处理后返回name字符串
|
||||
# 输入:字符串name-version-release
|
||||
# 返回值: 字符串name
|
||||
def extract_name(nvr_string):
|
||||
# 匹配形如 name-version-release 格式,只提取 name 部分
|
||||
match = re.match(r"^(.*)-[^-]+-[^-]+$", nvr_string)
|
||||
return match.group(1) if match else nvr_string
|
||||
|
||||
|
||||
# 定义命令对列表,每个元素为 (组名, 命令1, 命令2)
|
||||
command_pairs = [
|
||||
|
||||
(
|
||||
"NS7.9 aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/aarch64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/aarch64/ --repofrompath updates,https://update.cs2c.com.cn/private_test/repo/V7/V7Update9/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP1.1 aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/aarch64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP2 aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/aarch64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP3 aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/aarch64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP3 2403 aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/aarch64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"HOSTOS aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"HOSTOS 2309 aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"HOSTOS 2406 aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/HOST/2406/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/HOST/2406/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/HOST/2406/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP3 2309A aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/2309A/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/V10/2309A/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/2309A/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/2309A/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP3 2309B aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/2309B/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/V10/2309B/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/2309B/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/2309B/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10 HPC aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10 aarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/base/aarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/updates/aarch64/ --check updates --all -n|grep \'package:\''
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
file_name = "package_comparison_aarch64.md"
|
||||
with open(file_name, 'a') as f:
|
||||
f.write(f"# 软件包依赖对比结果\n")
|
||||
f.write(f"## 生成时间: {datetime.now()}\n\n")
|
||||
|
||||
for index, (group_name, command1, command2) in enumerate(command_pairs, 1):
|
||||
package_list1 = get_package_list(command1)
|
||||
package_list2 = get_package_list(command2)
|
||||
|
||||
more_packages = package_list2 - package_list1
|
||||
less_packages = package_list1 - package_list2
|
||||
|
||||
names1 = [extract_name(nvr) for nvr in less_packages]
|
||||
names2 = [extract_name(nvr) for nvr in more_packages]
|
||||
more_names = set(names2) - set(names1)
|
||||
|
||||
with open(file_name, 'a') as f:
|
||||
f.write(f"## {group_name} 对比结果\n\n")
|
||||
f.write("### 已发布仓库中执行的依赖检查命令\n")
|
||||
f.write("```bash\n")
|
||||
f.write(command1 + "\n")
|
||||
f.write("```\n\n")
|
||||
f.write("### 测试仓库中执行的依赖检查命令\n")
|
||||
f.write("```bash\n")
|
||||
f.write(command2 + "\n")
|
||||
f.write("```\n\n")
|
||||
f.write("## 结果1:已发布仓库中依赖报错软件包列表\n")
|
||||
if less_packages:
|
||||
for package in sorted(less_packages):
|
||||
f.write(f"- {package}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n## 结果2:测试仓库中依赖报错软件包列表\n")
|
||||
if more_packages:
|
||||
for package in sorted(more_packages):
|
||||
f.write(f"- {package}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n## 结果3:结果2中列表比结果1中列表多的软件包的包名,即测试仓库新增的报错包列表\n")
|
||||
if more_names:
|
||||
for name in sorted(more_names):
|
||||
f.write(f"- {name}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n---\n\n")
|
||||
|
||||
print(f"{group_name} 对比结果已输出到 {file_name} 文件中。")
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import subprocess
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# 功能:检查某个产品的仓库中软件包的依赖报错
|
||||
# 输入:依赖检测的bash命令
|
||||
# 返回值: 依赖报错软件包列表
|
||||
def get_package_list(command):
|
||||
try:
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True, check=True)
|
||||
output = result.stdout
|
||||
pattern = r'package: (\S+)'
|
||||
packages = re.findall(pattern, output)
|
||||
return set(packages)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"执行命令时出错: {e.stderr}")
|
||||
return set()
|
||||
|
||||
|
||||
# 功能:将软件包的nvr处理后返回name字符串
|
||||
# 输入:字符串name-version-release
|
||||
# 返回值: 字符串name
|
||||
def extract_name(nvr_string):
|
||||
# 匹配形如 name-version-release 格式,只提取 name 部分
|
||||
match = re.match(r"^(.*)-[^-]+-[^-]+$", nvr_string)
|
||||
return match.group(1) if match else nvr_string
|
||||
|
||||
|
||||
# 定义命令对列表,每个元素为 (组名, 命令1, 命令2)
|
||||
command_pairs = [
|
||||
(
|
||||
"V10SP1.1 loongarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/loongarch64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/loongarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/loongarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/loongarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP3 loongarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/loongarch64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/updates/loongarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/loongarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/loongarch64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP3 2403 loongarch64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/loongarch64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/loongarch64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/loongarch64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/loongarch64/ --check updates --all -n|grep \'package:\''
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
with open(file_name, 'a') as f:
|
||||
f.write(f"# 软件包依赖对比结果\n")
|
||||
f.write(f"## 生成时间: {datetime.now()}\n\n")
|
||||
|
||||
for index, (group_name, command1, command2) in enumerate(command_pairs, 1):
|
||||
package_list1 = get_package_list(command1)
|
||||
package_list2 = get_package_list(command2)
|
||||
|
||||
more_packages = package_list2 - package_list1
|
||||
less_packages = package_list1 - package_list2
|
||||
|
||||
names1 = [extract_name(nvr) for nvr in less_packages]
|
||||
names2 = [extract_name(nvr) for nvr in more_packages]
|
||||
more_names = set(names2) - set(names1)
|
||||
|
||||
with open(file_name, 'a') as f:
|
||||
f.write(f"## {group_name} 对比结果\n\n")
|
||||
f.write("### 已发布仓库中执行的依赖检查命令\n")
|
||||
f.write("```bash\n")
|
||||
f.write(command1 + "\n")
|
||||
f.write("```\n\n")
|
||||
f.write("### 测试仓库中执行的依赖检查命令\n")
|
||||
f.write("```bash\n")
|
||||
f.write(command2 + "\n")
|
||||
f.write("```\n\n")
|
||||
f.write("## 结果1:已发布仓库中依赖报错软件包列表\n")
|
||||
if less_packages:
|
||||
for package in sorted(less_packages):
|
||||
f.write(f"- {package}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n## 结果2:测试仓库中依赖报错软件包列表\n")
|
||||
if more_packages:
|
||||
for package in sorted(more_packages):
|
||||
f.write(f"- {package}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n## 结果3:结果2中列表比结果1中列表多的软件包的包名,即测试仓库新增的报错包列表\n")
|
||||
if more_names:
|
||||
for name in sorted(more_names):
|
||||
f.write(f"- {name}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n---\n\n")
|
||||
|
||||
print(f"{group_name} 对比结果已输出到 {file_name} 文件中。")
|
||||
|
||||
89
script/06_dnf_repoclosure/test_pkg_repoclosure_mips64.py
Normal file
89
script/06_dnf_repoclosure/test_pkg_repoclosure_mips64.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import subprocess
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# 功能:检查某个产品的仓库中软件包的依赖报错
|
||||
# 输入:依赖检测的bash命令
|
||||
# 返回值: 依赖报错软件包列表
|
||||
def get_package_list(command):
|
||||
try:
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True, check=True)
|
||||
output = result.stdout
|
||||
pattern = r'package: (\S+)'
|
||||
packages = re.findall(pattern, output)
|
||||
return set(packages)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"执行命令时出错: {e.stderr}")
|
||||
return set()
|
||||
|
||||
|
||||
# 功能:将软件包的nvr处理后返回name字符串
|
||||
# 输入:字符串name-version-release
|
||||
# 返回值: 字符串name
|
||||
def extract_name(nvr_string):
|
||||
# 匹配形如 name-version-release 格式,只提取 name 部分
|
||||
match = re.match(r"^(.*)-[^-]+-[^-]+$", nvr_string)
|
||||
return match.group(1) if match else nvr_string
|
||||
|
||||
|
||||
# 定义命令对列表,每个元素为 (组名, 命令1, 命令2)
|
||||
command_pairs = [
|
||||
(
|
||||
"V10SP1.1 mips64el 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/mips64el/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/mips64el/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/mips64el/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/mips64el/ --check updates --all -n|grep \'package:\''
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
||||
file_name = "package_comparison_mips64el.md"
|
||||
|
||||
with open(file_name, 'a') as f:
|
||||
f.write(f"# 软件包依赖对比结果\n")
|
||||
f.write(f"## 生成时间: {datetime.now()}\n\n")
|
||||
|
||||
for index, (group_name, command1, command2) in enumerate(command_pairs, 1):
|
||||
package_list1 = get_package_list(command1)
|
||||
package_list2 = get_package_list(command2)
|
||||
|
||||
more_packages = package_list2 - package_list1
|
||||
less_packages = package_list1 - package_list2
|
||||
|
||||
names1 = [extract_name(nvr) for nvr in less_packages]
|
||||
names2 = [extract_name(nvr) for nvr in more_packages]
|
||||
more_names = set(names2) - set(names1)
|
||||
|
||||
with open(file_name, 'a') as f:
|
||||
f.write(f"## {group_name} 对比结果\n\n")
|
||||
f.write("### 已发布仓库中执行的依赖检查命令\n")
|
||||
f.write("```bash\n")
|
||||
f.write(command1 + "\n")
|
||||
f.write("```\n\n")
|
||||
f.write("### 测试仓库中执行的依赖检查命令\n")
|
||||
f.write("```bash\n")
|
||||
f.write(command2 + "\n")
|
||||
f.write("```\n\n")
|
||||
f.write("## 结果1:已发布仓库报错列表比测试仓库列表多的包\n")
|
||||
if less_packages:
|
||||
for package in sorted(less_packages):
|
||||
f.write(f"- {package}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n## 结果2:测试仓库报错列表比已发布仓库列表多的包\n")
|
||||
if more_packages:
|
||||
for package in sorted(more_packages):
|
||||
f.write(f"- {package}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n## 结果3:结果2中列表比结果1中列表多的软件包的包名\n")
|
||||
if more_names:
|
||||
for name in sorted(more_names):
|
||||
f.write(f"- {name}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n---\n\n")
|
||||
|
||||
print(f"{group_name} 对比结果已输出到 {file_name} 文件中。")
|
||||
|
||||
132
script/06_dnf_repoclosure/test_pkg_repoclosure_x86_64.py
Normal file
132
script/06_dnf_repoclosure/test_pkg_repoclosure_x86_64.py
Normal file
@@ -0,0 +1,132 @@
|
||||
import subprocess
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# 功能:检查某个产品的仓库中软件包的依赖报错
|
||||
# 输入:依赖检测的bash命令
|
||||
# 返回值: 依赖报错软件包列表
|
||||
def get_package_list(command):
|
||||
try:
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True, check=True)
|
||||
output = result.stdout
|
||||
pattern = r'package: (\S+)'
|
||||
packages = re.findall(pattern, output)
|
||||
return set(packages)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"执行命令时出错: {e.stderr}")
|
||||
return set()
|
||||
|
||||
|
||||
# 功能:将软件包的nvr处理后返回name字符串
|
||||
# 输入:字符串name-version-release
|
||||
# 返回值: 字符串name
|
||||
def extract_name(nvr_string):
|
||||
# 匹配形如 name-version-release 格式,只提取 name 部分
|
||||
match = re.match(r"^(.*)-[^-]+-[^-]+$", nvr_string)
|
||||
return match.group(1) if match else nvr_string
|
||||
|
||||
|
||||
# 定义命令对列表,每个元素为 (组名, 命令1, 命令2)
|
||||
command_pairs = [
|
||||
(
|
||||
"NS7.9 x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/x86_64 --repofrompath updates,https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/x86_64 --repofrompath updates,https://update.cs2c.com.cn/private_test/repo/V7/V7Update9/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP1.1 x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/x86_64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP2 x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/x86_64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP3 x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/x86_64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10SP3 2403 x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/ --repofrompath updates,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath base,https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"HOSTOS x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"HOSTOS 2309 x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"HOSTOS 2406 x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/HOST/2406/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/HOST/2406/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/HOST/2406/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10 HPC x86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
),
|
||||
(
|
||||
"V10 86_64 对比",
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\'',
|
||||
'yum clean all;dnf repoclosure --disablerepo="*" --repofrompath=base,https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/base/x86_64/ --repofrompath=updates,https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/updates/x86_64/ --check updates --all -n|grep \'package:\''
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
file_name = "package_comparison_aarch64.md"
|
||||
with open(file_name, 'a') as f:
|
||||
f.write(f"# 软件包依赖对比结果\n")
|
||||
f.write(f"## 生成时间: {datetime.now()}\n\n")
|
||||
|
||||
for index, (group_name, command1, command2) in enumerate(command_pairs, 1):
|
||||
package_list1 = get_package_list(command1)
|
||||
package_list2 = get_package_list(command2)
|
||||
|
||||
more_packages = package_list2 - package_list1
|
||||
less_packages = package_list1 - package_list2
|
||||
|
||||
names1 = [extract_name(nvr) for nvr in less_packages]
|
||||
names2 = [extract_name(nvr) for nvr in more_packages]
|
||||
more_names = set(names2) - set(names1)
|
||||
|
||||
with open(file_name, 'a') as f:
|
||||
f.write(f"## {group_name} 对比结果\n\n")
|
||||
f.write("### 已发布仓库中执行的依赖检查命令\n")
|
||||
f.write("```bash\n")
|
||||
f.write(command1 + "\n")
|
||||
f.write("```\n\n")
|
||||
f.write("### 测试仓库中执行的依赖检查命令\n")
|
||||
f.write("```bash\n")
|
||||
f.write(command2 + "\n")
|
||||
f.write("```\n\n")
|
||||
f.write("## 结果1:已发布仓库中依赖报错软件包列表\n")
|
||||
if less_packages:
|
||||
for package in sorted(less_packages):
|
||||
f.write(f"- {package}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n## 结果2:测试仓库中依赖报错软件包列表\n")
|
||||
if more_packages:
|
||||
for package in sorted(more_packages):
|
||||
f.write(f"- {package}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n## 结果3:结果2中列表比结果1中列表多的软件包的包名,即测试仓库新增的报错包列表\n")
|
||||
if more_names:
|
||||
for name in sorted(more_names):
|
||||
f.write(f"- {name}\n")
|
||||
else:
|
||||
f.write("无\n")
|
||||
f.write("\n---\n\n")
|
||||
|
||||
print(f"{group_name} 对比结果已输出到 {file_name} 文件中。")
|
||||
|
||||
22
script/07_compat_repo_check/README.md
Normal file
22
script/07_compat_repo_check/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
1.<2E><><EFBFBD><EFBFBD>˵<EFBFBD><CBB5>
|
||||
<20>ֿ⼶<D6BF><E2BCB6><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>Լ<EFBFBD><D4BC>⡢<EFBFBD><E2A1A2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
2.Ŀ¼<C4BF>ṹ
|
||||
- compat
|
||||
- compat.py
|
||||
- repolist
|
||||
- requirements
|
||||
- README.md
|
||||
compat.py <20><><EFBFBD><EFBFBD><EFBFBD>Խű<D4BD>
|
||||
repolist <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʒ<EFBFBD>ֿ<EFBFBD><D6BF>б<EFBFBD>
|
||||
3.<2E><>װ<EFBFBD><D7B0><EFBFBD><EFBFBD>
|
||||
pip3 -r requirements
|
||||
4.<2E><><EFBFBD>й<EFBFBD><D0B9><EFBFBD>
|
||||
python3 compat.py
|
||||
5.<2E><><EFBFBD><EFBFBD>˵<EFBFBD><CBB5>
|
||||
<20><><EFBFBD><EFBFBD><EFBFBD>Ա<EFBFBD><D4B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ¼<C4BF><C2BC><EFBFBD>£<EFBFBD>
|
||||
/root/result/common/sp1/x86_64/repo-compare-report-20250425114902/repo.html
|
||||
ʹ<><CAB9>parser_repo_json<6F><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>
|
||||
6.repolist<73>ṹ˵<E1B9B9><CBB5>
|
||||
baseurl<72><6C><EFBFBD><EFBFBD><EFBFBD><EFBFBD>base<73>ֿ<EFBFBD><D6BF><EFBFBD>ַ
|
||||
updateurl<72><6C><EFBFBD><EFBFBD><EFBFBD><EFBFBD>update<74>ֿ<EFBFBD><D6BF><EFBFBD>ַ
|
||||
testurl<72><6C><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֿ<EFBFBD><D6BF><EFBFBD>ַ
|
||||
179
script/07_compat_repo_check/tools/compat.py
Normal file
179
script/07_compat_repo_check/tools/compat.py
Normal file
@@ -0,0 +1,179 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""compat.py"""
|
||||
|
||||
import concurrent.futures
|
||||
import pandas as pd
|
||||
import configparser
|
||||
import subprocess
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from itertools import zip_longest
|
||||
import time
|
||||
import shutil
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read('repolist')
|
||||
|
||||
RESULT_DIR = "/root/result/common/"
|
||||
RESULT_DIR1 = "/root/result/migrate/"
|
||||
|
||||
def prepare_dirs(dirs):
|
||||
if os.path.exists(dirs):
|
||||
shutil.rmtree(dirs)
|
||||
os.makedirs(dirs)
|
||||
|
||||
def test_task(section, arch):
|
||||
baseurl = config.get(section, 'baseurl')
|
||||
updateurl = config.get(section, 'updateurl')
|
||||
testurl = config.get(section, 'testurl')
|
||||
origurl = ','.join([baseurl, updateurl])
|
||||
work_dir = RESULT_DIR1 + section + '/' + arch
|
||||
prepare_dirs(work_dir)
|
||||
cmd = "/usr/bin/kylin_pkgchecker -output %s repo -l 2 -o %s -t %s -a %s" % (work_dir, updateurl, origurl, arch)
|
||||
popen = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=True,
|
||||
text=True
|
||||
)
|
||||
stdout, stderr = popen.communicate()
|
||||
|
||||
sections = ['sp1', 'sp2', 'sp3', 'sp3-2403']
|
||||
|
||||
def test_repo_update(index):
|
||||
start_index = index + 1
|
||||
orig_arch = config.get(sections[index], 'arch')
|
||||
orig_arch_list = re.split(',', orig_arch)
|
||||
orig_baseurl = config.get(sections[index], 'baseurl')
|
||||
orig_updateurl = config.get(sections[index], 'updateurl')
|
||||
orig_testurl = ','.join([orig_baseurl, orig_updateurl])
|
||||
for j in range(start_index,len(sections)):
|
||||
target_baseurl = config.get(sections[j], 'baseurl')
|
||||
target_updateurl = config.get(sections[j], 'updateurl')
|
||||
target_arch = config.get(sections[j], 'arch')
|
||||
target_testurl = ','.join([target_baseurl, target_updateurl])
|
||||
target_arch_list = re.split(',',target_arch)
|
||||
arch_list = list(set(orig_arch_list) & set(target_arch_list))
|
||||
# print(arch_list)
|
||||
for arch in arch_list:
|
||||
work_dir = RESULT_DIR
|
||||
# prepare_dirs(work_dir)
|
||||
# print(sections[index],sections[j],arch)
|
||||
cmd = "/usr/bin/kylin_pkgchecker -output %s repo -l 3 -o %s -t %s -a %s" % (work_dir, target_testurl, orig_testurl, arch)
|
||||
popen = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=True,
|
||||
text=True
|
||||
)
|
||||
stdout, stderr = popen.communicate()
|
||||
|
||||
def test_repo_migrations():
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
|
||||
futures = [executor.submit(test_repo_update, section) for section in range(len(sections))]
|
||||
|
||||
results = [future.result() for future in concurrent.futures.as_completed(futures)]
|
||||
|
||||
|
||||
def parser_config(section):
|
||||
arch = config.get(section,'arch')
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:
|
||||
futures = [executor.submit(test_task, section, a) for a in arch.split(',')]
|
||||
|
||||
results = [future.result() for future in concurrent.futures.as_completed(futures)]
|
||||
|
||||
|
||||
def get_current_date():
|
||||
timestamp = time.time()
|
||||
localtime = time.localtime(timestamp)
|
||||
now = time.strftime("%Y%m%d", localtime)
|
||||
return now
|
||||
|
||||
def get_max_len():
|
||||
pass
|
||||
|
||||
def write_excel(data):
|
||||
col = ["产品", "架构", "依赖失败", "兼容失败", "源仓库", "目标仓库"]
|
||||
whitedata = list(zip_longest(data[0], data[1], data[2] , list(set(data[3])), data[4] , data[5], fillvalue = None))
|
||||
|
||||
now = get_current_date()
|
||||
excel_file = "仓库兼容性检测%s.xlsx" % now
|
||||
sheet_name = "失败列表"
|
||||
df = pd.DataFrame(whitedata, columns = col)
|
||||
df.to_excel(
|
||||
excel_file,
|
||||
sheet_name = sheet_name,
|
||||
index = False,
|
||||
startrow = 0,
|
||||
engine = "openpyxl"
|
||||
)
|
||||
|
||||
def append_sheet_excel(data):
|
||||
book = load_workbook()
|
||||
sheet = book['失败列表']
|
||||
last_row = sheet.max_row
|
||||
append_df = pd.DataFrame(data)
|
||||
|
||||
def parser_repo_json():
|
||||
"""
|
||||
{"兼容":0,"NA":1,"不兼容":2,"待分析":3}
|
||||
{"依赖完整":0,"依赖缺失":1,"NA":"NA"}
|
||||
:return:
|
||||
"""
|
||||
|
||||
#缺少依赖包列表
|
||||
requirefaild = []
|
||||
#兼容性失败包列表
|
||||
compatfaild = []
|
||||
data_list = []
|
||||
# outfile = "/opt/repotest/repo.json"
|
||||
filename = "repo.json"
|
||||
|
||||
for dirpath, dirname, files in os.walk("/var/www/html/output/20250425/result/"):
|
||||
if filename in files:
|
||||
outfile = os.path.join(dirpath, filename)
|
||||
|
||||
with open(outfile) as f:
|
||||
data = json.load(f)
|
||||
for k,v in data.items():
|
||||
if k == 'repo_data':
|
||||
for r in v:
|
||||
if r["otherRequiresRet"] == 1:
|
||||
requirefaild.append(r["name"])
|
||||
if r["fileRet"] == 2 or r["cmdRet"] == 2 or r["serviceRet"] == 2 or r["configRet"] == 2 or r["abiRet"] == 2 or r["apiRet"] == 2:
|
||||
compatfaild.append(r["name"])
|
||||
if k == 'summary_data':
|
||||
arch = v["sys_arch"]
|
||||
origin_repo_url = v["origin_repo"]
|
||||
target_repo_url = v["target_repo"]
|
||||
|
||||
maxlen = max(len(requirefaild), len(compatfaild))
|
||||
archlist = [arch] * maxlen
|
||||
origin_repo_url_list = [origin_repo_url] * maxlen
|
||||
target_repo_url_list = [target_repo_url] * maxlen
|
||||
product = ["Kylin-v7"] * maxlen
|
||||
data_slist.append(product)
|
||||
data_list.append(archlist)
|
||||
data_list.append(requirefaild)
|
||||
data_list.append(compatfaild)
|
||||
data_list.append(origin_repo_url_list)
|
||||
data_list.append(target_repo_url_list)
|
||||
print(data_list)
|
||||
write_excel(data_list)
|
||||
|
||||
def test_single_repo():
|
||||
sections = config.sections()
|
||||
prepare_dirs(RESULT_DIR)
|
||||
for section in sections:
|
||||
parser_config(section)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_single_repo()
|
||||
77
script/07_compat_repo_check/tools/repolist
Normal file
77
script/07_compat_repo_check/tools/repolist
Normal file
@@ -0,0 +1,77 @@
|
||||
[sp1]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/V10SP1.1/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64,mips64el,loongarch64
|
||||
|
||||
[sp2]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64
|
||||
|
||||
[sp3]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64,loongarch64
|
||||
|
||||
[sp3-2403]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64,loongarch64
|
||||
|
||||
[hpc]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/HPC/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64
|
||||
|
||||
[zj]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/V10-ZJ/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64
|
||||
|
||||
[2309A]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/2309A/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/2309A/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/2309A/os/adv/lic/updates/
|
||||
arch=aarch64
|
||||
|
||||
[2309B]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/2309B/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/2309B/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/2309B/os/adv/lic/updates/
|
||||
arch=aarch64
|
||||
|
||||
[HOST-2309]
|
||||
baseurl=https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/HOST/2309/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64
|
||||
|
||||
[HOST-2406]
|
||||
baseurl=https://update.cs2c.com.cn/NS/HOST/2406/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/HOST/2406/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/HOST/2406/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64
|
||||
|
||||
[HOST]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/HOST/SP3/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64
|
||||
|
||||
[V7]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V17/V7Update9/os/adv/lic/base/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V7/V7Update9/os/adv/lic/updates/
|
||||
arch=x86_64,aarch64
|
||||
|
||||
[8U8]
|
||||
baseurl=https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/BaseOS/x86_64/os/,https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/AppStream/x86_64/os/
|
||||
updateurl=https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/BaseOS-updates/x86_64/os/,https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/AppStream-updates/x86_64/os/
|
||||
testurl=https://update.cs2c.com.cn/private_test/repo/V7/V7Update9/os/adv/lic/updates/
|
||||
arch=x86_64
|
||||
8
script/07_compat_repo_check/tools/requirements
Normal file
8
script/07_compat_repo_check/tools/requirements
Normal file
@@ -0,0 +1,8 @@
|
||||
pandas
|
||||
configparser
|
||||
subprocess
|
||||
threading
|
||||
itertools
|
||||
shutil
|
||||
time
|
||||
json
|
||||
0
script/08_V10SPX_products_upgrade_test/.gitkeep
Normal file
0
script/08_V10SPX_products_upgrade_test/.gitkeep
Normal file
22
script/08_V10SPX_products_upgrade_test/README.md
Normal file
22
script/08_V10SPX_products_upgrade_test/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 产品间升级测试
|
||||
|
||||
## 前置条件
|
||||
- 确保测试机网络在重启后能自动连接
|
||||
|
||||
## 使用步骤
|
||||
1. 将脚本传输到测试机
|
||||
2. 执行升级工具:
|
||||
```bash
|
||||
python3 upgrade_tool_pri.py
|
||||
```
|
||||
|
||||
3. 选择升级路径:
|
||||
- 1) V10SP1.1 → V10SP2 → V10SP3 → V10SP3-2403
|
||||
- 2) (V10SP1.1/V10SP2) → V10SP3 → V10SP3-2403
|
||||
- 3) V10SP1.1直接升级V10SP2
|
||||
- 4) (V10SP1.1/V10SP2)直接升级V10SP3
|
||||
- 5) (V10SP1.1/V10SP2/V10SP3)直接升级V10SP3-2403
|
||||
|
||||
## 注意事项
|
||||
- 脚本会生成守护进程`upgrade_tool.service`,机器重启后仍会执行;如果单步升级,需要手动systemctl stop upgrade_tool.service
|
||||
- 结果日志存放在`/var/log/`目录upgrade_tool_*.log
|
||||
465
script/08_V10SPX_products_upgrade_test/upgrade_tool_pri_0512.py
Normal file
465
script/08_V10SPX_products_upgrade_test/upgrade_tool_pri_0512.py
Normal file
@@ -0,0 +1,465 @@
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
import shutil
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# 全局系统架构变量
|
||||
SYSTEM_ARCH = None
|
||||
|
||||
def get_log_file_path(arch):
|
||||
"""获取当前升级路径对应的日志文件路径
|
||||
|
||||
Returns:
|
||||
str: 日志文件路径
|
||||
"""
|
||||
choice, _, _ = load_state()
|
||||
if choice and choice in UPGRADE_PATHS:
|
||||
return f"/var/log/upgrade_tool_path{choice}_{arch}.log"
|
||||
return "/var/log/upgrade_tool.log"
|
||||
|
||||
def log_message(message):
|
||||
"""记录日志信息并打印到控制台
|
||||
|
||||
Args:
|
||||
message (str): 要记录的日志消息
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
log_file = get_log_file_path(SYSTEM_ARCH)
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_entry = f"[{timestamp}] {message}\n"
|
||||
with open(log_file, "a") as f:
|
||||
f.write(log_entry)
|
||||
print(log_entry.strip()) # 输出带时间戳的日志格式
|
||||
|
||||
# 升级路径定义
|
||||
UPGRADE_PATHS = {
|
||||
"1": ["V10SP2", "V10SP3", "V10SP3-2403"],
|
||||
"2": ["V10SP3", "V10SP3-2403"],
|
||||
"3": ["V10SP2"],
|
||||
"4": ["V10SP3"],
|
||||
"5": ["V10SP3-2403"],
|
||||
}
|
||||
|
||||
NKVERS_CHECK = {
|
||||
"V10SP1.1": "SP1",
|
||||
"V10SP2": "SP2",
|
||||
"V10SP3": "SP3",
|
||||
"V10SP3-2403": "Halberd",
|
||||
}
|
||||
|
||||
# 每个版本对应的 repo 文件
|
||||
REPO_CONTENTS = {
|
||||
"V10SP2": """
|
||||
[base]
|
||||
name=Base Repo V10SP2
|
||||
baseurl=https://10.44.16.185/NS/V10/V10SP2/os/adv/lic/base/x86_64/
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
sslverify=False
|
||||
|
||||
[updates]
|
||||
name=Updates Repo V10SP2
|
||||
baseurl=https://10.44.16.185/private_test/repo/V10/V10SP2/os/adv/lic/updates/$basearch/
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
sslverify=False
|
||||
""",
|
||||
|
||||
"V10SP3": """
|
||||
[base]
|
||||
name=Base Repo V10SP3
|
||||
baseurl=https://10.44.16.185/NS/V10/V10SP3/os/adv/lic/base/x86_64/
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
sslverify=False
|
||||
|
||||
[updates]
|
||||
name=Updates Repo V10SP3
|
||||
baseurl=https://10.44.16.185/private_test/repo/V10/V10SP3/os/adv/lic/updates/$basearch/
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
sslverify=False
|
||||
""",
|
||||
|
||||
"V10SP3-2403": """
|
||||
[base]
|
||||
name=Base Repo V10SP3-2403
|
||||
baseurl=https://10.44.16.185/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
sslverify=False
|
||||
|
||||
[updates]
|
||||
name=Updates Repo V10SP3-2403
|
||||
baseurl=https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/$basearch/
|
||||
enabled=1
|
||||
gpgcheck=0
|
||||
sslverify=False
|
||||
"""
|
||||
}
|
||||
|
||||
YUM_REPO_DIR = "/etc/yum.repos.d"
|
||||
|
||||
|
||||
def run_command(cmd, check=True):
|
||||
"""执行系统命令并记录输出
|
||||
|
||||
Args:
|
||||
cmd (str): 要执行的命令
|
||||
check (bool): 如果为True,命令失败会退出程序
|
||||
|
||||
Returns:
|
||||
str: 命令的完整输出
|
||||
"""
|
||||
log_message(f"\n>>> 执行命令:{cmd}")
|
||||
|
||||
# 使用Popen实时输出
|
||||
process = subprocess.Popen(cmd, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
bufsize=1) # 行缓冲
|
||||
|
||||
output_lines = []
|
||||
while True:
|
||||
# 实时读取输出
|
||||
line = process.stdout.readline()
|
||||
if not line and process.poll() is not None:
|
||||
break
|
||||
if line:
|
||||
line = line.strip()
|
||||
print(line) # 实时打印
|
||||
output_lines.append(line)
|
||||
|
||||
# 检查返回码
|
||||
returncode = process.wait()
|
||||
if returncode != 0 and check:
|
||||
error_msg = f"❌ 命令执行失败:{process.stderr.read().strip()}"
|
||||
log_message(error_msg)
|
||||
sys.exit(1)
|
||||
|
||||
full_output = "\n".join(output_lines)
|
||||
log_message(f"命令完整输出:\n{full_output}")
|
||||
return full_output
|
||||
|
||||
|
||||
def get_system_arch():
|
||||
"""获取系统架构并返回对应的repo架构字符串
|
||||
|
||||
Returns:
|
||||
str: 系统架构(x86_64/aarch64/loongarch64)
|
||||
"""
|
||||
arch = run_command("uname -m").strip()
|
||||
# 架构映射
|
||||
arch_map = {
|
||||
"x86_64": "x86_64",
|
||||
"aarch64": "aarch64",
|
||||
"loongarch64": "loongarch64",
|
||||
}
|
||||
# 查找匹配的架构
|
||||
for key, value in arch_map.items():
|
||||
if key in arch:
|
||||
return value
|
||||
log_message(f"⚠️ 未知架构: {arch}, 默认使用x86_64")
|
||||
return "x86_64" # 默认回退
|
||||
|
||||
def write_repo_file(version):
|
||||
"""写入指定版本的YUM源配置文件
|
||||
|
||||
Args:
|
||||
version (str): 目标版本(V10SP2/V10SP3/V10SP3-2403)
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
log_message(f"📦 配置 {version} 的 YUM 源")
|
||||
|
||||
# 获取系统架构并替换URL
|
||||
arch = get_system_arch()
|
||||
repo_content = REPO_CONTENTS[version].replace("x86_64", arch)
|
||||
|
||||
# 备份现有repo文件
|
||||
for f in os.listdir(YUM_REPO_DIR):
|
||||
if f.endswith(".repo") and not f.endswith(".bak"):
|
||||
src = os.path.join(YUM_REPO_DIR, f)
|
||||
dst = os.path.join(YUM_REPO_DIR, f"{f}.bak")
|
||||
if os.path.exists(dst):
|
||||
os.remove(dst)
|
||||
os.rename(src, dst)
|
||||
log_message(f"🔁 已备份: {f} -> {f}.bak")
|
||||
|
||||
# 写入新repo文件
|
||||
new_repo_path = os.path.join(YUM_REPO_DIR, f"{version}.repo")
|
||||
with open(new_repo_path, "w") as f:
|
||||
f.write(repo_content)
|
||||
log_message(f"✅ 已写入新 repo 文件:{new_repo_path}")
|
||||
|
||||
|
||||
def print_progress(current, total):
|
||||
progress = int((current / total) * 50)
|
||||
print(f"\n[{'=' * progress}{' ' * (50 - progress)}] {current}/{total}")
|
||||
|
||||
def upgrade_to_version(version, current_idx=None, total_steps=None):
|
||||
"""执行升级到指定版本的操作
|
||||
|
||||
Args:
|
||||
version (str): 目标版本
|
||||
current_idx (int, optional): 当前步骤索引
|
||||
total_steps (int, optional): 总步骤数
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if current_idx is not None and total_steps is not None:
|
||||
print_progress(current_idx, total_steps)
|
||||
log_message(f"\n🔧 开始升级到版本 {version} ...")
|
||||
# 在备份yum源前先执行系统更新
|
||||
log_message("🔄 正在执行系统更新(dnf -y update)...")
|
||||
run_command("dnf -y update")
|
||||
log_message("✅ 系统更新完成")
|
||||
write_repo_file(version)
|
||||
run_command("dnf clean all")
|
||||
run_command("dnf makecache")
|
||||
run_command("dnf -y update")
|
||||
|
||||
# 在重启前验证版本升级是否成功
|
||||
log_message("🔍 验证升级结果...")
|
||||
check_version(version)
|
||||
|
||||
# 在dnf update成功后更新状态(仅多步升级)
|
||||
if current_idx is not None and total_steps > 1:
|
||||
choice, versions, _ = load_state()
|
||||
save_state(choice, versions, current_idx + 1)
|
||||
|
||||
log_message("🔁 系统即将重启...")
|
||||
run_command("reboot", check=False)
|
||||
|
||||
|
||||
def wait_for_reboot(timeout=300):
|
||||
"""等待系统重启并恢复上线
|
||||
|
||||
Args:
|
||||
timeout (int): 超时时间(秒),默认300秒
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
log_message("⏳ 等待系统重启并恢复上线...")
|
||||
time.sleep(10)
|
||||
elapsed = 0
|
||||
while elapsed < timeout:
|
||||
try:
|
||||
run_command("ping -c 1 127.0.0.1", check=False)
|
||||
# 系统上线后重启网络服务
|
||||
try:
|
||||
run_command("ifdown ens192 && ifup ens192", check=False)
|
||||
log_message("🔄 网络服务已重启")
|
||||
except:
|
||||
log_message("⚠️ 网络服务重启失败,继续执行")
|
||||
log_message("✅ 系统已上线")
|
||||
time.sleep(10)
|
||||
return
|
||||
except:
|
||||
time.sleep(5)
|
||||
elapsed += 5
|
||||
log_message("❌ 系统未能在预期时间内恢复,请检查虚拟机状态")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def check_version(expected):
|
||||
"""验证当前系统版本是否符合预期
|
||||
|
||||
Args:
|
||||
expected (str): 预期的版本号
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
log_message("🔍 正在验证版本...")
|
||||
keyword = NKVERS_CHECK.get(expected, "")
|
||||
if not keyword:
|
||||
print(f"❌ 未知版本: {expected}")
|
||||
sys.exit(1)
|
||||
|
||||
# 使用grep直接检查关键字
|
||||
try:
|
||||
run_command(f"nkvers | grep -q '{keyword}'")
|
||||
print(f"✅ 升级成功,版本特征 '{keyword}' 验证通过")
|
||||
# 仅在最后一步升级完成后清除状态
|
||||
if os.path.exists(STATE_FILE):
|
||||
with open(STATE_FILE, "r") as f:
|
||||
lines = f.read().splitlines()
|
||||
if len(lines) >= 2 and int(lines[1]) == len(lines[2:]):
|
||||
clear_state()
|
||||
except:
|
||||
print(f"❌ 升级失败,缺少版本特征关键字: '{keyword}'")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_menu():
|
||||
"""打印升级路径选择菜单
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
log_message("\n请选择升级路径:")
|
||||
print("1) V10SP1.1 -> V10SP2 -> V10SP3 -> V10SP3-2403")
|
||||
print("2) (V10SP1.1/V10SP2)--> V10SP3 --> V10SP3-2403")
|
||||
print("3) V10SP1.1直接升级 V10SP2")
|
||||
print("4) (V10SP1.1/V10SP2)直接升级 V10SP3")
|
||||
print("5) (V10SP1.1/V10SP2/V10SP3)直接升级 V10SP3-2403")
|
||||
print("q) 退出")
|
||||
|
||||
|
||||
STATE_FILE = "/var/lib/upgrade_tool.state"
|
||||
|
||||
def save_state(choice, versions, current_idx):
|
||||
"""保存升级状态到文件
|
||||
|
||||
Args:
|
||||
choice (str): 用户选择的升级路径
|
||||
versions (list): 升级路径版本列表
|
||||
current_idx (int): 当前步骤索引
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
with open(STATE_FILE, "w") as f:
|
||||
f.write(f"# 用户选择的升级路径\n{choice}\n")
|
||||
f.write(f"# 当前执行的步骤索引(从0开始)\n{current_idx}\n")
|
||||
f.write("# 升级路径版本列表\n")
|
||||
f.write("\n".join(versions) + "\n")
|
||||
|
||||
def load_state():
|
||||
"""从文件加载升级状态
|
||||
|
||||
Returns:
|
||||
tuple: (choice, versions, current_idx) 或 (None, None, None)
|
||||
"""
|
||||
if not os.path.exists(STATE_FILE):
|
||||
return None, None, None
|
||||
with open(STATE_FILE, "r") as f:
|
||||
# 跳过注释行,只读取实际数据行
|
||||
lines = [line for line in f.read().splitlines()
|
||||
if not line.startswith("#")]
|
||||
if len(lines) < 2:
|
||||
return None, None, None
|
||||
choice = lines[0]
|
||||
current_idx = int(lines[1])
|
||||
versions = lines[2:]
|
||||
return choice, versions, current_idx
|
||||
|
||||
def clear_state():
|
||||
"""清除升级状态文件
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if os.path.exists(STATE_FILE):
|
||||
os.remove(STATE_FILE)
|
||||
|
||||
def install_as_service():
|
||||
"""将脚本安装为系统服务
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
service_content = f"""[Unit]
|
||||
Description=System Upgrade Tool
|
||||
After=network.target
|
||||
Wants=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python3 {os.path.abspath(__file__)}
|
||||
# 仅在非正常退出时重启(不包含正常退出码0)
|
||||
Restart=on-failure
|
||||
RestartSec=10s
|
||||
User=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
"""
|
||||
|
||||
service_path = "/etc/systemd/system/upgrade_tool.service"
|
||||
with open(service_path, "w") as f:
|
||||
f.write(service_content)
|
||||
|
||||
run_command("systemctl daemon-reload")
|
||||
run_command("systemctl enable upgrade_tool.service")
|
||||
log_message("✅ 已安装为系统服务")
|
||||
|
||||
def main():
|
||||
"""主函数,处理升级流程
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
global SYSTEM_ARCH
|
||||
SYSTEM_ARCH = get_system_arch()
|
||||
# 确保日志目录存在
|
||||
os.makedirs(os.path.dirname(get_log_file_path(SYSTEM_ARCH)), exist_ok=True)
|
||||
log_message("=== 升级工具启动 ===")
|
||||
|
||||
if not shutil.which("nkvers"):
|
||||
log_message("⚠️ 未找到 nkvers 命令,请确保它在 PATH 中")
|
||||
sys.exit(1)
|
||||
|
||||
# 如果是首次运行,安装为服务
|
||||
if not os.path.exists("/etc/systemd/system/upgrade_tool.service"):
|
||||
install_as_service()
|
||||
|
||||
# 检查是否有未完成的升级任务
|
||||
choice, versions, current_idx = load_state()
|
||||
if choice and versions and current_idx < len(versions):
|
||||
log_message(f"🔍 检测到未完成的升级任务,正在恢复...")
|
||||
|
||||
# 先验证上一步的升级结果(如果有)
|
||||
if current_idx > 0:
|
||||
prev_version = versions[current_idx - 1]
|
||||
log_message(f"⏳ 验证前一步升级结果: {prev_version}")
|
||||
check_version(prev_version)
|
||||
|
||||
# 立即保存状态,防止重启后丢失进度
|
||||
save_state(choice, versions, current_idx)
|
||||
|
||||
# 继续后续升级步骤
|
||||
for version in versions[current_idx:]:
|
||||
upgrade_to_version(version, current_idx, len(versions))
|
||||
wait_for_reboot()
|
||||
return
|
||||
if choice and versions and current_idx == len(versions):
|
||||
clear_state()
|
||||
log_message("\n🎉 全部升级步骤完成!\n")
|
||||
sys.exit(0) # 明确退出防止服务重启
|
||||
|
||||
|
||||
while True:
|
||||
print_menu()
|
||||
choice = input("请输入选项编号: ").strip()
|
||||
if choice.lower() == "q":
|
||||
print("👋 已退出。")
|
||||
sys.exit(0) # 明确退出防止服务重启
|
||||
elif choice in UPGRADE_PATHS:
|
||||
versions = UPGRADE_PATHS[choice]
|
||||
# 仅多步升级创建状态文件
|
||||
if len(versions) > 1:
|
||||
save_state(choice, versions, 0)
|
||||
|
||||
for idx, version in enumerate(versions):
|
||||
upgrade_to_version(version, idx, len(versions))
|
||||
wait_for_reboot()
|
||||
|
||||
if len(versions) > 1:
|
||||
clear_state()
|
||||
print("\n🎉 全部升级步骤完成!\n")
|
||||
|
||||
else:
|
||||
print("⚠️ 无效输入,请重新选择。")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
21
script/09_v10spx_repo-rpm_cmp/README.md
Normal file
21
script/09_v10spx_repo-rpm_cmp/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
脚本简介
|
||||
本脚本用于测试增量或者全量V10SPx 系列产品仓库中软件包版本对比。
|
||||
读取 “每周仓库更新内测清单-20250513-1.xlsx”里的bug以及cve sheet里的 中的源码包全名、关联产品、涉及架构三列。
|
||||
|
||||
使用方法
|
||||
|
||||
python3 check_update_prod_final.py
|
||||
|
||||
|
||||
输出文件
|
||||
|
||||
|
||||
output-0513-x86.csv output-0513-arm.csv output-0513-loong.csv
|
||||
csv里面内容有:包名 版本信息 关联产品 涉及架构 对比内容 对比结果 测试用例版本 _调试信息
|
||||
筛选出对比结果为error,upgrade的内容,提交到每周仓库更新内测清单的测试报错汇总表格中
|
||||
|
||||
|
||||
注意事项
|
||||
1.check_update_prod_final.py中引入的repo_cmp_20250103.py和repodata.py,按照实际路径写入
|
||||
2.check_update_prod_final.py中file_path和sheet_name读取的文件按照实际路径填写
|
||||
350
script/09_v10spx_repo-rpm_cmp/check_update_prod_final.py
Normal file
350
script/09_v10spx_repo-rpm_cmp/check_update_prod_final.py
Normal file
@@ -0,0 +1,350 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# Time:2025/1/3 14:13
|
||||
# Author:GuoChao
|
||||
# FileName:check_update.py
|
||||
|
||||
import openpyxl
|
||||
import rpm_vercmp
|
||||
from repo_cmp_20250103 import plus,bubbleSort
|
||||
import csv
|
||||
from collections import defaultdict
|
||||
from repodata import write_to_csv
|
||||
|
||||
|
||||
def update_test_results(file_path, sheet_name):
|
||||
"""
|
||||
读取Excel文件中指定工作表的数据,并更新测试结果。
|
||||
现在返回的是列表嵌套字典,字典的键是列标题,值是具体数据。
|
||||
|
||||
:param file_path: Excel文件路径
|
||||
:param sheet_name: 工作表名称
|
||||
"""
|
||||
# 打开Excel文件
|
||||
wb = openpyxl.load_workbook(file_path, data_only=True, read_only=True)
|
||||
|
||||
# 选择指定工作表
|
||||
if sheet_name not in wb.sheetnames:
|
||||
raise ValueError(f"工作表 '{sheet_name}' 不存在。")
|
||||
|
||||
sheet = wb[sheet_name] # 使用工作表名称选择
|
||||
|
||||
# 假设第一行是标题(列名)
|
||||
headers = [sheet.cell(row=1, column=col).value for col in range(1, sheet.max_column + 1)]
|
||||
|
||||
# 存储每行数据的字典列表
|
||||
test_case_list = []
|
||||
|
||||
# 假设读取B, C, O列
|
||||
#columns_to_read = [2, 3, 13] # B列、C列、O列
|
||||
columns_to_read = [1, 2, 3]
|
||||
for row in range(2, sheet.max_row + 1): # 从第二行开始读取(跳过标题行)
|
||||
test_case_dict = {headers[col-1]: sheet.cell(row=row, column=col).value for col in columns_to_read}
|
||||
test_case_list.append(test_case_dict)
|
||||
|
||||
return test_case_list
|
||||
|
||||
def process_dicts_in_list(dict_list):
|
||||
"""
|
||||
遍历列表中的字典,并对每个字典的元素进行处理。
|
||||
|
||||
:param dict_list: 一个包含字典的列表
|
||||
:return: 处理后的字典列表
|
||||
"""
|
||||
processed_list = []
|
||||
|
||||
# 遍历字典列表
|
||||
for dictionary in dict_list:
|
||||
processed_dict = {}
|
||||
|
||||
try:
|
||||
# 提取源码包的版本信息
|
||||
#package_name = dictionary['源码包全名'].rsplit('-', 2)[0] # 提取包名
|
||||
package_name = dictionary['源码包全名'] and dictionary['源码包全名'].rsplit('-', 2)[0] or '未知包名'
|
||||
#package_version = dictionary['源码包全名'].rsplit('-', 2)[1] + "-" + dictionary['源码包全名'].rsplit('-', 2)[2].rsplit('.', 3)[0] # 提取版本
|
||||
package_version = (dictionary['源码包全名'] and dictionary['源码包全名'].rsplit('-', 2)[1] + "-" +
|
||||
dictionary['源码包全名'].rsplit('-', 2)[2].rsplit('.', 3)[0]) or '未知版本'
|
||||
|
||||
except (IndexError, KeyError) as e:
|
||||
# 错误处理:如果数据格式不正确或缺少关键字段,跳过该条记录
|
||||
print(f"错误处理字典 {dictionary}: {e}")
|
||||
continue
|
||||
|
||||
# 获取关联产品和涉及架构
|
||||
package_product = dictionary.get('关联产品', 'N/A') # 使用 get 防止 KeyError
|
||||
package_arch = dictionary.get('涉及架构', 'N/A')
|
||||
|
||||
# 将处理后的信息存储到字典中
|
||||
processed_dict['包名'] = package_name
|
||||
processed_dict['版本信息'] = package_version
|
||||
processed_dict['关联产品'] = package_product
|
||||
processed_dict['涉及架构'] = package_arch
|
||||
|
||||
# 将处理后的字典添加到列表中
|
||||
processed_list.append(processed_dict)
|
||||
|
||||
return processed_list
|
||||
|
||||
|
||||
def extract_full_version(srpm_name):
|
||||
"""从SRPM文件名中提取完整版本信息(包含所有必要部分)"""
|
||||
if not srpm_name:
|
||||
return None
|
||||
try:
|
||||
# 移除.src.rpm后缀
|
||||
base = srpm_name.replace('.src.rpm', '')
|
||||
|
||||
# 分割架构部分
|
||||
if '.' in base:
|
||||
main_part, _ = base.rsplit('.', 1) # 丢弃架构信息
|
||||
else:
|
||||
main_part = base
|
||||
|
||||
# 分割版本和发行号
|
||||
if '-' in main_part:
|
||||
# 从右边找到第一个连字符分割
|
||||
parts = main_part.rsplit('-', 1)
|
||||
version_part = parts[0].split('-')[-1] # 获取真正的版本部分
|
||||
release_part = parts[1]
|
||||
return f"{version_part}-{release_part}"
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"版本解析错误 [{srpm_name}]: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
def process_dicts_in_list(dict_list):
|
||||
"""处理原始数据,使用新的版本提取方法"""
|
||||
processed_list = []
|
||||
for dictionary in dict_list:
|
||||
processed_dict = {}
|
||||
try:
|
||||
full_name = dictionary.get('源码包全名', '')
|
||||
|
||||
# 提取包名(保留原始逻辑)
|
||||
package_name = full_name.rsplit('-', 2)[0] if full_name else '未知包名'
|
||||
|
||||
# 使用新方法提取版本
|
||||
package_version = extract_full_version(full_name) or '未知版本'
|
||||
|
||||
processed_dict.update({
|
||||
'包名': package_name,
|
||||
'版本信息': package_version,
|
||||
'关联产品': dictionary.get('关联产品', 'N/A'),
|
||||
'涉及架构': dictionary.get('涉及架构', 'N/A')
|
||||
})
|
||||
processed_list.append(processed_dict)
|
||||
except Exception as e:
|
||||
print(f"处理记录出错: {str(e)}")
|
||||
continue
|
||||
return processed_list
|
||||
|
||||
def check_version(processed_list, productinfo):
|
||||
"""版本对比逻辑:仅对比测试用例关联产品对应的仓库"""
|
||||
for processed_dict in processed_list:
|
||||
# 获取当前测试用例的关联产品名称
|
||||
current_product = processed_dict.get('关联产品', '未知产品')
|
||||
|
||||
# 如果产品不存在或未配置仓库,跳过
|
||||
if current_product not in productinfo:
|
||||
processed_dict.update({
|
||||
'对比内容': "无关联产品信息",
|
||||
'对比结果': "N/A"
|
||||
})
|
||||
continue
|
||||
|
||||
# 仅获取当前关联产品对应的仓库数据
|
||||
pkglist, filename = productinfo[current_product]['repolist']
|
||||
|
||||
# 收集所有版本及所属产品(用于展示)
|
||||
all_versions = {}
|
||||
# 按产品线分组的版本(用于比较)
|
||||
product_line_versions = defaultdict(list)
|
||||
|
||||
# 仅遍历当前产品的仓库
|
||||
for pkg in filename:
|
||||
if pkg['srcname'] == processed_dict['包名']:
|
||||
ver = extract_full_version(pkg['sourcerpm'])
|
||||
if ver:
|
||||
# 获取该包所属的产品线(如V10SP3-2403)
|
||||
product_line = pkg.get('product', 'unknown')
|
||||
|
||||
# 收集展示数据
|
||||
if ver not in all_versions:
|
||||
all_versions[ver] = set()
|
||||
all_versions[ver].add(product_line)
|
||||
|
||||
# 收集比较数据
|
||||
product_line_versions[product_line].append(ver)
|
||||
|
||||
# ... 其余逻辑保持不变 ...
|
||||
# 计算各产品线的最高版本
|
||||
max_versions = {}
|
||||
for product_line, versions in product_line_versions.items():
|
||||
sorted_versions = bubbleSort(versions)
|
||||
if sorted_versions:
|
||||
max_versions[product_line] = sorted_versions[-1]
|
||||
|
||||
# 执行版本比较
|
||||
test_case_ver = processed_dict['版本信息']
|
||||
result = "无匹配版本"
|
||||
error_flag = False
|
||||
all_higher = True # 假设所有产品线版本都比测试用例高
|
||||
|
||||
if max_versions:
|
||||
for product_line, max_ver in max_versions.items():
|
||||
try:
|
||||
cmp = rpm_vercmp.vercmp(test_case_ver, max_ver)
|
||||
if cmp > 0:
|
||||
error_flag = True # 发现任一产品线版本低于测试用例
|
||||
break
|
||||
if cmp >= 0:
|
||||
all_higher = False # 存在不高于的情况
|
||||
except Exception as e:
|
||||
print(f"版本比较错误: {str(e)}")
|
||||
|
||||
if error_flag:
|
||||
result = "error,upgrade"
|
||||
elif all_higher:
|
||||
result = "downgrade"
|
||||
else:
|
||||
result = "equal"
|
||||
|
||||
# 生成对比内容(保持原始顺序)
|
||||
sorted_versions = bubbleSort(list(all_versions.keys()))
|
||||
compared_content = []
|
||||
for ver in sorted_versions:
|
||||
products = '|'.join(all_versions[ver])
|
||||
compared_content.append(f"{ver} ({products})")
|
||||
|
||||
# 更新结果
|
||||
processed_dict.update({
|
||||
'对比内容': '\n'.join(compared_content) or "无匹配版本",
|
||||
'对比结果': result,
|
||||
'测试用例版本': test_case_ver,
|
||||
'_调试信息': ' | '.join([f"{k}:{v}" for k, v in max_versions.items()]) # 可选调试字段
|
||||
})
|
||||
|
||||
return processed_list
|
||||
def parse_product_from_url(url):
|
||||
"""
|
||||
从仓库URL中解析产品名称(例如V10SP1)
|
||||
示例URL:https://update.cs2c.com.cn/NS/V10/V10SP1/os/adv/lic/base/x86_64/
|
||||
"""
|
||||
parts = url.strip('/').split('/')
|
||||
try:
|
||||
v10_index = parts.index('V10')
|
||||
if v10_index + 1 < len(parts):
|
||||
return parts[v10_index + 1]
|
||||
except ValueError:
|
||||
pass
|
||||
return '未知产品'
|
||||
# 示例调用
|
||||
#file_path = r'C:/Users/lenovo.DESKTOP-JAA702O/Desktop/update内网源集成测试单-20241231.xlsx'
|
||||
#file_path = r'C:/Users/Lenovo/Desktop/update内网源集成测试单-20250318.xlsx'
|
||||
file_path = r'/root/mxh/每周仓库更新内测清单-20250513-1.xlsx'
|
||||
#file_path = r'D:\Desktop\sp1.1loon.xlsx'
|
||||
#file_path = r'D:\Desktop\0422.xlsx'
|
||||
#sheet_name = 'bug【20250415功能测试】' # 工作表名称
|
||||
sheet_name = 'x86'
|
||||
#sheet_name = 'cve【20250408】'
|
||||
#sheet_name = 'bug【20250325测试结果统计】' # 工作表名称
|
||||
# 定义产品名称与对应 URL 列表的映射
|
||||
product_urls = {
|
||||
"银河麒麟高级服务器操作系统 V10 SP3": [
|
||||
"https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/",
|
||||
"https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/x86_64/"
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/aarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/aarch64/"
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/loongarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/loongarch64"
|
||||
],
|
||||
"银河麒麟高级服务器操作系统 V10 SP2": [
|
||||
"https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/",
|
||||
"https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/x86_64/",
|
||||
"https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/x86_64/",
|
||||
"https://10.44.16.185/private_test/repo/V10/V10SP3/os/adv/lic/updates/x86_64/"
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/aarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/aarch64/",
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/aarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3/os/adv/lic/updates/aarch64/"
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/loongarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/loongarch64/",
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/loongarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3/os/adv/lic/updates/loongarch64/"
|
||||
],
|
||||
"银河麒麟高级服务器操作系统 V10 SP1": [
|
||||
"https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/",
|
||||
"https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/x86_64/",
|
||||
"https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/x86_64/",
|
||||
"https://10.44.16.185/private_test/repo/V10/V10SP3/os/adv/lic/updates/x86_64/",
|
||||
"https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/x86_64/",
|
||||
"https://10.44.16.185/private_test/repo/V10/V10SP2/os/adv/lic/updates/x86_64/"
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/aarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/aarch64/",
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/aarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3/os/adv/lic/updates/aarch64/",
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP2/os/adv/lic/base/aarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP2/os/adv/lic/updates/aarch64/",
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/loongarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/loongarch64/",
|
||||
# "https://update.cs2c.com.cn/NS/V10/V10SP3/os/adv/lic/base/loongarch64/",
|
||||
# "https://10.44.16.185/private_test/repo/V10/V10SP3/os/adv/lic/updates/loongarch64/"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# 动态生成 products_info(关键修改部分)
|
||||
products_info = {}
|
||||
for product_name, urls in product_urls.items():
|
||||
pkglist_total = []
|
||||
filename_total = []
|
||||
for url in urls:
|
||||
# 调用 plus 处理单个 URL,返回 (pkglist_part, filename_part)
|
||||
pkglist_part, filename_part = plus(url)
|
||||
|
||||
# 解析当前 URL 的产品名称(例如 V10SP1)
|
||||
product = parse_product_from_url(url)
|
||||
|
||||
# 为每个软件包字典添加 product 字段
|
||||
for pkg in filename_part:
|
||||
pkg['product'] = product # 添加关键字段
|
||||
|
||||
# 合并数据
|
||||
filename_total.extend(filename_part)
|
||||
pkglist_total.extend(pkglist_part)
|
||||
|
||||
# 更新 products_info
|
||||
products_info[product_name] = {
|
||||
'repolist': (pkglist_total, filename_total)
|
||||
}
|
||||
|
||||
|
||||
result = update_test_results(file_path, sheet_name)
|
||||
processed_list=process_dicts_in_list(result)
|
||||
|
||||
processed_list=check_version(processed_list,products_info)
|
||||
|
||||
|
||||
def write_to_csv(processed_list, output_filename):
|
||||
"""
|
||||
将处理后的数据写入 CSV 文件
|
||||
"""
|
||||
# 动态收集所有字段名(保持顺序)
|
||||
fieldnames = []
|
||||
seen = set()
|
||||
for item in processed_list:
|
||||
for key in item.keys():
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
fieldnames.append(key)
|
||||
|
||||
# 写入 CSV 文件
|
||||
with open(output_filename, 'w', newline='', encoding='utf-8') as csvfile:
|
||||
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
for row in processed_list:
|
||||
# 填充缺失字段为空值
|
||||
full_row = {key: row.get(key, "") for key in fieldnames}
|
||||
writer.writerow(full_row)
|
||||
print(f"数据已保存至: {output_filename}")
|
||||
write_to_csv(processed_list,'output-0515-x86.csv')
|
||||
759
script/09_v10spx_repo-rpm_cmp/repo_cmp_20250103.py
Normal file
759
script/09_v10spx_repo-rpm_cmp/repo_cmp_20250103.py
Normal file
@@ -0,0 +1,759 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
# Time:2025/1/3 9:16
|
||||
# Author:GuoChao
|
||||
# FileName:repo_cmp_20250103.py
|
||||
import requests
|
||||
from lxml import etree
|
||||
import rpm_vercmp
|
||||
import xlsxwriter
|
||||
import datetime
|
||||
import re
|
||||
import xlwt
|
||||
import os
|
||||
import subprocess
|
||||
import logging
|
||||
logging.captureWarnings(True)
|
||||
from repodata import plus,getsrc
|
||||
|
||||
#####获取软件包列表#####
|
||||
|
||||
def openfile(*files): # 方法1:用于从文件中获取软件包列表
|
||||
pkglist = []
|
||||
for file in files: # 对多个文件进行处理
|
||||
with open(file, "r", encoding='utf-8') as output: # 打开文件
|
||||
for line in output.readlines(): # 依次读取每行
|
||||
if ".rpm" in line: # 去除非rpm包的影响 也可以修改为判断其他关键字
|
||||
pkglist.append(line.strip()) # 去除每行头尾的空白
|
||||
print('\n' + '###### 00 通过 Openfile 获取软件包列表完成,软件包数量:', len(pkglist), '#####')
|
||||
return pkglist
|
||||
|
||||
|
||||
def gethtml(*baseurls): #方法2:从html文件解析软件包列表
|
||||
head = {
|
||||
"User-Agent": "Mozilla / 5.0(Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 80.0.3987.122 Safari / 537.36"}
|
||||
pkglist = []
|
||||
for baseurl in baseurls: # 对多个url进行处理
|
||||
response = requests.get(baseurl, headers=head,verify=False)
|
||||
# 解决 requests.exceptions.SSLError 请求异常,SSL错误,证书认证失败问题进行解决
|
||||
html = response.text
|
||||
html = etree.HTML(html)
|
||||
a_links = html.xpath("//a") # 查找所有 a 标签 ,//a 代表 html 下所有 a,html/body/a 同理
|
||||
for item in a_links:
|
||||
item1 = item.get('href') # 获取a标签某一属性
|
||||
if ".rpm" in item1: # 去除非rpm包的影响 也可以修改为判断其他关键字,备选.el8
|
||||
item1 = item1.replace("%2B", "+") # 对url不能识别”+“进行修正
|
||||
pkglist.append(item1)
|
||||
print('\n' + '##### 00 通过 Gethtml 获取软件包列表完成,软件包数量:', len(pkglist), '#####')
|
||||
return pkglist
|
||||
|
||||
|
||||
#####对仓库软件包进行分析#####
|
||||
def rjbfx(lab,listA): #对软件包构成进行分析
|
||||
|
||||
# 首先对总的列表进行分析
|
||||
|
||||
# 区分i686和非i686
|
||||
i686l=[x for x in listA if ".i686" in x]
|
||||
fi686l=[x for x in listA if ".i686" not in x]
|
||||
|
||||
# 区分mbs和非mbs软件包
|
||||
mbs1 = [x for x in listA if ".module" in x]
|
||||
fmbs1 = [x for x in listA if ".module" not in x]
|
||||
|
||||
print('\n' + '##### 01 通过 Rjbgfh 分析 ' + lab + ' 软件包完成,涉及软件包总数量:', len(listA), '#####')
|
||||
print("\n" + "其中涉及i686相关包数量:", len(i686l), "\n" + "其中涉及非i686相关软件包数量:", len(fi686l))
|
||||
print("其中涉及模块包数量:", len(mbs1), "\n" + "其中涉及非模块包数量:", len(fmbs1))
|
||||
|
||||
# 其次对其中i686软件包进行处理
|
||||
mbs2 = [x for x in i686l if ".module" in x]
|
||||
fmbs2 = [x for x in i686l if ".module" not in x]
|
||||
print('\n' + '##### 01 通过 Rjbgfh 分析 '+lab+' i686 软件包完成,涉及软件包数量:', len(i686l), '#####')
|
||||
print("其中涉及模块包数量:", len(mbs2),"\n" + "其中涉及非模块包数量:", len(fmbs2))
|
||||
|
||||
# 最后对其中非i686软件包进行处理
|
||||
mbs3 = [x for x in fi686l if ".module" in x]
|
||||
fmbs3 = [x for x in fi686l if ".module" not in x]
|
||||
print('\n' + '##### 01 通过 Rjbgfh 分析 '+lab+' 非i686 软件包完成,涉及软件包数量:', len(fi686l), '#####')
|
||||
print("其中涉及模块包数量:", len(mbs3),"\n" + "其中涉及非模块包数量:", len(fmbs3))
|
||||
|
||||
return listA,mbs1,fmbs1,i686l,mbs2,fmbs2,fi686l,mbs3,fmbs3
|
||||
|
||||
|
||||
#####严格模式比较#####
|
||||
def ygjgfl(pkglist): #对严格模式处理下的结果进行分类
|
||||
|
||||
name = []
|
||||
types = []
|
||||
arch = []
|
||||
|
||||
for pkg in pkglist:
|
||||
name.append(pkg)
|
||||
|
||||
if ".module" in pkg and ".ks8" in pkg: # 对软件包类型、以及是否产品化进行判断
|
||||
types.append("模块包(产品化)")
|
||||
elif ".module" in pkg:
|
||||
types.append("模块包")
|
||||
elif ".ks8" in pkg:
|
||||
types.append("非模块包(产品化)")
|
||||
else:
|
||||
types.append("非模块包")
|
||||
|
||||
# 对软件包类型进行判断
|
||||
if ".i686" in pkg:
|
||||
arch.append("i686")
|
||||
elif ".x86_64" in pkg:
|
||||
arch.append("x86_64")
|
||||
elif ".noarch" in pkg:
|
||||
arch.append("noarch")
|
||||
elif ".aarch64" in pkg:
|
||||
arch.append("aarch64")
|
||||
elif ".src" in pkg:
|
||||
arch.append("src")
|
||||
else:
|
||||
arch.append("N/A")
|
||||
mks=types.count("模块包(产品化)")+types.count("模块包")
|
||||
fmks=types.count("非模块包")+types.count("非模块包(产品化)")
|
||||
return name,types,arch,mks,fmks
|
||||
|
||||
|
||||
def comp1(labA,labB,listA1,listB1): #严格模式下比较
|
||||
|
||||
|
||||
listA=[]
|
||||
for pkg in listA1:
|
||||
# if ".oe" in pkg:
|
||||
# pkg1 = pkg.replace(".oe1|oe2003sp4", "")
|
||||
# elif ".ky10" in pkg:
|
||||
# pkg1 = pkg.replace(".ky10|.p\d{2}.ky10", "")
|
||||
# else:
|
||||
# pkg1=pkg
|
||||
pkg1=pkg.rsplit("-",1)[0]+"-"+pkg.rsplit("-",1)[-1].split(".",1)[0]
|
||||
listA.append(pkg1)
|
||||
listB = []
|
||||
for pkg in listB1:
|
||||
pkg1 = pkg.rsplit("-", 1)[0] + "-" + pkg.rsplit("-", 1)[-1].split(".", 1)[0]
|
||||
# if ".oe" in pkg:
|
||||
# pkg1 = pkg.replace(".oe1|oe2003sp4", "")
|
||||
# elif ".ky10" in pkg:
|
||||
# pkg1 = pkg.replace(".ky10|.p\d{2}.ky10", "")
|
||||
# else:
|
||||
# pkg1 = pkg
|
||||
listB.append(pkg1)
|
||||
listA = list(set(listA))
|
||||
print("去重后A",len(listA))
|
||||
listB= list(set(listB))
|
||||
print("去重后B" ,len(listB))
|
||||
|
||||
|
||||
common_pkg = [x for x in listA if x in listB] # 整体ab都有
|
||||
incommon_pkg1 = [x for x in listA if x not in listB] # 整体a有b没有
|
||||
incommon_pkg2 = [x for x in listB if x not in listA] # 整体b有a没有
|
||||
|
||||
ABtong,leixing1,arch1,mk1,fmk1=ygjgfl(common_pkg) # 对相同包进行分类
|
||||
Ashao,leixing2,arch2,mk2,fmk2=ygjgfl(incommon_pkg2) # 对A的少包进行分类 B独有
|
||||
Aduo,leixing3,arch3,mk3,fmk3=ygjgfl(incommon_pkg1) # 对A多的包进行分类 A独有
|
||||
|
||||
print('\n'+"##### 02 严格模式 软件包对比开始 #####")
|
||||
print(labA+" 与 "+labB+"相同的软件包数量:",len(ABtong),"\n"+'模块包:',leixing1.count("模块包(产品化)")+leixing1.count("模块包"),"\n"+'其中非模块包:',leixing1.count("非模块包")+leixing1.count("非模块包(产品化)"))
|
||||
print(labA+" 比 "+labB+"少的软件包数量:",len(Ashao),"\n"+'模块包:',leixing2.count("模块包(产品化)")+leixing2.count("模块包"),"\n"+'其中非模块包:',leixing2.count("非模块包")+leixing2.count("非模块包(产品化)"))
|
||||
print(labA+" 比 "+labB+"多的软件包数量:",len(Aduo),"\n"+'模块包:',leixing3.count("模块包(产品化)")+leixing3.count("模块包"),"\n"+'其中非模块包:',leixing3.count("非模块包")+leixing3.count("非模块包(产品化)"))
|
||||
print("##### 02 严格模式 软件包对比完成 #####"+'\n')
|
||||
return Aduo,leixing3,Ashao,leixing2,ABtong,leixing1,arch1,arch2,arch3,mk1,fmk1,mk2,fmk2,mk3,fmk3
|
||||
|
||||
|
||||
|
||||
#####宽松模式比较#####
|
||||
|
||||
def bubbleSort(arr): # 对同一仓库的多版本进行冒泡排序#
|
||||
n = len(arr)
|
||||
for i in range(n): # 遍历所有数组元素
|
||||
for j in range(0, n - i - 1): # Last i elements are already in place
|
||||
if rpm_vercmp.vercmp(arr[j], arr[j + 1]) == 1:
|
||||
arr[j], arr[j + 1] = arr[j + 1], arr[j]
|
||||
return arr
|
||||
|
||||
def getnvra(data): # 获取软件包列表nvra
|
||||
filename=[]
|
||||
for i in data:
|
||||
(n, v, r, a) = (i.rsplit('-', 2)[0], i.rsplit('-', 2)[1], i.rsplit('-', 2)[2].rsplit('.', 2)[0], i.rsplit('.', 2)[1]) # 切片方式获取软件包nvra
|
||||
filename1 = {'name': n, 'version': v, 'release': r, 'arch': a}
|
||||
# (n, v, r) = (i.rsplit('-', 2)[0], i.rsplit('-', 2)[1], i.rsplit('-', 2)[2].rsplit('.', 2)[0]) # 切片方式获取软件包nvr
|
||||
# filename1 = {'name': n, 'version': v, 'release': r}
|
||||
filename.append(filename1)
|
||||
print('\n'+'##### 02 通过 getnvra 获取软件包nvr完成,软件包个数:',len(filename),'#####')
|
||||
return filename
|
||||
|
||||
|
||||
def relgfh(j): #对release进行统一规范化修改
|
||||
|
||||
if ".module" in j: # 对模块包进行统一修正
|
||||
j = j.replace(".module+", ".module_") # 对红帽模块、龙蜥包命名进行修改
|
||||
#j = j.rsplit('+', 2)[0] # 仅去除模块包release中 +10155+7f691889 字段
|
||||
j = j.split('_', 1)[0] # 去除模块包relases中 +/_el8.2.0+10155+7f691889 字段,去除模块el8.x平台影响
|
||||
result = re.search(".ks8.\d{2}", j) # 去除 ks/ns 产品化影响###
|
||||
if result:
|
||||
result = result.group()
|
||||
j = j.replace(str(result), "")
|
||||
|
||||
elif ".oe" in j: # 对oe进行统一修正
|
||||
#j = j.replace(".module+", ".module_")
|
||||
#j = j.rsplit('+', 2)[0] # 仅去除模块包release中 +10155+7f691889 字段
|
||||
j = j.split('_', 1)[0] # 去除模块包relases中 +/_el8.2.0+10155+7f691889 字段,去除模块el8.x平台影响
|
||||
# result = re.search(".oe\d{8}", j) # 去除 ks/ns 产品化影响###
|
||||
result = re.search(".oe1", j) # 去除 ks/ns 产品化影响###
|
||||
if result:
|
||||
result = result.group()
|
||||
j = j.replace(str(result), "")
|
||||
|
||||
else: # # 对非模块包进行统一修正
|
||||
j = j.replace("ky10", "")
|
||||
result = re.search(".ks8.\d{2}|.ns7.\d{2}|.p\d{2}", j) # 去除 ks/ns/p 产品化影响###
|
||||
if result:
|
||||
result = result.group()
|
||||
j = j.replace(str(result), "")
|
||||
|
||||
return j
|
||||
|
||||
|
||||
|
||||
def ksjgfl(namelist,data):
|
||||
|
||||
pkglist = []
|
||||
types=[]
|
||||
arch=[]
|
||||
|
||||
for i in namelist:
|
||||
rpmname = []
|
||||
for j in data:
|
||||
if j["name"] == i:
|
||||
rpmname.append(j["name"] + "-" + j["version"] + "-" + j["release"] + "." + j["arch"]+ "." + "rpm")
|
||||
#rpmname.append(j["name"] + "-" + j["version"] + "-" + j["release"])
|
||||
rpmname = bubbleSort(rpmname)
|
||||
#print(rpmname)
|
||||
if ".module" in rpmname[-1] and ".ks8" in rpmname[-1]:
|
||||
types.append("模块包(产品化)")
|
||||
elif ".module" in rpmname[-1]:
|
||||
types.append("模块包")
|
||||
elif ".ks8" in rpmname[-1]:
|
||||
types.append("非模块包(产品化)")
|
||||
else:
|
||||
types.append("非模块包")
|
||||
|
||||
if ".i686" in rpmname[-1]:
|
||||
arch.append("i686")
|
||||
elif ".x86_64" in rpmname[-1]:
|
||||
arch.append("x86_64")
|
||||
elif ".noarch" in rpmname[-1]:
|
||||
arch.append("noarch")
|
||||
elif ".aarch64" in rpmname[-1]:
|
||||
arch.append("aarch64")
|
||||
elif ".src" in rpmname[-1]:
|
||||
arch.append("src")
|
||||
else:
|
||||
arch.append("N/A")
|
||||
pkglist.append("\n".join(rpmname))
|
||||
return namelist,pkglist,types,arch
|
||||
|
||||
def comp2(labA,labB,listA,listB):
|
||||
|
||||
data1 = getnvra(listA)
|
||||
data2 = getnvra(listB)
|
||||
|
||||
listA_keys = []
|
||||
listB_keys = []
|
||||
|
||||
for i in data1:
|
||||
key = i["name"]
|
||||
listA_keys.append(key)
|
||||
for i in data2:
|
||||
key = i["name"]
|
||||
listB_keys.append(key)
|
||||
|
||||
common_keys = [x for x in listA_keys if x in listB_keys]
|
||||
icommon_keys1 = [x for x in listA_keys if x not in common_keys]
|
||||
icommon_keys2 = [x for x in listB_keys if x not in common_keys]
|
||||
|
||||
common_keys = list(set(common_keys))
|
||||
icommon_keys1 = list(set(icommon_keys1))
|
||||
icommon_keys2 = list(set(icommon_keys2))
|
||||
|
||||
tongming,pkglist1,leixing4,arch6 = ksjgfl(common_keys, data1)
|
||||
Aqueshi1,Aqueshi2,leixing5,arch4 = ksjgfl(icommon_keys2,data2)
|
||||
Bqueshi1,Bqueshi2,leixing6,arch5 = ksjgfl(icommon_keys1, data1)
|
||||
|
||||
|
||||
shengjiang = []
|
||||
pkglist2 = []
|
||||
|
||||
# 对AB同名软件包进行处理
|
||||
for i in common_keys:
|
||||
ccc = []
|
||||
for dict1 in data1:
|
||||
if dict1["name"] == i:
|
||||
ccc.append(dict1["name"] + "-" + dict1["version"] + "-" + dict1["release"]+ "." + dict1["arch"] + "." + "rpm")
|
||||
ccc = bubbleSort(ccc)
|
||||
list1_release = ccc[-1].rsplit('-', 2)[2].rsplit('.', 2)[0]
|
||||
|
||||
release1 = relgfh(list1_release)
|
||||
#print(release1)
|
||||
list1_version = ccc[-1].rsplit('-', 2)[1] + '-' + release1
|
||||
list1_arch = ccc[-1].rsplit('.', 2)[1]
|
||||
|
||||
ddd = []
|
||||
for dict2 in data2:
|
||||
if dict2["name"] == i:
|
||||
ddd.append(dict2["name"] + "-" + dict2["version"] + "-" + dict2["release"] + "." + dict2["arch"] + "." + "rpm")
|
||||
ddd = bubbleSort(ddd)
|
||||
list2_version = ddd[-1].rsplit('-', 2)[2].rsplit('.', 2)[0]
|
||||
release2 = relgfh(list2_version)
|
||||
#print(release2)
|
||||
list2_version = ddd[-1].rsplit('-', 2)[1] + '-' + release2
|
||||
list2_arch = ddd[-1].rsplit('.', 2)[1]
|
||||
pkglist2.append("\n".join(ddd))
|
||||
|
||||
if rpm_vercmp.vercmp(list1_version, list2_version) == 1 and list1_arch == list2_arch:
|
||||
shengjiang.append("upgrade")
|
||||
elif rpm_vercmp.vercmp(list1_version, list2_version) == 1 and list1_arch != list2_arch:
|
||||
shengjiang.append("最高版本架构不同,无法比较")
|
||||
elif rpm_vercmp.vercmp(list1_version, list2_version) == -1 and list1_arch == list2_arch:
|
||||
shengjiang.append("downgrade")
|
||||
elif rpm_vercmp.vercmp(list1_version, list2_version) == -1 and list1_arch != list2_arch:
|
||||
shengjiang.append("最高版本架构不同,无法比较")
|
||||
elif rpm_vercmp.vercmp(list1_version, list2_version) == 0 and list1_arch == list2_arch:
|
||||
shengjiang.append("nochange")
|
||||
elif rpm_vercmp.vercmp(list1_version, list2_version) == 0 and list1_arch != list2_arch:
|
||||
shengjiang.append("最高版本架构不同,无法比较")
|
||||
|
||||
print("\n" + labA + " 与 " + labB + "同名软件包升降级判断完成数量:", len(shengjiang))
|
||||
print(labA + " 与 " + labB + "同名软件包数:", len(tongming), '\n' + labA + " 较 " + labB + "缺失的软件包数:",len(Aqueshi1),"\n" + labB + " 较 " + labA + "缺失的软件包数:", len(Bqueshi1))
|
||||
|
||||
return tongming, pkglist1, pkglist2, shengjiang, Aqueshi1, Aqueshi2, Bqueshi1, Bqueshi2, leixing4, leixing5, leixing6, arch4, arch5, arch6
|
||||
|
||||
|
||||
|
||||
|
||||
def Genexcel1(A,B,list1,list2,list3,list4,list5,list6,list7,list8,list9,list11,list12,list13,list14,list15,list16,list21,list22,list23,list24,list25,list26,list27,list28,list31,list32,list33,list34,srclist0,srclist1,srclist2,srclist11,srclist21):
|
||||
now = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') #当前时间
|
||||
fileName = A+"_"+B+"-"+u'%s-%s.xlsx' % ("仓库对比测试(郭超)",now)
|
||||
#fileName = "仓库对比-identity_new1.xls" #
|
||||
output_dir = "test_results/" + fileName
|
||||
workbook = xlsxwriter.Workbook(output_dir) # 创建工作簿
|
||||
# 新增一个粗体格式
|
||||
bold = workbook.add_format({'bold': True})
|
||||
# 设置自动换行
|
||||
column_text_wrap = workbook.add_format()
|
||||
column_text_wrap.set_text_wrap()
|
||||
# 创建子表
|
||||
|
||||
worksheet4 = workbook.add_worksheet("结果汇总")
|
||||
worksheet1 = workbook.add_worksheet("软件包差异情况对比-严格")
|
||||
worksheet2 = workbook.add_worksheet("同名软件包版本变化情况对比-宽松")
|
||||
worksheet3 = workbook.add_worksheet("软件包缺失情况-宽松")
|
||||
worksheet5 = workbook.add_worksheet("软件包增多情况-宽松")
|
||||
|
||||
#data1=[A+'较'+B+'多的软件包',A+'较'+B+'少的软件包',A+'较'+B+'少的软件包对应源码包',A+'较'+B+'相同的软件包','相同软件包类型']
|
||||
data1 = [A + '比' + B + '多的软件包','多的软件包对应类型' ,'多的软件包对应架构' ,A + '比' + B + '缺少的软件包', '缺少的软件包对应类型','缺少的软件包对应架构', A + '较' + B + '相同的软件包',
|
||||
'相同的软件包对应类型','相同的软件包对应架构']
|
||||
data2=[A+"与"+B+"同名的软件包",A+"中的软件包版本",B+"中的软件包版本",A+"相对"+ B+"的升降级情况","对应类型","对应架构",A+"中对应源码包"]
|
||||
data3=[A+"比"+B+"缺失的软件包名",A+"比"+B+"缺失的软件包","缺失的软件包对应类型","缺失的软件包对应架构","对应源码包","对应源码包(去重)"]
|
||||
data4=["汇总项","总数量",'模块包数量','非模块包数量']
|
||||
data5=[A+"比"+B+"增多的软件包名",A+"较"+B+"增多的软件包","增多的软件包对应类型","增多的软件包对应架构","对应源码包","对应源码包(去重)"]
|
||||
|
||||
worksheet1.write_row('A1',data1,bold)
|
||||
worksheet2.write_row('A1', data2, bold)
|
||||
worksheet3.write_row('A1', data3, bold)
|
||||
worksheet4.write_row('A1', data4, bold)
|
||||
worksheet5.write_row('A1', data5, bold)
|
||||
col = ord('A')
|
||||
|
||||
worksheet1.write_column(chr(col) + "2", list1)
|
||||
worksheet1.write_column(chr(col + 1) + "2", list2)
|
||||
worksheet1.write_column(chr(col + 2) + "2", list3)
|
||||
worksheet1.write_column(chr(col + 3) + "2", list4)
|
||||
worksheet1.write_column(chr(col + 4) + "2", list5)
|
||||
worksheet1.write_column(chr(col + 5) + "2", list6)
|
||||
worksheet1.write_column(chr(col + 6) + "2", list7)
|
||||
worksheet1.write_column(chr(col + 7) + "2", list8)
|
||||
worksheet1.write_column(chr(col + 8) + "2", list9)
|
||||
|
||||
worksheet2.write_column(chr(col) + "2", list11)
|
||||
worksheet2.write_column(chr(col + 1) + "2", list12)
|
||||
worksheet2.write_column(chr(col + 2) + "2", list13)
|
||||
worksheet2.write_column(chr(col + 3) + "2", list14)
|
||||
worksheet2.write_column(chr(col + 4) + "2", list15)
|
||||
worksheet2.write_column(chr(col + 5) + "2", list16)
|
||||
worksheet2.write_column(chr(col + 6) + "2", srclist0)
|
||||
|
||||
worksheet3.write_column(chr(col) + "2", list21)
|
||||
worksheet3.write_column(chr(col + 1) + "2", list22)
|
||||
worksheet3.write_column(chr(col + 2) + "2", list23)
|
||||
worksheet3.write_column(chr(col + 3) + "2", list24)
|
||||
worksheet3.write_column(chr(col + 4) + "2", srclist1)
|
||||
worksheet3.write_column(chr(col + 5) + "2", srclist11)
|
||||
|
||||
worksheet5.write_column(chr(col ) + "2", list25)
|
||||
worksheet5.write_column(chr(col + 1) + "2", list26)
|
||||
worksheet5.write_column(chr(col + 2) + "2", list27)
|
||||
worksheet5.write_column(chr(col + 3) + "2", list28)
|
||||
worksheet5.write_column(chr(col + 4) + "2", srclist2)
|
||||
worksheet5.write_column(chr(col + 5) + "2", srclist21)
|
||||
|
||||
worksheet4.write_column(chr(col) + "2", list31)
|
||||
worksheet4.write_column(chr(col + 1) + "2", list32)
|
||||
worksheet4.write_column(chr(col + 2) + "2", list33)
|
||||
worksheet4.write_column(chr(col + 3) + "2", list34)
|
||||
workbook.close()
|
||||
|
||||
return output_dir
|
||||
|
||||
|
||||
def main(labA,labB,data1,data2,filename1,filename2):
|
||||
""""i686 x86_64 aarch64 ,module 非module 需要区分开,分别进行比较
|
||||
首先对仓库软件包架构进行区分,不同架构的包又分为module 以及非moule,再进行比较
|
||||
例如 i686 x86_64 aarch64 noarch in name ,只对 i686进行区分
|
||||
"""
|
||||
#print(Openfile(labA,data1))
|
||||
#LISTA1A,Mbs1A,Fmbs1A,LISTA2A,Mbs2A,Fmbs2A,LISTA3A,Mbs3A,Fmbs3A=rjbgfh(Openfile(labA,data1))
|
||||
#LISTA1B,Mbs1B,Fmbs1B,LISTA2B,Mbs2B,Fmbs2B,LISTA3B,Mbs3B,Fmbs3B=rjbgfh(Openfile(labB,data2))
|
||||
|
||||
LISTA1A, Mbs1A, Fmbs1A, LISTA2A, Mbs2A, Fmbs2A, LISTA3A, Mbs3A, Fmbs3A = rjbfx(labA,data1)
|
||||
LISTA1B, Mbs1B, Fmbs1B, LISTA2B, Mbs2B, Fmbs2B, LISTA3B, Mbs3B, Fmbs3B = rjbfx(labB,data2)
|
||||
|
||||
Aduo, leixing3, Ashao, leixing2, ABtong, leixing1,arch1,arch2,arch3,mk1,fmk1,mk2,fmk2,mk3,fmk3= comp1(labA, labB, LISTA1A,LISTA1B)
|
||||
|
||||
print('\n' + "##### 03 i686 模块 宽松模式(按包名统计) 软件包对比开始 #####")
|
||||
Atongming, Apkglist1, Apkglist2, Ashengjiang,AAqueshi1,AAqueshi2,ABqueshi1,ABqueshi2,Aleixing4,Aleixing5,Aleixing6,Aarch4,Aarch5,Aarch6=comp2(labA+ ' 模块',labB+ ' 模块',Mbs2A,Mbs2B)
|
||||
|
||||
print('\n' + "##### 04 i686 非模块 宽松模式(按包名统计) 软件包对比开始 #####")
|
||||
Btongming, Bpkglist1, Bpkglist2, Bshengjiang, BAqueshi1, BAqueshi2, BBqueshi1, BBqueshi2, Bleixing4, Bleixing5, Bleixing6 ,Barch4,Barch5,Barch6= comp2(labA + ' 非模块', labB + ' 非模块', Fmbs2A, Fmbs2B)
|
||||
|
||||
print('\n' + "##### 03 非i686 模块 宽松模式(按包名统计) 软件包对比开始 #####")
|
||||
Ctongming, Cpkglist1, Cpkglist2, Cshengjiang,CAqueshi1,CAqueshi2,CBqueshi1,CBqueshi2,Cleixing4,Cleixing5,Cleixing6,Carch4,Carch5,Carch6=comp2(labA+ ' 模块',labB+ ' 模块',Mbs3A,Mbs3B)
|
||||
|
||||
print('\n' + "##### 04 非i686 非模块 宽松模式(按包名统计) 软件包对比开始 #####")
|
||||
Dtongming, Dpkglist1, Dpkglist2, Dshengjiang, DAqueshi1, DAqueshi2, DBqueshi1, DBqueshi2, Dleixing4, Dleixing5, Dleixing6,Darch4,Darch5,Darch6 = comp2(labA + ' 非模块', labB + ' 非模块', Fmbs3A, Fmbs3B)
|
||||
|
||||
|
||||
|
||||
tongming=Atongming+Btongming+Ctongming+Dtongming
|
||||
pkglist1=Apkglist1+Bpkglist1+Cpkglist1+Dpkglist1
|
||||
pkglist2=Apkglist2+Bpkglist2+Cpkglist2+Dpkglist2
|
||||
shengjiang=Ashengjiang+Bshengjiang+Cshengjiang+Dshengjiang
|
||||
|
||||
Aqueshi1=AAqueshi1+BAqueshi1+CAqueshi1+DAqueshi1 #包名
|
||||
Aqueshi2=AAqueshi2+BAqueshi2+CAqueshi2+DAqueshi2 #包全名
|
||||
|
||||
|
||||
Bqueshi1=ABqueshi1+BBqueshi1+CBqueshi1+DBqueshi1
|
||||
Bqueshi2=ABqueshi2+BBqueshi2+CBqueshi2+DBqueshi2
|
||||
|
||||
leixing4=Aleixing4+Bleixing4+Cleixing4+Dleixing4
|
||||
leixing5=Aleixing5+Bleixing5+Cleixing5+Dleixing5
|
||||
leixing6=Aleixing6+Bleixing6+Cleixing6+Dleixing6
|
||||
|
||||
arch4=Aarch4+Barch4+Carch4+Darch4
|
||||
arch5=Aarch5+Barch5+Carch5+Darch5
|
||||
arch6=Aarch6+Barch6+Carch6+Darch6
|
||||
|
||||
srclist0=getsrc(pkglist1, filename1)
|
||||
srclist1=getsrc(Aqueshi2, filename2)
|
||||
srclist11 = list(set(srclist1))
|
||||
srclist2=getsrc(Bqueshi2, filename1)
|
||||
srclist21 = list(set(srclist2))
|
||||
|
||||
huizong=[labA+'获取到软件包:','',labB+'获取到软件包:','','严格模式'+labA + " 与 " + labB + "同名软件包:",'严格模式'+labA+"缺失软件包:",'严格模式'+labA+"增加软件包:",'','宽松模式'+labA + " 与 " + labB + "同名软件包:",'宽松模式'+labA+"缺失软件包:",'宽松模式'+labA+"增加软件包:"]
|
||||
huizong1=[len(LISTA1A),'',len(LISTA1B),'',len(ABtong),len(Ashao),len(Aduo),'',len(tongming),len(Aqueshi1),len(Bqueshi1)]
|
||||
huizong2=[len(Mbs1A),'',len(Mbs1B),'',mk1,mk2,mk3,'',len(Atongming)+len(Ctongming),len(AAqueshi1)+len(CAqueshi1),len(ABqueshi1)+len(CBqueshi1)]
|
||||
huizong3=[len(Fmbs1A),'',len(Fmbs1B),'',fmk1,fmk2,fmk3,'',len(Btongming)+len(Dtongming),len(BAqueshi1)+len(DAqueshi1),len(BBqueshi1)+len(DBqueshi1)]
|
||||
|
||||
resultname=Genexcel1(labA, labB, Aduo, leixing3,arch3, Ashao, leixing2,arch2,ABtong, leixing1,arch1,tongming, pkglist1, pkglist2, shengjiang,
|
||||
leixing4,arch6, Aqueshi1, Aqueshi2, leixing5,arch4,Bqueshi1, Bqueshi2, leixing6,arch5,huizong,huizong1,huizong2,huizong3,srclist0,srclist1,srclist2,srclist11,srclist21)
|
||||
|
||||
# return labA, labB, Aduo, leixing3,arch3, Ashao, leixing2,arch2,ABtong, leixing1,arch1,tongming, pkglist1, pkglist2, shengjiang,arch6,leixing4, Aqueshi1, Aqueshi2, leixing5,arch4,Bqueshi1, Bqueshi2, leixing6,arch5,huizong,huizong1,huizong2,huizong3
|
||||
|
||||
return resultname
|
||||
|
||||
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
#用于从网页获取软件包列表
|
||||
def Getpkglist(url):
|
||||
head = {"User-Agent": "Mozilla / 5.0(Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 80.0.3987.122 Safari / 537.36"}
|
||||
# 查找所有<a>标签
|
||||
response = requests.get(url)
|
||||
soup = BeautifulSoup(response.content, 'html.parser')
|
||||
PkgList = []
|
||||
packages = []
|
||||
directories = []
|
||||
|
||||
for a in soup.find_all('a', href=True):
|
||||
href = a['href']
|
||||
if href.endswith('/'):
|
||||
# 如果链接以斜杠结尾,表示是一个目录
|
||||
directories.append(href)
|
||||
elif href.endswith('.rpm'):
|
||||
# 如果链接以.rpm结尾,表示是一个软件包
|
||||
packages.append(href)
|
||||
print(directories)
|
||||
|
||||
directory_urls=[]
|
||||
for directory in directories:
|
||||
directory_url= '/'.join([url, directory])
|
||||
directory_urls.append(directory_url)
|
||||
print(directory_urls)
|
||||
|
||||
|
||||
for baseurl in directory_urls:
|
||||
response = requests.get(baseurl, headers=head)
|
||||
html = response.text
|
||||
html = etree.HTML(html)
|
||||
a_links = html.xpath("//a") # 查找所有 a 标签 ,//a 代表 html 下所有 a,html/body/a 同理
|
||||
for item in a_links:
|
||||
item1 = item.get('href') # 获取a标签某一属性
|
||||
if ".rpm" in item1: # 去除非rpm包的影响,备选.el8
|
||||
item1 = item1.replace("%2B", "+") # 对url不能识别”+“进行修正
|
||||
PkgList.append(item1)
|
||||
|
||||
print('\n'+'##### 00 通过 Gethtml 获取软件包列表完成,软件包数量:',len(PkgList),'#####') #检修口
|
||||
return PkgList
|
||||
|
||||
|
||||
# repo1=("https://update.cs2c.com.cn/NS/V10/8U4/os/adv/lic/BaseOS/x86_64/","https://update.cs2c.com.cn/NS/V10/8U4/os/adv/lic/AppStream/x86_64/","https://update.cs2c.com.cn/NS/V10/8U4/os/adv/lic/PowerTools/x86_64/")
|
||||
# repo2=("http://192.168.114.103/update.cs2c.com.cn/NS/V10/8U4/os/adv/lic/BaseOS/x86_64/","http://192.168.114.103/update.cs2c.com.cn/NS/V10/8U4/os/adv/lic/AppStream/x86_64/","http://192.168.114.103/update.cs2c.com.cn/NS/V10/8U4/os/adv/lic/PowerTools/x86_64/")
|
||||
|
||||
|
||||
# data1,filename1=plus("https://10.44.16.185/private_test/history/repo/20231217/pub/V7/V7Update9/os/adv/lic/base/x86_64/ ")#,"https://update.cs2c.com.cn/NS/V10/8U4/os/adv/lic/AppStream/x86_64/")
|
||||
# data2,filename2=plus("http://192.168.114.103/update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/base/x86_64/")#,"http://192.168.114.103/update.cs2c.com.cn/NS/V10/8U4/os/adv/lic/AppStream/x86_64/")
|
||||
|
||||
#data2,filename2=plus("https://10.44.16.185/NS/V10/8U6/os/adv/lic/BaseOS-updates/x86_64/os/","https://10.44.16.185/NS/V10/8U6/os/adv/lic/AppStream-updates/x86_64/os/")
|
||||
|
||||
#data2,filename2=plus("http://10.44.34.103/ns8.4/compose/BaseOS-updates/x86_64/os/","http://10.44.34.103/ns8.4/compose/AppStream-updates/x86_64/os/")
|
||||
# data2,filename2=plus("https://dl.fedoraproject.org/pub/epel/8/Everything/x86_64/")
|
||||
#
|
||||
# #Baseos
|
||||
# data1,filename1=plus("https://eps-server.openkylin.top/NS/V10/V10SP3/EPKL/main/x86_64/")
|
||||
# data2,filename2=plus("https://10.44.16.185/private_test/history/repo/20231217/pub/V7/V7Update9/os/adv/lic/base/x86_64/","https://10.44.16.185/private_test/history/repo/20231217/pub/V7/V7Update9/os/adv/lic/base/i686/")
|
||||
# https://eps-server.openkylin.top/NS/V10/V10SP3/EPKL/main/x86_64/
|
||||
|
||||
|
||||
# data1,filename1=plus("http://192.168.114.104/sp3-epkl-updates-3/source/")
|
||||
# data1,filename1=plus("http://192.168.114.104/sp3-epkl-base/","http://192.168.114.104/sp3-epkl-updates-5/source/","http://192.168.114.104/sp3-epkl-220x/source/","http://192.168.114.104/sp3-epkl-230x/source/")
|
||||
# data1,filename1=plus("http://192.168.114.104/sp1-epkl-base/","http://192.168.114.104/sp3-epkl-updates-3/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.openatom.cn/openEuler-24.03-LTS/EPOL/main/source/","https://repo.openeuler.openatom.cn/openEuler-24.09/EPOL/main/source/")
|
||||
# main("epkl-base+updates+22.0x+23.0x","epol-24.0x",data1,data2,filename1,filename2)
|
||||
|
||||
# data2,filename2=plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP1/EPOL/source/","https://repo.openeuler.org/openEuler-20.03-LTS-SP2/EPOL/main/source/","https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP3/EPOL/main/source/","https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP4/EPOL/main/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP1/EPOL/update/source/","https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP2/EPOL/update/main/source/","https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP3/EPOL/update/main/source/","https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP4/EPOL/update/main/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS/EPOL/main/source/","https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP1/EPOL/main/source/","https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP2/EPOL/main/source/","https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP3/EPOL/main/source/","https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP4/EPOL/main/source/")
|
||||
|
||||
#epol 仓库地址
|
||||
# data2, filename2 = plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP1/EPOL/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP2/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP3/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP4/EPOL/main/source/")
|
||||
|
||||
# data2, filename2 = plus("https://archives.openeuler.openatom.cn/openEuler-21.03/EPOL/source/")
|
||||
# data2, filename2 = plus("https://archives.openeuler.openatom.cn/openEuler-21.09/EPOL/main/source/")
|
||||
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP1/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP2/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP3/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP4/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://archives.openeuler.openatom.cn/openEuler-22.09/EPOL/main/source/")
|
||||
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-23.03/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-23.09/EPOL/main/source/")
|
||||
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-24.03-LTS/EPOL/main/source/")
|
||||
# data2, filename2 = plus("https://repo.openeuler.openatom.cn/openEuler-24.09/EPOL/main/source/")
|
||||
|
||||
# data1,filename1=plus("http://192.168.114.104/sp3-epkl-base/","http://192.168.114.104/sp3-epkl-updates-4/source/")
|
||||
|
||||
# data2, filename2 = plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP1/EPOL/source/")
|
||||
# data3, filename3 = plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/main/source/")
|
||||
# data4, filename4 = plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP3/EPOL/main/source/")
|
||||
# data5, filename5 = plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP4/EPOL/main/source/")
|
||||
# #21
|
||||
# data6, filename6 = plus("https://archives.openeuler.openatom.cn/openEuler-21.03/EPOL/source/")
|
||||
# data7, filename7 = plus("https://archives.openeuler.openatom.cn/openEuler-21.09/EPOL/main/source/")
|
||||
# #22
|
||||
# data8, filename8 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS/EPOL/main/source/")
|
||||
# data9, filename9 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP1/EPOL/main/source/")
|
||||
# data10, filename10 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP2/EPOL/main/source/")
|
||||
# data11, filename11 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP3/EPOL/main/source/")
|
||||
# data12, filename12 = plus("https://repo.openeuler.openatom.cn/openEuler-22.03-LTS-SP4/EPOL/main/source/")
|
||||
# data13, filename13 = plus("https://archives.openeuler.openatom.cn/openEuler-22.09/EPOL/main/source/")
|
||||
#23
|
||||
# data14, filename14 = plus("https://repo.openeuler.openatom.cn/openEuler-23.03/EPOL/main/source/")
|
||||
# data15, filename15 = plus("https://repo.openeuler.openatom.cn/openEuler-23.09/EPOL/main/source/")
|
||||
|
||||
#24
|
||||
# data16, filename16 = plus("https://repo.openeuler.openatom.cn/openEuler-24.03-LTS/EPOL/main/source/")
|
||||
# data17, filename17 = plus("https://repo.openeuler.openatom.cn/openEuler-24.09/EPOL/main/source/")
|
||||
|
||||
|
||||
#oepkg
|
||||
# data18, filename18 = plus('https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS/extras/source/',"https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS-SP1/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS-SP2/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS-SP3/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS-SP4/extras/source/")
|
||||
# data19, filename19 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS-SP1/extras/source/")
|
||||
# data20, filename20 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS-SP2/extras/source/")
|
||||
# data21, filename21 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS-SP3/extras/source/")
|
||||
# data22, filename22 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-20.03-LTS-SP4/extras/source/")
|
||||
|
||||
# data23, filename23 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS-SP1/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS-SP2/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS-SP3/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS-SP4/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-24.09/extras/source/","https://repo.oepkgs.net/openEuler/rpm/openEuler-24.09-LTS/extras/source/")
|
||||
# data24, filename24 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS-SP1/extras/source/")
|
||||
# data25, filename25 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS-SP2/extras/source/")
|
||||
# data26, filename26 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS-SP3/extras/source/")
|
||||
# data27, filename27 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-22.03-LTS-SP4/extras/source/")
|
||||
# data28, filename28 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/extras/source/")
|
||||
# data29, filename29 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-24.09/extras/source/")
|
||||
# data30, filename30 = plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-24.09-LTS/extras/source/")
|
||||
|
||||
# main("v10-sp3-epkl-base+updates","20.03-LTS-sp1-epol",data1,data2,filename1,filename2)
|
||||
# main("v10-sp3-epkl-base+updates","20.03-LTS-sp2-epol",data1,data3,filename1,filename3)
|
||||
# main("v10-sp3-epkl-base+updates","20.03-LTS-sp3-epol",data1,data4,filename1,filename4)
|
||||
# main("v10-sp3-epkl-base+updates","20.03-LTS-sp4-epol",data1,data5,filename1,filename5)
|
||||
#
|
||||
# main("v10-sp3-epkl-base+updates","21.03-epol",data1,data6,filename1,filename6)
|
||||
# main("v10-sp3-epkl-base+updates","21.09-epol",data1,data7,filename1,filename7)
|
||||
#
|
||||
# main("v10-sp3-epkl-base+updates","22.03-LTS-epol",data1,data8,filename1,filename8)
|
||||
# main("v10-sp3-epkl-base+updates","22.03-LTS-sp1-epol",data1,data9,filename1,filename9)
|
||||
# main("v10-sp3-epkl-base+updates","22.03-LTS-sp2-epol",data1,data10,filename1,filename10)
|
||||
# main("v10-sp3-epkl-base+updates","22.03-LTS-sp3-epol",data1,data11,filename1,filename11)
|
||||
# main("v10-sp3-epkl-base+updates","22.03-LTS-sp4-epol",data1,data12,filename1,filename12)
|
||||
# main("v10-sp3-epkl-base+updates","22.09-epol",data1,data13,filename1,filename13)
|
||||
|
||||
# main("v10-sp3-epkl-base+updates4","23.03-epol",data1,data14,filename1,filename14)
|
||||
# main("v10-sp3-epkl-base+updates4","23.09-epol",data1,data15,filename1,filename15)
|
||||
# main("v10-sp3-epkl-base+updates","24.03-epol",data1,data16,filename1,filename16)
|
||||
# main("v10-sp3-epkl-base+updates","24.09-epol",data1,data17,filename1,filename17)
|
||||
|
||||
|
||||
# main("v10-sp3-epkl-updates4","oepkg-20.03-LTS-SPX",data1,data23,filename1,filename18)
|
||||
# main("v10-sp3-epkl-updates4","oepkg-22-24",data1,data23,filename23,filename23)
|
||||
# main("oe-sp2-source+epol","oepkg-20.03-SP2",data3,data18,filename3,filename18)
|
||||
|
||||
|
||||
|
||||
|
||||
#epol updates 仓库
|
||||
#20.03
|
||||
# data2,filename2=plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP1/EPOL/update/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP2/EPOL/update/main/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP3/EPOL/update/main/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP4/EPOL/update/main/source/")
|
||||
#21.0x
|
||||
# data2,filename2=plus("https://archives.openeuler.openatom.cn/openEuler-21.03/EPOL/update/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.openatom.cn/openEuler-20.03-LTS-SP4/EPOL/update/main/source/")
|
||||
|
||||
# main("v10-epkl-base1+updates","22.03-lts-sp1-epol",data1,data2,filename1,filename2)
|
||||
# main("v10-epkl-base1+updates","v10-epkl-base",data1,data2,filename1,filename2)
|
||||
|
||||
|
||||
# data2,filename2=plus('https://dl.fedoraproject.org/pub/archive/fedora/linux/releases/33/Server/source/tree/','https://dl.fedoraproject.org/pub/archive/fedora/linux/releases/34/Server/source/tree/','https://dl.fedoraproject.org/pub/archive/fedora/linux/releases/35/Server/source/tree/','https://dl.fedoraproject.org/pub/archive/fedora/linux/releases/36/Server/source/tree/','https://dl.fedoraproject.org/pub/archive/fedora/linux/releases/37/Server/source/tree/','https://dl.fedoraproject.org/pub/archive/fedora/linux/releases/38/Server/source/tree/','https://dl.fedoraproject.org/pub/archive/fedora/linux/releases/39/Server/source/tree/')
|
||||
# data2,filename2=plus('http://192.168.114.104/fedora/everything/f30/','http://192.168.114.104/fedora/everything/f31/','http://192.168.114.104/fedora/everything/f32/','http://192.168.114.104/fedora/everything/f33/','http://192.168.114.104/fedora/everything/f34/','http://192.168.114.104/fedora/everything/f35/','http://192.168.114.104/fedora/everything/f36/','http://192.168.114.104/fedora/everything/f37/','http://192.168.114.104/fedora/everything/f38/','http://192.168.114.104/fedora/everything/f39/')
|
||||
# data3,filename3=plus("http://192.168.114.104/fedora/server/f30/")
|
||||
# data2,filename2=plus("http://192.168.114.104/fedora/everything/f39/")
|
||||
# main("oepkg-20.0x","fedora-server-30",data18,data3,filename18,filename3)
|
||||
# main("oepkg-20.0x","fedora-everything-30-39",data18,data2,filename18,filename2)
|
||||
|
||||
# data1,filename1=plus("https://dl.fedoraproject.org/pub/fedora/linux/releases/40/Everything/source/tree/")
|
||||
# data2,filename2=plus("https://repo.oepkgs.net/openEuler/rpm/openEuler-24.03-LTS/fedora40/source/")
|
||||
# data1,filename1=plus("http://192.168.114.104/fedora40/")
|
||||
# data2,filename2=plus("http://192.168.114.104/oe-fedora40/")
|
||||
# main("fedora40","fedora40",data1,data2,filename1,filename2)
|
||||
|
||||
# data1,filename1=plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/main/source/","https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/update/main/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP2/EPOL/main/source/","https://repo.openeuler.org/openEuler-20.03-LTS-SP2/EPOL/update/main/source/")
|
||||
# data1,filename1=plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/main/source/","https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/update/main/source/")
|
||||
# data2,filename2=plus("https://repo.openeuler.org/openEuler-20.03-LTS-SP4/EPOL/main/source/","https://repo.openeuler.org/openEuler-20.03-LTS-SP4/EPOL/update/main/source/")
|
||||
|
||||
#AppStream
|
||||
#data1,filename1=plus("http://10.44.34.103/ns8.2-new/compose/b09/x86_64/BaseOS/","http://10.44.34.103/ns8.2-new/compose/b09/x86_64/AppStream/","http://10.44.34.103/ns8.2-new/compose/b09/x86_64/PowerTools/")
|
||||
# data2,filename2=plus("http://192.168.114.15/iso/centos8.2/BaseOS/","http://192.168.114.15/iso/centos8.2/AppStream/")
|
||||
#zong,
|
||||
# 仅repodata
|
||||
# data1,filename1=plus("http://10.44.34.103/ns8.2-new/compose/b09/x86_64/BaseOS/","http://10.44.34.103/ns8.2-new/compose/b09/x86_64/AppStream/","http://10.44.34.103/ns8.2-new/compose/b09/x86_64/PowerTools/")
|
||||
#data1,filename1=plus("http://10.44.34.103/ns8.2-new/compose/b09/aarch64/BaseOS/","http://10.44.34.103/ns8.2-new/compose/b09/aarch64/AppStream/","http://10.44.34.103/ns8.2-new/compose/b09/aarch64/PowerTools/")
|
||||
|
||||
# 全部仓库
|
||||
# data2,filename2=plus("http://10.44.34.103/ns8.2-new/compose/b17/BaseOS/x86_64/os/","http://10.44.34.103/ns8.2-new/compose/b17/AppStream/x86_64/os/","http://10.44.34.103/ns8.2-new/compose/b17/PowerTools/x86_64/os/")
|
||||
#data1,filename1=plus("http://10.44.34.103/ns8.2-new/compose/b16/BaseOS/aarch64/os/","http://10.44.34.103/ns8.2-new/compose/b16/AppStream/aarch64/os/","http://10.44.34.103/ns8.2-new/compose/b16/PowerTools/aarch64/os/")
|
||||
#data1,filename1=plus("http://10.44.34.103/ns8.8/b09/x86_64/BaseOS/","http://10.44.34.103/ns8.8/b09/x86_64/AppStream/","http://10.44.34.103/ns8.8/b09/x86_64/kernel418/")
|
||||
|
||||
# data1,filename1=plus("https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/BaseOS/x86_64/os/","https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/AppStream/x86_64/os/","https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/kernel418/x86_64/os/","https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/kernel419/x86_64/os/")
|
||||
# data2,filename2=plus("https://dl.rockylinux.org/vault/rocky/8.8/BaseOS/x86_64/os/","https://dl.rockylinux.org/vault/rocky/8.8/AppStream/x86_64/os/","https://dl.rockylinux.org/vault/rocky/8.8/PowerTools/x86_64/os/")
|
||||
# #data1,filename1=plus("https://10.44.16.185/private_test/history/repo/20231226/pub/V10/8U6/os/adv/lic/AppStream-updates/x86_64/os/","https://10.44.16.185/private_test/history/repo/20231226/pub/V10/8U6/os/adv/lic/BaseOS-updates/x86_64/os/")
|
||||
#data2,filename2=plus("http://192.168.114.15/iso/centos8.2/BaseOS/","http://192.168.114.15/iso/centos8.2/AppStream/","http://192.168.114.15/repo/centos82/powertool/")
|
||||
# # #data2,filename2=plus("http://192.168.114.15/repo/centos82/baseos/","http://192.168.114.15/repo/centos82/appstrem/","http://192.168.114.15/repo/centos82/powertool/")
|
||||
#data2,filename2=plus("https://10.44.16.185/NS/V10/8U2/os/adv/lic/BaseOS/x86_64/","https://10.44.16.185/NS/V10/8U2/os/adv/lic/AppStream/x86_64/","https://10.44.16.185/NS/V10/8U2/os/adv/lic/PowerTools/x86_64/")#"https://10.44.16.185/NS/V10/8U2/os/adv/lic/BaseOS-updates/x86_64/","https://10.44.16.185/NS/V10/8U2/os/adv/lic/AppStream-updates/x86_64/","https://10.44.16.185/NS/V10/8U2/os/adv/lic/PowerTools-updates/x86_64/","https://10.44.16.185/NS/V10/8U2/os/adv/lic/Plus-updates/x86_64/")
|
||||
# 全部仓库
|
||||
#data2,filename2=plus("http://10.44.34.103/ns8.2-new/compose/b17/BaseOS/x86_64/os/","http://10.44.34.103/ns8.2-new/compose/b17/AppStream/x86_64/os/","http://10.44.34.103/ns8.2-new/compose/b17/PowerTools/x86_64/os/")
|
||||
#data2,filename2=plus("http://10.44.34.103/ns8.2-new/compose/b17/BaseOS/aarch64/os/","http://10.44.34.103/ns8.2-new/compose/b17/AppStream/aarch64/os/","http://10.44.34.103/ns8.2-new/compose/b17/PowerTools/aarch64/os/")
|
||||
|
||||
#data2,filename2=plus("http://10.44.51.209/centos-iso/centos8.2/BaseOS/","http://10.44.51.209/centos-iso/centos8.2/AppStream/","https://172.30.13.199/download/CENTOS8/centos8.2/PowerTools/aarch64/os/")
|
||||
#data2,filename2=plus("https://10.44.16.185/private_test/repo/V10/8U4/os/adv/lic/AppStream-updates/x86_64/","https://10.44.16.185/private_test/repo/V10/8U4/os/adv/lic/BaseOS-updates/x86_64/")
|
||||
# data1,filename1=plus("https://10.44.16.185/private_test/repo/V10/8U8/Build06/os/adv/lic/AppStream/x86_64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build06/os/adv/lic/BaseOS/x86_64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build06/os/adv/lic/kernel418/x86_64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build06/os/adv/lic/kernel419/x86_64/os/")
|
||||
# #data2,filename2=plus("https://10.44.16.185/private_test/repo/V10/8U8/Build04/os/adv/lic/AppStream/aarch64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build04/os/adv/lic/BaseOS/aarch64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build04/os/adv/lic/kernel418/aarch64/os/")
|
||||
|
||||
# data1,filename1=plus("https://10.44.16.185/private_test/repo/V10/8U8/Build06/os/adv/lic/BaseOS-updates/x86_64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build06/os/adv/lic/AppStream-updates/x86_64/os/")
|
||||
# data2,filename2=plus("https://10.44.16.185/private_test/repo/V10/8U8/Build05/os/adv/lic/BaseOS-updates/x86_64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build05/os/adv/lic/AppStream-updates/x86_64/os/")
|
||||
|
||||
# data2,filename2=plus("https://10.44.16.185/private_test/repo/V10/8U8/Build05/os/adv/lic/AppStream/x86_64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build05/os/adv/lic/BaseOS/x86_64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build05/os/adv/lic/kernel418/x86_64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build05/os/adv/lic/kernel419/x86_64/os/")
|
||||
# # data2,filename2=plus("https://10.44.16.185/private_test/repo/V10/8U8/Build03/os/adv/lic/AppStream/aarch64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build03/os/adv/lic/BaseOS/aarch64/os/","https://10.44.16.185/private_test/repo/V10/8U8/Build03/os/adv/lic/kernel418/aarch64/os/")
|
||||
|
||||
# pkglist=['nginx-filesystem-1.14.1-9.module_el8.6.0+647+66e644f1.ks8.01.noarch.rpm nginx-filesystem-1.16.1-2.module_el8.6.0+648+f70baf93.1.ks8.01.noarch.rpm nginx-filesystem-1.18.0-3.module_el8.6.0+649+52b76903.1.ks8.01.noarch.rpm nginx-filesystem-1.20.1-1.module_el8.6.0+707+e975214f.noarch.rpm nginx-filesystem-1.22.1-1.module_el8.8.0+948+03a664b8.noarch.rpm','cesi-fonts-1.0.0-1.el8.noarch.rpm']
|
||||
# print(getsrc(pkglist,filename1))
|
||||
|
||||
#data2=gethtml("http://10.44.51.209/guochao/rhel8.8/x86_64/AppStream/Packages/","http://10.44.51.209/guochao/rhel8.8/x86_64/BaseOS/Packages/")
|
||||
#data2=gethtml("http://10.44.51.209/guochao/rhel8.8/aarch64/AppStream/Packages/","http://10.44.51.209/guochao/rhel8.8/aarch64/BaseOS/Packages/")
|
||||
#data2=openfile('C:/Users/lenovo.DESKTOP-JAA702O/Desktop/123')
|
||||
|
||||
|
||||
#data2=gethtml("https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.2.2004/AppStream/x86_64/os/Packages/","https://mirrors.tuna.tsinghua.edu.cn//centos-vault/8.2.2004/BaseOS/x86_64/os/Packages/","https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.2.2004/PowerTools/x86_64/os/Packages/")
|
||||
#data2,filename2=plus("https://10.44.16.185/NS/V10/8U8/os/adv/lic/AppStream/x86_64/os/","https://10.44.16.185/NS/V10/8U8/os/adv/lic/BaseOS/x86_64/os/")
|
||||
#data2,filename2=plus("https://10.44.16.185/NS/V10/8U8/os/adv/lic/AppStream/aarch64/os/","https://10.44.16.185/NS/V10/8U8/os/adv/lic/BaseOS/aarch64/os/")
|
||||
|
||||
|
||||
# # data1,filename1=plus("https://update.cs2c.com.cn/NS/V10/8U8/os/adv/lic/AppStream/x86_64/os/")#e.cs2c.com.cn/NS/V10/8U4/os/adv/lic/AppStream/x86_64/")
|
||||
# data1=gethtml("http://192.168.114.206/iso/rh8.10src/AppStream/Packages/")#,
|
||||
|
||||
# main("rhel8.10-app-src","ns8.8-app-src",data1,data2,filename1,filename2)
|
||||
# main("185-6.5","103-6.5",data1,data2,filename1,filename2)
|
||||
# main("v10-sp3-epel","openeuler-20.03-sp4-epol",data1,data2,filename1,filename2)
|
||||
# main("v10-sp3-epel","centos8-epel",data1,data2,filename1,filename2)
|
||||
# main("ns7.9-0206","ns7.9-1217",data1,data2,filename1,filename2)
|
||||
# main("ns8.4-updates-103","ns8.4-185",data1,data2,filename1,filename2)
|
||||
# main("ns8.2-b01-arm-zong","centos8.2",data1,data2,filename1,filename2)
|
||||
|
||||
|
||||
|
||||
# data2=gethtml("https://172.30.13.199/download/epel/epel/7/x86_64/Packages/")
|
||||
# filename2=""
|
||||
|
||||
# ###文件对比###
|
||||
# data1=openfile("Kylin-Linux-Advanced-Server-v10-Release-Build06.12.06-lic-zj-package.txt")
|
||||
# #data2=openfile('C:/Users/lenovo.DESKTOP-JAA702O/Desktop/ns8.2-baseos-ww.txt','C:/Users/lenovo.DESKTOP-JAA702O/Desktop/ns8.2-app-ww.txt')
|
||||
# data2=openfile("nsV7Update6-adv-lic-build4-package.txt")
|
||||
# filename1=""
|
||||
# filename2=""
|
||||
# main("v10-zj","v7.6",data1,data2,filename1,filename2)
|
||||
|
||||
# data1,filename1=plus("https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/base/x86_64/","https://update.cs2c.com.cn/NS/V7/V7Update9/os/adv/lic/updates/x86_64/")
|
||||
# data1,filename1=plus("http://192.168.114.102/v10.1/base/","http://192.168.114.102/v10.1/kernel/")
|
||||
# data2=gethtml("https://vault.centos.org/7.9.2009/os/x86_64/Packages/","https://vault.centos.org/7.9.2009/updates/x86_64/Packages/")
|
||||
# # main("ns8.10-061420-src","0619",data1,data2,filename1,filename2)
|
||||
# filename2=""
|
||||
# main("v10.1-cs2c","centos7.9-base+updates",data1,data2,filename1,filename2)
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
过于繁琐
|
||||
函数改为自动判断、选择
|
||||
鲁棒性,容错性太低
|
||||
log输出
|
||||
结果输出格式需要易用性高
|
||||
"""
|
||||
453
script/09_v10spx_repo-rpm_cmp/repodata.py
Normal file
453
script/09_v10spx_repo-rpm_cmp/repodata.py
Normal file
@@ -0,0 +1,453 @@
|
||||
import bz2
|
||||
import lzma
|
||||
import shutil
|
||||
import sqlite3
|
||||
import requests
|
||||
from lxml import etree
|
||||
import logging
|
||||
logging.captureWarnings(True)
|
||||
import gzip
|
||||
import os
|
||||
|
||||
# def Getsqlite(baseurl):
|
||||
# """
|
||||
# 从repodata中获取数据库压缩文件,下载并解压
|
||||
# url 格式 例如 basrutl=https://update.cs2c.com.cn/NS/V10/8U6/os/adv/lic/AppStream/x86_64/os/
|
||||
# """
|
||||
# head = {
|
||||
# "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||
# }
|
||||
# path = f"{baseurl}repodata/"
|
||||
# try:
|
||||
# response = requests.get(path, headers=head, verify=False)
|
||||
# html = response.text
|
||||
# html = etree.HTML(html)
|
||||
# a_links = html.xpath("//a")
|
||||
#
|
||||
# for item in a_links:
|
||||
# item1 = item.get('href')
|
||||
# if item1.endswith("primary.sqlite.bz2"):
|
||||
# sqlite_url = '/'.join([path, item1])
|
||||
# print(sqlite_url)
|
||||
# response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
# un_path = "repotest.sqlite.bz2"
|
||||
# with open(un_path, 'wb') as code:
|
||||
# code.write(response.content)
|
||||
# bz2file = bz2.BZ2File(un_path)
|
||||
# data = bz2file.read()
|
||||
# newfilepath = "repotest.sqlite"
|
||||
# open(newfilepath, 'wb').write(data)
|
||||
# return newfilepath
|
||||
#
|
||||
# elif item1.endswith("primary.sqlite.xz"):
|
||||
# sqlite_url = '/'.join([path, item1])
|
||||
# print(sqlite_url)
|
||||
# response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
# un_path = "repotest.sqlite.xz"
|
||||
#
|
||||
# with open(un_path, 'wb') as code:
|
||||
# code.write(response.content)
|
||||
#
|
||||
# with lzma.open(un_path, 'rb') as input:
|
||||
# with open("repotest.sqlite", 'wb') as output:
|
||||
# shutil.copyfileobj(input, output)
|
||||
# return "repotest.sqlite"
|
||||
#
|
||||
# elif item1.endswith("primary.sqlite.gz"):
|
||||
# sqlite_url = '/'.join([path, item1])
|
||||
# print(sqlite_url)
|
||||
# response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
# un_path = "repotest.sqlite.gz"
|
||||
#
|
||||
# with open(un_path, 'wb') as code:
|
||||
# code.write(response.content)
|
||||
#
|
||||
# with gzip.open(un_path, 'rb') as input:
|
||||
# with open("repotest.sqlite", 'wb') as output:
|
||||
# shutil.copyfileobj(input, output)
|
||||
# return "repotest.sqlite"
|
||||
#
|
||||
# print("获取数据库文件失败,请检查!")
|
||||
# return None
|
||||
#
|
||||
# except Exception as e:
|
||||
# print("发生异常:", e)
|
||||
# return None
|
||||
|
||||
def Getsqlite(baseurl):
|
||||
"""
|
||||
从仓库 URL 下载并解压 repodata.sqlite 文件
|
||||
"""
|
||||
import os # 确保在函数内导入(如果全局未导入)
|
||||
head = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||
}
|
||||
try:
|
||||
# 确保 baseurl 以 '/' 结尾
|
||||
if not baseurl.endswith('/'):
|
||||
baseurl += '/'
|
||||
|
||||
# 正确拼接 repodata 路径
|
||||
repodata_url = f"{baseurl}repodata/"
|
||||
response = requests.get(repodata_url, headers=head, verify=False)
|
||||
response.raise_for_status() # 确保请求成功
|
||||
|
||||
html = etree.HTML(response.text)
|
||||
a_links = html.xpath("//a")
|
||||
|
||||
# 支持的压缩格式
|
||||
supported_formats = [
|
||||
("primary.sqlite.bz2", bz2.BZ2File),
|
||||
("primary.sqlite.xz", lzma.open),
|
||||
("primary.sqlite.gz", gzip.open)
|
||||
]
|
||||
|
||||
for item in a_links:
|
||||
href = item.get('href', '')
|
||||
for suffix, opener in supported_formats:
|
||||
if href.endswith(suffix):
|
||||
# 正确拼接下载 URL
|
||||
sqlite_url = f"{repodata_url}{href}"
|
||||
print(f"下载数据库文件: {sqlite_url}")
|
||||
|
||||
# 下载压缩文件
|
||||
response = requests.get(sqlite_url, headers=head, verify=False)
|
||||
response.raise_for_status()
|
||||
|
||||
# 生成临时压缩文件路径
|
||||
temp_compressed = f"repotest.sqlite.{suffix.split('.')[-1]}"
|
||||
with open(temp_compressed, 'wb') as f:
|
||||
f.write(response.content)
|
||||
|
||||
# 解压文件
|
||||
newfilepath = "repotest.sqlite"
|
||||
with opener(temp_compressed, 'rb') as compressed_file:
|
||||
with open(newfilepath, 'wb') as decompressed_file:
|
||||
shutil.copyfileobj(compressed_file, decompressed_file)
|
||||
|
||||
# 验证文件是否存在
|
||||
if not os.path.isfile(newfilepath):
|
||||
raise FileNotFoundError(f"解压失败: {newfilepath} 不存在")
|
||||
|
||||
return newfilepath # 返回有效路径
|
||||
|
||||
raise ValueError(f"未找到支持的数据库文件: {repodata_url}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取数据库文件失败: {e}")
|
||||
raise # 抛出异常供上层处理
|
||||
def Getsqlite1(baseurl):
|
||||
"""
|
||||
从repodata中获取数据库压缩文件,下载并解压
|
||||
url 格式 例如 basrutl=https://update.cs2c.com.cn/NS/V10/8U6/os/adv/lic/AppStream/x86_64/os/
|
||||
"""
|
||||
head = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
|
||||
}
|
||||
path = f"{baseurl}repodata"
|
||||
response = requests.get(path, headers=head,verify=False)
|
||||
#print(response)
|
||||
html = response.text
|
||||
#print(html)
|
||||
html = etree.HTML(html)
|
||||
a_links = html.xpath("//a") # 查找所有 a 标签 ,//a 代表 html 下所有 a,html/body/a 同理
|
||||
|
||||
for item in a_links:
|
||||
item1 = item.get('href') # 获取a标签某一属性
|
||||
if "primary.sqlite.bz2" or "primary.sqlite.xz" in item1:
|
||||
sqlite_url = '/'.join([path, item1])
|
||||
#sqlite_url=f"{path}/{item1}"# 其他方法
|
||||
|
||||
response = requests.get(sqlite_url,headers=head,verify=False)
|
||||
un_path = "repotest.sqlite"
|
||||
if "primary.sqlite.bz2" in item1:#识别bz2压缩文件
|
||||
print(sqlite_url)
|
||||
bz2path = 'repotest.sqlite.bz2'
|
||||
#un_path = "repotest.sqlite"
|
||||
with open(bz2path, 'wb') as code:
|
||||
code.write(response.content)
|
||||
bz2file = bz2.BZ2File(bz2path)
|
||||
data = bz2file.read()
|
||||
newfilepath = un_path
|
||||
open(newfilepath, 'wb').write(data)
|
||||
|
||||
elif "primary.sqlite.xz" in item1: #识别xz压缩文件
|
||||
print(sqlite_url)
|
||||
xzfile = 'repotest.sqlite.xz'
|
||||
#un_path = 'repotest.sqlite'
|
||||
with open(xzfile, 'wb') as code:
|
||||
code.write(response.content)
|
||||
with lzma.open(xzfile, 'rb') as input:
|
||||
with open(un_path, 'wb') as output:
|
||||
shutil.copyfileobj(input, output)
|
||||
else:
|
||||
print("获取数据库文件失败,请检查!")
|
||||
|
||||
return un_path
|
||||
|
||||
# def Getrpminfo(database):
|
||||
# """
|
||||
# 连接sqlite数据库获取软件包信息
|
||||
# """
|
||||
# con = sqlite3.connect(database)
|
||||
# ##读取sqlite数据
|
||||
# cursor = con.cursor()
|
||||
# ##创建游标cursor来执行executeSQL语句
|
||||
# cursor.execute("SELECT * from packages")
|
||||
# # packs = cursor.fetchall() # 获取整个列表数据
|
||||
# content = cursor.execute("SELECT name, version, epoch, release, arch, rpm_sourcerpm, pkgId from packages")
|
||||
# rpmname=[]
|
||||
# filename1 = []
|
||||
# for row in cursor:
|
||||
# rpm=row[0] + "-" + row[1] + "-" + row[3] + "." + row[4] + ".rpm"
|
||||
# srcname=row[5].rsplit('-',2)[0]
|
||||
# #rpmname.append(row[0] + "-" + row[1] + "-" + row[3] + "." + row[4] + ".rpm")
|
||||
# #(n, v, e, r, a, s, i) = (row[0], row[1], row[2], row[3], row[4], row[5], row[6])
|
||||
# # filename = {'rpmname': rpm, 'name': row[0], 'version': row[1], 'epoch': row[2], 'release': row[3], 'arch': row[4], 'sourcerpm': row[5], 'pkgId': row[6]}
|
||||
# filename = {'rpmname':rpm,'name':row[0], 'version': row[1], 'epoch':row[2],'release': row[3], 'arch': row[4],'sourcerpm':row[5],'pkgId': row[6],'srcname': srcname}
|
||||
# filename1.append(filename)
|
||||
# #print('\n' + '##### 通过 Getrpminfo 获取软件包 获取软件包nvr:', len(filename1)) # 检修口
|
||||
# #print(len(PkgList), filename2)
|
||||
# return filename1
|
||||
# cursor.close()
|
||||
# con.close()
|
||||
|
||||
def Getrpminfo(database):
|
||||
"""
|
||||
连接sqlite数据库获取软件包信息
|
||||
"""
|
||||
if not os.path.isfile(database):
|
||||
raise FileNotFoundError(f"数据库文件不存在: {database}")
|
||||
|
||||
try:
|
||||
con = sqlite3.connect(database)
|
||||
cursor = con.cursor()
|
||||
cursor.execute("SELECT name, version, epoch, release, arch, rpm_sourcerpm, pkgId from packages")
|
||||
|
||||
filename1 = []
|
||||
for row in cursor:
|
||||
srcname = row[5].rsplit('-', 2)[0]
|
||||
filename = {
|
||||
'rpmname': f"{row[0]}-{row[1]}-{row[3]}.{row[4]}.rpm",
|
||||
'name': row[0],
|
||||
'version': row[1],
|
||||
'epoch': row[2],
|
||||
'release': row[3],
|
||||
'arch': row[4],
|
||||
'sourcerpm': row[5],
|
||||
'pkgId': row[6],
|
||||
'srcname': srcname
|
||||
}
|
||||
filename1.append(filename)
|
||||
|
||||
return filename1
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"数据库操作失败: {e}")
|
||||
return []
|
||||
|
||||
finally:
|
||||
if 'con' in locals():
|
||||
con.close()
|
||||
# def plus(*baseurls):
|
||||
# """
|
||||
# 用于处理多个url,合并处理结果
|
||||
# """
|
||||
# filename2=[]
|
||||
# PkgList=[]
|
||||
# for baseurl in baseurls:
|
||||
# un_path=Getsqlite(baseurl)
|
||||
# filename1=Getrpminfo(un_path)
|
||||
# filename2.extend(filename1)
|
||||
# for filenme in filename2:
|
||||
# PkgList.append(filenme["rpmname"])
|
||||
# print('\n' + '##### 通过 Getrpminfo , 获取软件包nvr 完成:', len(filename2)) # 检修口
|
||||
# return PkgList,filename2
|
||||
# def plus(*baseurls):
|
||||
# """
|
||||
# 用于处理多个url,合并处理结果
|
||||
# """
|
||||
# filename2 = []
|
||||
# PkgList = []
|
||||
# for baseurl in baseurls:
|
||||
# try:
|
||||
# un_path = Getsqlite(baseurl)
|
||||
# filename1 = Getrpminfo(un_path)
|
||||
# filename2.extend(filename1)
|
||||
# except Exception as e:
|
||||
# print(f"处理仓库 {baseurl} 失败: {e}")
|
||||
# continue # 跳过当前仓库,继续处理下一个
|
||||
#
|
||||
# for filenme in filename2:
|
||||
# PkgList.append(filenme["rpmname"])
|
||||
#
|
||||
# print(f"成功处理 {len(filename2)} 个软件包")
|
||||
# return PkgList, filename2
|
||||
def plus(*baseurls):
|
||||
"""
|
||||
处理多个仓库 URL,合并结果
|
||||
"""
|
||||
filename2 = []
|
||||
PkgList = []
|
||||
for baseurl in baseurls:
|
||||
try:
|
||||
un_path = Getsqlite(baseurl)
|
||||
filename1 = Getrpminfo(un_path)
|
||||
filename2.extend(filename1)
|
||||
except Exception as e:
|
||||
print(f"跳过仓库 {baseurl}: {str(e)}")
|
||||
continue
|
||||
|
||||
for filenme in filename2:
|
||||
PkgList.append(filenme["rpmname"])
|
||||
|
||||
print(f"成功处理 {len(filename2)} 个软件包")
|
||||
return PkgList, filename2
|
||||
|
||||
def plus1(url):
|
||||
"""
|
||||
用于处理多个url,合并处理结果
|
||||
"""
|
||||
filename2=[]
|
||||
PkgList=[]
|
||||
baseurls=url.split(',')
|
||||
for baseurl in baseurls:
|
||||
un_path=Getsqlite(baseurl)
|
||||
# filename1=Getrpminfo(un_path)
|
||||
filename1 = Getrpminfo(un_path)
|
||||
filename2.extend(filename1)
|
||||
for filenme in filename2:
|
||||
PkgList.append(filenme["rpmname"])
|
||||
print('\n' + '##### 通过 Getrpminfo , 获取软件包nvr 完成:', len(filename2)) # 检修口
|
||||
return PkgList,filename2
|
||||
|
||||
|
||||
def getsrc(pkglist,filename):
|
||||
"""
|
||||
用于获取对应软件包的源码包名称,name为全名
|
||||
"""
|
||||
srclist=[]
|
||||
pkgidlist = []
|
||||
for pkg in pkglist:
|
||||
pkg=list(pkg.split())
|
||||
for i in filename:
|
||||
if pkg[-1]==i["rpmname"]:
|
||||
srclist.append(i["sourcerpm"]) ####获取其他标签###
|
||||
# pkgidlist.append(i["pkgId"]) ####获取其他标签###
|
||||
#srclist = list(set(srclist))
|
||||
|
||||
return srclist
|
||||
|
||||
def getsrc1(pkglist,filename):
|
||||
"""
|
||||
用于获取对应软件包的源码包名称,name为全名
|
||||
"""
|
||||
srclist=[]
|
||||
pkgidlist = []
|
||||
for pkg in pkglist:
|
||||
pkg=list(pkg.split())
|
||||
for i in filename:
|
||||
if pkg[-1]==i["rpmname"]:
|
||||
srclist.append(i["sourcerpm"]) ####获取其他标签###
|
||||
pkgidlist.append(i["pkgId"]) ####获取其他标签###
|
||||
#srclist = list(set(srclist))
|
||||
|
||||
return pkglist,pkgidlist,srclist
|
||||
|
||||
def getsrc2(pkglist,filename):
|
||||
"""
|
||||
用于获取对应软件包的源码包名称,name为全名
|
||||
"""
|
||||
srclist=[]
|
||||
srcnamelist = []
|
||||
for pkg in pkglist:
|
||||
pkg=list(pkg.split())
|
||||
for i in filename:
|
||||
if pkg[-1]==i["rpmname"]:
|
||||
srclist.append(i["sourcerpm"]) ####获取其他标签###
|
||||
srcnamelist.append(i["srcname"]) ####获取其他标签###
|
||||
#srclist = list(set(srclist))
|
||||
|
||||
return srclist,srcnamelist
|
||||
|
||||
def getpkgid(pkglist,filename):
|
||||
"""
|
||||
用于获取对应软件包的 pkgid,name为全名
|
||||
"""
|
||||
pkgidlist=[]
|
||||
for pkg in pkglist:
|
||||
#pkg=list(pkg.split())
|
||||
for i in filename:
|
||||
if pkg==i["rpmname"]:
|
||||
pkgidlist.append(i["pkgId"]) ####获取其他标签###
|
||||
#srclist = list(set(srclist))
|
||||
#print(pkgidlist)
|
||||
return pkgidlist
|
||||
|
||||
|
||||
def pkgiddiff(pkglist,filename1,filename2):
|
||||
result1=[]
|
||||
for pkg in pkglist:
|
||||
for i in filename1:
|
||||
if i["rpmname"]==pkg:
|
||||
pkgId1=i["pkgId"]
|
||||
for J in filename2:
|
||||
if J["rpmname"] == pkg:
|
||||
pkgId2 = J["pkgId"]
|
||||
if pkgId1 == pkgId2:
|
||||
result1.append("一致")
|
||||
else:
|
||||
result1.append("不一致")
|
||||
return result1
|
||||
|
||||
|
||||
|
||||
|
||||
#方法3.0:保存结果,生成txt
|
||||
def GenTxT(Sourcerpm):
|
||||
filename = input("please input your filename for generate result: ")
|
||||
f=open("./"+filename+".txt", "w", encoding="utf-8")
|
||||
for pkg in Sourcerpm:
|
||||
f.write(str(pkg+'\n'))
|
||||
f.close
|
||||
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def write_to_csv(pkglist, pkgidlist, srclist, filename='output.csv'):
|
||||
"""
|
||||
将 pkglist、pkgidlist 和 srclist 写入 CSV 文件。
|
||||
|
||||
参数:
|
||||
pkglist : list
|
||||
软件包全名的列表。
|
||||
pkgidlist : list
|
||||
包 ID 的列表。
|
||||
srclist : list
|
||||
源码包名称的列表。
|
||||
filename : str
|
||||
输出 CSV 文件的名称。
|
||||
"""
|
||||
# 创建一个 DataFrame
|
||||
data = {
|
||||
'Package Name': pkglist,
|
||||
'Package ID': pkgidlist,
|
||||
'Source RPM': srclist
|
||||
}
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
# 将 DataFrame 写入 CSV 文件
|
||||
df.to_csv(filename, index=False, encoding='utf-8-sig')
|
||||
|
||||
#
|
||||
# pkglist,filename=plus("https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/updates/x86_64/")
|
||||
# print(pkglist,filename)
|
||||
# pkglist, pkgidlist, srclist=getsrc1(pkglist,filename)
|
||||
|
||||
# write_to_csv(pkglist, pkgidlist, srclist, filename='sp3-loongarch-output.csv')
|
||||
|
||||
|
||||
# Getsqlite("https://dl.fedoraproject.org/pub/archive/fedora/linux/releases/36/Server/source/tree/")
|
||||
# plus("http://10.44.34.84/ns8.2/x86/BaseOS/","http://10.44.34.84/ns8.2/x86/AppStream/")
|
||||
# plus("https://10.44.16.185/NS/V10/8U8/os/adv/lic/AppStream/x86_64/os/","https://10.44.16.185/NS/V10/8U8/os/adv/lic/BaseOS/x86_64/os/")
|
||||
18
script/10_black_check/README.md
Normal file
18
script/10_black_check/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
## 脚本简介
|
||||
本脚本用于测试updates仓库中是否有黑名单列表中的软件包。
|
||||
如果有,列出导出的黑名单软件包
|
||||
如果没有,则表示测试通过
|
||||
|
||||
## 使用方法
|
||||
执行脚本:python3 check_black_rpms_in_repos.py V10SP1.1 V10SP2
|
||||
参数V10SP1.1 V10SP2为测试的产品列表,可以有多个,如果没有参数,默认测试所有产品
|
||||
参数的值有:'V10SP1.1', 'V10SP2', 'V10SP3', 'V10SP3-2403', 'V10-HPC', 'V10-ZJ', 'HOST', 'HOST-2309', 'HOST-2406', 'V10SP3-2309A', 'V10SP3-2309B'
|
||||
|
||||
|
||||
## 输出:
|
||||
测试结果为输出字符串
|
||||
测试通过:说明无黑名单软件包导出到仓库
|
||||
测试有报错:说明有黑名单软件包导出到仓库
|
||||
|
||||
# 注意事项
|
||||
119
script/10_black_check/check_black_rpms_in_repos.py
Normal file
119
script/10_black_check/check_black_rpms_in_repos.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
import re
|
||||
import json
|
||||
import sys
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.util.retry import Retry
|
||||
|
||||
|
||||
def create_retry_session():
|
||||
session = requests.Session()
|
||||
retry = Retry(
|
||||
total=3,
|
||||
backoff_factor=1,
|
||||
status_forcelist=[500, 502, 503, 504]
|
||||
)
|
||||
adapter = HTTPAdapter(max_retries=retry)
|
||||
session.mount('http://', adapter)
|
||||
session.mount('https://', adapter)
|
||||
return session
|
||||
|
||||
|
||||
def fetch_rpm_packages(url):
|
||||
session = create_retry_session()
|
||||
try:
|
||||
# 发送 HTTP 请求获取仓库页面内容
|
||||
response = requests.get(url, timeout=(10, 30)) # 添加超时设置
|
||||
response.raise_for_status()
|
||||
|
||||
# 使用 BeautifulSoup 解析 HTML 内容
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
|
||||
# 使用正则表达式匹配 RPM 包文件名
|
||||
rpm_pattern = re.compile(r'.*\.rpm$')
|
||||
rpm_links = soup.find_all('a', href=rpm_pattern)
|
||||
|
||||
# 提取 RPM 包名称
|
||||
packages = []
|
||||
for link in rpm_links:
|
||||
rpm = link.get('href').replace('%2B', '+') # 替换转移字符+
|
||||
if rpm.strip():
|
||||
rpm_parts = rpm.rsplit("-", 2) # 更清晰的变量名
|
||||
if len(rpm_parts) >= 3: # 确保至少分割出包名、版本和架构
|
||||
name = rpm_parts[0]
|
||||
packages.append(name)
|
||||
else:
|
||||
print(f"无法解析的软件包: {rpm}")
|
||||
|
||||
# 按包名排序并返回
|
||||
packages.sort()
|
||||
print(f"从仓库获取到软件包列表,共 {len(packages)} 个 RPM 包")
|
||||
return packages
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"获取RPM包列表时出错: {e}")
|
||||
return [] # 失败时返回空列表
|
||||
except Exception as e:
|
||||
print(f"获取RPM包列表时发生未知错误: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def fetch_and_read_remote_file(url):
|
||||
try:
|
||||
# 发送 HTTP 请求获取文件内容,设置超时
|
||||
response = requests.get(url, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
# 获取文件内容并按行分割
|
||||
lines = response.text.splitlines()
|
||||
lines.sort() # 排序
|
||||
print(f"成功获取黑名单列表,共 {len(lines)} 个 RPM 包")
|
||||
return lines
|
||||
except requests.exceptions.HTTPError as http_err:
|
||||
print(f"黑名单获取:HTTP 错误发生: {http_err}")
|
||||
return []
|
||||
except requests.exceptions.RequestException as req_err:
|
||||
print(f"黑名单获取:请求错误发生: {req_err}")
|
||||
return []
|
||||
except Exception as err:
|
||||
print(f"黑名单获取:其他错误发生: {err}")
|
||||
return []
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with open('repository_config.json', 'r', encoding='utf-8') as file:
|
||||
data = json.load(file)
|
||||
if len(sys.argv) > 1:
|
||||
print("测试产品:", sys.argv[1:]) # 跳过脚本名
|
||||
product_list = sys.argv[1:]
|
||||
print(product_list)
|
||||
else:
|
||||
print("未提供具体测试产品,默认测试所有产品")
|
||||
product_list = list(data.keys())
|
||||
print(f"可选其中的值作为脚本参数:{product_list}")
|
||||
# 解析json文件
|
||||
for product in product_list:
|
||||
try:
|
||||
print(f"\n--------产品【{product}】--------")
|
||||
product_data = data.get(product, {})
|
||||
if not product_data:
|
||||
print(f"警告:未找到产品 {product} 的配置")
|
||||
continue
|
||||
|
||||
for arch, urls in product_data.items():
|
||||
print(f"\n{arch}仓库:")
|
||||
if len(urls) < 2:
|
||||
print(f"警告:{product}/{arch} 配置不完整,跳过")
|
||||
continue
|
||||
|
||||
rpm_packages = fetch_rpm_packages(urls[0])
|
||||
rpm_black_packages = fetch_and_read_remote_file(urls[1])
|
||||
|
||||
set_common = set(rpm_packages) & set(rpm_black_packages)
|
||||
|
||||
if set_common:
|
||||
print(f"测试有报错:发现 {len(set_common)} 个黑名单软件包:{set_common}")
|
||||
else:
|
||||
print("测试通过:updates仓库没有发现黑名单软件包")
|
||||
except Exception as err:
|
||||
print(f"{product} 仓库处理失败:{err}")
|
||||
124
script/10_black_check/repository_config.json
Normal file
124
script/10_black_check/repository_config.json
Normal file
@@ -0,0 +1,124 @@
|
||||
{
|
||||
"V10SP1.1": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP1.1/os/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP1.1/os/adv/lic/updates/aarch64/black"
|
||||
],
|
||||
"loongarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/loongarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP1.1/os/adv/lic/updates/loongarch64/black"
|
||||
],
|
||||
"mips64el": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP1.1/os/adv/lic/updates/mips64el/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP1.1/os/adv/lic/updates/mips64el/black"
|
||||
]
|
||||
},
|
||||
"V10SP2": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP2/os/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP2/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP2/os/adv/lic/updates/aarch64/black"
|
||||
]
|
||||
},
|
||||
"V10SP3": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP3/os/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP3/os/adv/lic/updates/aarch64/black"
|
||||
],
|
||||
"loongarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP3/os/adv/lic/updates/loongarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP3/os/adv/lic/updates/loongarch64/black"
|
||||
]
|
||||
},
|
||||
"V10SP3-2403": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP3-2403/os/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP3-2403/os/adv/lic/updates/aarch64/black"
|
||||
],
|
||||
"loongarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10SP3-2403/os/adv/lic/updates/loongarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10SP3-2403/os/adv/lic/updates/loongarch64/black"
|
||||
]
|
||||
},
|
||||
"V10-HPC": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/HPC/os/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/HPC/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/HPC/os/adv/lic/updates/aarch64/black"
|
||||
]
|
||||
},
|
||||
"V10-ZJ": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10-ZJ/os/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10-ZJ/os/adv/lic/updates/aarch64/black"
|
||||
],
|
||||
"mips64el": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/V10-ZJ/os/adv/lic/updates/mips64el/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/V10-ZJ/os/adv/lic/updates/mips64el/black"
|
||||
]
|
||||
},
|
||||
"HOST": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/HOST/SP3/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/HOST/SP3/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/HOST/SP3/adv/lic/updates/aarch64/black"
|
||||
]
|
||||
},
|
||||
"HOST-2309": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/HOST/2309/os/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/HOST/2309/os/adv/lic/updates/aarch64/black"
|
||||
]
|
||||
},
|
||||
"HOST-2406": {
|
||||
"x86_64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/HOST/2406/os/adv/lic/updates/x86_64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/HOST/2406/os/adv/lic/updates/x86_64/black"
|
||||
],
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/HOST/2309/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/HOST/2309/os/adv/lic/updates/aarch64/black"
|
||||
]
|
||||
},
|
||||
"V10SP3-2309A": {
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/2309A/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/2309A/os/adv/lic/updates/aarch64/black"
|
||||
]
|
||||
},
|
||||
"V10SP3-2309B": {
|
||||
"aarch64": [
|
||||
"https://update.cs2c.com.cn/private_test/repo/V10/2309B/os/adv/lic/updates/aarch64/Packages/",
|
||||
"https://update.cs2c.com.cn/private_test/repo_black/V10/2309B/os/adv/lic/updates/aarch64/black"
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user