惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Last Week in AI
Last Week in AI
罗磊的独立博客
腾讯CDC
云风的 BLOG
云风的 BLOG
月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
宝玉的分享
宝玉的分享
U
Unit 42
I
InfoQ
D
DataBreaches.Net
Blog — PlanetScale
Blog — PlanetScale
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
V2EX
美团技术团队
IT之家
IT之家
Stack Overflow Blog
Stack Overflow Blog
F
Fortinet All Blogs
GbyAI
GbyAI
S
SegmentFault 最新的问题

Sam's lab

python 100 lines of code, bulk config of network devices (II) - Sam's lab python 100 lines of code, bulk config of network devices (II) - Sam's lab Homelab | files sharing apps — Send&Pingvin Share - Sam's lab Homelab | files sharing apps — Send&Pingvin Share - Sam's lab Homelab | Low-level design—Proxmox - Sam's lab Homelab | Low-level design—Proxmox - Sam's lab Homelab | Low-level design –Nginx Reverse Proxy - Sam's lab Homelab | Low-level design –Nginx Reverse Proxy - Sam's lab Homelab | Architecture Design and Implementation – High-level design - Sam's lab Homelab | Architecture Design and Implementation – High-level design - Sam's lab Typecho | Migrated - Sam's lab Typecho | Migrated - Sam's lab Let’s Encrypt | Wildcard Certificates - Sam's lab Let’s Encrypt | Wildcard Certificates - Sam's lab Python | Monitoring, Alarms - Sam's lab Python | Monitoring, Alarms - Sam's lab PVE | Managing Virtual Machines via rest api - Sam's lab Python ORM | peewee - Sam's lab Python ORM | peewee - Sam's lab
PVE | Managing Virtual Machines via rest api - Sam's lab
sam · 2022-08-07 · via Sam's lab

结合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

Post Views: 4,102