├── .gitignore ├── TODO ├── test └── testpyproxmox.py ├── README.md └── src └── pyproxmox.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | dist 4 | setup.py 5 | MANIFEST 6 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Add KVM POST methods - DONE 2 | - Add PUT functionality and methods. - DONE 3 | - Add DELETE functionality and methods. - DONE 4 | - Clean up commenting. - DONE 5 | - Better documentation. 6 | -------------------------------------------------------------------------------- /test/testpyproxmox.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append("../src") 3 | from pyproxmox import prox_auth,pyproxmox 4 | 5 | a = prox_auth('pnode01','apiuser@pve', 'apipasswd') 6 | 7 | b = pyproxmox(a) 8 | 9 | status = b.getClusterStatus() 10 | print(status) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pyproxmox 2 | ========= 3 | 4 | ## A Python wrapper for the Proxmox 2.x API 5 | 6 | # This project and repository is now archived and unsupported. Please think about using a different library that supports the more modern versions of proxmox. 7 | 8 | ### Installation and dependency 9 | 10 | sudo pip install pyproxmox requests 11 | 12 | ###### Example usage 13 | 14 | 1. Import everything from the module 15 | 16 | from pyproxmox import * 17 | 18 | 2. Create an instance of the prox_auth class by passing in the 19 | url or ip of a server in the cluster, username and password 20 | 21 | a = prox_auth('vnode01.example.org','apiuser@pve','examplePassword') 22 | 23 | 3. Create and instance of the pyproxmox class using the auth object as a parameter 24 | 25 | b = pyproxmox(a) 26 | 27 | 4. Run the pre defined methods of the pyproxmox class 28 | 29 | status = b.getClusterStatus() 30 | 31 | NOTE They all return data, usually in JSON format. 32 | 33 | For more information see https//github.com/Daemonthread/pyproxmox 34 | 35 | #### Methods requiring post_data 36 | 37 | These methods need to passed a correctly formatted dictionary. 38 | for example, if I was to use the createOpenvzContainer for the above example node 39 | I would need to pass the post_data with all the required variables for proxmox. 40 | 41 | post_data = {'ostemplate':'local:vztmpl/debian-6.0-standard_6.0-4_amd64.tar.gz', 42 | 'vmid':'9001','cpus':'4','description':'test container', 43 | 'disk':'10','hostname':'test.example.org','memory':'1024', 44 | 'password':'testPassword','swap':'1024'} 45 | 46 | b.createOpenvzContainer('vnode01',post_data) 47 | 48 | For more information on the accepted variables please see http//pve.proxmox.com/pve2-api-doc/ 49 | 50 | ### Current List of Methods 51 | 52 | #### GET Methods 53 | 54 | ##### Cluster Methods 55 | getClusterStatus() 56 | "Get cluster status information. Returns JSON" 57 | 58 | getClusterBackupSchedule() 59 | "List vzdump backup schedule. Returns JSON" 60 | 61 | getClusterVmNextId() 62 | "Get next VM ID of cluster. Returns JSON" 63 | 64 | ##### Node Methods 65 | getNodeNetworks(node) 66 | "List available networks. Returns JSON" 67 | 68 | getNodeInterface(node,interface) 69 | "Read network device configuration. Returns JSON" 70 | 71 | getNodeContainerIndex(node) 72 | "OpenVZ container index (per node). Returns JSON" 73 | 74 | getNodeVirtualIndex(node) 75 | "Virtual machine index (per node). Returns JSON" 76 | 77 | getNodeServiceList(node) 78 | "Service list. Returns JSON" 79 | 80 | getNodeServiceState(node,service) 81 | "Read service properties" 82 | 83 | getNodeStorage(node) 84 | "Get status for all datastores. Returns JSON" 85 | 86 | getNodeFinishedTasks(node) 87 | "Read task list for one node (finished tasks). Returns JSON" 88 | 89 | getNodeDNS(node) 90 | "Read DNS settings. Returns JSON" 91 | 92 | getNodeStatus(node) 93 | "Read node status. Returns JSON" 94 | 95 | getNodeSyslog(node) 96 | "Read system log. Returns JSON" 97 | 98 | getNodeRRD(node) 99 | "Read node RRD statistics. Returns PNG" 100 | 101 | getNodeRRDData(node) 102 | "Read node RRD statistics. Returns RRD" 103 | 104 | getNodeBeans(node) 105 | "Get user_beancounters failcnt for all active containers. Returns JSON" 106 | 107 | getNodeTaskByUPID(node,upid) 108 | "Get tasks by UPID. Returns JSON" 109 | 110 | getNodeTaskLogByUPID(node,upid) 111 | "Read task log. Returns JSON" 112 | 113 | getNodeTaskStatusByUPID(node,upid) 114 | "Read task status. Returns JSON" 115 | 116 | ##### Scan 117 | 118 | getNodeScanMethods(node) 119 | "Get index of available scan methods, Returns JSON" 120 | 121 | getRemoteiSCSI(node) 122 | "Scan remote iSCSI server." 123 | 124 | getNodeLVMGroups(node) 125 | "Scan local LVM groups" 126 | 127 | getRemoteNFS(node) 128 | "Scan remote NFS server" 129 | 130 | getNodeUSB(node) 131 | "List local USB devices" 132 | 133 | 134 | ##### OpenVZ Methods 135 | 136 | getContainerIndex(node,vmid) 137 | "Directory index. Returns JSON" 138 | 139 | getContainerStatus(node,vmid) 140 | "Get virtual machine status. Returns JSON" 141 | 142 | getContainerBeans(node,vmid) 143 | "Get container user_beancounters. Returns JSON" 144 | 145 | getContainerConfig(node,vmid) 146 | "Get container configuration. Returns JSON" 147 | 148 | getContainerInitLog(node,vmid) 149 | "Read init log. Returns JSON" 150 | 151 | getContainerRRD(node,vmid) 152 | "Read VM RRD statistics. Returns PNG" 153 | 154 | 155 | def getContainerRRDData(node,vmid) 156 | "Read VM RRD statistics. Returns RRD" 157 | 158 | ##### KVM Methods 159 | 160 | getVirtualIndex(node,vmid) 161 | "Directory index. Returns JSON" 162 | 163 | getVirtualStatus(node,vmid) 164 | "Get virtual machine status. Returns JSON" 165 | 166 | getVirtualConfig(node,vmid) 167 | "Get virtual machine configuration. Returns JSON" 168 | 169 | getVirtualRRD(node,vmid) 170 | "Read VM RRD statistics. Returns JSON" 171 | 172 | getVirtualRRDData(node,vmid) 173 | "Read VM RRD statistics. Returns JSON" 174 | 175 | ##### Storage Methods 176 | 177 | getStorageVolumeData(node,storage,volume) 178 | "Get volume attributes. Returns JSON" 179 | 180 | getStorageConfig(storage) 181 | "Read storage config. Returns JSON" 182 | 183 | getNodeStorageContent(node,storage) 184 | "List storage content. Returns JSON" 185 | 186 | getNodeStorageRRD(node,storage) 187 | "Read storage RRD statistics. Returns JSON" 188 | 189 | getNodeStorageRRDData(node,storage) 190 | "Read storage RRD statistics. Returns JSON" 191 | 192 | 193 | #### POST Methods 194 | 195 | 196 | ##### OpenVZ Methods 197 | 198 | createOpenvzContainer(node,post_data) 199 | "Create or restore a container. Returns JSON 200 | Requires a dictionary of tuples formatted [('postname1','data'),('postname2','data')]" 201 | 202 | mountOpenvzPrivate(node,vmid) 203 | "Mounts container private area. Returns JSON" 204 | 205 | shutdownOpenvzContainer(node,vmid) 206 | "Shutdown the container. Returns JSON" 207 | 208 | startOpenvzContainer(node,vmid) 209 | "Start the container. Returns JSON" 210 | 211 | stopOpenvzContainer(node,vmid) 212 | "Stop the container. Returns JSON" 213 | 214 | unmountOpenvzPrivate(node,vmid) 215 | "Unmounts container private area. Returns JSON" 216 | 217 | migrateOpenvzContainer(node,vmid,target) 218 | "Migrate the container to another node. Creates a new migration task. Returns JSON" 219 | 220 | ##### KVM Methods 221 | 222 | createVirtualMachine(node,post_data) 223 | "Create or restore a virtual machine. Returns JSON 224 | Requires a dictionary of tuples formatted [('postname1','data'),('postname2','data')]" 225 | 226 | cloneVirtualMachine(node,vmid,post_data) 227 | "Create a copy of virtual machine/template. Returns JSON 228 | Requires a dictionary of tuples formatted [('postname1','data'),('postname2','data')]" 229 | 230 | resetVirtualMachine(node,vmid) 231 | "Reset a virtual machine. Returns JSON" 232 | 233 | resumeVirtualMachine(node,vmid) 234 | "Resume a virtual machine. Returns JSON" 235 | 236 | shutdownVirtualMachine(node,vmid) 237 | "Shut down a virtual machine. Returns JSON" 238 | 239 | startVirtualMachine(node,vmid) 240 | "Start a virtual machine. Returns JSON" 241 | 242 | stopVirtualMachine(node,vmid) 243 | "Stop a virtual machine. Returns JSON" 244 | 245 | suspendVirtualMachine(node,vmid) 246 | "Suspend a virtual machine. Returns JSON" 247 | 248 | migrateVirtualMachine(node,vmid,target) 249 | "Migrate a virtual machine. Returns JSON" 250 | 251 | monitorVirtualMachine(node,vmid,command) 252 | "Send monitor command to a virtual machine. Returns JSON" 253 | 254 | vncproxyVirtualMachine(node,vmid) 255 | "Creates a VNC Proxy for a virtual machine. Returns JSON" 256 | 257 | rollbackVirtualMachine(node,vmid,snapname) 258 | "Rollback a snapshot of a virtual machine. Returns JSON" 259 | 260 | getSnapshotConfigVirtualMachine(node,vmid,snapname) 261 | "Get snapshot config of a virtual machine. Returns JSON" 262 | 263 | #### DELETE Methods 264 | 265 | ##### OPENVZ 266 | 267 | deleteOpenvzContainer(node,vmid) 268 | "Deletes the specified openvz container" 269 | 270 | ##### NODE 271 | 272 | deleteNodeNetworkConfig(node) 273 | "Revert network configuration changes." 274 | 275 | deleteNodeInterface(node,interface) 276 | "Delete network device configuration" 277 | 278 | ##### KVM 279 | 280 | deleteVirtualMachine(node,vmid) 281 | "Destroy the vm (also delete all used/owned volumes)." 282 | 283 | ##### POOLS 284 | deletePool(poolid) 285 | "Delete Pool" 286 | 287 | ##### STORAGE 288 | deleteStorageConfiguration(storageid) 289 | "Delete storage configuration" 290 | 291 | #### PUT Methods 292 | 293 | ##### NODE 294 | setNodeDNSDomain(node,domain) 295 | "Set the nodes DNS search domain" 296 | 297 | setNodeSubscriptionKey(node,key) 298 | "Set the nodes subscription key" 299 | 300 | setNodeTimeZone(node,timezone) 301 | "Set the nodes timezone" 302 | 303 | ##### OPENVZ 304 | setOpenvzContainerOptions(node,vmid,post_data) 305 | "Set openvz virtual machine options." 306 | 307 | ##### KVM 308 | setVirtualMachineOptions(node,vmide,post_data) 309 | "Set KVM virtual machine options." 310 | 311 | sendKeyEventVirtualMachine(node,vmid, key) 312 | "Send key event to virtual machine" 313 | 314 | unlinkVirtualMachineDiskImage(node,vmid, post_data) 315 | "Unlink disk images" 316 | 317 | ##### POOLS 318 | setPoolData(poolid, post_data) 319 | "Update pool data." 320 | 321 | ##### STORAGE 322 | updateStorageConfiguration(storageid,post_data) 323 | "Update storage configuration" 324 | -------------------------------------------------------------------------------- /src/pyproxmox.py: -------------------------------------------------------------------------------- 1 | """ 2 | A python wrapper for the Proxmox 2.x API. 3 | 4 | Example usage: 5 | 6 | 1) Create an instance of the prox_auth class by passing in the 7 | url or ip of a server, username and password: 8 | 9 | a = prox_auth('vnode01.example.org','apiuser@pve','examplePassword') 10 | 11 | 2) Create and instance of the pyproxmox class using the auth object as a parameter: 12 | 13 | b = pyproxmox(a) 14 | 15 | 3) Run the pre defined methods of the pyproxmox class. NOTE: they all return data, usually in JSON format: 16 | 17 | status = b.getClusterStatus('vnode01') 18 | 19 | For more information see https://github.com/Daemonthread/pyproxmox. 20 | """ 21 | import json 22 | import requests 23 | import sys 24 | 25 | # Authentication class 26 | class prox_auth: 27 | """ 28 | The authentication class, requires three strings: 29 | 30 | 1. An IP/resolvable url (minus the https://) 31 | 2. Valid username, including the @pve or @pam 32 | 3. A password 33 | 34 | Creates the required ticket and CSRF prevention token for future connections. 35 | 36 | Designed to be instanciated then passed to the new pyproxmox class as an init parameter. 37 | """ 38 | def __init__(self,url,username,password): 39 | self.url = url 40 | self.connect_data = { "username":username, "password":password } 41 | self.full_url = "https://%s:8006/api2/json/access/ticket" % (self.url) 42 | 43 | self.setup_connection() 44 | 45 | def setup_connection(self): 46 | self.ticket = "" 47 | self.CSRF = "" 48 | 49 | self.response = requests.post(self.full_url,verify=False,data=self.connect_data) 50 | result = self.response 51 | 52 | if not self.response.ok: 53 | raise AssertionError('Authentification Error: HTTP Result: \n {}'.format(self.response)) 54 | 55 | self.returned_data={'status': {'code': self.response.status_code, 'ok': self.response.ok, 'reason': self.response.reason}} 56 | self.returned_data.update(result.json()) 57 | 58 | self.ticket = {'PVEAuthCookie':self.returned_data['data']['ticket']} 59 | self.CSRF = self.returned_data['data']['CSRFPreventionToken'] 60 | 61 | # The meat and veg class 62 | class pyproxmox: 63 | """ 64 | A class that acts as a python wrapper for the Proxmox 2.x API. 65 | Requires a valid instance of the prox_auth class when initializing. 66 | 67 | GET and POST methods are currently implemented along with quite a few 68 | custom API methods. 69 | """ 70 | # INIT 71 | def __init__(self, auth_class): 72 | """Take the prox_auth instance and extract the important stuff""" 73 | self.auth_class = auth_class 74 | self.get_auth_data() 75 | 76 | def get_auth_data(self,): 77 | self.url = self.auth_class.url 78 | self.ticket = self.auth_class.ticket 79 | self.CSRF = self.auth_class.CSRF 80 | 81 | def connect(self, conn_type, option, post_data): 82 | """ 83 | The main communication method. 84 | """ 85 | self.full_url = "https://%s:8006/api2/json/%s" % (self.url,option) 86 | 87 | httpheaders = {'Accept':'application/json','Content-Type':'application/x-www-form-urlencoded'} 88 | requests.packages.urllib3.disable_warnings() 89 | if conn_type == "post": 90 | httpheaders['CSRFPreventionToken'] = str(self.CSRF) 91 | self.response = requests.post(self.full_url, verify=False, 92 | data = post_data, 93 | cookies = self.ticket, 94 | headers = httpheaders) 95 | 96 | elif conn_type == "put": 97 | httpheaders['CSRFPreventionToken'] = str(self.CSRF) 98 | self.response = requests.put(self.full_url, verify=False, 99 | data = post_data, 100 | cookies = self.ticket, 101 | headers = httpheaders) 102 | elif conn_type == "delete": 103 | httpheaders['CSRFPreventionToken'] = str(self.CSRF) 104 | self.response = requests.delete(self.full_url, verify=False, 105 | data = post_data, 106 | cookies = self.ticket, 107 | headers = httpheaders) 108 | elif conn_type == "get": 109 | self.response = requests.get (self.full_url, verify=False, 110 | cookies = self.ticket) 111 | 112 | try: 113 | self.returned_data = self.response.json() 114 | self.returned_data.update({'status':{'code':self.response.status_code,'ok':self.response.ok,'reason':self.response.reason}}) 115 | return self.returned_data 116 | except: 117 | print("Error in trying to process JSON") 118 | print(self.response) 119 | if self.response.status_code==401 and (not sys._getframe(1).f_code.co_name == sys._getframe(0).f_code.co_name): 120 | print "Unexpected error: %s : %s" % (str(sys.exc_info()[0]), str(sys.exc_info()[1])) 121 | print "try to recover connection auth" 122 | self.auth_class.setup_connection() 123 | self.get_auth_data() 124 | return self.connect(conn_type, option, post_data) 125 | 126 | 127 | """ 128 | Methods using the GET protocol to communicate with the Proxmox API. 129 | """ 130 | 131 | # Cluster Methods 132 | def getClusterStatus(self): 133 | """Get cluster status information. Returns JSON""" 134 | data = self.connect('get','cluster/status',None) 135 | return data 136 | 137 | def getClusterBackupSchedule(self): 138 | """List vzdump backup schedule. Returns JSON""" 139 | data = self.connect('get','cluster/backup',None) 140 | return data 141 | 142 | def getClusterVmNextId(self): 143 | """Get next VM ID of cluster. Returns JSON""" 144 | data = self.connect('get','cluster/nextid',None) 145 | return data 146 | 147 | def getClusterNodeList(self): 148 | """Node list. Returns JSON""" 149 | data = self.connect('get','nodes/',None) 150 | return data 151 | 152 | def getClusterLog(self): 153 | """log from Cluster""" 154 | data = self.connect('get','cluster/log',None) 155 | return data 156 | 157 | # Node Methods 158 | def getNodeNetworks(self,node): 159 | """List available networks. Returns JSON""" 160 | data = self.connect('get','nodes/%s/network' % (node),None) 161 | return data 162 | 163 | def getNodeInterface(self,node,interface): 164 | """Read network device configuration. Returns JSON""" 165 | data = self.connect('get','nodes/%s/network/%s' % (node,interface),None) 166 | return data 167 | 168 | def getNodeContainerIndex(self,node): 169 | """OpenVZ container index (per node). Returns JSON""" 170 | data = self.connect('get','nodes/%s/openvz' % (node),None) 171 | return data 172 | 173 | def getNodeVirtualIndex(self,node): 174 | """Virtual machine index (per node). Returns JSON""" 175 | data = self.connect('get','nodes/%s/qemu' % (node),None) 176 | return data 177 | 178 | def getNodeServiceList(self,node): 179 | """Service list. Returns JSON""" 180 | data = self.connect('get','nodes/%s/services' % (node),None) 181 | return data 182 | 183 | def getNodeServiceState(self,node,service): 184 | """Read service properties""" 185 | data = self.connect('get','nodes/%s/services/%s/state' % (node,service),None) 186 | return data 187 | 188 | def getNodeStorage(self,node): 189 | """Get status for all datastores. Returns JSON""" 190 | data = self.connect('get','nodes/%s/storage' % (node),None) 191 | return data 192 | 193 | def getNodeFinishedTasks(self,node): 194 | """Read task list for one node (finished tasks). Returns JSON""" 195 | data = self.connect('get','nodes/%s/tasks' % (node),None) 196 | return data 197 | 198 | def getNodeDNS(self,node): 199 | """Read DNS settings. Returns JSON""" 200 | data = self.connect('get','nodes/%s/dns' % (node),None) 201 | return data 202 | 203 | def getNodeStatus(self,node): 204 | """Read node status. Returns JSON""" 205 | data = self.connect('get','nodes/%s/status' % (node),None) 206 | return data 207 | 208 | def getNodeSyslog(self,node): 209 | """Read system log. Returns JSON""" 210 | data = self.connect('get','nodes/%s/syslog' % (node),None) 211 | return data 212 | 213 | def getNodeRRD(self,node): 214 | """Read node RRD statistics. Returns PNG""" 215 | data = self.connect('get','nodes/%s/rrd' % (node),None) 216 | return data 217 | 218 | def getNodeRRDData(self,node): 219 | """Read node RRD statistics. Returns RRD""" 220 | data = self.connect('get','nodes/%s/rrddata' % (node),None) 221 | return data 222 | 223 | def getNodeBeans(self,node): 224 | """Get user_beancounters failcnt for all active containers. Returns JSON""" 225 | data = self.connect('get','nodes/%s/ubfailcnt' % (node),None) 226 | return data 227 | 228 | def getNodeTaskByUPID(self,node,upid): 229 | """Get tasks by UPID. Returns JSON""" 230 | data = self.connect('get','nodes/%s/tasks/%s' % (node,upid),None) 231 | return data 232 | 233 | def getNodeTaskLogByUPID(self,node,upid): 234 | """Read task log. Returns JSON""" 235 | data = self.connect('get','nodes/%s/tasks/%s/log' % (node,upid),None) 236 | return data 237 | 238 | def getNodeTaskStatusByUPID(self,node,upid): 239 | """Read task status. Returns JSON""" 240 | data = self.connect('get','nodes/%s/tasks/%s/status' % (node,upid),None) 241 | return data 242 | 243 | # Scan 244 | 245 | def getNodeScanMethods(self,node): 246 | """Get index of available scan methods""" 247 | data = self.connect('get','nodes/%s/scan' % (node),None) 248 | return data 249 | 250 | def getRemoteiSCSI(self,node): 251 | """Scan remote iSCSI server.""" 252 | data = self.connect('get','nodes/%s/scan/iscsi' % (node),None) 253 | return data 254 | 255 | def getNodeLVMGroups(self,node): 256 | """Scan local LVM groups""" 257 | data = self.connect('get','nodes/%s/scan/lvm' % (node),None) 258 | return data 259 | 260 | def getRemoteNFS(self,node): 261 | """Scan remote NFS server""" 262 | data = self.connect('get','nodes/%s/scan/nfs' % (node),None) 263 | return data 264 | 265 | def getNodeUSB(self,node): 266 | """List local USB devices""" 267 | data = self.connect('get','nodes/%s/scan/usb' % (node),None) 268 | return data 269 | 270 | # Access 271 | 272 | def getClusterACL(self): 273 | """log from Cluster""" 274 | data = self.connect('get','access/acl',None) 275 | return data 276 | 277 | 278 | # OpenVZ Methods 279 | 280 | def getContainerIndex(self,node,vmid): 281 | """Directory index. Returns JSON""" 282 | data = self.connect('get','nodes/%s/openvz/%s' % (node,vmid),None) 283 | return data 284 | 285 | def getContainerStatus(self,node,vmid): 286 | """Get virtual machine status. Returns JSON""" 287 | data = self.connect('get','nodes/%s/openvz/%s/status/current' % (node,vmid),None) 288 | return data 289 | 290 | def getContainerBeans(self,node,vmid): 291 | """Get container user_beancounters. Returns JSON""" 292 | data = self.connect('get','nodes/%s/openvz/%s/status/ubc' % (node,vmid),None) 293 | return data 294 | 295 | def getContainerConfig(self,node,vmid): 296 | """Get container configuration. Returns JSON""" 297 | data = self.connect('get','nodes/%s/openvz/%s/config' % (node,vmid),None) 298 | return data 299 | 300 | def getContainerInitLog(self,node,vmid): 301 | """Read init log. Returns JSON""" 302 | data = self.connect('get','nodes/%s/openvz/%s/initlog' % (node,vmid),None) 303 | return data 304 | 305 | def getContainerRRD(self,node,vmid): 306 | """Read VM RRD statistics. Returns PNG""" 307 | data = self.connect('get','nodes/%s/openvz/%s/rrd' % (node,vmid),None) 308 | return data 309 | 310 | def getContainerRRDData(self,node,vmid): 311 | """Read VM RRD statistics. Returns RRD""" 312 | data = self.connect('get','nodes/%s/openvz/%s/rrddata' % (node,vmid),None) 313 | return data 314 | 315 | # KVM Methods 316 | 317 | def getVirtualIndex(self,node,vmid): 318 | """Directory index. Returns JSON""" 319 | data = self.connect('get','nodes/%s/qemu/%s' % (node,vmid),None) 320 | return data 321 | 322 | def getVirtualStatus(self,node,vmid): 323 | """Get virtual machine status. Returns JSON""" 324 | data = self.connect('get','nodes/%s/qemu/%s/status/current' % (node,vmid),None) 325 | return data 326 | 327 | def getVirtualConfig(self,node,vmid,current=False): 328 | """Get virtual machine configuration. Returns JSON""" 329 | if current: 330 | data = self.connect('get','nodes/%s/qemu/%s/config' % (node,vmid),None) 331 | else: 332 | data = self.connect('get','nodes/%s/qemu/%s/config' % (node,vmid),current) 333 | return data 334 | 335 | def getVirtualRRD(self,node,vmid): 336 | """Read VM RRD statistics. Returns JSON""" 337 | data = self.connect('get','nodes/%s/qemu/%s/rrd' % (node,vmid),None) 338 | return data 339 | 340 | def getVirtualRRDData(self,node,vmid): 341 | """Read VM RRD statistics. Returns JSON""" 342 | data = self.connect('get','nodes/%s/qemu/%s/rrddata' % (node,vmid),None) 343 | return data 344 | 345 | # Storage Methods 346 | 347 | def getStorageVolumeData(self,node,storage,volume): 348 | """Get volume attributes. Returns JSON""" 349 | data = self.connect('get','nodes/%s/storage/%s/content/%s' % (node,storage,volume),None) 350 | return data 351 | 352 | def getStorageConfig(self,storage): 353 | """Read storage config. Returns JSON""" 354 | data = self.connect('get','storage/%s' % (storage),None) 355 | return data 356 | 357 | def getNodeStorageContent(self,node,storage): 358 | """List storage content. Returns JSON""" 359 | data = self.connect('get','nodes/%s/storage/%s/content' % (node,storage),None) 360 | return data 361 | 362 | def getNodeStorageRRD(self,node,storage): 363 | """Read storage RRD statistics. Returns JSON""" 364 | data = self.connect('get','nodes/%s/storage/%s/rrd' % (node,storage),None) 365 | return data 366 | 367 | def getNodeStorageRRDData(self,node,storage): 368 | """Read storage RRD statistics. Returns JSON""" 369 | data = self.connect('get','nodes/%s/storage/%s/rrddata' % (node,storage),None) 370 | return data 371 | 372 | """ 373 | Methods using the POST protocol to communicate with the Proxmox API. 374 | """ 375 | 376 | # OpenVZ Methods 377 | 378 | def createOpenvzContainer(self,node,post_data): 379 | """ 380 | Create or restore a container. Returns JSON 381 | Requires a dictionary of tuples formatted [('postname1','data'),('postname2','data')] 382 | """ 383 | data = self.connect('post','nodes/%s/openvz' % (node), post_data) 384 | return data 385 | 386 | def mountOpenvzPrivate(self,node,vmid): 387 | """Mounts container private area. Returns JSON""" 388 | post_data = None 389 | data = self.connect('post','nodes/%s/openvz/%s/status/mount' % (node,vmid), post_data) 390 | return data 391 | 392 | def shutdownOpenvzContainer(self,node,vmid): 393 | """Shutdown the container. Returns JSON""" 394 | post_data = None 395 | data = self.connect('post','nodes/%s/openvz/%s/status/shutdown' % (node,vmid), post_data) 396 | return data 397 | 398 | def startOpenvzContainer(self,node,vmid): 399 | """Start the container. Returns JSON""" 400 | post_data = None 401 | data = self.connect('post','nodes/%s/openvz/%s/status/start' % (node,vmid), post_data) 402 | return data 403 | 404 | def stopOpenvzContainer(self,node,vmid): 405 | """Stop the container. Returns JSON""" 406 | post_data = None 407 | data = self.connect('post','nodes/%s/openvz/%s/status/stop' % (node,vmid), post_data) 408 | return data 409 | 410 | def unmountOpenvzPrivate(self,node,vmid): 411 | """Unmounts container private area. Returns JSON""" 412 | post_data = None 413 | data = self.connect('post','nodes/%s/openvz/%s/status/unmount' % (node,vmid), post_data) 414 | return data 415 | 416 | def migrateOpenvzContainer(self,node,vmid,target): 417 | """Migrate the container to another node. Creates a new migration task. Returns JSON""" 418 | post_data = {'target': str(target)} 419 | data = self.connect('post','nodes/%s/openvz/%s/migrate' % (node,vmid), post_data) 420 | return data 421 | 422 | # KVM Methods 423 | 424 | def createVirtualMachine(self,node,post_data): 425 | """ 426 | Create or restore a virtual machine. Returns JSON 427 | Requires a dictionary of tuples formatted [('postname1','data'),('postname2','data')] 428 | """ 429 | data = self.connect('post',"nodes/%s/qemu" % (node), post_data) 430 | return data 431 | 432 | def cloneVirtualMachine(self,node,vmid,post_data): 433 | """ 434 | Create a copy of virtual machine/template 435 | Requires a dictionary of tuples formatted [('postname1','data'),('postname2','data')] 436 | """ 437 | data = self.connect('post',"nodes/%s/qemu/%s/clone" % (node,vmid), post_data) 438 | return data 439 | 440 | def resetVirtualMachine(self,node,vmid): 441 | """Reset a virtual machine. Returns JSON""" 442 | post_data = None 443 | data = self.connect('post',"nodes/%s/qemu/%s/status/reset" % (node,vmid), post_data) 444 | return data 445 | 446 | def resumeVirtualMachine(self,node,vmid): 447 | """Resume a virtual machine. Returns JSON""" 448 | post_data = None 449 | data = self.connect('post',"nodes/%s/qemu/%s/status/resume" % (node,vmid), post_data) 450 | return data 451 | 452 | def shutdownVirtualMachine(self,node,vmid): 453 | """Shut down a virtual machine. Returns JSON""" 454 | post_data = None 455 | data = self.connect('post',"nodes/%s/qemu/%s/status/shutdown" % (node,vmid), post_data) 456 | return data 457 | 458 | def startVirtualMachine(self,node,vmid): 459 | """Start a virtual machine. Returns JSON 460 | :param node: node name 461 | :param vmid: vm id (e.g. 167) 462 | :type node: str 463 | :type vmid: int 464 | :return: { 'status': { 'code': http returncode, 'reason': http return string, 'ok': return status } 465 | 'data': { 'task String (UPID)'} 466 | :rtype dict 467 | """ 468 | post_data = None 469 | data = self.connect('post',"nodes/%s/qemu/%s/status/start" % (node,vmid), post_data) 470 | return data 471 | 472 | def stopVirtualMachine(self,node,vmid): 473 | """Stop a virtual machine. Returns JSON""" 474 | post_data = None 475 | data = self.connect('post',"nodes/%s/qemu/%s/status/stop" % (node,vmid), post_data) 476 | return data 477 | 478 | def suspendVirtualMachine(self,node,vmid): 479 | """Suspend a virtual machine. Returns JSON""" 480 | post_data = None 481 | data = self.connect('post',"nodes/%s/qemu/%s/status/suspend" % (node,vmid), post_data) 482 | return data 483 | 484 | def migrateVirtualMachine(self,node,vmid,target,online=False,force=False): 485 | """Migrate a virtual machine. Returns JSON""" 486 | post_data = {'target': str(target)} 487 | if online: 488 | post_data['online'] = '1' 489 | if force: 490 | post_data['force'] = '1' 491 | data = self.connect('post',"nodes/%s/qemu/%s/migrate" % (node,vmid), post_data) 492 | return data 493 | 494 | def monitorVirtualMachine(self,node,vmid,command): 495 | """Send monitor command to a virtual machine. Returns JSON""" 496 | post_data = {'command': str(command)} 497 | data = self.connect('post',"nodes/%s/qemu/%s/monitor" % (node,vmid), post_data) 498 | return data 499 | 500 | def vncproxyVirtualMachine(self,node,vmid): 501 | """Creates a VNC Proxy for a virtual machine. Returns JSON""" 502 | post_data = None 503 | data = self.connect('post',"nodes/%s/qemu/%s/vncproxy" % (node,vmid), post_data) 504 | return data 505 | 506 | def rollbackVirtualMachine(self,node,vmid,snapname): 507 | """Rollback a snapshot of a virtual machine. Returns JSON""" 508 | post_data = None 509 | data = self.connect('post',"nodes/%s/qemu/%s/snapshot/%s/rollback" % (node,vmid,snapname), post_data) 510 | return data 511 | 512 | def getSnapshotConfigVirtualMachine(self,node,vmid,snapname): 513 | """Get snapshot config of a virtual machine. Returns JSON""" 514 | post_data = None 515 | data = self.connect('get',"nodes/%s/qemu/%s/snapshot/%s/config" % (node,vmid,snapname), post_data) 516 | return data 517 | 518 | def getSnapshotsVirtualMachine(self,node,vmid): 519 | """Get list of snapshots a virtual machine. Returns JSON""" 520 | post_data = None 521 | data = self.connect('get',"nodes/%s/qemu/%s/snapshot" % (node,vmid), post_data) 522 | if type(data['data']) is list: 523 | try: 524 | #data['data'].remove([s for s in data['data'] if s['name']=='current']) 525 | for s in data['data'][:]: 526 | if s['name']=='current': 527 | data['data'].remove(s) 528 | except : 529 | import sys 530 | print("Unexpected error:", sys.exc_info()[0]) 531 | 532 | return data 533 | 534 | def createSnapshotVirtualMachine(self,node,vmid,snapname,description='',vmstate=False): 535 | """ 536 | create Snapshot from VM 537 | :param node: name of the node 538 | :param vmid: id of the vm 539 | :param snapname: title of the snapshot 540 | :param description: snapshot description 541 | :param vmstate: set if vmstatus should be saved too (useful for running vms) 542 | :return: dictionary with rest result and returned data 543 | """ 544 | if vmstate==True: vmstate=0 545 | else: vmstate=1 546 | post_data = { 'snapname': snapname ,'description':description,'vmstate':vmstate} 547 | data = self.connect('post',"nodes/%s/qemu/%s/snapshot" % (node,vmid), post_data) 548 | return data 549 | 550 | """ 551 | Methods using the DELETE protocol to communicate with the Proxmox API. 552 | """ 553 | 554 | # OPENVZ 555 | 556 | def deleteOpenvzContainer(self,node,vmid): 557 | """Deletes the specified openvz container""" 558 | data = self.connect('delete',"nodes/%s/openvz/%s" % (node,vmid),None) 559 | return data 560 | 561 | # NODE 562 | 563 | def deleteNodeNetworkConfig(self,node): 564 | """Revert network configuration changes.""" 565 | data = self.connect('delete',"nodes/%s/network" % (node),None) 566 | return data 567 | 568 | def deleteNodeInterface(self,node,interface): 569 | """Delete network device configuration""" 570 | data = self.connect('delete',"nodes/%s/network/%s" % (node,interface),None) 571 | return data 572 | 573 | #KVM 574 | 575 | def deleteVirtualMachine(self,node,vmid): 576 | """Destroy the vm (also delete all used/owned volumes).""" 577 | data = self.connect('delete',"nodes/%s/qemu/%s" % (node,vmid),None) 578 | return data 579 | 580 | def deleteSnapshotVirtualMachine(self,node,vmid,title,force=False): 581 | """Destroy the vm snapshot (also delete all used/owned volumes). 582 | :param force: (Boolean) For removal from config file, even if removing disk snapshots fails. """ 583 | post_data=None 584 | if force: 585 | post_data={} 586 | post_data['force'] = '1' 587 | data = self.connect('delete',"nodes/%s/qemu/%s/snapshot/%s" % (node,vmid,title),post_data) 588 | return data 589 | 590 | # POOLS 591 | def deletePool(self,poolid): 592 | """Delete Pool""" 593 | data = self.connect('delete',"pools/%s" % (poolid),None) 594 | return data 595 | 596 | # STORAGE 597 | def deleteStorageConfiguration(self,storageid): 598 | """Delete storage configuration""" 599 | data = self.connect('delete',"storage/%s" % (storageid),None) 600 | return data 601 | 602 | """ 603 | Methods using the PUT protocol to communicate with the Proxmox API. 604 | """ 605 | 606 | # NODE 607 | def setNodeDNSDomain(self,node,domain): 608 | """Set the nodes DNS search domain""" 609 | post_data = {'search': str(domain)} 610 | data = self.connect('put',"nodes/%s/dns" % (node), post_data) 611 | return data 612 | 613 | def setNodeSubscriptionKey(self,node,key): 614 | """Set the nodes subscription key""" 615 | post_data = {'key': str(key)} 616 | data = self.connect('put',"nodes/%s/subscription" % (node), post_data) 617 | return data 618 | 619 | def setNodeTimeZone(self,node,timezone): 620 | """Set the nodes timezone""" 621 | post_data = {'timezone': str(timezone)} 622 | data = self.connect('put',"nodes/%s/time" % (node), post_data) 623 | return data 624 | 625 | # OPENVZ 626 | def setOpenvzContainerOptions(self,node,vmid,post_data): 627 | """Set openvz virtual machine options.""" 628 | data = self.connect('put',"nodes/%s/openvz/%s/config" % (node,vmid), post_data) 629 | return data 630 | 631 | # KVM 632 | def setVirtualMachineOptions(self,node,vmid,post_data): 633 | """Set KVM virtual machine options.""" 634 | data = self.connect('put',"nodes/%s/qemu/%s/config" % (node,vmid), post_data) 635 | return data 636 | 637 | def sendKeyEventVirtualMachine(self,node,vmid, key): 638 | """Send key event to virtual machine""" 639 | post_data = {'key': str(key)} 640 | data = self.connect('put',"nodes/%s/qemu/%s/sendkey" % (node,vmid), post_data) 641 | return data 642 | 643 | def unlinkVirtualMachineDiskImage(self,node,vmid, post_data): 644 | """Unlink disk images""" 645 | data = self.connect('put',"nodes/%s/qemu/%s/unlink" % (node,vmid), post_data) 646 | return data 647 | 648 | # POOLS 649 | def setPoolData(self,poolid, post_data): 650 | """Update pool data.""" 651 | data = self.connect('put',"pools/%s" (poolid), post_data) 652 | return data 653 | 654 | # STORAGE 655 | def updateStorageConfiguration(self,storageid,post_data): 656 | """Update storage configuration""" 657 | data = self.connect('put',"storage/%s" % (storageid), post_data) 658 | return data 659 | 660 | 661 | --------------------------------------------------------------------------------