python3 aptly api

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))



APTLY 命令:

# 查看源码包名
aptly repo search -format="包名:{{.Package}} | 架构:{{.Architecture}} | 版本:{{.Version}} | 源码包名:{{.Source}} " test 'Name (~ ^.*)'


13# aptly 基本使用

# 查看 aptly 当前本地的配置
aptly config show

# 清除 aptly 清除db
# 数据库清理删除有关未引用包的信息并删除,包池中不再被包使用的文件
aptly db cleanup

# 恢复 aptly 数据库
# 数据库恢复在崩溃后尽力恢复数据库。建议在运行recover之前备份数据库
aptly db recover

# 命令图显示镜像、本地存储库和,使用graphviz包呈现快照和发布的存储库,以图形表示图像。
aptly graph

# 根据远程镜像地址创建仓库
aptly mirror create wheezy-main http://mirror.yandex.ru/debian/ wheezy main
# 同步仓库信息
aptly mirror update wheezy-main
# 创建快照信息
aptly snapshot create wheezy-main-2014-08-16 from mirror wheezy-main
# 上传快照
aptly publish snapshot wheezy-main-2014-08-16

# 查看镜像列表
aptly mirror list
# 每行显示名称
aptly mirror list -raw
# 显示仓库对应所有的包与版本信息
aptly mirror show -with-packages test

# 镜像修改架构
aptly mirror edit --architectures=i386,amd64,arm64,mips64el test

# 搜索根据名称, 过滤架构, 过滤关键字
aptly mirror search test 'Name (% systemd*), $Architecture (i386), !Name (% *tests*)'
aptly mirror search test 'Name (% lib*), $Architecture (i386), Version (>=3)'

# 命令更改镜像的名称。镜像名称应唯一
aptly mirror rename <old-name> <new-name>

# Drop删除有关远程存储库镜像的信息。软件包数据不会被删除(其他镜像或快照仍可以使用)。如果将镜像用作创建快照的源,则适当地拒绝删除该镜像,请使用标志-force进行覆盖。
aptly mirror drop <name>

# 创建本地仓库
aptly repo create <name>
aptly mirror create -dep-follow-source test-1031 http://pools.uniontech.com/desktop-professional test/1031 main contrib non-free
aptly mirror create -dep-follow-source test-1041 http://aptly.uniontech.com/pkg/test-1041 unstable main
aptly mirror create -ignore-signatures -architectures=i386,amd64,arm64,sw_64,mips64el,loongarch,loongarch64 -with-sources drive-service http://drive-packages.uniontech.com/drive test non-free


# 删除本地仓库
aptly repo drop local-repo

# 查看仓库中的信息
aptly repo list
aptly repo list -raw

# 查看对应的名称的信息
aptly repo show <name>
aptly repo show -with-packages <name>

# 像仓库推入包
aptly repo add test-repo apt_1.4.8+deepin_amd64.deb
# 从仓库删除制定包
aptly repo remove test-repo apt

# 从指定mirror导入到仓库中
aptly repo import test test-repo udev_241.3-1+deepin_i386
aptly repo import test test-repo udev_241.3-1+deepin_mips64el

# 从删除指定的包名(全名则可删除指定包) 
aptly repo remove test-repo2 udev_241.3-1+deepin_mips64el

# 从一个仓库直接复制指定包到另一个仓库, 增加 -with-deps 可自动解决依赖
aptly repo copy test-repo test-repo2 udev
aptly repo copy test-repo test-repo2 udev_241.3-1+deepin_mips64el
# 同上
aptly repo move test-repo2 test-repo udev
aptly repo move test-repo test-repo2 udev_241.3-1+deepin_mips64el

# 根据指定版本过滤搜索
aptly repo search test-repo 'Version (>=2)'
# 修改对应仓库的组件信息与分发信息
aptly repo edit -comment="Nightly builds" -distribution=wheezy testing
# 重命名
aptly repo rename <old-name> <new-name>
# 删除仓库
aptly repo drop <name>
# 修改仓库的分配
aptly repo edit -distribution="test" test-repo2


# 查看快照列表
aptly snapshot list
# 每行显示名称
aptly snapshot list -raw
# 显示快照对应所有的包与版本信息
aptly snapshot show -with-packages test-2020-07-14

# 从本地仓库创建快照
aptly snapshot create snap-stable from repo stable

# 验证快照中软件包之间的依赖关系
aptly snapshot verify test-2020-07-15

# 从快照中提取 新包(及其依赖项)到快照。如果快照已包含要拉出的软件包,则pull命令还可以升级软件包版本 。作为此过程的结果,将创建新的快照。
aptly snapshot pull test-2020-07-15 test-2020-07-14 test libgtk-3-common_3.24.5.7-1+deepin_all

# 通过过滤快照的条件生成新的快照
aptly snapshot filter test-2020-07-14 new_snapshot 'Version (>=3)'

# 显示两个快照之间的程序包差异。快照是软件包的列表,因此快照之间的区别是软件包列表之间的区别。一个快照中的软件包可能完全丢失,或者两个快照中都有不同版本的软件包。
aptly snapshot diff <name-a> <name-b>
# -only-matching 仅显示软件包版本的差异(不显示缺少的软件包)
aptly snapshot diff -only-matching new_snapshot test-2020-07-14

# 使用 merge 可以合并多个快照
aptly snapshot merge merge-snapshot test test-2020-07-14 test-2020-07-15

# 快照搜索匹配
aptly snapshot search merge-snapshot 'Name (~ ^libgtk*)'

# 命令更改快照的名称。快照名称应唯一
aptly snapshot rename <old-name> <new-name>

# Drop删除有关快照的信息。如果快照已发布,则不能删除(首先删除快照的发布)。
aptly snapshot drop <name>

# 包搜索工具,根据名称和格式搜索展示
aptly package search -format="{{.Package}} (version {{.Version}})" 'Name (~ ^lib.*)'

# 通过包管理搜索版本之间的包信息
aptly package search 'Version (>=1.2.3-2), Version (<2)'

# 通过包管理查看指定包的详细信息包括映射镜像和仓库
aptly package show -with-files -with-references exfat-fuse-dbgsym
# 查看指定包信息
aptly package show -with-files -with-references exfat-fuse-dbgsym_1.3.0.3-1+deepin_i386


# 查看发布的信息
aptly publish list

# 发布仓库的信息,可以根据 -distribution 的不同名称去区分,发布是热更新无需重启服务
aptly publish repo <repo-name>

# 删除发布的信息
aptly publish drop unstable filesystem:test:demo


# 发布到Minio上
aptly publish repo -architectures="amd64" -skip-signing repo-test s3:test:aptly/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值