import requests
import json
from aptly_api.client import Client as AptlyCilent
class Aptly(object):
def __init__(self):
self.host = 'http://192.168.88.128'
self.port = 8080
self.timeout = 300
self.base_url = f"{self.host}:{self.port}"
def _req_get(self, api, params=None):
response = requests.get(f'{self.base_url}{api}', timeout=self.timeout)
if response.status_code < 200 or response.status_code >= 300:
return False, f"API request failed with status code: {response.status_code} "
else:
return True, response.json()
def _req_post(self, api, data=None, params=None):
headers = {'Content-Type': 'application/json'}
response = requests.post(f'{self.base_url}{api}', data=data, headers=headers, params=params, timeout=self.timeout)
if response.status_code < 200 or response.status_code >= 300:
return False, f"API request failed with status code: {response.status_code} {response.json()}"
else:
return True, response.json()
def _req_post_file(self, api, data):
# headers = {
# 'Content-Type': 'application/octet-stream', # 设置内容类型为二进制流文件
# }
response = requests.post(f'{self.base_url}{api}', files=data, timeout=self.timeout)
if response.status_code < 200 or response.status_code >= 300:
return False, f"API request failed with status code: {response.status_code} {response.json()}"
else:
return True, response.json()
def _req_put(self, api, data, params=None):
headers = {'Content-Type': 'application/json'}
response = requests.put(f'{self.base_url}{api}', data=json.dumps(data), headers=headers, params=params, timeout=self.timeout)
if response.status_code < 200 or response.status_code >= 300:
return False, f"API request failed with status code: {response.status_code} {response.json()}"
else:
return True, response.json()
def _req_delete(self, api, json=None, params=None):
response = requests.delete(f'{self.base_url}{api}', json=json, params=params,timeout=self.timeout)
if response.status_code < 200 or response.status_code >= 300:
return False, f"API request failed with status code: {response.status_code} {response.json()}"
else:
return True, response.json()
def create_repo(self, name, comment=None, defaultDistribution=None, defaultComponent='main'):
'''
:param name: 仓库名
:param comment: 仓库说明
:param defaultDistribution: 类似reprepro中的codename
:param defaultComponent: 通常会有main(主要), contrib(贡献), non-free(非自由)等, 默认main
:return:
curl -X POST -H 'Content-Type: application/json' --data '{"Name": "aptly-repo"}' http://localhost:8080/api/repos
{"Name":"aptly-repo","Comment":"","DefaultDistribution":"","DefaultComponent":""}
命令: aptly repo create <name> 参数:-comment="" -distribution="" -component="main"
eg:
# repo = aptly.create_repo('nfs5','5.0','nfs5','main')
'''
body = {}
body["Name"] = name
if comment:
body["Comment"] = comment
if defaultDistribution:
body["DefaultDistribution"] = defaultDistribution
if defaultComponent:
body["DefaultComponent"] = defaultComponent
is_ok, res = self._req_post(f'/api/repos/',body)
if is_ok:
return res
return is_ok
def edit_repo(self, name, comment=None, defaultDistribution=None, defaultComponent=None):
'''
:param name: 仓库名
:param comment: 仓库说明
:param defaultDistribution: 类似reprepro中的codename
:param defaultComponent: 通常会有main(主要), contrib(贡献), non-free(非自由)等, 默认main
:return:
curl -X PUT -H 'Content-Type: application/json' --data '{"DefaultDistribution": "trusty"}' http://localhost:8080/api/repos/local1
{"Name":"local1","Comment":"","DefaultDistribution":"trusty","DefaultComponent":"main"}
eg: # repo = aptly.edit_repo('myrepo', comment='test myrepo')
'''
body = {}
if comment:
body["Comment"] = comment
if defaultDistribution:
body["DefaultDistribution"] = defaultDistribution
if defaultComponent:
body["DefaultComponent"] = defaultComponent
is_ok, res = self._req_put(f'/api/repos/{name}', body)
if is_ok:
return res
return is_ok
def delete_repo(self, name, force=False):
'''
:param name:
:param force:
:return:
eg: repo = aptly.delete_repo('nfs5')
'''
is_ok, res = self._req_delete(f'/api/repos/{name}', params={"force": "1" if force else "0"})
print(res)
if is_ok:
return res
return is_ok
def show_repos(self):
'''
:return:
curl http://localhost:8080/api/repos
eg: repos = aptly.show_repos()
'''
is_ok, res = self._req_get(f'/api/repos')
if is_ok:
return res
return is_ok
def show_repo(self, name):
'''
:param name: 仓库名
:return:
curl http://localhost:8080/api/repos/aptly-repo
{"Name":"aptly-repo","Comment":"","DefaultDistribution":"","DefaultComponent":""}
命令: aptly repo show <name> 参数:-with-packages
eg: aptly.show_repo('nfs5')
'''
is_ok, res = self._req_get(f'/api/repos/{name}')
if is_ok:
return res
return is_ok
def search_packages(self, key):
'''
:param key:
:return:
{
'Architecture': 'amd64',
'Description': ' 腾讯QQ Linux版本。\n',
'Filename': 'linuxqq_2.0.0-b2-1089_amd64.deb',
'FilesHash': '805665250fdd288f',
'Homepage': 'https://im.qq.com/linuxqq',
'Installed-Size': '35452',
'Key': 'Pamd64 linuxqq 2.0.0-b2 805665250fdd288f',
'MD5sum': 'c521b08830eaccba0d0ac2f7de374606',
'Maintainer': 'Tencent <support@tencent.com>',
'Package': 'linuxqq',
'Priority': 'optional',
'Recommends': 'tint2',
'SHA1': '5f46a467911b198e77e6e303c4ebb2499733c253',
'SHA256': 'f8e96b0fa3a09f7d36385a446ddfd86b173cf1cef1eb0f40493250538807c52c',
'SHA512': '42ab8fdecdc2782f8411415d62854f61a8f11b152031672cf43fcb95757bbb55a9c5856c300628428a33ae1a40516acc8e37222f422bf4c75736da40c9e5fabe',
'Section': 'Internet',
'ShortKey': 'Pamd64 linuxqq 2.0.0-b2',
'Size': '12393292',
'Version': '2.0.0-b2'
}
eg: # pakcages = aptly.search_packages('Pamd64 linuxqq 2.0.0-b2 805665250fdd288f')
'''
is_ok, res = self._req_get(f'/api/packages/{key}')
if is_ok:
return res
return is_ok
def get_repo_packages(self, name):
'''
:param name:
:return:
'''
is_ok, res = self._req_get(f'/api/repos/{name}/packages')
if is_ok:
packages = []
for key in res:
packages.append(self.search_packages(key))
return packages
return is_ok
def add_package(self, repo, files, force_replace=False, remove_processed_files=True):
"""
:param repo: 仓库
:param files: 上传的软件列表
:param force_replace: 若上的软件与仓库里的冲突是否强制替换
:param remove_processed_files: 上传的文件默认存放在 f'/opt/aptly/upload/{repo}',上传后是否立即删除
:return:
success: (True, {'FailedFiles': [], 'Report': {'Warnings': [], 'Added': ['linuxqq_3.1.2-13107_amd64 added'], 'Removed': []}})
failed: (True, {'FailedFiles': ['/opt/aptly/upload/nfs4/linuxqq_1.1.2-13107_amd64.deb'], 'Report': {'Warnings': ['Unable to add package to repo linuxqq_3.1.2-13107_amd64: conflict in package linuxqq_3.1.2-13107_amd64'], 'Added': [], 'Removed': []}})
源命令: aptly repo add testing files/libboost-program-options-dev_1.49.0.1_i386.deb
上传得文件所在位置 : f'/opt/aptly/upload/{repo}'
"""
# eg: res = aptly.add_package('nfs4',[
# \Users\apple\develop\py\demo\linuxqq_1.1.2-13107_amd64.deb,
# \Users\apple\develop\py\demo\nginx_1.18.0-6.1+nfs5_all.deb
# ])
base_upload_path = f'/opt/aptly/upload/{repo}/'
params = {
"noRemove": "0" if remove_processed_files else "1",
}
if force_replace:
params["forceReplace"] = "1"
to_upload = []
for f in files:
fh = open(f, mode="rb")
to_upload.append((f, fh),)
# pkg_file = {
# 'file':open(files, 'rb')
# }
_, uploaded_file = self._req_post_file(f'/api/files/{repo}',to_upload)
# 关闭文件
for fn, to_close in to_upload:
if not to_close.closed:
to_close.close()
success_deb = []
failed_deb = []
for deb in uploaded_file:
res = self._req_post(f"/api/repos/{repo}/file/{deb}",params=params)
# /api/repos/nfs4/file/nfs4/linuxqq_3.1.2-13107_amd64.deb 单个包上传
# /api/repos/nfs4/file/nfs4/ 将nfs4 目录下的deb包批量上传
print(res)
if res[1].get('FailedFiles'):
failed_msg = res[1].get('Report').get('Warnings')
deb_name = res[1].get('FailedFiles')[0].split(base_upload_path)[1]
failed_deb.append({deb_name:failed_msg})
else:
deb_name = res[1].get('Report').get('Added')[0].split(' added')[0]
success_deb.append({deb_name:True})
return {'success':success_deb, 'failed':failed_deb}
def delete_packages_by_key(self,repo, pkg_key):
'''
:param repo:
:param file:
:return:
源命令:
aptly repo remove nfs4 linuxqq_1.1.2-13107_amd64.deb 参数: -dry-run 试运行,可查看命令会将删除的deb包
aptly repo remove nfs4 linuxqq 默认匹配 linuxqq*
'''
# self._req_delete(f"/api/files/{repo}", file)
is_ok, resp = self._req_delete(f"/api/repos/{repo}/packages", json={
"PackageRefs": pkg_key,
})
if is_ok:
return resp
return resp
def delete_file(self, repo, deb):
'''
:param repo:
:param deb:
:return:
eg:
/opt/aptly/upload/nfs4/linuxqq_1.1.2-13107_amd64.deb
'''
self._req_post(f"/api/files/{repo}/{deb}")
if __name__ == '__main__':
'''
=== repo ===
*** 查看所有仓库 ***
# aptly repo list
List of local repos:
* [myrepo]: test myrepo (packages: 2)
* [nfs4]: 4.0 (packages: 2)
*** 查看<name>仓库信息 ***
# aptly repo show nfs4
Name: nfs4
Comment: 4.0
Default Distribution: nfs4
Default Component: main
Number of packages: 2
*** 查看<name>仓库信息,包括仓库内的deb包 ***
# aptly repo show -with-packages nfs4
Name: nfs4
Comment: 4.0
Default Distribution: nfs4
Default Component: main
Number of packages: 2
Packages:
linuxqq_3.1.2-13107_amd64
nginx_1.18.0-6.1+nfs5_all
=== publish ===
*** 查看所有已经发布的仓库 ***
# aptly publish list
Published repositories:
* ./nfs4 [amd64] publishes {main: [nfs4]: 4.0}
*** 发布仓库 ***
# aptly publish repo -distribution=nfs4 nfs4
Loading packages...
Generating metadata files and linking package files...
Finalizing metadata files...
Signing file 'Release' with gpg, please enter your passphrase when prompted:
You need a passphrase to unlock the secret key for
user: "First Last (Aptly Repo Signing) <test@test.com>"
4096-bit RSA key, ID 2B5CE95B, created 2023-07-05
Local repo nfs4 has been successfully published.
Please setup your webserver to serve directory '/opt/aptly/public' with autoindexing.
Now you can add following line to apt sources:
deb http://your-server/ nfs4 main
Don't forget to add your GPG key to apt with apt-key.
You can also use `aptly serve` to publish your repositories over HTTP quickly.
*** 查看发布的仓库 ***
# aptly publish show nfs4
Prefix: .
Distribution: nfs4
Architectures: amd64
Sources:
main: nfs4 [local]
*** 取消发布 ***
# aptly publish drop nfs4
Removing /opt/aptly/public/dists...
Removing /opt/aptly/public/pool...
Published repository has been removed successfully.
'''
aptly = Aptly()
repo = aptly.create_repo('nfs5','5.0','nfs5','main')
print(repo)
repo = aptly.edit_repo('myrepo', comment='test myrepo')
print(repo)
repo = aptly.delete_repo('nfs5')
repos = aptly.show_repos()
print(repos)
repo = aptly.show_repo('nfs4')
print(repo)
pakcages = aptly.search_packages('Pamd64 linuxqq 2.0.0-b2 805665250fdd288f')
res = aptly.add_package('nfs4',[
r'C:\Users\apple\develop\py\demo\linuxqq_3.1.2-13107_amd64.deb',
r'C:\Users\apple\develop\py\demo\nginx_1.18.0-6.1+nfs5_all.deb'
])
print(res)
aptly.delete_packages_by_key('nfs4',['Pamd64 linuxqq 3.1.2-13107 77650f003cb39465'])
packages = aptly.get_repo_packages('nfs4')
print(packages)
print(len(packages))
python3 aptly api
于 2023-07-14 22:56:32 首次发布