结合python 和 pve 的api,方便的管理虚拟机,可以单独的开发个小脚本,也可以嵌入到任何其他系统内,作为一个小功能。

介绍

关于pve rest api的官方介绍和 api文档:

https://pve.proxmox.com/wiki/Proxmox_VE_API

https://pve.proxmox.com/pve-docs/api-viewer/index.html#/nodes/{node}/qemu/{vmid}/status/stop

调用pve api ,有两种认证方式

  1. Ticket Cookie

先发送一个包含用户名密码的POST,然后server会返回json格式认证信息,提取必要信息并附加到下次http request请求头部即可

该ticket权限等同于对应用户的权限,两小时后超时失效

shell 命令

curl -k -d 'username=root@pam' --data-urlencode 'password=xxxxxxxx' https://192.168.0.101:8006/api2/json/access/ticket

python脚本

import requests


# solving self-sign CA warning
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
       
path = '/api2/json/access/ticket'
url = 'https://' + ip + ':' + port + path
r = requests.post(url=url, json={"username": username, "password": password}, verify=False)
print(r.json())

Postman(Talend API Tester )

请输入图片描述

返回格式

  {
    "data":{
    "ticket": "PVE:root@pam:62EF3DBE::UPduHl969t0bv/eVUs54Ez5z86epC86WvUi81kAgGbs/zw2aZbD7l2loroqOV58/u+iZFrCFRlEsvIKDYskSx0NWM4eW7081u4eMHquHPOnfwm1fblUnIClfs0tNjxwA7ZmFTVsNDgvEPFG5Uw98K8sYDvHLHixSHQEU8+MUyRubeYaJCTNSzNW2FhX+gVZU3xe7hUZSAO2wo0BI5Ciz2tK8WFtAoh7o4UyLR0nJb8qCf1XV6SWUxrq4NpwnmcqEUg7GIjS4ueZ874tjbvnLUdBF62aiKVdECZjAtbxv3ocqKUlw1jN/Gl6xDfSEpgwQiQCKjG2gMdPrNdu9Fp2Tow==",
    "cap":{"vms":{"VM.Clone": 1, "VM.Backup": 1, "VM.Config.Options": 1, "VM.Snapshot": 1,…},
    "CSRFPreventionToken": "62EF3DBE:/7meKBIZRMV6lpaQ9G9uylNI+/5qHYq3+gDj1Oie6Tw",
    "username": "root@pam"
    }
    }
  1. API Tokens

手动从PVE GUI控制台创建,并赋予合适的权限,可以设置超期时间(可以永久),记下token信息,后续把token信息附件到HTTP request 头部即可

添加token

添加权限

通过API操作PVE

例如,认证信息获取之后,通过api 查看当前pve node 虚拟机列表

  1. Ticket Cookie 方式

查询所有节点名称

        ticket = {.................}
        path = '/api2/json/nodes'
        url = 'https://' + ip + ':' + port + path
        headers = {'CSRFPreventionToken': ticket['data']['CSRFPreventionToken'], 'Cookie': 'PVEAuthCookie=' + ticket['data']['ticket']}
        r = requests.get(url=url, headers=headers, verify=False)
        print(r.json())

请输入图片描述

返回格式

{
    "data": [
        {
            "id": "node/pve01",
            "maxcpu": 4,
            "status": "online",
            "type": "node",
            "node": "pve01",
            "maxdisk": 100861726720,
            "cpu": 0.0333665338645418,
            "disk": 41166532608,
            "uptime": 2235423,
            "mem": 6703284224,
            "level": "",
            "maxmem": 8073363456,
            "ssl_fingerprint": "1F:D1:48:1A:46:19:2F:92:A1:70:3B:4A:EC:FA:67:FA:91:83:4E:5B:AF:68:92:4A:6C:8A:6F:83:D6:66:96:CB"
        }
    ]
}

查询特定节点下虚拟机列表

        ticket = {.................}
        node = 'pve01'
        path = f'/api2/json/nodes/{node}/qemu'
        url = 'https://' + ip + ':' + port + path
        headers = {'CSRFPreventionToken': ticket['data']['CSRFPreventionToken'], 'Cookie': 'PVEAuthCookie=' + ticket['data']['ticket']}
        r = requests.get(url=url, headers=headers, verify=False)
        print(r.json())

返回格式

{
"data":[
{"pid": 2648205, "cpus": 2, "status": "running", "name": "OpenMediaVault",…},
{"disk": 0, "mem": 1316534861, "cpu": 0.0445637817533544, "diskread": 0, "diskwrite": 0,…},
{"mem": 3197312520, "cpu": 0.118665826973757, "disk": 0, "maxmem": 4294967296, "maxdisk": 53687091200,…},
{"pid": 1400, "cpus": 1, "status": "running", "name": "OpenWRT",…},
{"cpus": 1, "status": "stopped", "name": "openwrt-test", "vmid": 104,…}
]
}
  1. Token 方式

查询所有节点名称

        path = '/api2/json/nodes'
        token = '...........'
        url = 'https://' + ip + ':' + port + path
        headers = {'Authorization': 'PVEAPIToken=' + token} # Authorization : PVEAPIToken=root@pam!myid=1e1b7cbd-e7eqweq553141525564c-c324519041e0
        r = requests.get(url=url, headers=headers, verify=False)
        print(r.json())

请输入图片描述

查询特定节点下虚拟机列表

        token = '...........'
        node = 'pve01'
        path = f'/api2/json/nodes/{node}/qemu'
        url = 'https://' + ip + ':' + port + path
        headers = {'Authorization': 'PVEAPIToken=' + token} # Authorization : PVEAPIToken=root@pam!myid=1e1b7cbd-e7eqweq553141525564c-c324519041e0
        r = requests.get(url=url, headers=headers, verify=False)
        print(r.json())

附上完整代码,

import requests


# self-sign CA warning
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class Pve_api(object):

    def __init__(self, ip, username = None, password = None, port = '8006'):
        self.ip = ip
        self.username = username
        self.password = password
        self.port = port
        self.ticket = None

    '''
    ####### ticket data structure
    {
    "data":{
    "ticket": "PVE:root@pam:62EF3DBE::UPduHl969t0bv/eVUs54Ez5z86epC86WvUi81kAgGbs/zw2aZbD7l2loroqOV58/u+iZFrCFRlEsvIKDYskSx0NWM4eW7081u4eMHquHPOnfwm1fblUnIClfs0tNjxwA7ZmFTVsNDgvEPFG5Uw98K8sYDvHLHixSHQEU8+MUyRubeYaJCTNSzNW2FhX+gVZU3xe7hUZSAO2wo0BI5Ciz2tK8WFtAoh7o4UyLR0nJb8qCf1XV6SWUxrq4NpwnmcqEUg7GIjS4ueZ874tjbvnLUdBF62aiKVdECZjAtbxv3ocqKUlw1jN/Gl6xDfSEpgwQiQCKjG2gMdPrNdu9Fp2Tow==",
    "cap":{"vms":{"VM.Clone": 1, "VM.Backup": 1, "VM.Config.Options": 1, "VM.Snapshot": 1,…},
    "CSRFPreventionToken": "62EF3DBE:/7meKBIZRMV6lpaQ9G9uylNI+/5qHYq3+gDj1Oie6Tw",
    "username": "root@pam"
    }
    }

    '''


    def get_ticket(self):
        path = '/api2/json/access/ticket'
        url = 'https://' + self.ip + ':' + self.port + path
        r = requests.post(url=url, json={"username": self.username, "password": self.password}, verify=False)
        self.ticket = r.json() # dict rather than string
        return r.json()

    def ticket_node_list(self):
        path = '/api2/json/nodes'
        url = 'https://' + self.ip + ':' + self.port + path
        headers = {'CSRFPreventionToken': self.ticket['data']['CSRFPreventionToken'], 'Cookie': 'PVEAuthCookie=' + self.ticket['data']['ticket']}
        r = requests.get(url=url, headers=headers, verify=False)
        return r.json()


    def ticket_vm_list(self, node):
        path = f'/api2/json/nodes/{node}/qemu'
        url = 'https://' + self.ip + ':' + self.port + path
        headers = {'CSRFPreventionToken': self.ticket['data']['CSRFPreventionToken'], 'Cookie': 'PVEAuthCookie=' + self.ticket['data']['ticket']}
        r = requests.get(url=url, headers=headers, verify=False)
        return r.json()

    def ticket_vm_current(self, node, vmid):
        path = f'/api2/json/nodes/{node}/qemu/{vmid}/status/current'
        url = 'https://' + self.ip + ':' + self.port + path
        headers = {'CSRFPreventionToken': self.ticket['data']['CSRFPreventionToken'], 'Cookie': 'PVEAuthCookie=' + self.ticket['data']['ticket']}
        r = requests.get(url=url, headers=headers, verify=False)
        return r.json()

    def ticket_vm_start(self, node, vmid):
        path = f'/api2/json/nodes/{node}/qemu/{vmid}/status/start'
        url = 'https://' + self.ip + ':' + self.port + path
        headers = {'CSRFPreventionToken': self.ticket['data']['CSRFPreventionToken'], 'Cookie': 'PVEAuthCookie=' + self.ticket['data']['ticket']}
        r = requests.post(url=url, headers=headers, verify=False)
        return r.json()

    def token_vm_stop(self, node, vmid, token):
        path = f'/api2/json/nodes/{node}/qemu/{vmid}/status/stop'
        url = 'https://' + self.ip + ':' + self.port + path
        headers = {'Authorization': 'PVEAPIToken=' + token} # Authorization : PVEAPIToken=root@pam!myid=1e1b7cbd-e7553141525564c-c324519041e0
        r = requests.post(url=url, headers=headers, verify=False)
        return r.json()


if __name__ == '__main__':
    op = Pve_api(ip='192.168.0.101', username='root@pam', password='xxxxxx')
    print(op.ticket)
    # it must run 'get_ticket()' first to get a new ticket when you do the 'op' down below
    # op.get_ticket()
    # print(op.ticket)
    # print('@'*10)
    # print(op.ticket_node_list())
    # print('@'*10)
    # print(op.ticket_vm_list('pve01'))
    # print('@'*10)
    # print(op.ticket_vm_current('pve01', '104'))
    # print('@'*10)
    # print(op.ticket_vm_start('pve01', '104'))
    # print('@'*10)
    # print(op.token_vm_stop('pve01', '104', 'root@pam!myid=1e1b7cbd-e755-4171-b32c-c324519041e0'))

更多相关代码和资料在这个仓库

https://github.com/sshuangliu/Proxmox-VE-api

最后修改:2022 年 08 月 14 日
如果觉得我的文章对你有用,请随意赞赏