├── .gitignore ├── .vscode └── launch.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── README.md ├── api ├── LICENSE ├── README.md ├── changelog.md ├── cjs │ └── package.json ├── esm │ └── package.json ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── src │ ├── ProxmoxEngine.ts │ ├── QmMonitor.ts │ ├── constructor.ts │ ├── index.ts │ ├── main.test.ts │ ├── model.ts │ └── proxy.ts ├── tsconfig-cjs.json └── tsconfig.json ├── docs ├── .nojekyll ├── assets │ ├── highlight.css │ ├── style.css │ ├── widgets.png │ └── widgets@2x.png ├── classes │ ├── ProxmoxEngine.html │ └── QmMonitor.html ├── functions │ └── proxmoxApi.html ├── index.html ├── interfaces │ ├── ApiRequestable.html │ ├── Proxmox.Api.html │ ├── Proxmox.accessAclReadAcl.html │ ├── Proxmox.accessDomainsIndex.html │ ├── Proxmox.accessGroupsIndex.html │ ├── Proxmox.accessGroupsReadGroup.html │ ├── Proxmox.accessIndex.html │ ├── Proxmox.accessOpenidIndex.html │ ├── Proxmox.accessOpenidLoginLogin.html │ ├── Proxmox.accessRolesIndex.html │ ├── Proxmox.accessRolesReadRole.html │ ├── Proxmox.accessTfaAddTfaEntry.html │ ├── Proxmox.accessTfaGetTfaEntry.html │ ├── Proxmox.accessTfaListTfa.html │ ├── Proxmox.accessTfaListUserTfa.html │ ├── Proxmox.accessTfaVerifyTfa.html │ ├── Proxmox.accessTicketCreateTicket.html │ ├── Proxmox.accessUsersIndex.html │ ├── Proxmox.accessUsersReadUser.html │ ├── Proxmox.accessUsersTfaReadUserTfaType.html │ ├── Proxmox.accessUsersTokenGenerateToken.html │ ├── Proxmox.accessUsersTokenReadToken.html │ ├── Proxmox.accessUsersTokenTokenIndex.html │ ├── Proxmox.accessUsersTokenUpdateTokenInfo.html │ ├── Proxmox.clusterAcmeAccountAccountIndex.html │ ├── Proxmox.clusterAcmeAccountGetAccount.html │ ├── Proxmox.clusterAcmeChallengeSchemaChallengeschema.html │ ├── Proxmox.clusterAcmeDirectoriesGetDirectories.html │ ├── Proxmox.clusterAcmeIndex.html │ ├── Proxmox.clusterAcmePluginsIndex.html │ ├── Proxmox.clusterBackupIncludedVolumesGetVolumeBackupIncluded.html │ ├── Proxmox.clusterBackupIndex.html │ ├── Proxmox.clusterBackupInfoIndex.html │ ├── Proxmox.clusterBackupInfoNotBackedUpGetGuestsNotInBackup.html │ ├── Proxmox.clusterCephCephindex.html │ ├── Proxmox.clusterCephFlagsGetAllFlags.html │ ├── Proxmox.clusterConfigIndex.html │ ├── Proxmox.clusterConfigJoinJoinInfo.html │ ├── Proxmox.clusterConfigNodesAddnode.html │ ├── Proxmox.clusterConfigNodesNodes.html │ ├── Proxmox.clusterFirewallAliasesGetAliases.html │ ├── Proxmox.clusterFirewallGroupsGetRule.html │ ├── Proxmox.clusterFirewallGroupsGetRules.html │ ├── Proxmox.clusterFirewallGroupsListSecurityGroups.html │ ├── Proxmox.clusterFirewallIndex.html │ ├── Proxmox.clusterFirewallIpsetGetIpset.html │ ├── Proxmox.clusterFirewallIpsetIpsetIndex.html │ ├── Proxmox.clusterFirewallMacrosGetMacros.html │ ├── Proxmox.clusterFirewallOptionsGetOptions.html │ ├── Proxmox.clusterFirewallRefsRefs.html │ ├── Proxmox.clusterFirewallRulesGetRule.html │ ├── Proxmox.clusterFirewallRulesGetRules.html │ ├── Proxmox.clusterHaGroupsIndex.html │ ├── Proxmox.clusterHaIndex.html │ ├── Proxmox.clusterHaResourcesIndex.html │ ├── Proxmox.clusterHaResourcesRead.html │ ├── Proxmox.clusterHaStatusIndex.html │ ├── Proxmox.clusterIndex.html │ ├── Proxmox.clusterJobsIndex.html │ ├── Proxmox.clusterJobsScheduleAnalyzeScheduleAnalyze.html │ ├── Proxmox.clusterLogLog.html │ ├── Proxmox.clusterMetricsIndex.html │ ├── Proxmox.clusterMetricsServerServerIndex.html │ ├── Proxmox.clusterReplicationIndex.html │ ├── Proxmox.clusterResourcesResources.html │ ├── Proxmox.clusterSdnControllersIndex.html │ ├── Proxmox.clusterSdnDnsIndex.html │ ├── Proxmox.clusterSdnIndex.html │ ├── Proxmox.clusterSdnIpamsIndex.html │ ├── Proxmox.clusterSdnVnetsIndex.html │ ├── Proxmox.clusterSdnVnetsSubnetsIndex.html │ ├── Proxmox.clusterSdnZonesIndex.html │ ├── Proxmox.clusterStatusGetStatus.html │ ├── Proxmox.clusterTasksTasks.html │ ├── Proxmox.nodesAplinfoAplinfo.html │ ├── Proxmox.nodesAptIndex.html │ ├── Proxmox.nodesAptRepositoriesRepositories.html │ ├── Proxmox.nodesAptUpdateListUpdates.html │ ├── Proxmox.nodesAptVersionsVersions.html │ ├── Proxmox.nodesCapabilitiesIndex.html │ ├── Proxmox.nodesCapabilitiesQemuCpuIndex.html │ ├── Proxmox.nodesCapabilitiesQemuMachinesTypes.html │ ├── Proxmox.nodesCapabilitiesQemuQemuCapsIndex.html │ ├── Proxmox.nodesCephConfigdbConfigdb.html │ ├── Proxmox.nodesCephFsIndex.html │ ├── Proxmox.nodesCephIndex.html │ ├── Proxmox.nodesCephLogLog.html │ ├── Proxmox.nodesCephMdsIndex.html │ ├── Proxmox.nodesCephMgrIndex.html │ ├── Proxmox.nodesCephMonListmon.html │ ├── Proxmox.nodesCephPoolsGetpool.html │ ├── Proxmox.nodesCephPoolsLspools.html │ ├── Proxmox.nodesCephRulesRules.html │ ├── Proxmox.nodesCertificatesAcmeIndex.html │ ├── Proxmox.nodesCertificatesCustomUploadCustomCert.html │ ├── Proxmox.nodesCertificatesIndex.html │ ├── Proxmox.nodesCertificatesInfoInfo.html │ ├── Proxmox.nodesDisksDirectoryIndex.html │ ├── Proxmox.nodesDisksIndex.html │ ├── Proxmox.nodesDisksListList.html │ ├── Proxmox.nodesDisksLvmIndex.html │ ├── Proxmox.nodesDisksLvmthinIndex.html │ ├── Proxmox.nodesDisksSmartSmart.html │ ├── Proxmox.nodesDisksZfsDetail.html │ ├── Proxmox.nodesDisksZfsIndex.html │ ├── Proxmox.nodesDnsDns.html │ ├── Proxmox.nodesFirewallIndex.html │ ├── Proxmox.nodesFirewallLogLog.html │ ├── Proxmox.nodesFirewallOptionsGetOptions.html │ ├── Proxmox.nodesFirewallRulesGetRule.html │ ├── Proxmox.nodesFirewallRulesGetRules.html │ ├── Proxmox.nodesHardwareIndex.html │ ├── Proxmox.nodesHardwarePciMdevMdevscan.html │ ├── Proxmox.nodesHardwarePciPciindex.html │ ├── Proxmox.nodesHardwarePciPciscan.html │ ├── Proxmox.nodesHardwareUsbUsbscan.html │ ├── Proxmox.nodesHostsGetEtcHosts.html │ ├── Proxmox.nodesIndex.html │ ├── Proxmox.nodesIndex2.html │ ├── Proxmox.nodesLxcConfigVmConfig.html │ ├── Proxmox.nodesLxcFeatureVmFeature.html │ ├── Proxmox.nodesLxcFirewallAliasesGetAliases.html │ ├── Proxmox.nodesLxcFirewallIndex.html │ ├── Proxmox.nodesLxcFirewallIpsetGetIpset.html │ ├── Proxmox.nodesLxcFirewallIpsetIpsetIndex.html │ ├── Proxmox.nodesLxcFirewallLogLog.html │ ├── Proxmox.nodesLxcFirewallOptionsGetOptions.html │ ├── Proxmox.nodesLxcFirewallRefsRefs.html │ ├── Proxmox.nodesLxcFirewallRulesGetRule.html │ ├── Proxmox.nodesLxcFirewallRulesGetRules.html │ ├── Proxmox.nodesLxcPendingVmPending.html │ ├── Proxmox.nodesLxcRrdRrd.html │ ├── Proxmox.nodesLxcRrddataRrddata.html │ ├── Proxmox.nodesLxcSnapshotList.html │ ├── Proxmox.nodesLxcSnapshotSnapshotCmdIdx.html │ ├── Proxmox.nodesLxcSpiceproxySpiceproxy.html │ ├── Proxmox.nodesLxcStatusCurrentVmStatus.html │ ├── Proxmox.nodesLxcStatusVmcmdidx.html │ ├── Proxmox.nodesLxcTermproxyTermproxy.html │ ├── Proxmox.nodesLxcVm.html │ ├── Proxmox.nodesLxcVmdiridx.html │ ├── Proxmox.nodesLxcVncproxyVncproxy.html │ ├── Proxmox.nodesLxcVncwebsocketVncwebsocket.html │ ├── Proxmox.nodesNetstatNetstat.html │ ├── Proxmox.nodesNetworkIndex.html │ ├── Proxmox.nodesNetworkNetworkConfig.html │ ├── Proxmox.nodesQemuAgentExecExec.html │ ├── Proxmox.nodesQemuAgentExecStatusExecStatus.html │ ├── Proxmox.nodesQemuAgentFileReadFileRead.html │ ├── Proxmox.nodesQemuAgentIndex.html │ ├── Proxmox.nodesQemuConfigVmConfig.html │ ├── Proxmox.nodesQemuFeatureVmFeature.html │ ├── Proxmox.nodesQemuFirewallAliasesGetAliases.html │ ├── Proxmox.nodesQemuFirewallIndex.html │ ├── Proxmox.nodesQemuFirewallIpsetGetIpset.html │ ├── Proxmox.nodesQemuFirewallIpsetIpsetIndex.html │ ├── Proxmox.nodesQemuFirewallLogLog.html │ ├── Proxmox.nodesQemuFirewallOptionsGetOptions.html │ ├── Proxmox.nodesQemuFirewallRefsRefs.html │ ├── Proxmox.nodesQemuFirewallRulesGetRule.html │ ├── Proxmox.nodesQemuFirewallRulesGetRules.html │ ├── Proxmox.nodesQemuMigrateMigrateVmPrecondition.html │ ├── Proxmox.nodesQemuPendingVmPending.html │ ├── Proxmox.nodesQemuRrdRrd.html │ ├── Proxmox.nodesQemuRrddataRrddata.html │ ├── Proxmox.nodesQemuSnapshotSnapshotCmdIdx.html │ ├── Proxmox.nodesQemuSnapshotSnapshotList.html │ ├── Proxmox.nodesQemuSpiceproxySpiceproxy.html │ ├── Proxmox.nodesQemuStatusCurrentVmStatus.html │ ├── Proxmox.nodesQemuStatusVmcmdidx.html │ ├── Proxmox.nodesQemuTermproxyTermproxy.html │ ├── Proxmox.nodesQemuVm.html │ ├── Proxmox.nodesQemuVmdiridx.html │ ├── Proxmox.nodesQemuVncproxyVncproxy.html │ ├── Proxmox.nodesQemuVncwebsocketVncwebsocket.html │ ├── Proxmox.nodesQueryUrlMetadataQueryUrlMetadata.html │ ├── Proxmox.nodesReplicationIndex.html │ ├── Proxmox.nodesReplicationLogReadJobLog.html │ ├── Proxmox.nodesReplicationStatus.html │ ├── Proxmox.nodesRrdRrd.html │ ├── Proxmox.nodesRrddataRrddata.html │ ├── Proxmox.nodesScanCifsCifsscan.html │ ├── Proxmox.nodesScanGlusterfsGlusterfsscan.html │ ├── Proxmox.nodesScanIndex.html │ ├── Proxmox.nodesScanIscsiIscsiscan.html │ ├── Proxmox.nodesScanLvmLvmscan.html │ ├── Proxmox.nodesScanLvmthinLvmthinscan.html │ ├── Proxmox.nodesScanNfsNfsscan.html │ ├── Proxmox.nodesScanPbsPbsscan.html │ ├── Proxmox.nodesScanZfsZfsscan.html │ ├── Proxmox.nodesSdnSdnindex.html │ ├── Proxmox.nodesSdnZonesContentIndex.html │ ├── Proxmox.nodesSdnZonesDiridx.html │ ├── Proxmox.nodesSdnZonesIndex.html │ ├── Proxmox.nodesServicesIndex.html │ ├── Proxmox.nodesServicesSrvcmdidx.html │ ├── Proxmox.nodesSpiceshellSpiceshell.html │ ├── Proxmox.nodesStorageContentIndex.html │ ├── Proxmox.nodesStorageContentInfo.html │ ├── Proxmox.nodesStorageDiridx.html │ ├── Proxmox.nodesStorageFileRestoreListList.html │ ├── Proxmox.nodesStorageIndex.html │ ├── Proxmox.nodesStoragePrunebackupsDryrun.html │ ├── Proxmox.nodesStorageRrdRrd.html │ ├── Proxmox.nodesStorageRrddataRrddata.html │ ├── Proxmox.nodesSyslogSyslog.html │ ├── Proxmox.nodesTasksLogReadTaskLog.html │ ├── Proxmox.nodesTasksNodeTasks.html │ ├── Proxmox.nodesTasksStatusReadTaskStatus.html │ ├── Proxmox.nodesTasksUpidIndex.html │ ├── Proxmox.nodesTermproxyTermproxy.html │ ├── Proxmox.nodesTimeTime.html │ ├── Proxmox.nodesVersionVersion.html │ ├── Proxmox.nodesVncshellVncshell.html │ ├── Proxmox.nodesVncwebsocketVncwebsocket.html │ ├── Proxmox.nodesVzdumpDefaultsDefaults.html │ ├── Proxmox.poolsIndex.html │ ├── Proxmox.poolsReadPool.html │ ├── Proxmox.storageCreate.html │ ├── Proxmox.storageIndex.html │ ├── Proxmox.storageUpdate.html │ ├── Proxmox.versionVersion.html │ ├── ProxmoxEngineOptionsCommon.html │ ├── ProxmoxEngineOptionsPass.html │ ├── ProxmoxEngineOptionsToken.html │ ├── USBHostInfo.html │ └── USBInfo.html ├── modules.html ├── modules │ └── Proxmox.html └── types │ ├── Proxmox.CIDR.html │ ├── Proxmox.CIDR_1.html │ ├── Proxmox.CIDRv4.html │ ├── Proxmox.CIDRv6.html │ ├── Proxmox.IPorCIDR.html │ ├── Proxmox.IPorCIDRorAlias.html │ ├── Proxmox.String0_1024.html │ ├── Proxmox.String0_128.html │ ├── Proxmox.String0_2048.html │ ├── Proxmox.String0_255.html │ ├── Proxmox.String0_256.html │ ├── Proxmox.String0_40.html │ ├── Proxmox.String0_4096.html │ ├── Proxmox.String0_512.html │ ├── Proxmox.String0_61440.html │ ├── Proxmox.String0_64.html │ ├── Proxmox.String0_65536.html │ ├── Proxmox.String0_80.html │ ├── Proxmox.String0_8192.html │ ├── Proxmox.String5_1024.html │ ├── Proxmox.String5_64.html │ ├── Proxmox.Tacme.html │ ├── Proxmox.Tacmedomain.html │ ├── Proxmox.Taction.html │ ├── Proxmox.Tagent.html │ ├── Proxmox.Talias.html │ ├── Proxmox.Tapi.html │ ├── Proxmox.Tapplication.html │ ├── Proxmox.Tarch.html │ ├── Proxmox.Tarch_1.html │ ├── Proxmox.Taudio0.html │ ├── Proxmox.Tbase_dn.html │ ├── Proxmox.Tbios.html │ ├── Proxmox.Tbond_mode.html │ ├── Proxmox.Tbond_xmit_hash_policy.html │ ├── Proxmox.Tbwlimit.html │ ├── Proxmox.Tcf.html │ ├── Proxmox.Tchecksumalgorithm.html │ ├── Proxmox.Tcitype.html │ ├── Proxmox.Tcmd.html │ ├── Proxmox.Tcmode.html │ ├── Proxmox.Tcommand.html │ ├── Proxmox.Tcommand_1.html │ ├── Proxmox.Tcompress.html │ ├── Proxmox.Tcompression.html │ ├── Proxmox.Tconsole.html │ ├── Proxmox.Tcpulimit.html │ ├── Proxmox.Tcpulimit_1.html │ ├── Proxmox.Tcrush_rule.html │ ├── Proxmox.Tdirectory.html │ ├── Proxmox.Tdisk.html │ ├── Proxmox.Tdisk_1.html │ ├── Proxmox.Tdisk_2.html │ ├── Proxmox.Tdisk_3.html │ ├── Proxmox.Tdomain.html │ ├── Proxmox.Tefidisk0.html │ ├── Proxmox.Tfeature.html │ ├── Proxmox.Tfeatures.html │ ├── Proxmox.Tfencing.html │ ├── Proxmox.Tfilesystem.html │ ├── Proxmox.Tfingerprint.html │ ├── Proxmox.Tformat.html │ ├── Proxmox.Tformat_1.html │ ├── Proxmox.Tgroup.html │ ├── Proxmox.Tha.html │ ├── Proxmox.Thttp_proxy.html │ ├── Proxmox.Thugepages.html │ ├── Proxmox.Tide.html │ ├── Proxmox.Tinfluxdbproto.html │ ├── Proxmox.Tivshmem.html │ ├── Proxmox.Tkey.html │ ├── Proxmox.Tkeyboard.html │ ├── Proxmox.Tlanguage.html │ ├── Proxmox.Tlink.html │ ├── Proxmox.Tlock.html │ ├── Proxmox.Tlock_1.html │ ├── Proxmox.Tlog.html │ ├── Proxmox.Tlog_ratelimit.html │ ├── Proxmox.Tmachine.html │ ├── Proxmox.Tmailnotification.html │ ├── Proxmox.Tmigrate_downtime.html │ ├── Proxmox.Tmigration.html │ ├── Proxmox.Tmigration_type.html │ ├── Proxmox.Tmode.html │ ├── Proxmox.Tmode_1.html │ ├── Proxmox.Tmp.html │ ├── Proxmox.Tname.html │ ├── Proxmox.Tname_1.html │ ├── Proxmox.Tnet.html │ ├── Proxmox.Tnet_1.html │ ├── Proxmox.Tnuma.html │ ├── Proxmox.Tostype.html │ ├── Proxmox.Tostype_1.html │ ├── Proxmox.Tparallel.html │ ├── Proxmox.Tpassword.html │ ├── Proxmox.Tpg_autoscale_mode.html │ ├── Proxmox.Tpolicy_in.html │ ├── Proxmox.Tpreallocation.html │ ├── Proxmox.Tproperty.html │ ├── Proxmox.Tproto.html │ ├── Proxmox.Traidlevel.html │ ├── Proxmox.Trate.html │ ├── Proxmox.Tremove_job.html │ ├── Proxmox.Trng0.html │ ├── Proxmox.Trootfs.html │ ├── Proxmox.Tsata.html │ ├── Proxmox.Tscope.html │ ├── Proxmox.Tscope_1.html │ ├── Proxmox.Tscsi.html │ ├── Proxmox.Tscsihw.html │ ├── Proxmox.Tserial.html │ ├── Proxmox.Tserial_1.html │ ├── Proxmox.Tservice.html │ ├── Proxmox.Tservice_1.html │ ├── Proxmox.Tsince.html │ ├── Proxmox.Tsize.html │ ├── Proxmox.Tsize_1.html │ ├── Proxmox.Tsmbversion.html │ ├── Proxmox.Tsource.html │ ├── Proxmox.Tspice_enhancements.html │ ├── Proxmox.Tsslversion.html │ ├── Proxmox.Tstartdate.html │ ├── Proxmox.Tstarttime.html │ ├── Proxmox.Tstate.html │ ├── Proxmox.Tsync_attributes.html │ ├── Proxmox.Ttarget_size.html │ ├── Proxmox.Ttarget_size_ratio.html │ ├── Proxmox.Ttargetvolume.html │ ├── Proxmox.Ttimeframe.html │ ├── Proxmox.Ttpmstate0.html │ ├── Proxmox.Ttransport.html │ ├── Proxmox.Ttype.html │ ├── Proxmox.Ttype_1.html │ ├── Proxmox.Ttype_10.html │ ├── Proxmox.Ttype_11.html │ ├── Proxmox.Ttype_12.html │ ├── Proxmox.Ttype_13.html │ ├── Proxmox.Ttype_14.html │ ├── Proxmox.Ttype_15.html │ ├── Proxmox.Ttype_16.html │ ├── Proxmox.Ttype_17.html │ ├── Proxmox.Ttype_18.html │ ├── Proxmox.Ttype_19.html │ ├── Proxmox.Ttype_2.html │ ├── Proxmox.Ttype_20.html │ ├── Proxmox.Ttype_3.html │ ├── Proxmox.Ttype_4.html │ ├── Proxmox.Ttype_5.html │ ├── Proxmox.Ttype_6.html │ ├── Proxmox.Ttype_7.html │ ├── Proxmox.Ttype_8.html │ ├── Proxmox.Ttype_9.html │ ├── Proxmox.Tu2f.html │ ├── Proxmox.Tunused.html │ ├── Proxmox.Tunused_1.html │ ├── Proxmox.Turl.html │ ├── Proxmox.Tusb.html │ ├── Proxmox.Tuser_attr.html │ ├── Proxmox.Tuserid.html │ ├── Proxmox.Tusernameclaim.html │ ├── Proxmox.Tuuid.html │ ├── Proxmox.Tvg.html │ ├── Proxmox.Tvga.html │ ├── Proxmox.Tvirtio.html │ ├── Proxmox.Tvlanprotocol.html │ ├── Proxmox.Tvmgenid.html │ ├── Proxmox.Twal_dev_size.html │ ├── Proxmox.Twebauthn.html │ ├── Proxmox.address.html │ ├── Proxmox.address_1.html │ ├── Proxmox.addresslist.html │ ├── Proxmox.dnsname.html │ ├── Proxmox.dnsname_1.html │ ├── Proxmox.dnsnamelist.html │ ├── Proxmox.emaillist.html │ ├── Proxmox.emailopt.html │ ├── Proxmox.emailorusernamelist.html │ ├── Proxmox.graphitepath.html │ ├── Proxmox.integer.html │ ├── Proxmox.integer0_128.html │ ├── Proxmox.integer0_172800.html │ ├── Proxmox.integer0_300.html │ ├── Proxmox.integer0_50000.html │ ├── Proxmox.integer0_500000.html │ ├── Proxmox.integer0_6.html │ ├── Proxmox.integer0_8.html │ ├── Proxmox.integer1280_65520.html │ ├── Proxmox.integer16_2160.html │ ├── Proxmox.integer16_4096.html │ ├── Proxmox.integer1_100.html │ ├── Proxmox.integer1_30.html │ ├── Proxmox.integer1_32768.html │ ├── Proxmox.integer1_4094.html │ ├── Proxmox.integer1_65535.html │ ├── Proxmox.integer1_65536.html │ ├── Proxmox.integer1_7.html │ ├── Proxmox.integer1_7_1.html │ ├── Proxmox.integer1_7_2.html │ ├── Proxmox.integer1_8192.html │ ├── Proxmox.integer2_262144.html │ ├── Proxmox.integer30_60.html │ ├── Proxmox.integer512_65536.html │ ├── Proxmox.integer5900_5999.html │ ├── Proxmox.integer6_14.html │ ├── Proxmox.integer8_32768.html │ ├── Proxmox.integer9_16.html │ ├── Proxmox.integerMax32768.html │ ├── Proxmox.integer_Min0.html │ ├── Proxmox.integer_Min1.html │ ├── Proxmox.integer_Min16.html │ ├── Proxmox.integer_Min32768.html │ ├── Proxmox.integer_Min7875.html │ ├── Proxmox.ip.html │ ├── Proxmox.iplist.html │ ├── Proxmox.ipv4.html │ ├── Proxmox.ipv4mask.html │ ├── Proxmox.ipv6.html │ ├── Proxmox.ldapsimpleattr.html │ ├── Proxmox.ldapsimpleattrlist.html │ ├── Proxmox.lxcipwithllifacelist.html │ ├── Proxmox.macaddr.html │ ├── Proxmox.macprefix.html │ ├── Proxmox.pemcertificatechain.html │ ├── Proxmox.pemstring.html │ ├── Proxmox.prunebackups.html │ ├── Proxmox.pvecalendarevent.html │ ├── Proxmox.pveconfigid.html │ ├── Proxmox.pveconfigid_1.html │ ├── Proxmox.pveconfigid_2.html │ ├── Proxmox.pveconfigid_3.html │ ├── Proxmox.pveconfigid_4.html │ ├── Proxmox.pveconfigid_5.html │ ├── Proxmox.pveconfigid_6.html │ ├── Proxmox.pveconfigidlist.html │ ├── Proxmox.pveconfigidlist_1.html │ ├── Proxmox.pvecttimezone.html │ ├── Proxmox.pvedayofweeklist.html │ ├── Proxmox.pvefwaddrspec.html │ ├── Proxmox.pvefwdportspec.html │ ├── Proxmox.pvefwicmptypespec.html │ ├── Proxmox.pvefwprotocolspec.html │ ├── Proxmox.pvefwsportspec.html │ ├── Proxmox.pvegroupid.html │ ├── Proxmox.pvegroupidlist.html │ ├── Proxmox.pvehagroupnodelist.html │ ├── Proxmox.pveharesourceorvmid.html │ ├── Proxmox.pvehotplugfeatures.html │ ├── Proxmox.pveiface.html │ ├── Proxmox.pveiface_1.html │ ├── Proxmox.pveifacelist.html │ ├── Proxmox.pvenode.html │ ├── Proxmox.pvenode_1.html │ ├── Proxmox.pvenodelist.html │ ├── Proxmox.pvepoolid.html │ ├── Proxmox.pveprivlist.html │ ├── Proxmox.pveprivlist_1.html │ ├── Proxmox.pveqmboot.html │ ├── Proxmox.pveqmbootdisk.html │ ├── Proxmox.pveqmcicustom.html │ ├── Proxmox.pveqmhostpci.html │ ├── Proxmox.pveqmide.html │ ├── Proxmox.pveqmipconfig.html │ ├── Proxmox.pveqmsmbios1.html │ ├── Proxmox.pveqmwatchdog.html │ ├── Proxmox.pverealm.html │ ├── Proxmox.pvereplicationjobid.html │ ├── Proxmox.pveroleid.html │ ├── Proxmox.pveroleidlist.html │ ├── Proxmox.pvesdncontrollerid.html │ ├── Proxmox.pvesdndnsid.html │ ├── Proxmox.pvesdnipamid.html │ ├── Proxmox.pvesdnsubnetid.html │ ├── Proxmox.pvesdnvnetid.html │ ├── Proxmox.pvesdnzoneid.html │ ├── Proxmox.pvestartuporder.html │ ├── Proxmox.pvestoragecontent.html │ ├── Proxmox.pvestoragecontent_1.html │ ├── Proxmox.pvestoragecontentlist.html │ ├── Proxmox.pvestorageformat.html │ ├── Proxmox.pvestorageid.html │ ├── Proxmox.pvestorageidlist.html │ ├── Proxmox.pvestorageoptions.html │ ├── Proxmox.pvestoragepath.html │ ├── Proxmox.pvestorageportaldns.html │ ├── Proxmox.pvestorageportaldnslist.html │ ├── Proxmox.pvestorageserver.html │ ├── Proxmox.pvestoragevgname.html │ ├── Proxmox.pvetaglist.html │ ├── Proxmox.pvetaskstatustypelist.html │ ├── Proxmox.pvetfaconfig.html │ ├── Proxmox.pvetokenidlist.html │ ├── Proxmox.pveuserid.html │ ├── Proxmox.pveuseridlist.html │ ├── Proxmox.pvevmcpuconf.html │ ├── Proxmox.pvevmid.html │ ├── Proxmox.pvevmidlist.html │ ├── Proxmox.pvevolumeid.html │ ├── Proxmox.realmsyncoptions.html │ ├── Proxmox.storagepairlist.html │ ├── Proxmox.stringalist.html │ ├── Proxmox.stringlist.html │ ├── Proxmox.urlencoded.html │ └── ProxmoxEngineOptions.html ├── generator ├── README.md ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── src │ ├── gen.ts │ ├── pveapi6.ts │ ├── pveapi7.ts │ ├── pveapi8.ts │ └── pveapiModel.ts └── tsconfig.json ├── proxmox-usb-hotplug ├── README.md ├── bin │ └── proxmox-usb-hotplug ├── package-lock.json ├── package.json ├── src │ ├── HotPlugService.ts │ ├── config.ts │ ├── delay.ts │ ├── proxmox-hotkey.ts │ └── proxmox-usb-hotplug.ts └── tsconfig.json └── sample ├── .vscode └── launch.json ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── src ├── common.ts ├── sample-connect-kvm.ts ├── sample-stream.ts ├── sample.ts └── sample2.ts ├── tsconfig.json └── usage.gif /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/*.js 3 | api/dist/ 4 | auth.ts 5 | api/pnpm-lock.yaml 6 | api/esm/*.d.ts 7 | api/.vscode/launch.json 8 | api/cjs/package-lock.json 9 | api/esm/package-lock.json 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "test", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "cwd": "${workspaceFolder}/generator", 15 | // "runtimeArgs": ["-r", "ts-node/register"], 16 | "runtimeExecutable": "tsx", 17 | "console": "integratedTerminal", 18 | "args": ["${workspaceFolder}/api/src/main.test.ts"] 19 | }, 20 | 21 | { 22 | "type": "node", 23 | "request": "launch", 24 | "name": "Generate APi", 25 | "skipFiles": [ 26 | "/**" 27 | ], 28 | "cwd": "${workspaceFolder}/generator", 29 | // "runtimeArgs": ["-r", "ts-node/register"], 30 | "runtimeExecutable": "tsx", 31 | "console": "integratedTerminal", 32 | "args": ["${workspaceFolder}/generator/src/gen.ts"] 33 | }, 34 | { 35 | "type": "node", 36 | "request": "launch", 37 | "name": "run sample", 38 | "skipFiles": [ 39 | "/**" 40 | ], 41 | "cwd": "${workspaceFolder}/sample", 42 | // "runtimeArgs": ["-r", "ts-node/register"], 43 | "runtimeExecutable": "tsx", 44 | "console": "integratedTerminal", 45 | "args": ["${workspaceFolder}/sample/src/sample.ts"] 46 | }, 47 | { 48 | "type": "node", 49 | "request": "launch", 50 | "name": "run sample2", 51 | "skipFiles": [ 52 | "/**" 53 | ], 54 | "cwd": "${workspaceFolder}/sample", 55 | // "runtimeArgs": ["-r", "ts-node/register"], 56 | "runtimeExecutable": "tsx", 57 | "console": "integratedTerminal", 58 | "args": ["${workspaceFolder}/sample/src/sample2.ts"] 59 | }, 60 | 61 | { 62 | "type": "node", 63 | "request": "launch", 64 | "name": "run sample-connect-kvm", 65 | "skipFiles": [ 66 | "/**" 67 | ], 68 | "cwd": "${workspaceFolder}/sample", 69 | // "runtimeArgs": ["-r", "ts-node/register"], 70 | "runtimeExecutable": "tsx", 71 | "console": "integratedTerminal", 72 | "args": ["${workspaceFolder}/sample/src/sample-connect-kvm.ts"] 73 | }, 74 | 75 | { 76 | "type": "node", 77 | "request": "launch", 78 | "name": "proxmox-usb-hotplug --help", 79 | "skipFiles": [ 80 | "/**" 81 | ], 82 | "cwd": "${workspaceFolder}/proxmox-usb-hotplug", 83 | // "runtimeArgs": ["-r", "ts-node/register"], 84 | "runtimeExecutable": "tsx", 85 | "console": "integratedTerminal", 86 | "args": ["${workspaceFolder}/proxmox-usb-hotplug/src/proxmox-usb-hotplug.ts", "--help"], 87 | // "args": ["${workspaceFolder}/proxmox-usb-hotplug/src/index.ts", "-c", "../../auth.conf", "byVendor"], 88 | }, 89 | 90 | { 91 | "type": "node", 92 | "request": "launch", 93 | "name": "proxmox-usb-hotplug", 94 | "skipFiles": [ 95 | "/**" 96 | ], 97 | "cwd": "${workspaceFolder}/proxmox-usb-hotplug", 98 | // "runtimeArgs": ["-r", "ts-node/register"], 99 | "runtimeExecutable": "tsx", 100 | "console": "integratedTerminal", 101 | "args": ["${workspaceFolder}/proxmox-usb-hotplug/src/proxmox-usb-hotplug.ts", "-c", "/etc/usb-hotplug.conf"], 102 | } 103 | 104 | ] 105 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # proxmox-api Code of Conduct 2 | 3 | ## 1. Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## 2. Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## 3. Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## 4. Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 34 | 35 | ## 5. Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at uchemoui@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. 38 | 39 | ## 6. Pull Requests and Testing 40 | 41 | We encourage all community members to actively participate in the development process by submitting pull requests. All non-trivial fixes should include comprehensive testing to ensure stability and prevent regression. We aim to process pull requests in a timely manner and appreciate your patience. 42 | 43 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 44 | 45 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 46 | 47 | For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to proxmox-api 2 | 3 | Thank you for your interest in contributing to proxmox-api! Your time and effort are greatly appreciated. Every contribution, no matter how small, is valued. 4 | 5 | ## Code of Conduct 6 | 7 | Before contributing, please read our [Code of Conduct](CODE_OF_CONDUCT.md). All contributors are expected to adhere to these guidelines to ensure a positive and inclusive environment. 8 | 9 | ## Getting Started 10 | 11 | - Ensure you have a [GitHub](https://github.com/) account. 12 | - Fork the repository on GitHub (click the "Fork" button at the top right of the main repository page). 13 | - Clone the forked repository to your local machine. 14 | 15 | ## Making Changes 16 | 17 | - You can make changes directly to the `main` branch of your forked repository or create a new branch for your changes: `git checkout -b your-feature-name`. 18 | - Make your changes. 19 | - Try to commit your changes in logical chunks and provide clear and descriptive commit messages. 20 | - Push your changes to your forked repository on GitHub. 21 | 22 | ## Code Formatting 23 | 24 | To maintain code consistency and readability, avoid making code reformatting changes in your PRs. Any PR solely for code reformatting will be rejected. If you believe code reformatting is necessary, consider updating or adding a formatting script instead. By doing this, we can ensure the codebase remains consistent and easily reviewable, focusing on the implementation changes. 25 | 26 | ## Submitting Changes 27 | 28 | - Once you've pushed your changes, create a Pull Request from your forked repository's interface. 29 | - In the Pull Request description, clearly describe the problem and solution, and include the relevant issue number if applicable. 30 | - All non-trivial fixes should include comprehensive tests to prevent regression. 31 | 32 | ## Testing 33 | 34 | We value and encourage contributions that include testing. If you're submitting non-trivial changes, please include tests that cover the new functionality or bug fix. 35 | 36 | ## Review Process 37 | 38 | After submitting a Pull Request, project maintainers and contributors will review your contribution. This is a collaborative process, and we aim to work together to help get your PRs merged. 39 | 40 | Thank you again for your interest in contributing. We look forward to seeing your contributions! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Proxmox Api 2 | 3 | [![NPM Version](https://img.shields.io/npm/v/proxmox-api.svg?style=flat)](https://www.npmjs.org/package/proxmox-api) Typescript Api to manage proxmox servers. 4 | 5 | * [API Viewer](https://pve.proxmox.com/pve-docs/api-viewer/) 6 | * [API Docs](https://pve.proxmox.com/wiki/Proxmox_VE_API) 7 | 8 | Mapping 100% of available calls, contains all api documentation in typing file. 9 | code size < 10Ko including docs 10 | 11 | ## Usage 12 | 13 | ![intellisense](https://github.com/UrielCh/proxmox-api/blob/master/sample/usage.gif?raw=true "preview") 14 | 15 | ### Overview 16 | 17 | to use this API take the Path you want to call, and replace: 18 | - the `/` by `.` 19 | - the `${variable}` by `.(variable)` 20 | - append the http method you want to call with a $ at the end (`.$get()`, `.$post()`, `.$put()` or `.$delete()`) 21 | 22 | that it. 23 | 24 | ### Example 25 | 26 | To call `GET /cluster/acme/account/{name}` you will call `proxmox.cluster.acme.account.$(name).$get()` 27 | 28 | To call `GET /api2/json/cluster/backup/{id}/included_volumes` you will call `proxmox.cluster.backup.{id}.included_volumes.$get()` 29 | 30 | To call `GET /api2/json/nodes` you will call `proxmox.nodes.$get()` 31 | 32 | The provided typing will assist you within intelisense, so you do not need to read any external doc. 33 | 34 | ## Code sample 35 | 36 | 37 | * [![NPM Version](https://img.shields.io/npm/v/proxmox-usb-hotplug.svg?style=flat)](https://www.npmjs.org/package/proxmox-usb-hotplug) an hotplug usb service based on this API. 38 | 39 | ```bash 40 | npm install proxmox-api 41 | ``` 42 | 43 | ```typescript 44 | import proxmoxApi from "proxmox-api"; 45 | 46 | // authorize self signed cert if you do not use a valid SSL certificat 47 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 48 | 49 | async function test() { 50 | // connect to proxmox 51 | const proxmox = proxmoxApi({host: '127.0.0.1', password: 'password', username: 'user1@pam'}); 52 | // list nodes 53 | const nodes = await proxmox.nodes.$get(); 54 | // iterate cluster nodes 55 | for (const node of nodes) { 56 | const theNode = proxmox.nodes.$(node.node); 57 | // list Qemu VMS 58 | const qemus = await theNode.qemu.$get({full:true}); 59 | // iterate Qemu VMS 60 | for (const qemu of qemus) { 61 | // do some suff. 62 | const config = await theNode.qemu.$(qemu.vmid).config.$get(); 63 | console.log(`vm: ${config.name}, memory: ${config.memory}`); 64 | // const vnc = await theNode.qemu.$(qemu.vmid).vncproxy.$post(); 65 | // console.log('vnc:', vnc); 66 | // const spice = await theNode.qemu.$(qemu.vmid).spiceproxy.$post(); 67 | // console.log('spice:', spice); 68 | } 69 | } 70 | } 71 | 72 | test().catch(console.error); 73 | ``` 74 | 75 | ### Initialisation alternatives: 76 | 77 | - keeping access to ProxmoxEngine object (that can be use to share a ticket, or to access it) 78 | 79 | ```typescript 80 | import proxmoxApi, { ProxmoxEngine } from "proxmox-api"; 81 | 82 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 83 | 84 | async function test() { 85 | // connect to proxmox 86 | const engine = new ProxmoxEngine({host: '127.0.0.1', password: 'password', username: 'user1@pam'}); 87 | const proxmox = proxmoxApi(engine); 88 | } 89 | ``` 90 | 91 | - Using Api token 92 | 93 | 94 | ```typescript 95 | import proxmoxApi from "proxmox-api"; 96 | 97 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 98 | 99 | async function test() { 100 | // connect to proxmox 101 | const proxmox = proxmoxApi({host: '127.0.0.1', tokenID: 'USER@REALM!TOKENID', tokenSecret: '12345678-1234-1234-1234-1234567890ab'}); 102 | } 103 | ``` 104 | 105 | ## Notes 106 | 107 | - if the call path contains a hyphen, you will need to use the `['field']` syntax ex: 108 | 109 | ```typescript 110 | await theNode.qemu.$(vmid).agent['get-fsinfo'].$get() 111 | ``` 112 | 113 | ## Changelog 114 | 115 | ### V0.1.3 116 | - add authTimeout option, to limit authentification time. 117 | - add queryTimeout option to limit non auth request timeout. 118 | -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | # Proxmox Api 2 | 3 | [![NPM Version](https://img.shields.io/npm/v/proxmox-api.svg?style=flat)](https://www.npmjs.org/package/proxmox-api) Typescript Api to manage proxmox servers. 4 | 5 | * [API Viewer](https://pve.proxmox.com/pve-docs/api-viewer/) 6 | * [API Docs](https://pve.proxmox.com/wiki/Proxmox_VE_API) 7 | 8 | Mapping 100% of available calls, contains all api documentation in typing file. 9 | code size < 10Ko including docs 10 | 11 | ## Usage 12 | 13 | ![intellisense](https://github.com/UrielCh/proxmox-api/blob/master/sample/usage.gif?raw=true "preview") 14 | 15 | ### Overview 16 | 17 | to use this API take the Path you want to call, and replace: 18 | - the `/` by `.` 19 | - the `${variable}` by `.(variable)` 20 | - append the http method you want to call with a $ at the end (`.$get()`, `.$post()`, `.$put()` or `.$delete()`) 21 | 22 | that it. 23 | 24 | ### Example 25 | 26 | To call `GET /cluster/acme/account/{name}` you will call `proxmox.cluster.acme.account.$(name).$get()` 27 | 28 | To call `GET /api2/json/cluster/backup/{id}/included_volumes` you will call `proxmox.cluster.backup.{id}.included_volumes.$get()` 29 | 30 | To call `GET /api2/json/nodes` you will call `proxmox.nodes.$get()` 31 | 32 | The provided typing will assist you within intelisense, so you do not need to read any external doc. 33 | 34 | ## Code sample 35 | 36 | 37 | * [![NPM Version](https://img.shields.io/npm/v/proxmox-usb-hotplug.svg?style=flat)](https://www.npmjs.org/package/proxmox-usb-hotplug) an hotplug usb service based on this API. 38 | 39 | ```bash 40 | npm install proxmox-api 41 | ``` 42 | 43 | ```typescript 44 | import proxmoxApi from "proxmox-api"; 45 | 46 | // authorize self signed cert if you do not use a valid SSL certificat 47 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 48 | 49 | async function test() { 50 | // connect to proxmox 51 | const proxmox = proxmoxApi({host: '127.0.0.1', password: 'password', username: 'user1@pam'}); 52 | // list nodes 53 | const nodes = await proxmox.nodes.$get(); 54 | // iterate cluster nodes 55 | for (const node of nodes) { 56 | const theNode = proxmox.nodes.$(node.node); 57 | // list Qemu VMS 58 | const qemus = await theNode.qemu.$get({full:true}); 59 | // iterate Qemu VMS 60 | for (const qemu of qemus) { 61 | // do some suff. 62 | const config = await theNode.qemu.$(qemu.vmid).config.$get(); 63 | console.log(`vm: ${config.name}, memory: ${config.memory}`); 64 | // const vnc = await theNode.qemu.$(qemu.vmid).vncproxy.$post(); 65 | // console.log('vnc:', vnc); 66 | // const spice = await theNode.qemu.$(qemu.vmid).spiceproxy.$post(); 67 | // console.log('spice:', spice); 68 | } 69 | } 70 | } 71 | 72 | test().catch(console.error); 73 | ``` 74 | 75 | ### Initialisation alternavives: 76 | 77 | - keeping access to ProxmoxEngine object (that can be use to share a ticket, or to access it) 78 | 79 | ```typescript 80 | import proxmoxApi, { ProxmoxEngine } from "proxmox-api"; 81 | 82 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 83 | 84 | async function test() { 85 | // connect to proxmox 86 | const engine = new ProxmoxEngine({host: '127.0.0.1', password: 'password', username: 'user1@pam'}); 87 | const proxmox = proxmoxApi(engine); 88 | } 89 | ``` 90 | 91 | - Using Api token 92 | 93 | 94 | ```typescript 95 | import proxmoxApi from "proxmox-api"; 96 | 97 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 98 | 99 | async function test() { 100 | // connect to proxmox 101 | const proxmox = proxmoxApi({host: '127.0.0.1', tokenID: 'USER@REALM!TOKENID', tokenSecret: '12345678-1234-1234-1234-1234567890ab'}); 102 | } 103 | ``` 104 | 105 | ## Notes 106 | 107 | - if the call path contains a hyphen, you will need to use the `['field']` syntax ex: 108 | 109 | ```typescript 110 | await theNode.qemu.$(vmid).agent['get-fsinfo'].$get() 111 | ``` 112 | -------------------------------------------------------------------------------- /api/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.1.0 4 | * update ESM compatibility 5 | * add support for Proxmox 8 6 | * update deps 7 | 8 | ## v1.0.2 9 | * Merge PR 20 10 | 11 | ## v1.0.1 12 | * improve codebase adding CODE_OF_CONDUCT.md, CONTRIBUTING.md 13 | * add funding 14 | * fix common-js usage for the project 15 | 16 | ## v1.0.0 17 | * Dual stack package, ESM + CJS 18 | * Should work with deno 19 | 20 | ## v0.4.2 21 | * add application/octet-stream support 22 | 23 | ## v0.4.1 24 | * rewrite undici fetch integration. 25 | * add debug parameter. 26 | * add cause in Exception 27 | * remove console.log 28 | 29 | ## v0.4.0 30 | * use undici fetch (will fix issue 14 in the next release) 31 | * add proxmoxApi export (same as default) 32 | * fix missing GET params. (issue 11 and 12) 33 | 34 | ## v0.3.2 35 | * add missing uppercase on method. (issue 10) 36 | 37 | ## v0.3.1 38 | * fix regrestion in API Keys usage. (issue 9) 39 | 40 | ## V0.3.0 41 | * fix project layout 42 | * add doc 43 | * change proxmoxApi signature to proxmoxApi(options: ProxmoxEngineOptions | ApiRequestable): Proxmox.Api. 44 | 45 | ## V0.1.3 46 | * add authTimeout option, to limit authentification time. 47 | * add queryTimeout option to limit non auth request timeout. 48 | -------------------------------------------------------------------------------- /api/cjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs" 3 | } 4 | -------------------------------------------------------------------------------- /api/esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proxmox-api", 3 | "version": "1.1.1", 4 | "description": "Cutting edge javascript / Typescript / ESM / CJS Proxmox API", 5 | "keywords": [ 6 | "proxmox", 7 | "client", 8 | "typing", 9 | "api", 10 | "typescript", 11 | "async", 12 | "promise" 13 | ], 14 | "author": "Uriel Chemouni (https://uriel.ovh/)", 15 | "homepage": "https://urielch.github.io/proxmox-api/", 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/UrielCh/proxmox-api.git" 19 | }, 20 | "license": "GPL-3.0", 21 | "bugs": { 22 | "url": "https://github.com/UrielCh/proxmox-api/issues" 23 | }, 24 | "main": "./cjs/index.js", 25 | "module": "./esm/index.js", 26 | "typings": "./esm/index.d.ts", 27 | "exports": { 28 | ".": { 29 | "require": "./cjs/index.js", 30 | "import": "./esm/index.js", 31 | "types": "./esm/index.d.ts" 32 | } 33 | }, 34 | "scripts": { 35 | "clean": "rimraf esm/*.js esm/*.ts cjs/*.js", 36 | "prepack": "npm run build", 37 | "docs": "typedoc --entryPointStrategy expand", 38 | "build": "tsc --pretty --project . && tsc --pretty --project tsconfig-cjs.json", 39 | "test": "tsx src/main.test.ts", 40 | "prepare": "npm run clean && npm run build" 41 | }, 42 | "funding": "https://github.com/sponsors/urielch", 43 | "contributors": [], 44 | "engine-strict": { 45 | "node": ">=18" 46 | }, 47 | "dependencies": { 48 | "undici": "^6.19.8" 49 | }, 50 | "devDependencies": { 51 | "@types/node": "^22.5.4", 52 | "rimraf": "^6.0.1", 53 | "tsx": "^4.19.0", 54 | "typedoc": "^0.26.7", 55 | "typedoc-plugin-rename-defaults": "^0.7.1", 56 | "typescript": "^5.6.2" 57 | }, 58 | "files": [ 59 | "esm", 60 | "cjs", 61 | "src" 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /api/src/QmMonitor.ts: -------------------------------------------------------------------------------- 1 | // Proxmox-API Interactive proxmox API for developpers how do not like reading docs 2 | // Copyright (C) 2020-2022 Chemouni Uriel 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | import { Proxmox } from './model.js'; 18 | 19 | // 'mem', // ERROR: VM XXX qmp command 'human-monitor-command' failed - got timeout 20 | // 'tlb', // ERROR: VM XXX qmp command 'human-monitor-command' failed - got timeout 21 | 22 | export const VALID_QEMU_INFO_SIMPLE = ['backup', 'balloon', 'block-jobs', 'blockstats', 'capture', 23 | 'chardev', 'cpus', 'cpustats', 'dump', 'history', 'hotpluggable-cpus', 'ioapic', 'iothreads', 24 | 'irq', 'jit', 'kvm', 'memdev', 'memory-devices', 'memory_size_summary', 'mice', 'migrate', 25 | 'migrate_cache_size', 'migrate_capabilities', 'migrate_parameters', 'name', 'network', 'numa', 26 | 'opcount', 'pci', 'pic', 'profile', 'qdm', 'qtree', 'ramblock', 'rdma', 'roms', 'savevm', 27 | 'sev', 'snapshots', 'spice', 'status', 'tpm', 'usb', 'usbhost', 'usernet', 'uuid', 'version', 28 | 'vm-generation-id', 'vnc'] as const; 29 | 30 | export const VALID_QEMU_INFO_OPTION = ['block', 'lapic', 'mtree', 'qom-tree', 'registers', 'sync-profile', 'trace-events'] as const; 31 | 32 | export const VALID_QEMU_INFO_PARAM = ['rocker-of-dpa-flows', 'rocker-of-dpa-groups', 'rocker-ports'] as const; 33 | 34 | export type QemuInfoSimple = typeof VALID_QEMU_INFO_SIMPLE[number]; 35 | export type QemuInfoOption = typeof VALID_QEMU_INFO_OPTION[number]; 36 | export type QemuInfoParam = typeof VALID_QEMU_INFO_PARAM[number]; 37 | 38 | export interface USBHostInfo { 39 | bus: number, 40 | addr: number, 41 | port: string, 42 | speed: string, 43 | class: string, 44 | vendorId: string, 45 | productId: string, 46 | name: string, 47 | } 48 | 49 | export interface USBInfo { 50 | device: string, 51 | port: string, 52 | speed: string, 53 | product: string, 54 | id: string, 55 | } 56 | 57 | export class QmMonitor { 58 | private _vmid: number; 59 | private _node: string; 60 | monitor: (command: string) => Promise; 61 | 62 | constructor(private proxmox: Proxmox.Api, node: string, vmid: number) { 63 | this._node = node; 64 | this._vmid = vmid; 65 | const call = proxmox.nodes.$(node).qemu.$(vmid).monitor.$post; 66 | this.monitor = (command) => { 67 | // this.calls++ 68 | return call({ command }) 69 | }; 70 | } 71 | 72 | public get vmid(): number { 73 | return this._vmid; 74 | } 75 | 76 | public get node(): string { 77 | return this._node; 78 | } 79 | 80 | info(type: QemuInfoSimple): Promise; 81 | info(type: QemuInfoOption, ...args: string[]): Promise; 82 | info(type: QemuInfoParam, arg1: string, ...args: string[]): Promise; 83 | async info(type: QemuInfoSimple | QemuInfoOption | QemuInfoParam, ...args: string[]): Promise { 84 | let ext = args.join(' '); 85 | if (ext) 86 | ext = ' ' + ext; 87 | return this.monitor(`info ${type}${ext}`); 88 | } 89 | 90 | async infoUsb(filters?: { vendorId?: RegExp, productId?: RegExp, name?: RegExp }): Promise { 91 | //Device 1.1, Port 1, Speed 1.5 Mb/s, Product USB OPTICAL MOUSE , ID: mouse 92 | //Device 1.0, Port 2, Speed 12 Mb/s, Product Gaming KB , ID: keyboard 93 | const text = await this.info('usb'); 94 | const expected = ((text.match(/[\r\n]+/g) || []).length); 95 | const matches = text.matchAll(/Device ([\d.]+), Port ([\d.]+), Speed ([\d KMGTbfs\/.]+)(?:, Product (.+), ID: (.+))?/g); 96 | let results = [...matches].map(m => ({ 97 | device: m[1], 98 | port: m[2], 99 | speed: m[3], 100 | product: m[4], 101 | id: m[5], 102 | })) 103 | if (expected != results.length) { 104 | throw Error(`Identify ${results.length} usb element should find ${expected} Error in API, Failed to parse:\n${text}`); 105 | } 106 | return results; 107 | } 108 | 109 | async deviceDel(id: string): Promise { 110 | const text = await this.monitor(`device_del ${id}`); 111 | // return `Error: Device '${id}' not found` 112 | // console.log(text); 113 | return text; 114 | } 115 | 116 | /** 117 | * list available usb on host 118 | */ 119 | async infoUsbhost(filters?: { vendorId?: RegExp, productId?: RegExp, name?: RegExp }): Promise { 120 | const text = await this.info('usbhost'); 121 | const matches = text.matchAll(/Bus (\d+), Addr (\d+), Port ([\d.]+), Speed ([\d KMGTbfs\/.]+)[\r\n]\s+Class (\d+): USB device ([0-9a-f]{4}):([0-9a-f]{4}), (.*)/gm); 122 | let results = [...matches].map(m => ({ 123 | bus: Number(m[1]), 124 | addr: Number(m[2]), 125 | port: m[3], 126 | speed: m[4], 127 | class: m[5], 128 | vendorId: m[6], 129 | productId: m[7], 130 | name: m[8], 131 | })) 132 | if (filters) { 133 | results = results.filter(usb => { 134 | if (filters.name && !filters.name.test(usb.name)) { 135 | return false; 136 | } 137 | if (filters.vendorId && !filters.vendorId.test(usb.name)) { 138 | return false; 139 | } 140 | if (filters.productId && !filters.productId.test(usb.name)) { 141 | return false; 142 | } 143 | return true; 144 | }) 145 | } 146 | return results; 147 | } 148 | // 149 | // https://www.linux-kvm.org/page/USB 150 | // https://www.qemu.org/docs/master/qemu-doc.html 151 | async deviceAddById(id: string, params: { vendorId: string, productId: string }): Promise { 152 | // TODO: check param values 153 | params.vendorId = params.vendorId.replace(/0x/i, ''); 154 | params.productId = params.productId.replace(/0x/i, ''); 155 | const text = await this.monitor(`device_add usb-host,vendorid=0x${params.vendorId},productid=0x${params.productId},id=${id}`); 156 | // if (text) 157 | // console.log(`deviceAddById return: '${text}'`); 158 | return text; 159 | } 160 | 161 | // usb-host,hostbus=2,hostport=4,id=front2 162 | // device_add driver[,prop=value][,...] -- add device, like -device on the command line 163 | async deviceAddByPort(id: string, params: { bus: number, port: string }): Promise { 164 | // TODO: check param values 165 | const text = await this.monitor(`device_add usb-host,hostbus=${params.bus},hostport=${params.port},id=${id}`); 166 | // `Duplicate ID '${id}' for device\nTry "help device_add" for more information` 167 | //if (text) 168 | // console.log(`deviceAddByPort return: '${text}'`); 169 | return text; 170 | } 171 | 172 | async deviceAddMissing(id: string, filters: { vendorId?: RegExp, productId?: RegExp, name?: RegExp }): Promise { 173 | let connected = await this.infoUsb(); 174 | if (connected.findIndex(v => v.id === id) >= 0) { 175 | throw Error(`USB device ${id} already present`); 176 | } 177 | { 178 | let usbs = await this.infoUsbhost(filters); 179 | for (let i = 0; i < usbs.length; i++) { 180 | const id2 = i ? `${id}-${i}` : id; 181 | // console.log(`connecting '${usbs[i].name}' as ${id2}`); 182 | this.deviceAddByPort(id2, usbs[i]); 183 | } 184 | } 185 | } 186 | } 187 | 188 | -------------------------------------------------------------------------------- /api/src/constructor.ts: -------------------------------------------------------------------------------- 1 | // Proxmox-API Interactive proxmox API for developpers how do not like reading docs 2 | // Copyright (C) 2020-2022 Chemouni Uriel 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | import { type Proxmox } from "./model.js"; 17 | import ProxmoxEngine from "./ProxmoxEngine.js"; 18 | import { ApiRequestable, buildApiProxy } from "./proxy.js"; 19 | import { ProxmoxEngineOptions } from './ProxmoxEngine.js'; 20 | 21 | function isProxmoxEngine(val: ProxmoxEngineOptions | ApiRequestable): val is ApiRequestable { 22 | return 'doRequest' in val; 23 | } 24 | 25 | /** 26 | * Default main package function, this function return a javascript Proxy to use your proxmox API. 27 | * 28 | * @param options authentification option or a doRequest provider. 29 | * 30 | * @returns the proxy object 31 | */ 32 | export function proxmoxApi(options: ProxmoxEngineOptions | ApiRequestable): Proxmox.Api { 33 | if (isProxmoxEngine(options)) 34 | return buildApiProxy(options, '/api2/json'); 35 | else 36 | return buildApiProxy(new ProxmoxEngine(options), '/api2/json'); 37 | } 38 | 39 | export default proxmoxApi; 40 | -------------------------------------------------------------------------------- /api/src/index.ts: -------------------------------------------------------------------------------- 1 | // Proxmox-API Interactive proxmox API for developpers how do not like reading docs 2 | // Copyright (C) 2020-2022 Chemouni Uriel 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | export { proxmoxApi as default } from './constructor.js'; 17 | export { proxmoxApi } from './constructor.js'; 18 | export { type Proxmox } from './model.js'; 19 | export { type USBHostInfo, type USBInfo, QmMonitor } from './QmMonitor.js' 20 | export { type ApiRequestable } from './proxy.js'; 21 | export { 22 | ProxmoxEngine, 23 | type ProxmoxEngineOptions, 24 | type ProxmoxEngineOptionsCommon, 25 | type ProxmoxEngineOptionsPass, 26 | type ProxmoxEngineOptionsToken 27 | } from './ProxmoxEngine.js'; 28 | 29 | // for stress test 30 | // export { buildApiProxy } from './proxy'; 31 | -------------------------------------------------------------------------------- /api/src/main.test.ts: -------------------------------------------------------------------------------- 1 | import proxmoxApi from "./index.js"; 2 | import test from "node:test"; 3 | import assert from "node:assert"; 4 | 5 | type DummyType = typeof fetch & { 6 | url: string; 7 | responseData: unknown; 8 | options?: RequestInit; 9 | }; 10 | 11 | function dumyFetch(responseData: unknown): DummyType { 12 | const dumy: DummyType = ((url: string | URL, options?: RequestInit) => { 13 | dumy.url = url.toString(); 14 | dumy.options = options; 15 | let text = ""; 16 | let status = 200; 17 | const headers = new Headers({ 18 | "content-type": "application/json;charset=UTF-8", 19 | }); 20 | 21 | if (url === "https://127.0.0.1/api2/json/access/ticket") { 22 | text = JSON.stringify({ 23 | data: { ticket: "1234", CSRFPreventionToken: "1234" }, 24 | }); 25 | } else { 26 | text = JSON.stringify(dumy.responseData); 27 | } 28 | // return Promise.resolve({ data: { ticket: '1234' } }); 29 | const response = { 30 | text: () => Promise.resolve(text), 31 | status, 32 | headers, 33 | } as any; 34 | // console.log('fetch', url, options); 35 | return Promise.resolve(response); 36 | }) as DummyType; 37 | dumy.responseData = responseData; 38 | 39 | return dumy as DummyType; 40 | } 41 | 42 | test("test", async (t) => { 43 | // This test passes because it does not throw an exception. 44 | const myfetch: any = dumyFetch({ data: {}, errors: undefined }); 45 | const proxmox = proxmoxApi({ 46 | host: "127.0.0.1", 47 | password: "password", 48 | username: "user1@pam", 49 | fetch: myfetch, 50 | }); 51 | const theNode = proxmox.nodes.$("node1"); 52 | await theNode.qemu.$get({full:true}); 53 | assert.strictEqual(myfetch.url, "https://127.0.0.1/api2/json/nodes/node1/qemu?full=1", "true is encoded as 1"); 54 | // console.log(myfetch.options.body); 55 | 56 | const qemu = theNode.qemu.$(100); 57 | const post = qemu.agent.exec.$post; 58 | await post({ command: ["touch", "/test"] }); 59 | assert.strictEqual( 60 | myfetch.url, 61 | "https://127.0.0.1/api2/json/nodes/node1/qemu/100/agent/exec", 62 | ); 63 | // old: "command=touch%2C%2Ftest" 64 | // new: "command=touch&command=%2Ftest" 65 | assert.strictEqual(myfetch.options.body, "command=touch&command=%2Ftest", "array parammeter should be passed as mutiple values"); 66 | }); 67 | -------------------------------------------------------------------------------- /api/src/proxy.ts: -------------------------------------------------------------------------------- 1 | // Proxmox-API Interactive proxmox API for developpers how do not like reading docs 2 | // Copyright (C) 2020-2022 Chemouni Uriel 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | /** 18 | * Generic type for Api parameters 19 | */ 20 | export type ApiParamType = { [key: string]: any; }; 21 | 22 | /** 23 | * Common interface used to call API engine 24 | */ 25 | export interface ApiRequestable { 26 | /** 27 | * Execute a request on the API with promise 28 | * 29 | * @param httpMethod: The HTTP method GET POST PUT DELETE 30 | * @param path: The request final path 31 | * @param pathTemplate: The request path with \{pathParams\} 32 | * @param params: The request parameters (passed as query string or body params) 33 | */ 34 | doRequest(httpMethod: string, path: string, pathTemplate: string, params?: {[key:string]: any}): Promise; 35 | } 36 | 37 | /** 38 | * Common Getter part fot handlers 39 | * - $() 40 | * - $getv/$put()/$post()/$delete() 41 | * - path navigation 42 | */ 43 | const commonGet = (key: string, target: ProxyApi) => { 44 | if (key.startsWith('$')) { 45 | // give parameter in path 46 | if (key === '$') { 47 | return (id: string | number) => { 48 | // escape '/' char 49 | const idStr = String(id).replace(/\//g, '%2F'); 50 | const child = new ProxyApi(target._engine, `${target._path}/${idStr}`, `${target._model}/*`); 51 | return new Proxy(child, handlerChild); 52 | } 53 | } 54 | // $get $post $delete $put 55 | const fnc = (params: any) => { 56 | const mtd = key.substring(1); 57 | return target._engine.doRequest(mtd, target._path, target._model, params); 58 | } 59 | return fnc.bind(target._engine); 60 | } 61 | if (key.startsWith('_')) 62 | key = key.substring(1); 63 | const child = new ProxyApi(target._engine, `${target._path}/${key}`, `${target._model}/${key}`); 64 | return new Proxy(child, handlerChild); 65 | } 66 | 67 | /** 68 | * handler for all proxy level except the root one 69 | * handle: 70 | * - Object Field 71 | * - $() 72 | * - $getv/$put()/$post()/$delete() 73 | * - path navigation 74 | */ 75 | const handlerChild = >{ 76 | construct(target: ProxyApi, argArray: any, newTarget?: any) { 77 | return target; 78 | }, 79 | get(target: ProxyApi, p: PropertyKey, receiver: any) { 80 | if (typeof p === 'symbol') 81 | return (target)[p]; 82 | const key = p.toString(); 83 | switch (key) { 84 | case 'toString': 85 | case 'valueOf': 86 | case 'toLocaleString': 87 | return (target)[p]; 88 | } 89 | return commonGet(key, target); 90 | } 91 | } 92 | /** 93 | * handler for the first level of the proxy 94 | * handle: 95 | * - Object Field 96 | * - EventEmitter Field 97 | * - $() 98 | * - $get()/$put()/$post()/$delete() 99 | * - path navigation 100 | */ 101 | const handlerRoot = >{ 102 | construct(target: ProxyApi, argArray: any, newTarget?: any) { 103 | return target; 104 | }, 105 | get(target: ProxyApi, p: PropertyKey, receiver: any) { 106 | if (typeof p === 'symbol') 107 | return (target as any)[p]; 108 | let key = p.toString(); 109 | switch (key) { 110 | // object 111 | case 'toString': 112 | case 'valueOf': 113 | case 'toLocaleString': 114 | // hasOwnProperty 115 | // isPrototypeOf 116 | // propertyIsEnumerable 117 | // constructor 118 | return (target as any)[p]; 119 | // EventEmitter 120 | case 'addListener': 121 | case 'on': 122 | case 'once': 123 | case 'prependListener': 124 | case 'prependOnceListener': 125 | case 'removeListener': 126 | case 'off': 127 | case 'removeAllListeners': 128 | case 'setMaxListeners': 129 | case 'getMaxListeners': 130 | case 'listeners': 131 | case 'rawListeners': 132 | case 'emit': 133 | case 'eventNames': 134 | case 'listenerCount': 135 | return (target as any)[p]; 136 | } 137 | return commonGet(key, target); 138 | } 139 | } 140 | 141 | /** 142 | * Data cloned on each Proxy node call 143 | * maintains full PATH for each calls 144 | */ 145 | class ProxyApi { 146 | public _model: string; 147 | 148 | constructor(public _engine: ApiRequestable, public _path: string, model?: string) { 149 | this._model = model || this._path; 150 | } 151 | toString(): string { 152 | return `ProxyApi{path:${this._path}}`; 153 | } 154 | } 155 | /** 156 | * Build API API Proxy 157 | * 158 | * @param engine Api logic code 159 | * @param path base prefix for url 160 | */ 161 | export function buildApiProxy(engine: ApiRequestable, path: string): any { 162 | return new Proxy(new ProxyApi(engine, path), handlerRoot) as any; 163 | } 164 | -------------------------------------------------------------------------------- /api/tsconfig-cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "declaration": false, 6 | "outDir": "cjs", 7 | }, 8 | } -------------------------------------------------------------------------------- /api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "ES2020", 5 | "lib": ["ES2020"], 6 | "declaration": true, 7 | "outDir": "esm", 8 | "strict": true, 9 | "noImplicitAny": true, 10 | "strictNullChecks": true, 11 | "strictFunctionTypes": true, 12 | "alwaysStrict": true, 13 | "moduleResolution": "node", 14 | "rootDirs": ["src"], 15 | "esModuleInterop": true, 16 | "forceConsistentCasingInFileNames": true 17 | }, 18 | "include": ["src/index.ts"], 19 | "typedocOptions": { 20 | "entryPoints": ["src/index.ts"], 21 | "out": "../docs" 22 | } 23 | } -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /docs/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #000000; 3 | --dark-hl-0: #D4D4D4; 4 | --light-hl-1: #AF00DB; 5 | --dark-hl-1: #C586C0; 6 | --light-hl-2: #001080; 7 | --dark-hl-2: #9CDCFE; 8 | --light-hl-3: #A31515; 9 | --dark-hl-3: #CE9178; 10 | --light-hl-4: #008000; 11 | --dark-hl-4: #6A9955; 12 | --light-hl-5: #0070C1; 13 | --dark-hl-5: #4FC1FF; 14 | --light-hl-6: #0000FF; 15 | --dark-hl-6: #569CD6; 16 | --light-hl-7: #795E26; 17 | --dark-hl-7: #DCDCAA; 18 | --light-hl-8: #000000FF; 19 | --dark-hl-8: #D4D4D4; 20 | --light-code-background: #FFFFFF; 21 | --dark-code-background: #1E1E1E; 22 | } 23 | 24 | @media (prefers-color-scheme: light) { :root { 25 | --hl-0: var(--light-hl-0); 26 | --hl-1: var(--light-hl-1); 27 | --hl-2: var(--light-hl-2); 28 | --hl-3: var(--light-hl-3); 29 | --hl-4: var(--light-hl-4); 30 | --hl-5: var(--light-hl-5); 31 | --hl-6: var(--light-hl-6); 32 | --hl-7: var(--light-hl-7); 33 | --hl-8: var(--light-hl-8); 34 | --code-background: var(--light-code-background); 35 | } } 36 | 37 | @media (prefers-color-scheme: dark) { :root { 38 | --hl-0: var(--dark-hl-0); 39 | --hl-1: var(--dark-hl-1); 40 | --hl-2: var(--dark-hl-2); 41 | --hl-3: var(--dark-hl-3); 42 | --hl-4: var(--dark-hl-4); 43 | --hl-5: var(--dark-hl-5); 44 | --hl-6: var(--dark-hl-6); 45 | --hl-7: var(--dark-hl-7); 46 | --hl-8: var(--dark-hl-8); 47 | --code-background: var(--dark-code-background); 48 | } } 49 | 50 | :root[data-theme='light'] { 51 | --hl-0: var(--light-hl-0); 52 | --hl-1: var(--light-hl-1); 53 | --hl-2: var(--light-hl-2); 54 | --hl-3: var(--light-hl-3); 55 | --hl-4: var(--light-hl-4); 56 | --hl-5: var(--light-hl-5); 57 | --hl-6: var(--light-hl-6); 58 | --hl-7: var(--light-hl-7); 59 | --hl-8: var(--light-hl-8); 60 | --code-background: var(--light-code-background); 61 | } 62 | 63 | :root[data-theme='dark'] { 64 | --hl-0: var(--dark-hl-0); 65 | --hl-1: var(--dark-hl-1); 66 | --hl-2: var(--dark-hl-2); 67 | --hl-3: var(--dark-hl-3); 68 | --hl-4: var(--dark-hl-4); 69 | --hl-5: var(--dark-hl-5); 70 | --hl-6: var(--dark-hl-6); 71 | --hl-7: var(--dark-hl-7); 72 | --hl-8: var(--dark-hl-8); 73 | --code-background: var(--dark-code-background); 74 | } 75 | 76 | .hl-0 { color: var(--hl-0); } 77 | .hl-1 { color: var(--hl-1); } 78 | .hl-2 { color: var(--hl-2); } 79 | .hl-3 { color: var(--hl-3); } 80 | .hl-4 { color: var(--hl-4); } 81 | .hl-5 { color: var(--hl-5); } 82 | .hl-6 { color: var(--hl-6); } 83 | .hl-7 { color: var(--hl-7); } 84 | .hl-8 { color: var(--hl-8); } 85 | pre, code { background: var(--code-background); } 86 | -------------------------------------------------------------------------------- /docs/assets/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrielCh/proxmox-api/7db0cc6500c9e49d7b9e73f090fe5fccdd57d3e1/docs/assets/widgets.png -------------------------------------------------------------------------------- /docs/assets/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrielCh/proxmox-api/7db0cc6500c9e49d7b9e73f090fe5fccdd57d3e1/docs/assets/widgets@2x.png -------------------------------------------------------------------------------- /docs/interfaces/Proxmox.accessIndex.html: -------------------------------------------------------------------------------- 1 | accessIndex | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface accessIndex

17 |
18 |

Returned by GET /access

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • accessIndex
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
subdir 37 |
38 |
39 |

Properties

40 |
41 | 42 |
subdir: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /docs/interfaces/Proxmox.clusterHaIndex.html: -------------------------------------------------------------------------------- 1 | clusterHaIndex | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface clusterHaIndex

17 |
18 |

Returned by GET /cluster/ha

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • clusterHaIndex
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
id 37 |
38 |
39 |

Properties

40 |
41 | 42 |
id: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /docs/interfaces/Proxmox.clusterSdnIndex.html: -------------------------------------------------------------------------------- 1 | clusterSdnIndex | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface clusterSdnIndex

17 |
18 |

Returned by GET /cluster/sdn

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • clusterSdnIndex
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
id 37 |
38 |
39 |

Properties

40 |
41 | 42 |
id: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /docs/interfaces/Proxmox.clusterTasksTasks.html: -------------------------------------------------------------------------------- 1 | clusterTasksTasks | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface clusterTasksTasks

17 |
18 |

Returned by GET /cluster/tasks

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • clusterTasksTasks
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
upid 37 |
38 |
39 |

Properties

40 |
41 | 42 |
upid: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /docs/interfaces/Proxmox.nodesAptIndex.html: -------------------------------------------------------------------------------- 1 | nodesAptIndex | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface nodesAptIndex

17 |
18 |

Returned by GET /nodes/{node}/apt

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • nodesAptIndex
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
id 37 |
38 |
39 |

Properties

40 |
41 | 42 |
id: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /docs/interfaces/Proxmox.nodesRrdRrd.html: -------------------------------------------------------------------------------- 1 | nodesRrdRrd | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface nodesRrdRrd

17 |
18 |

Returned by GET /nodes/{node}/rrd

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • nodesRrdRrd
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
filename 37 |
38 |
39 |

Properties

40 |
41 | 42 |
filename: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /docs/interfaces/Proxmox.nodesScanIndex.html: -------------------------------------------------------------------------------- 1 | nodesScanIndex | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface nodesScanIndex

17 |
18 |

Returned by GET /nodes/{node}/scan

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • nodesScanIndex
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
method 37 |
38 |
39 |

Properties

40 |
41 | 42 |
method: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /docs/interfaces/Proxmox.poolsIndex.html: -------------------------------------------------------------------------------- 1 | poolsIndex | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface poolsIndex

17 |
18 |

Returned by GET /pools

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • poolsIndex
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
poolid 37 |
38 |
39 |

Properties

40 |
41 | 42 |
poolid: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /docs/interfaces/Proxmox.storageIndex.html: -------------------------------------------------------------------------------- 1 | storageIndex | proxmox-api
2 |
3 | 8 |
9 |
10 |
11 |
12 | 16 |

Interface storageIndex

17 |
18 |

Returned by GET /storage

19 |
20 |
21 |

Hierarchy

22 |
    23 |
  • storageIndex
24 |
25 |

Indexable

26 |
[additionalProperties: string]: any
29 |
30 |
31 |
32 | 33 |
34 |
35 |

Properties

36 |
storage 37 |
38 |
39 |

Properties

40 |
41 | 42 |
storage: string
45 |
72 |
73 |

Generated using TypeDoc

74 |
-------------------------------------------------------------------------------- /generator/README.md: -------------------------------------------------------------------------------- 1 | # api client generator 2 | 3 | to submit change to the main proxmox: 4 | 5 | ## api-viewer present here: 6 | 7 | git clone https://git.proxmox.com/git/pve-docs.git 8 | in api-viewer/apidata.js 9 | 10 | git clone https://git.proxmox.com/git/pve-manager.git 11 | pve-manager/PVE/API2/ReplicationConfig.pm 12 | pve-manager/PVE/API2/Ceph/MDS.pm 13 | 14 | for https://git.proxmox.com/ 15 | 16 | ```javascript 17 | T=$$('a.list').map(A=>A.text).filter(A => A.endsWith('.git')) 18 | T.map(A=>`git clone https://git.proxmox.com/git/${A}`).join('\n') 19 | ``` 20 | grep -r 'Mark replication job for removal' pve* 21 | 22 | ## API doc fixs: 23 | 24 | replace "minimum": "0" by "minimum": 0 25 | replace "optional": "1" by "optional": 1 26 | add missing "type": "string" in parameter without "type" field 27 | 28 | ## project 29 | git clone https://git.proxmox.com/git/aab.git 30 | git clone https://git.proxmox.com/git/arch-pacman.git 31 | git clone https://git.proxmox.com/git/ceph.git 32 | git clone https://git.proxmox.com/git/corosync-pve.git 33 | git clone https://git.proxmox.com/git/corosync-qdevice.git 34 | git clone https://git.proxmox.com/git/criu.git 35 | git clone https://git.proxmox.com/git/dab-pve-appliances.git 36 | git clone https://git.proxmox.com/git/dab.git 37 | git clone https://git.proxmox.com/git/extjs.git 38 | git clone https://git.proxmox.com/git/fence-agents-pve.git 39 | git clone https://git.proxmox.com/git/framework7.git 40 | git clone https://git.proxmox.com/git/frr.git 41 | git clone https://git.proxmox.com/git/ifupdown2.git 42 | git clone https://git.proxmox.com/git/iproute2.git 43 | git clone https://git.proxmox.com/git/kronosnet.git 44 | git clone https://git.proxmox.com/git/ksm-control-daemon.git 45 | git clone https://git.proxmox.com/git/libanyevent-http-perl.git 46 | git clone https://git.proxmox.com/git/libarchive-perl.git 47 | git clone https://git.proxmox.com/git/libgtk3-webkit-perl.git 48 | git clone https://git.proxmox.com/git/libhttp-daemon-perl.git 49 | git clone https://git.proxmox.com/git/libiscsi.git 50 | git clone https://git.proxmox.com/git/libpve-u2f-server-perl.git 51 | git clone https://git.proxmox.com/git/libqb.git 52 | git clone https://git.proxmox.com/git/librados2-perl.git 53 | git clone https://git.proxmox.com/git/libxdgmime-perl.git 54 | git clone https://git.proxmox.com/git/lvm.git 55 | git clone https://git.proxmox.com/git/lxc.git 56 | git clone https://git.proxmox.com/git/lxcfs.git 57 | git clone https://git.proxmox.com/git/novnc-pve.git 58 | git clone https://git.proxmox.com/git/openvswitch.git 59 | git clone https://git.proxmox.com/git/pmg-api.git 60 | git clone https://git.proxmox.com/git/pmg-docs.git 61 | git clone https://git.proxmox.com/git/pmg-gui.git 62 | git clone https://git.proxmox.com/git/pmg-log-tracker.git 63 | git clone https://git.proxmox.com/git/proxmox-acme.git 64 | git clone https://git.proxmox.com/git/proxmox-archive-keyring.git 65 | git clone https://git.proxmox.com/git/proxmox-i18n.git 66 | git clone https://git.proxmox.com/git/proxmox-mailgateway.git 67 | git clone https://git.proxmox.com/git/proxmox-mini-journalreader.git 68 | git clone https://git.proxmox.com/git/proxmox-spamassassin.git 69 | git clone https://git.proxmox.com/git/proxmox-ve.git 70 | git clone https://git.proxmox.com/git/proxmox-widget-toolkit.git 71 | git clone https://git.proxmox.com/git/pve-access-control.git 72 | git clone https://git.proxmox.com/git/pve-apiclient.git 73 | git clone https://git.proxmox.com/git/pve-client.git 74 | git clone https://git.proxmox.com/git/pve-cluster.git 75 | git clone https://git.proxmox.com/git/pve-common.git 76 | git clone https://git.proxmox.com/git/pve-container.git 77 | git clone https://git.proxmox.com/git/pve-docs.git 78 | git clone https://git.proxmox.com/git/pve-edk2-firmware.git 79 | git clone https://git.proxmox.com/git/pve-eslint.git 80 | git clone https://git.proxmox.com/git/pve-firewall.git 81 | git clone https://git.proxmox.com/git/pve-firmware.git 82 | git clone https://git.proxmox.com/git/pve-guest-common.git 83 | git clone https://git.proxmox.com/git/pve-ha-manager.git 84 | git clone https://git.proxmox.com/git/pve-http-server.git 85 | git clone https://git.proxmox.com/git/pve-installer.git 86 | git clone https://git.proxmox.com/git/pve-jslint.git 87 | git clone https://git.proxmox.com/git/pve-kernel-meta.git 88 | git clone https://git.proxmox.com/git/pve-kernel.git 89 | git clone https://git.proxmox.com/git/pve-libseccomp2.4-dev.git 90 | git clone https://git.proxmox.com/git/pve-libspice-server.git 91 | git clone https://git.proxmox.com/git/pve-lxc-syscalld.git 92 | git clone https://git.proxmox.com/git/pve-manager.git 93 | git clone https://git.proxmox.com/git/pve-network.git 94 | git clone https://git.proxmox.com/git/pve-omping.git 95 | git clone https://git.proxmox.com/git/pve-qemu.git 96 | git clone https://git.proxmox.com/git/pve-spice-protocol.git 97 | git clone https://git.proxmox.com/git/pve-storage.git 98 | git clone https://git.proxmox.com/git/pve-xtermjs.git 99 | git clone https://git.proxmox.com/git/pve-zsync.git 100 | git clone https://git.proxmox.com/git/qemu-defaults.git 101 | git clone https://git.proxmox.com/git/qemu-server.git 102 | git clone https://git.proxmox.com/git/qemu.git 103 | git clone https://git.proxmox.com/git/smartmontools.git 104 | git clone https://git.proxmox.com/git/spiceterm.git 105 | git clone https://git.proxmox.com/git/tar.git 106 | git clone https://git.proxmox.com/git/vncterm.git 107 | git clone https://git.proxmox.com/git/zfs-grub.git 108 | git clone https://git.proxmox.com/git/zfsonlinux.git 109 | 110 | ## rust 111 | git clone https://git.proxmox.com/git/cargo.git 112 | git clone https://git.proxmox.com/git/debcargo-conf.git 113 | git clone https://git.proxmox.com/git/dh-cargo.git 114 | git clone https://git.proxmox.com/git/llvm-toolchain.git 115 | git clone https://git.proxmox.com/git/pathpatterns.git 116 | git clone https://git.proxmox.com/git/perlmod.git 117 | git clone https://git.proxmox.com/git/proxmox-backup-meta.git 118 | git clone https://git.proxmox.com/git/proxmox-backup-qemu.git 119 | git clone https://git.proxmox.com/git/proxmox-backup.git 120 | git clone https://git.proxmox.com/git/proxmox-fuse.git 121 | git clone https://git.proxmox.com/git/proxmox.git 122 | git clone https://git.proxmox.com/git/pxar.git 123 | git clone https://git.proxmox.com/git/rustc.git 124 | git clone https://git.proxmox.com/git/wasi-libc.git 125 | 126 | ## mirror 127 | git clone https://git.proxmox.com/git/mirror_acme.sh.git 128 | git clone https://git.proxmox.com/git/mirror_corosync-qdevice.git 129 | git clone https://git.proxmox.com/git/mirror_corosync.git 130 | git clone https://git.proxmox.com/git/mirror_dvb-firmware.git 131 | git clone https://git.proxmox.com/git/mirror_edk2.git 132 | git clone https://git.proxmox.com/git/mirror_frr.git 133 | git clone https://git.proxmox.com/git/mirror_ifupdown2.git 134 | git clone https://git.proxmox.com/git/mirror_iproute2.git 135 | git clone https://git.proxmox.com/git/mirror_kronosnet.git 136 | git clone https://git.proxmox.com/git/mirror_libseccomp.git 137 | git clone https://git.proxmox.com/git/mirror_linux-firmware.git 138 | git clone https://git.proxmox.com/git/mirror_lxc.git 139 | git clone https://git.proxmox.com/git/mirror_lxcfs.git 140 | git clone https://git.proxmox.com/git/mirror_novnc.git 141 | git clone https://git.proxmox.com/git/mirror_qemu.git 142 | git clone https://git.proxmox.com/git/mirror_smartmontools-debian.git 143 | git clone https://git.proxmox.com/git/mirror_spl-debian.git 144 | git clone https://git.proxmox.com/git/mirror_spl.git 145 | git clone https://git.proxmox.com/git/mirror_ubuntu-artful-kernel.git 146 | git clone https://git.proxmox.com/git/mirror_ubuntu-bionic-kernel.git 147 | git clone https://git.proxmox.com/git/mirror_ubuntu-disco-kernel.git 148 | git clone https://git.proxmox.com/git/mirror_ubuntu-eoan-kernel.git 149 | git clone https://git.proxmox.com/git/mirror_ubuntu-focal-kernel.git 150 | git clone https://git.proxmox.com/git/mirror_ubuntu-zesty-kernel.git 151 | git clone https://git.proxmox.com/git/mirror_xterm.js.git 152 | git clone https://git.proxmox.com/git/mirror_zfs-debian.git 153 | git clone https://git.proxmox.com/git/mirror_zfs.git 154 | 155 | ## legacy 156 | git clone https://git.proxmox.com/git/apt.git 157 | git clone https://git.proxmox.com/git/cgmanager.git 158 | git clone https://git.proxmox.com/git/dlm.git 159 | git clone https://git.proxmox.com/git/drbd-utils.git 160 | git clone https://git.proxmox.com/git/gfs2-utils.git 161 | git clone https://git.proxmox.com/git/glusterfs.git 162 | git clone https://git.proxmox.com/git/libnet-http-perl.git 163 | git clone https://git.proxmox.com/git/libusb.git 164 | git clone https://git.proxmox.com/git/openais-pve.git 165 | git clone https://git.proxmox.com/git/parted.git 166 | git clone https://git.proxmox.com/git/pve-kernel-2.6.32.git 167 | git clone https://git.proxmox.com/git/pve-kernel-3.10.0.git 168 | git clone https://git.proxmox.com/git/pve-kernel-jessie.git 169 | git clone https://git.proxmox.com/git/pve-qemu-kvm.git 170 | git clone https://git.proxmox.com/git/pve-sheepdog.git 171 | git clone https://git.proxmox.com/git/pve2-api-doc.git 172 | git clone https://git.proxmox.com/git/redhat-cluster-pve.git 173 | git clone https://git.proxmox.com/git/resource-agents-pve.git 174 | git clone https://git.proxmox.com/git/usb-redir.git 175 | git clone https://git.proxmox.com/git/vzctl.git 176 | git clone https://git.proxmox.com/git/vzquota.git" -------------------------------------------------------------------------------- /generator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "clean": "rimraf src/*.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@types/node": "^16.11.11", 14 | "rimraf": "^3.0.2", 15 | "ts-node": "^10.4.0", 16 | "typescript": "^4.5.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /generator/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.1' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | devDependencies: 8 | '@types/node': 9 | specifier: ^16.11.11 10 | version: 16.11.47 11 | rimraf: 12 | specifier: ^3.0.2 13 | version: 3.0.2 14 | ts-node: 15 | specifier: ^10.4.0 16 | version: 10.9.1(@types/node@16.11.47)(typescript@4.7.4) 17 | typescript: 18 | specifier: ^4.5.2 19 | version: 4.7.4 20 | 21 | packages: 22 | 23 | /@cspotcode/source-map-support@0.8.1: 24 | resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 25 | engines: {node: '>=12'} 26 | dependencies: 27 | '@jridgewell/trace-mapping': 0.3.9 28 | dev: true 29 | 30 | /@jridgewell/resolve-uri@3.1.0: 31 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} 32 | engines: {node: '>=6.0.0'} 33 | dev: true 34 | 35 | /@jridgewell/sourcemap-codec@1.4.14: 36 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} 37 | dev: true 38 | 39 | /@jridgewell/trace-mapping@0.3.9: 40 | resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 41 | dependencies: 42 | '@jridgewell/resolve-uri': 3.1.0 43 | '@jridgewell/sourcemap-codec': 1.4.14 44 | dev: true 45 | 46 | /@tsconfig/node10@1.0.9: 47 | resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} 48 | dev: true 49 | 50 | /@tsconfig/node12@1.0.11: 51 | resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} 52 | dev: true 53 | 54 | /@tsconfig/node14@1.0.3: 55 | resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} 56 | dev: true 57 | 58 | /@tsconfig/node16@1.0.3: 59 | resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} 60 | dev: true 61 | 62 | /@types/node@16.11.47: 63 | resolution: {integrity: sha512-fpP+jk2zJ4VW66+wAMFoBJlx1bxmBKx4DUFf68UHgdGCOuyUTDlLWqsaNPJh7xhNDykyJ9eIzAygilP/4WoN8g==} 64 | dev: true 65 | 66 | /acorn-walk@8.2.0: 67 | resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} 68 | engines: {node: '>=0.4.0'} 69 | dev: true 70 | 71 | /acorn@8.8.0: 72 | resolution: {integrity: sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==} 73 | engines: {node: '>=0.4.0'} 74 | hasBin: true 75 | dev: true 76 | 77 | /arg@4.1.3: 78 | resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} 79 | dev: true 80 | 81 | /balanced-match@1.0.2: 82 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 83 | dev: true 84 | 85 | /brace-expansion@1.1.11: 86 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 87 | dependencies: 88 | balanced-match: 1.0.2 89 | concat-map: 0.0.1 90 | dev: true 91 | 92 | /concat-map@0.0.1: 93 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} 94 | dev: true 95 | 96 | /create-require@1.1.1: 97 | resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} 98 | dev: true 99 | 100 | /diff@4.0.2: 101 | resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} 102 | engines: {node: '>=0.3.1'} 103 | dev: true 104 | 105 | /fs.realpath@1.0.0: 106 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 107 | dev: true 108 | 109 | /glob@7.2.3: 110 | resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} 111 | dependencies: 112 | fs.realpath: 1.0.0 113 | inflight: 1.0.6 114 | inherits: 2.0.4 115 | minimatch: 3.1.2 116 | once: 1.4.0 117 | path-is-absolute: 1.0.1 118 | dev: true 119 | 120 | /inflight@1.0.6: 121 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 122 | dependencies: 123 | once: 1.4.0 124 | wrappy: 1.0.2 125 | dev: true 126 | 127 | /inherits@2.0.4: 128 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 129 | dev: true 130 | 131 | /make-error@1.3.6: 132 | resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} 133 | dev: true 134 | 135 | /minimatch@3.1.2: 136 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 137 | dependencies: 138 | brace-expansion: 1.1.11 139 | dev: true 140 | 141 | /once@1.4.0: 142 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 143 | dependencies: 144 | wrappy: 1.0.2 145 | dev: true 146 | 147 | /path-is-absolute@1.0.1: 148 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 149 | engines: {node: '>=0.10.0'} 150 | dev: true 151 | 152 | /rimraf@3.0.2: 153 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 154 | hasBin: true 155 | dependencies: 156 | glob: 7.2.3 157 | dev: true 158 | 159 | /ts-node@10.9.1(@types/node@16.11.47)(typescript@4.7.4): 160 | resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} 161 | hasBin: true 162 | peerDependencies: 163 | '@swc/core': '>=1.2.50' 164 | '@swc/wasm': '>=1.2.50' 165 | '@types/node': '*' 166 | typescript: '>=2.7' 167 | peerDependenciesMeta: 168 | '@swc/core': 169 | optional: true 170 | '@swc/wasm': 171 | optional: true 172 | dependencies: 173 | '@cspotcode/source-map-support': 0.8.1 174 | '@tsconfig/node10': 1.0.9 175 | '@tsconfig/node12': 1.0.11 176 | '@tsconfig/node14': 1.0.3 177 | '@tsconfig/node16': 1.0.3 178 | '@types/node': 16.11.47 179 | acorn: 8.8.0 180 | acorn-walk: 8.2.0 181 | arg: 4.1.3 182 | create-require: 1.1.1 183 | diff: 4.0.2 184 | make-error: 1.3.6 185 | typescript: 4.7.4 186 | v8-compile-cache-lib: 3.0.1 187 | yn: 3.1.1 188 | dev: true 189 | 190 | /typescript@4.7.4: 191 | resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} 192 | engines: {node: '>=4.2.0'} 193 | hasBin: true 194 | dev: true 195 | 196 | /v8-compile-cache-lib@3.0.1: 197 | resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} 198 | dev: true 199 | 200 | /wrappy@1.0.2: 201 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 202 | dev: true 203 | 204 | /yn@3.1.1: 205 | resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} 206 | engines: {node: '>=6'} 207 | dev: true 208 | -------------------------------------------------------------------------------- /generator/src/pveapiModel.ts: -------------------------------------------------------------------------------- 1 | export type PveHttpMtd = "DELETE" | 'GET' | 'PUT' | 'POST'; 2 | 3 | export interface PveCommon { 4 | description?: string; 5 | verbose_description?: string; 6 | optional?: 0 | 1 | '1'; // add '1' since Proxmox 8 7 | } 8 | 9 | export interface PveFormatNumber extends PveCommon { 10 | type: 'integer' | 'number'; 11 | default?: 0 | 1 | 5 | 100 | 512 | 1024 | 1000 | 1000000, 12 | default_key?: 1, 13 | maximum?: number, 14 | minimum?: number | '0', 15 | max?: number, // duplicate with minimum since Proxmox 8 16 | min?: number, // duplicate with minimum since Proxmox 8 17 | format_description?: string; 18 | } 19 | 20 | export interface PveFormatBoolean extends PveCommon { 21 | type: 'boolean'; 22 | default_key?: 1; 23 | default?: '0' | 0 | '1' | 1; 24 | } 25 | 26 | export interface PveFormatString extends PveCommon { 27 | type: 'string'; 28 | default?: string, 29 | format_description?: string; 30 | maxLength?: number; 31 | default_key?: 1; 32 | format?: string; 33 | enum?: string[]; 34 | pattern?: string; 35 | typetext?: string; // Since Proxmox 8 36 | } 37 | 38 | export interface PveFormatAlias extends PveCommon { 39 | alias?: string; 40 | keyAlias? : "model"; 41 | } 42 | 43 | export type PveFormat = PveFormatNumber | PveFormatBoolean | PveFormatString | PveFormatAlias; 44 | 45 | export interface PveParametersCommon extends PveCommon { 46 | additionalProperties?: 0 | 1; 47 | } 48 | 49 | export interface PveParametersNumber extends PveParametersCommon { 50 | type: "number"; 51 | // format?: string; 52 | minimum?: number | '0',// add '0' since Proxmox 8 53 | maximum?: number, 54 | default?: string | number; 55 | typetext?: string; 56 | renderer?: 'bytes' | "timestamp" | 'timestamp_gmt' | 'duration' | 'fraction_as_percentage'; 57 | title?: string; // only inside items 58 | // reference to an other key of the current object 59 | requires?: string; // "delete" | "todisk" | "archive" | "db_dev" | "wal_dev"; 60 | } 61 | 62 | export interface PveParametersInteger extends PveParametersCommon { 63 | type: "integer"; 64 | format?: string; 65 | minimum?: number | '0' | '1', // add '0' | '1' since Proxmox 8 66 | maximum?: number, 67 | default?: string | number; // value or text like same as 68 | typetext?: string; 69 | // renderer?: 'duration'; 70 | renderer?: 'bytes' | "timestamp" | 'timestamp_gmt' | 'duration' | 'fraction_as_percentage'; 71 | title?: string; // only inside items 72 | // reference to an other key of the current object 73 | requires?: string; // "delete" | "todisk" | "archive" | "db_dev" | "wal_dev"; 74 | } 75 | 76 | /** 77 | * @since Proxmox 8 78 | */ 79 | export interface PveParametersEnum extends PveParametersCommon { 80 | type: 'enum'; 81 | enum: string[]; 82 | } 83 | 84 | export interface PveParametersString extends PveParametersCommon { 85 | type: 'string'; 86 | title?: string; // only inside items 87 | format?: string | {[name: string]: PveFormat}; // "mac-addr", 88 | minLength?: number; 89 | maxLength?: number; 90 | enum?: string[]; 91 | default?: string | null; 92 | // // reference to an other key of the current object 93 | requires?: string; // "delete" | "todisk" | "archive" | "db_dev" | "wal_dev"; 94 | pattern?: string; 95 | format_description?: string; 96 | typetext?: string; //"" | "" | "" | " (0 - N)" | " (1 - N)" | " (1 - N)" | "[address=] [,priority=]"|"[[type=]] [,memory=]"|" (0 - 8)"| ""| "[meta=] [,network=] [,user=]"|" (16 - N)"| " (0 - N)" | ":"| "[:]{,[:]}*"|""|"[gw=] [,gw6=] [,ip=] [,ip6=]"|"[file=] [,aio=] [,backup=<1|0>] [,bps=] [,bps_max_length=] [,bps_rd=] [,bps_rd_max_length=] [,bps_wr=] [,bps_wr_max_length=] [,cache=] [,cyls=] [,detect_zeroes=<1|0>] [,discard=] [,format=] [,heads=] [,iops=] [,iops_max=] [,iops_max_length=] [,iops_rd=] [,iops_rd_max=] [,iops_rd_max_length=] [,iops_wr=] [,iops_wr_max=] [,iops_wr_max_length=] [,mbps=] [,mbps_max=] [,mbps_rd=] [,mbps_rd_max=] [,mbps_wr=] [,mbps_wr_max=] [,media=] [,model=] [,replicate=<1|0>] [,rerror=] [,secs=] [,serial=] [,shared=<1|0>] [,size=] [,snapshot=<1|0>] [,ssd=<1|0>] [,trans=] [,werror=] [,wwn=]"|"size= [,name=]"; 97 | renderer?: 'bytes'; 98 | } 99 | 100 | export interface PveParametersBoolean extends PveParametersCommon { 101 | type: 'boolean'; 102 | default?: number | '1' | '0' | "yes" | 'off' | "0; for erasure coded pools: 1"; 103 | // @since PVE 7 104 | title?: string; 105 | // reference to an other key of the current object 106 | requires?: string; // "delete" | "todisk" | "archive" | "db_dev" | "wal_dev"; 107 | typetext?: string; //"" | "" | "" | " (0 - N)" | " (1 - N)" | " (1 - N)" | "[address=] [,priority=]"|"[[type=]] [,memory=]"|" (0 - 8)"| ""| "[meta=] [,network=] [,user=]"|" (16 - N)"| " (0 - N)" | ":"| "[:]{,[:]}*"|""|"[gw=] [,gw6=] [,ip=] [,ip6=]"|"[file=] [,aio=] [,backup=<1|0>] [,bps=] [,bps_max_length=] [,bps_rd=] [,bps_rd_max_length=] [,bps_wr=] [,bps_wr_max_length=] [,cache=] [,cyls=] [,detect_zeroes=<1|0>] [,discard=] [,format=] [,heads=] [,iops=] [,iops_max=] [,iops_max_length=] [,iops_rd=] [,iops_rd_max=] [,iops_rd_max_length=] [,iops_wr=] [,iops_wr_max=] [,iops_wr_max_length=] [,mbps=] [,mbps_max=] [,mbps_rd=] [,mbps_rd_max=] [,mbps_wr=] [,mbps_wr_max=] [,media=] [,model=] [,replicate=<1|0>] [,rerror=] [,secs=] [,serial=] [,shared=<1|0>] [,size=] [,snapshot=<1|0>] [,ssd=<1|0>] [,trans=] [,werror=] [,wwn=]"|"size= [,name=]"; 108 | } 109 | 110 | export interface PveCallParametersNull extends PveParametersCommon { 111 | type: "null"; 112 | } 113 | 114 | export interface PveParametersArray extends PveParametersCommon { 115 | type: "array"; 116 | // @since PVE 7 117 | title?: string; 118 | // if not set use a PveParametersString 119 | items?: PveParametersBaseSet, // subset of PveCallParameters, 120 | links?: { href: string, rel: "child" }[]; 121 | renderer?: 'yaml'; 122 | typetext?: "";// @since Proxmox 8 123 | } 124 | 125 | export interface PveParametersObject extends PveParametersCommon { 126 | type: "object"; 127 | // @since PVE 7 128 | title?: string; 129 | renderer?: "yaml", 130 | properties?: { [name: string]: PveParametersBaseSet | PveParametersBoolean | PveParametersEnum}; 131 | items?: PveParametersObject; // only used in Proxmox 8, looks to be an error 132 | } 133 | 134 | export interface PveParametersUndef extends PveParametersCommon { 135 | type?: never; 136 | properties?: { [name: string]: PveParametersBaseSet | PveParametersBoolean}; 137 | } 138 | 139 | // @since PVE 7 140 | export interface PveParametersAny extends PveParametersCommon { 141 | type: "any"; 142 | } 143 | 144 | export type PveParametersBaseSet = PveParametersString | PveParametersArray | PveParametersObject | PveParametersInteger | PveParametersNumber; 145 | 146 | export type PveCallParameters = PveParametersBaseSet | PveParametersBoolean | 147 | PveCallParametersNull | PveParametersUndef | PveParametersAny; 148 | 149 | export type PceCheck = 1 | string | PceCheck[]; 150 | 151 | export interface PveCallDesc { 152 | allowtoken: 0 | 1; 153 | description: string; 154 | method: PveHttpMtd, 155 | name: string, 156 | parameters: { 157 | additionalProperties: 0 | 1; 158 | properties?: { [name: string]: PveCallParameters; } 159 | type?: 'object'; 160 | }; 161 | permissions?: { 162 | user?: "all" | 'world' 163 | description?: string; 164 | check?: PceCheck[]; 165 | }; 166 | protected?: 1; 167 | proxyto?: "node" | null; 168 | returns: PveCallParameters; 169 | } 170 | export interface pveApiNode { 171 | children?: pveApiNode[]; 172 | info?: { // {[key in PveHttpMtd]: PveCallDesc} 173 | DELETE?: PveCallDesc; 174 | GET?: PveCallDesc; 175 | PUT?: PveCallDesc; 176 | POST?: PveCallDesc; 177 | } 178 | leaf: number; 179 | path: string; 180 | text: string; 181 | } 182 | -------------------------------------------------------------------------------- /generator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "ES2020", 6 | /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 7 | "module": "commonjs", 8 | /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | // "outDir": "./", /* Redirect output structure to the directory. */ 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true, 29 | /* Enable all strict type-checking options. */ 30 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 31 | // "strictNullChecks": true, /* Enable strict null checks. */ 32 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 33 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 34 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 35 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 36 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 37 | 38 | /* Additional Checks */ 39 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 40 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 41 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 42 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 43 | 44 | "moduleResolution": "node", 45 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 46 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 47 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 48 | // "typeRoots": [], /* List of folders to include type definitions from. */ 49 | // "types": [], /* Type declaration files to be included in compilation. */ 50 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 51 | "esModuleInterop": true, 52 | /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 55 | 56 | /* Source Map Options */ 57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 61 | 62 | /* Experimental Options */ 63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 65 | 66 | /* Advanced Options */ 67 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 68 | } 69 | } -------------------------------------------------------------------------------- /proxmox-usb-hotplug/README.md: -------------------------------------------------------------------------------- 1 | # proxmox-usb-hotplug 2 | 3 | * [![NPM Version](https://img.shields.io/npm/v/proxmox-usb-hotplug.svg?style=flat)](https://www.npmjs.org/package/proxmox-usb-hotplug) 4 | 5 | ## basic setup without configuration. 6 | 7 | ```bash 8 | sudo apt-get install libudev-dev 9 | npm install -g proxmox-usb-hotplug 10 | # double check your user PATH env for a local bode bion path 11 | echo $PATH 12 | sudo -i 13 | # update PATH if needed 14 | proxmox-usb-hotplug 15 | # enter the your localhost root password, it will be replace by ************************ after pressing return key 16 | ``` 17 | 18 | if you have a single GFX passthrough, it will works out of the box. 19 | 20 | ## usage 21 | 22 | This script monitor and plug any new USB device to running KVM hosted by a proxmox server. 23 | 24 | ```bash 25 | Usage: proxmox-usb-hotplug [options] 26 | 27 | Hotplug any new Usb device to your proxmox 28 | 29 | Options: 30 | -V, --version output the version number 31 | --vmid vmid will receive USB Devices, by default the first running VM having an hostpci0 32 | --user host to connect if not root@pam (default: "root@pam") 33 | --port port to connect if not 8006 (default: "8006") 34 | --host host to connect if not 127.0.0.1 (default: "127.0.0.1") 35 | -p, --pass [pass] host password, prefed stdin, nodejs script can not hide password from command line 36 | -c, --config provide a configuration file 37 | -h, --help display help for command 38 | 39 | ``` 40 | 41 | ## configuration file 42 | 43 | ``` 44 | # username used to connect to proxmox API 45 | username = root@pam 46 | # password used to connect to proxmox API 47 | password = prOxmOxp@sswOd 48 | # host used to connect to proxmox API should always be localhost 49 | host = 127.0.0.1 50 | 51 | # the node name to used 52 | # by default use the first node, if you are using a cluster you should give the cluster name 53 | ;node=proxmox 54 | 55 | # deny an usb device 56 | deny-usb=001c:100d 57 | # deny an usb hub 58 | # you should always deny USB hub to avoid a dual passthrough 59 | deny-usb=1a40:0101 60 | 61 | 62 | # Connect some device at startup recommanded for keyboard and mouse 63 | # a keyboard 64 | force-usb=258a:1006 65 | # a mouse 66 | force-usb=18f8:0f97 67 | 68 | # not implemented yet. 69 | ; watch = 60 70 | 71 | # unplug all existing device on script startup 72 | # then reconnect all force-usb devices 73 | flush = 1 74 | 75 | # try to autodetect USB Hub to avoid dual passthrough 76 | no-hub=1 77 | ``` 78 | 79 | ## sample 80 | 81 | ```bash 82 | # on the dom0 83 | echo password = myPassword > conf.txt 84 | echo flush = 1 >> conf.txt 85 | proxmox-usb-hotplug -c conf.txt 86 | ``` 87 | 88 | ## use as a service 89 | 90 | */etc/usb-hotplug.conf* 91 | ``` 92 | password = prOxmOxp@sswOd 93 | deny-usb=001c:100d 94 | deny-usb=1a40:0101 95 | force-usb=258a:1006 96 | force-usb=18f8:0f97 97 | flush = 1 98 | no-hub=1 99 | ``` 100 | 101 | *proxmox-usb-hotplug.config.js* 102 | ```javascript 103 | module.exports = { 104 | apps: [{ 105 | name: "proxmoxUsbHotplug", 106 | script: "proxmox-usb-hotplug", 107 | args: ["--config", "/etc/hotplug-usb.conf"], 108 | watch: [ "/etc/hotplug-usb.conf" ], 109 | }] 110 | } 111 | ``` 112 | 113 | ```bash 114 | chmod 600 /etc/usb-hotplug.conf 115 | npm install -g proxmox-usb-hotplug 116 | npm install -g pm2 117 | pm2 install pm2-logrotate 118 | pm2 startup 119 | # read the instruction 120 | pm2 status proxmox-usb-hotplug.config.js 121 | pm2 save 122 | ``` 123 | 124 | ## TODO 125 | 126 | - Deal with USB 3.x. 127 | - auto detect current cluster name. 128 | -------------------------------------------------------------------------------- /proxmox-usb-hotplug/bin/proxmox-usb-hotplug: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../src/proxmox-usb-hotplug.js') 3 | -------------------------------------------------------------------------------- /proxmox-usb-hotplug/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proxmox-usb-hotplug", 3 | "version": "0.1.6", 4 | "description": "Hot Plug any new USB device to a running proxmox KVM", 5 | "main": "src/proxmox-usb-hotplug.js", 6 | "keywords": [ 7 | "usb", 8 | "hotplug", 9 | "proxmox" 10 | ], 11 | "author": "Uriel Chemouni (https://urielch.github.io/)", 12 | "license": "GPL-3.0", 13 | "funding": "https://github.com/sponsors/urielch", 14 | "bin": { 15 | "proxmox-usb-hotplug": "./bin/proxmox-usb-hotplug" 16 | }, 17 | "publishConfig": { 18 | "access": "public" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/UrielCh/proxmox-api.git" 23 | }, 24 | "homepage": "https://github.com/UrielCh/proxmox-api/tree/master/proxmox-usb-hotplug", 25 | "bugs": "https://github.com/UrielCh/proxmox-api/issues", 26 | "scripts": { 27 | "build": "tsc -p .", 28 | "build:watch": "tsc -p . --watch", 29 | "prepare": "npm run build", 30 | "clean": "rimraf src/*.js" 31 | }, 32 | "dependencies": { 33 | "commander": "^6.1.0", 34 | "dev-input-reader": "0.0.1", 35 | "password-prompt": "^1.1.2", 36 | "proxmox-api": "^0.1.1", 37 | "usb": "^1.6.3" 38 | }, 39 | "devDependencies": { 40 | "@types/node": "^14.11.2", 41 | "@types/usb": "^1.5.2", 42 | "rimraf": "^3.0.2", 43 | "ts-node": "^9.0.0", 44 | "typescript": "^4.0.3" 45 | }, 46 | "files": [ 47 | "src/*.js", 48 | "bin" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /proxmox-usb-hotplug/src/HotPlugService.ts: -------------------------------------------------------------------------------- 1 | import { QmMonitor, Proxmox } from "proxmox-api"; 2 | import nodeUsb, { Device } from 'usb'; 3 | import delay from './delay'; 4 | 5 | export interface HotPlugServiceOption { 6 | vmid?: number; 7 | node?: string; 8 | denyUsb?: Set; 9 | // denyProduct?: Set; 10 | forceUsb?: Set; 11 | watch?: number; 12 | flush?: number; 13 | blockHub?: boolean; 14 | } 15 | 16 | export interface USBOverview { 17 | vendorId: string; 18 | productId: string; 19 | port: string; 20 | addr: number; 21 | bus: number; 22 | ancestor: string[]; 23 | } 24 | 25 | const vendorFromDeviceSync = (device: Device): USBOverview => { 26 | const deviceDescriptor = device.deviceDescriptor; 27 | const vendorId = deviceDescriptor.idVendor.toString(16).padStart(4, '0'); 28 | const productId = deviceDescriptor.idProduct.toString(16).padStart(4, '0'); 29 | const port = device.portNumbers ? device.portNumbers.join('.') : ''; 30 | const addr = device.deviceAddress; 31 | const bus = device.busNumber; 32 | const ancestor = [] as string[]; 33 | let parent = device.parent 34 | while (parent) { 35 | const deviceDescriptor = parent.deviceDescriptor; 36 | const vendorId = deviceDescriptor.idVendor.toString(16).padStart(4, '0'); 37 | const productId = deviceDescriptor.idProduct.toString(16).padStart(4, '0'); 38 | ancestor.push(`${vendorId}:${productId}`); 39 | parent = parent.parent; 40 | } 41 | return { vendorId, productId, port, addr, bus, ancestor }; 42 | } 43 | 44 | const vendorFromDevice = async (device: Device): Promise => { 45 | const overview = vendorFromDeviceSync(device) 46 | const getStringDescriptor = (id: number): Promise => new Promise((resolve, reject) => device.getStringDescriptor(id, (error, text) => { if (error) resolve(`${id}`); else resolve(text || '') })); 47 | const deviceDescriptor = device.deviceDescriptor; 48 | const manufacturer = await getStringDescriptor(deviceDescriptor.iManufacturer); 49 | const deviceName = await getStringDescriptor(deviceDescriptor.iProduct); 50 | return { ...overview, manufacturer, deviceName }; 51 | } 52 | 53 | export default class HotPlugService { 54 | private qmMonitor?: QmMonitor; 55 | private options: HotPlugServiceOption; 56 | 57 | constructor(private proxmox: Proxmox.Api, options?: HotPlugServiceOption) { 58 | this.options = options || {}; 59 | } 60 | 61 | async watch(interval: number) { 62 | while (true) { 63 | const qmMonitor = await this.getQmMonitor(); 64 | if (!qmMonitor) 65 | return; 66 | let devices = await qmMonitor.infoUsb(); 67 | for (const device of devices) { 68 | // if (!device.product && !device.id) { 69 | // console.log(`remove ${device.id}`); 70 | // await qmMonitor.deviceDel(device.id); 71 | // } 72 | //if (this.options.denyProduct && this.options.denyProduct.has(device.product)) { 73 | // await qmMonitor.deviceDel(device.id); 74 | // console.log(`remove ${device.id}`); 75 | // continue; 76 | //} 77 | //if (this.options.denyUsb && this.options.denyProduct.has(device.product)) { 78 | // await qmMonitor.deviceDel(device.id); 79 | // continue; 80 | //} 81 | } 82 | // console.log(devices); 83 | if (!interval) 84 | return; 85 | await delay(interval * 1000); 86 | } 87 | } 88 | 89 | async findPassthroughVmid(): Promise { 90 | let nodes = await this.proxmox.nodes.$get(); 91 | if (this.options.node) 92 | nodes = nodes.filter(n => n.node === node); 93 | if (!nodes.length) 94 | throw Error(`missng node ${this.options.node}`); 95 | const node = nodes[0].node; 96 | const vms = await this.proxmox.nodes.$(node).qemu.$get(); 97 | for (const vm of vms) { 98 | if (vm.status != 'running') { 99 | continue; 100 | } 101 | const config = await this.proxmox.nodes.$(node).qemu.$(vm.vmid).config.$get(); 102 | if (config.hostpci0) { 103 | console.log(`Using ${vm.vmid} as Passthrough vmid`) 104 | return vm.vmid; 105 | } 106 | } 107 | return 0; 108 | } 109 | 110 | private async getQmMonitor(): Promise { 111 | if (!this.options.vmid) { 112 | // MODE AUTODETECT 113 | if (this.qmMonitor) { 114 | const info = await this.qmMonitor.info('status'); 115 | // VM status: running 116 | // ERROR: VM 2001 not running 117 | if (info.includes('not running')) 118 | this.qmMonitor = undefined; 119 | } 120 | } 121 | 122 | if (!this.qmMonitor) { 123 | const nodes = await this.proxmox.nodes.$get(); 124 | // this.node = nodes[0].node; 125 | let vmid = this.options.vmid; 126 | if (!vmid) { 127 | vmid = await this.findPassthroughVmid(); 128 | } 129 | if (!vmid) { 130 | console.log('NO Passthrough currently running'); 131 | return null; 132 | } 133 | this.qmMonitor = new QmMonitor(this.proxmox, nodes[0].node, vmid); 134 | // New VM of first call 135 | if (this.options.flush) 136 | await this.detachAll(this.qmMonitor); 137 | await this.attacheForced(this.qmMonitor) 138 | } 139 | return this.qmMonitor; 140 | } 141 | 142 | private async detachAll(qmMonitor: QmMonitor) { 143 | // const qmMonitor = await this.getQmMonitor(); 144 | if (qmMonitor) { 145 | const devices = await qmMonitor.infoUsb(); 146 | for (const device of devices) 147 | await qmMonitor.deviceDel(device.id) 148 | } 149 | } 150 | 151 | private async attacheForced(qmMonitor: QmMonitor) { 152 | const forceUsb = this.options.forceUsb; 153 | //if (forceUsb && forceUsb.size) { 154 | // const qmMonitor = await this.getQmMonitor(); 155 | if (qmMonitor) { 156 | let devices: Device[] = nodeUsb.getDeviceList() 157 | for (const device of devices) { 158 | const info = vendorFromDeviceSync(device); 159 | if (this.options.blockHub) { 160 | for (const parent of info.ancestor) { 161 | if (!this.options.denyUsb) 162 | this.options.denyUsb = new Set(); 163 | this.options.denyUsb.add(parent); 164 | } 165 | } 166 | const { vendorId, productId, bus, port } = info; 167 | if (forceUsb && forceUsb.has(`${vendorId}:${productId}`)) { 168 | const key = `B${bus}P${port}`; 169 | const ret = await qmMonitor.deviceAddByPort(key, { bus, port }); 170 | } else { 171 | } 172 | } 173 | } 174 | } 175 | 176 | /** 177 | * start hotplug processing 178 | */ 179 | public async hotPlugByPort(): Promise { 180 | // complet initialization 181 | await this.getQmMonitor(); 182 | nodeUsb.on('attach', async (device: Device) => { 183 | const { vendorId, productId, manufacturer, deviceName, port, bus } = await vendorFromDevice(device); 184 | if (this.options.denyUsb && this.options.denyUsb.has(`${vendorId}:${productId}`)) { 185 | console.log(`ignoring ${manufacturer}(${deviceName})[${vendorId}:${productId}]`); 186 | return; 187 | } 188 | const qmMonitor = await this.getQmMonitor(); 189 | if (!qmMonitor) { 190 | console.log(`new USB Device, but no Passthrough detected: ${manufacturer}(${deviceName})[${vendorId}:${productId}]`); 191 | return; 192 | } 193 | const key = `B${bus}P${port}`; 194 | // console.log(device); 195 | const ret = await qmMonitor.deviceAddByPort(key, { bus, port }); 196 | console.log(`Add USB: ${manufacturer}(${deviceName})[${vendorId}:${productId}] with Key:${key} to VM ${qmMonitor.vmid}: ${ret || 'Ok'}`); 197 | }); 198 | 199 | nodeUsb.on('detach', async (device: Device) => { 200 | const { vendorId, productId, manufacturer, deviceName, port, bus } = await vendorFromDevice(device); 201 | const qmMonitor = await this.getQmMonitor(); 202 | let action = ''; 203 | if (qmMonitor) { 204 | const key = `B${bus}P${port}`; 205 | const ret = await qmMonitor.deviceDel(key); 206 | action = ` unplug ${key} from VM ${qmMonitor.vmid}: ${ret.trim() || 'Ok'}`; 207 | } 208 | console.log(`remove USB: ${manufacturer}(${deviceName})[${vendorId}:${productId}]${action}`); 209 | }); 210 | this.watch(this.options.watch || 0); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /proxmox-usb-hotplug/src/config.ts: -------------------------------------------------------------------------------- 1 | import commander from 'commander'; 2 | import { HotPlugServiceOption } from "./HotPlugService"; 3 | import fs from 'fs'; 4 | import { ProxmoxEngineOptions } from 'proxmox-api'; 5 | const prompt = require('password-prompt') 6 | 7 | export async function loadConfig(program: commander.Command): Promise<{proxmoxOption: ProxmoxEngineOptions, usbOption: HotPlugServiceOption}> { 8 | let password = program.pass; 9 | let denyUsb = new Set(); 10 | let forceUsb = new Set(); 11 | let usbOption: HotPlugServiceOption = { denyUsb, forceUsb }; 12 | if (!password) { 13 | password = program.p; 14 | } 15 | if (!password) { 16 | password = program.P; 17 | } 18 | let host = '127.0.0.1'; 19 | let username = 'root@pam'; 20 | let port = 8006; 21 | if (program.config) { 22 | let data = ''; 23 | try { 24 | data = await fs.promises.readFile(program.config, { encoding: 'utf-8' }) 25 | } catch (e) { 26 | console.error(`can not read file ${program.config}.`); 27 | process.exit(-1); 28 | } 29 | const lines = data.split(/[\r\n]+/); 30 | for (let line of lines) { 31 | line = line.trim(); 32 | if (line.startsWith('#') || line.startsWith(';') || line.startsWith('//')) 33 | continue; 34 | const m = line.match(/([a-zA-Z-]+)\s*=\s*([^\s]+)/) 35 | if (!m) 36 | continue; 37 | const [, k, v] = m; 38 | switch (k) { 39 | case 'password': 40 | case 'pass': 41 | if (!password) 42 | password = v; 43 | break; 44 | case 'node': 45 | usbOption.node = v; 46 | break 47 | case 'watch': 48 | usbOption.watch = Number(v); 49 | break 50 | case 'port': 51 | port = Number(port); 52 | break 53 | case 'flush': 54 | usbOption.flush = 1; 55 | break 56 | case 'host': 57 | host = v; 58 | break 59 | case 'username': 60 | username = v; 61 | break 62 | case 'deny-usb': 63 | for (const ref of [...v.matchAll(/[0-9a-fA-F]{4}:[0-9a-fA-F]{4}/g)]) 64 | denyUsb.add(ref[0].toLowerCase()) 65 | break; 66 | case 'force-usb': 67 | for (const ref of [...v.matchAll(/[0-9a-fA-F]{4}:[0-9a-fA-F]{4}/g)]) 68 | forceUsb.add(ref[0].toLowerCase()) 69 | break; 70 | case 'no-hub': 71 | usbOption.blockHub = true; 72 | break; 73 | default: 74 | console.error(`unknown option ${k} in ${program.config}.`); 75 | } 76 | } 77 | } 78 | if (program.host) { 79 | host = program.host; 80 | } 81 | if (program.port) { 82 | port = Number(program.port); 83 | } 84 | if (!password) { 85 | password = await prompt('proxmox password: ') 86 | } 87 | usbOption.vmid = Number(program.vmid); 88 | 89 | const proxmoxOption: ProxmoxEngineOptions = { host, port, password, username }; 90 | return {proxmoxOption, usbOption}; 91 | } -------------------------------------------------------------------------------- /proxmox-usb-hotplug/src/delay.ts: -------------------------------------------------------------------------------- 1 | const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); 2 | export default delay; 3 | 4 | -------------------------------------------------------------------------------- /proxmox-usb-hotplug/src/proxmox-hotkey.ts: -------------------------------------------------------------------------------- 1 | import DevInputReader from "dev-input-reader"; 2 | import { Proxmox, proxmoxApi } from 'proxmox-api'; 3 | import { Command } from 'commander'; 4 | import { loadConfig } from './config'; 5 | import { stat } from "fs"; 6 | 7 | const program = new Command(); 8 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 9 | 10 | class ProxmoxHotkey { 11 | api!: Proxmox.Api; 12 | node!: string; 13 | init: Promise | null; 14 | constructor() { 15 | this.init = this._init(); 16 | } 17 | 18 | async _init(): Promise { 19 | const { proxmoxOption } = await loadConfig(program); 20 | this.api = proxmoxApi(proxmoxOption); 21 | const nodes = await this.api.nodes.$get(); 22 | const node = nodes[0].node; 23 | this.node = node; 24 | 25 | const trigerAction = (type: 'simple' | 'long', keyCode: string) => { 26 | const key = `${type}${keyCode}`; 27 | const data = this.actionMap[key]; 28 | if (data) { 29 | if (data.action === 'start') { 30 | this.startVM(data.vmid); 31 | } else if (data.action === 'stop') { 32 | this.stopVM(data.vmid); 33 | } else if (data.action === 'forcestop') { 34 | this.stopVM(data.vmid, true); 35 | } 36 | } else { 37 | console.log(`No Action for ${key}`); 38 | } 39 | } 40 | 41 | const reader = new DevInputReader('usb-Antecer_AmusingKeyPadK6-event-kbd', { longPress: 3000, retryInterval: 5000 }) 42 | reader.on('long', (ev) => { 43 | // console.log('long', ev); 44 | trigerAction('long', ev.keyName); 45 | }) 46 | 47 | reader.on('simple', (ev) => { 48 | // console.log('simple', ev); 49 | trigerAction('simple', ev.keyName); 50 | }) 51 | } 52 | actionMap: { [key: string]: { action: 'start' | 'stop' | 'forcestop', vmid: number } } = {}; 53 | 54 | public registerKey(type: 'simple' | 'long', keyCode: string, action: 'start' | 'stop' | 'forcestop', vmid: number) { 55 | this.actionMap[`${type}${keyCode}`] = { action, vmid }; 56 | } 57 | 58 | private pciFromHostpci(txt?: string | null) { 59 | if (!txt) 60 | return ''; 61 | let pci = txt.split(',', 1) 62 | return pci[0]; 63 | } 64 | 65 | async getVmDep(vmid: number): Promise { 66 | await this.init; 67 | let vminfo = await this.api.nodes.$(this.node).qemu.$(vmid).config.$get(); 68 | const deps: string[] = []; 69 | for (const hostpci of [vminfo.hostpci0, vminfo.hostpci1, vminfo.hostpci2, vminfo.hostpci3, vminfo.hostpci4]) { 70 | let ressource = this.pciFromHostpci(hostpci); 71 | if (ressource) 72 | deps.push(ressource); 73 | } 74 | return deps; 75 | } 76 | 77 | /** 78 | * Build depencences to pci devices 79 | * @param proxmox 80 | */ 81 | async getVmDeps(): Promise<{ [pci: string]: number[] }> { 82 | await this.init; 83 | const allDeps = {} as { [pci: string]: number[] }; 84 | const vms = await this.api.nodes.$(this.node).qemu.$get(); 85 | for (const vm of vms) { 86 | const resources = await this.getVmDep(vm.vmid); 87 | for (const resource of resources) { 88 | let rVms = allDeps[resource]; 89 | if (!rVms) { 90 | rVms = []; 91 | allDeps[resource] = rVms; 92 | } 93 | rVms.push(vm.vmid); 94 | } 95 | } 96 | return allDeps; 97 | } 98 | 99 | async startVM(vmid: number): Promise { 100 | await this.init; 101 | const status = await this.api.nodes.$(this.node).qemu.$(vmid).status.current.$get(); 102 | if (status.qmpstatus === 'running') { 103 | console.log(`vm ${vmid} already running`); 104 | return; 105 | } 106 | console.log(`vm ${vmid} is not running: ${status.qmpstatus}`); 107 | if (status.qmpstatus === 'stopped') { 108 | const resource = await this.getVmDep(vmid); 109 | if (resource.length) { 110 | console.log(`SOULD release ${resource.join(', ')}`); 111 | const depsTree = await this.getVmDeps(); 112 | const toStop = new Set(); 113 | for (const r of resource) { 114 | if (depsTree[r]) { 115 | depsTree[r].forEach(v => toStop.add(v)); 116 | } 117 | } 118 | const toStopList = [...toStop].filter(id => id != vmid); 119 | console.log(`SOULD stop vms: ${toStopList.join(', ')}`); 120 | for (const vmid of toStopList) { 121 | await this.stopVM(vmid); 122 | } 123 | } 124 | const code = await this.api.nodes.$(this.node).qemu.$(vmid).status.start.$post(); 125 | console.log(`start VM ${vmid} return ${code}`); 126 | } 127 | } 128 | 129 | async stopVM(vmid: number, forceStop?: boolean): Promise { 130 | await this.init; 131 | let pass = 0; 132 | while (true) { 133 | pass++; 134 | const status = await this.api.nodes.$(this.node).qemu.$(vmid).status.current.$get(); 135 | if (status.qmpstatus === 'stopped') { 136 | console.log(`vm ${vmid} not running status: ${status.qmpstatus}`); 137 | return; 138 | } 139 | console.log(`${pass}-STOP vm ${vmid} PID: ${status.pid}`); 140 | const code = await this.api.nodes.$(this.node).qemu.$(vmid).status.shutdown.$post({ forceStop: forceStop ? true : false }); // , skiplock: true 141 | console.log(`${pass}-STOP vm ${vmid} return ${code}`); 142 | } 143 | } 144 | } 145 | 146 | let version = '?'; 147 | try { 148 | version = require('../package.json').version; 149 | } catch (e) { 150 | } 151 | program.version(version) 152 | .description('Hotkey keybord for proxmox') 153 | //.option('--vmid ', 'vmid will receive USB Devices, by default the first running VM having an hostpci0', /^[0-9]+$/) 154 | //.option('--user ', 'host to connect if not root@pam (default: "root@pam")') 155 | //.option('--port ', 'port to connect if not 8006') 156 | //.option('--host ', 'host to connect if not 127.0.0.1', '127.0.0.1') 157 | //.option('-p, --pass [pass]', 'host password, prefed stdin, nodejs script can not hide password from command line') 158 | .option('-c, --config ', 'provide a configuration file'); 159 | 160 | program.parse(process.argv); 161 | console.log('Adding USB devices by bus/port'); 162 | 163 | 164 | 165 | (async () => { 166 | const hp = new ProxmoxHotkey() 167 | await hp.init; 168 | console.log('sss'); 169 | hp.registerKey('simple', 'KEY_1', 'start', 2000); 170 | hp.registerKey('simple', 'KEY_2', 'start', 2001); 171 | hp.registerKey('simple', 'KEY_3', 'start', 2003); //2003 172 | 173 | hp.registerKey('simple', 'KEY_4', 'stop', 2000); 174 | hp.registerKey('simple', 'KEY_5', 'stop', 2001); 175 | hp.registerKey('simple', 'KEY_6', 'stop', 2003); //2003 176 | 177 | hp.registerKey('long', 'KEY_4', 'stop', 2000); 178 | hp.registerKey('long', 'KEY_5', 'stop', 2001); 179 | hp.registerKey('long', 'KEY_6', 'stop', 2003); //2003 180 | })() 181 | -------------------------------------------------------------------------------- /proxmox-usb-hotplug/src/proxmox-usb-hotplug.ts: -------------------------------------------------------------------------------- 1 | import { proxmoxApi } from 'proxmox-api'; 2 | import { Command } from 'commander'; 3 | import HotPlugService from './HotPlugService'; 4 | import { loadConfig } from './config'; 5 | 6 | const program = new Command(); 7 | 8 | async function initHotPlugService(): Promise { 9 | const {proxmoxOption, usbOption} = await loadConfig(program); 10 | 11 | const proxmox = proxmoxApi(proxmoxOption); 12 | /** 13 | * stress test 14 | */ 15 | // const engine = new ProxmoxEngine(proxmoxOption) 16 | // const proxmox = buildApiProxy(engine, '/api2/json'); 17 | // setInterval(() => { 18 | // console.log('break Ticket'); 19 | // engine.ticket='0000000000' 20 | // }, 20000); 21 | 22 | const hp = new HotPlugService(proxmox, usbOption); 23 | return hp; 24 | } 25 | 26 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 27 | 28 | let version = '?'; 29 | try { 30 | version = require('../package.json').version; 31 | } catch (e) { 32 | } 33 | program.version(version) 34 | .description('Hotplug any new Usb device to your proxmox') 35 | .option('--vmid ', 'vmid will receive USB Devices, by default the first running VM having an hostpci0', /^[0-9]+$/) 36 | .option('--user ', 'host to connect if not root@pam (default: "root@pam")') 37 | .option('--port ', 'port to connect if not 8006') 38 | .option('--host ', 'host to connect if not 127.0.0.1', '127.0.0.1') 39 | .option('-p, --pass [pass]', 'host password, prefed stdin, nodejs script can not hide password from command line') 40 | .option('-c, --config ', 'provide a configuration file'); 41 | 42 | program.parse(process.argv); 43 | console.log('Adding USB devices by bus/port'); 44 | initHotPlugService().then(hp => hp.hotPlugByPort()) 45 | -------------------------------------------------------------------------------- /proxmox-usb-hotplug/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "module": "commonjs", 5 | "lib": [ "ES2020" ], 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true 9 | }, 10 | "include": ["src/*.ts"], 11 | } 12 | -------------------------------------------------------------------------------- /sample/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [{ 7 | "name": "ts-node", 8 | "type": "node", 9 | "request": "launch", 10 | "args": [ 11 | "${relativeFile}" 12 | ], 13 | "runtimeArgs": [ 14 | "-r", 15 | "ts-node/register" 16 | ], 17 | "cwd": "${workspaceRoot}", 18 | "protocol": "inspector", 19 | "internalConsoleOptions": "openOnSessionStart" 20 | }] 21 | } -------------------------------------------------------------------------------- /sample/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample", 3 | "version": "1.0.0", 4 | "description": "", 5 | "type": "module", 6 | "dependencies": { 7 | "dev-input-reader": "1.0.0", 8 | "proxmox-api": "file:..\\api", 9 | "undici": "^5.14.0" 10 | }, 11 | "devDependencies": { 12 | "@types/node": "^18.11.11", 13 | "ts-node": "^10.9.1", 14 | "typescript": "^4.9.4" 15 | }, 16 | "scripts": { 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "author": "", 20 | "license": "ISC" 21 | } -------------------------------------------------------------------------------- /sample/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | '@types/node': ^18.11.11 5 | dev-input-reader: 1.0.0 6 | proxmox-api: file:..\api 7 | ts-node: ^10.9.1 8 | typescript: ^4.9.4 9 | undici: ^5.14.0 10 | 11 | dependencies: 12 | dev-input-reader: 1.0.0 13 | proxmox-api: file:../api 14 | undici: 5.14.0 15 | 16 | devDependencies: 17 | '@types/node': 18.11.11 18 | ts-node: 10.9.1_bspv7bpieoza2i5ctiw2ofswem 19 | typescript: 4.9.4 20 | 21 | packages: 22 | 23 | /@cspotcode/source-map-support/0.8.1: 24 | resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 25 | engines: {node: '>=12'} 26 | dependencies: 27 | '@jridgewell/trace-mapping': 0.3.9 28 | dev: true 29 | 30 | /@jridgewell/resolve-uri/3.1.0: 31 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} 32 | engines: {node: '>=6.0.0'} 33 | dev: true 34 | 35 | /@jridgewell/sourcemap-codec/1.4.14: 36 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} 37 | dev: true 38 | 39 | /@jridgewell/trace-mapping/0.3.9: 40 | resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 41 | dependencies: 42 | '@jridgewell/resolve-uri': 3.1.0 43 | '@jridgewell/sourcemap-codec': 1.4.14 44 | dev: true 45 | 46 | /@tsconfig/node10/1.0.9: 47 | resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} 48 | dev: true 49 | 50 | /@tsconfig/node12/1.0.11: 51 | resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} 52 | dev: true 53 | 54 | /@tsconfig/node14/1.0.3: 55 | resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} 56 | dev: true 57 | 58 | /@tsconfig/node16/1.0.3: 59 | resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} 60 | dev: true 61 | 62 | /@types/node/18.11.11: 63 | resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} 64 | dev: true 65 | 66 | /@u4/tinyrequest/0.1.3: 67 | resolution: {integrity: sha512-NNIQzZplr4Abc7fmI+M4Dgy6Cs73703TTsYN7JJmtmSXreniIz4l40DzrV+G0xmg+WVSpJrIx51id/rV1L38sg==} 68 | dev: false 69 | 70 | /acorn-walk/8.2.0: 71 | resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} 72 | engines: {node: '>=0.4.0'} 73 | dev: true 74 | 75 | /acorn/8.8.1: 76 | resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} 77 | engines: {node: '>=0.4.0'} 78 | hasBin: true 79 | dev: true 80 | 81 | /arg/4.1.3: 82 | resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} 83 | dev: true 84 | 85 | /busboy/1.6.0: 86 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 87 | engines: {node: '>=10.16.0'} 88 | dependencies: 89 | streamsearch: 1.1.0 90 | dev: false 91 | 92 | /create-require/1.1.1: 93 | resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} 94 | dev: true 95 | 96 | /dev-input-reader/1.0.0: 97 | resolution: {integrity: sha512-KMiyvpPZtAHp/GvL7EbluGsQkB2uhkArnBiDA9Yq4oOQcFqCx6fI0UWxeCXiIETc3ArBK01iYhOZu5XrodY2MA==} 98 | dev: false 99 | 100 | /diff/4.0.2: 101 | resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} 102 | engines: {node: '>=0.3.1'} 103 | dev: true 104 | 105 | /make-error/1.3.6: 106 | resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} 107 | dev: true 108 | 109 | /streamsearch/1.1.0: 110 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 111 | engines: {node: '>=10.0.0'} 112 | dev: false 113 | 114 | /ts-node/10.9.1_bspv7bpieoza2i5ctiw2ofswem: 115 | resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} 116 | hasBin: true 117 | peerDependencies: 118 | '@swc/core': '>=1.2.50' 119 | '@swc/wasm': '>=1.2.50' 120 | '@types/node': '*' 121 | typescript: '>=2.7' 122 | peerDependenciesMeta: 123 | '@swc/core': 124 | optional: true 125 | '@swc/wasm': 126 | optional: true 127 | dependencies: 128 | '@cspotcode/source-map-support': 0.8.1 129 | '@tsconfig/node10': 1.0.9 130 | '@tsconfig/node12': 1.0.11 131 | '@tsconfig/node14': 1.0.3 132 | '@tsconfig/node16': 1.0.3 133 | '@types/node': 18.11.11 134 | acorn: 8.8.1 135 | acorn-walk: 8.2.0 136 | arg: 4.1.3 137 | create-require: 1.1.1 138 | diff: 4.0.2 139 | make-error: 1.3.6 140 | typescript: 4.9.4 141 | v8-compile-cache-lib: 3.0.1 142 | yn: 3.1.1 143 | dev: true 144 | 145 | /typescript/4.9.4: 146 | resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} 147 | engines: {node: '>=4.2.0'} 148 | hasBin: true 149 | dev: true 150 | 151 | /undici/5.14.0: 152 | resolution: {integrity: sha512-yJlHYw6yXPPsuOH0x2Ib1Km61vu4hLiRRQoafs+WUgX1vO64vgnxiCEN9dpIrhZyHFsai3F0AEj4P9zy19enEQ==} 153 | engines: {node: '>=12.18'} 154 | dependencies: 155 | busboy: 1.6.0 156 | dev: false 157 | 158 | /v8-compile-cache-lib/3.0.1: 159 | resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} 160 | dev: true 161 | 162 | /yn/3.1.1: 163 | resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} 164 | engines: {node: '>=6'} 165 | dev: true 166 | 167 | file:../api: 168 | resolution: {directory: ../api, type: directory} 169 | name: proxmox-api 170 | version: 1.0.0 171 | dependencies: 172 | '@u4/tinyrequest': 0.1.3 173 | undici: 5.14.0 174 | dev: false 175 | -------------------------------------------------------------------------------- /sample/src/common.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | 3 | //getAuth.json should looks like: 4 | // { "host": "hosIP", "password": "passwd", "tokenSecret": ""} 5 | 6 | export async function getAuth(): Promise<{ 7 | host: string; 8 | username?: string; 9 | password: string; 10 | }> { 11 | const data = await fs.promises.readFile('../../auth.json', { encoding: 'utf-8' }); 12 | const auth = JSON.parse(data); 13 | const { host, password, tokenSecret } = auth; 14 | // const { host, password, tokenSecret } = auth;// .default; 15 | return Promise.resolve({ host, password, tokenSecret }); 16 | } 17 | -------------------------------------------------------------------------------- /sample/src/sample-connect-kvm.ts: -------------------------------------------------------------------------------- 1 | import { proxmoxApi, QmMonitor } from "proxmox-api"; 2 | import { getAuth } from "./common.js"; 3 | 4 | let vmid = 2000; 5 | 6 | async function test() { 7 | // authorize self signed cert 8 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 9 | // load sample authentification info 10 | const auth = await getAuth(); 11 | // connect to proxmox 12 | const proxmox = proxmoxApi(auth); 13 | // liste nodes 14 | const nodes = await proxmox.nodes.$get(); 15 | // iterate cluster nodes 16 | const theNode = proxmox.nodes.$(nodes[0].node); 17 | // list Qemu VMS 18 | const qmMonitor = new QmMonitor(proxmox, nodes[0].node, vmid); 19 | 20 | if (true) { 21 | // await qmMonitor.deviceDel('mouse'); 22 | // await qmMonitor.deviceDel('keyboard'); 23 | await qmMonitor.deviceAddMissing('mouse', { name: /mouse/i }); 24 | await qmMonitor.deviceAddMissing('keyboard', { name: /KB/ }); 25 | await qmMonitor.deviceAddMissing('audio', { name: /USB Audio Device/ }); 26 | await qmMonitor.deviceAddMissing('AirMouse', { name: /2\.4G Air Mouse/ }); 27 | } 28 | // await Bluebird.delay(500); 29 | // console.log(await qmMonitor.info('memdev')); 30 | // console.log(await qmMonitor.infoUsb()); 31 | //console.log(await qmMonitor.info('usb')); 32 | //console.log(await qmMonitor.info('usbhost')); 33 | // let connected = await qmMonitor.infoUsb(); 34 | // console.log(JSON.stringify(connected, null, 2)); 35 | // for (const type of VALID_QEMU_INFO_SIMPLE) { 36 | // await Bluebird.delay(500); 37 | // console.log(type, 'calls: ', qmMonitor.calls) 38 | // const text = await qmMonitor.info(type); 39 | // console.log(text); 40 | // console.log() 41 | // // 2 calls 42 | // } 43 | //const txt = await qmMonitor.info('pci'); 44 | // console.log(txt); 45 | // usbInfo = usbInfo.filter(e => e.name.toLowerCase().includes('mouse')); 46 | // if (usbInfo.length) { 47 | // qmMonitor.deviceAdd('mouse', usbInfo[0]); 48 | //} 49 | // console.log(usbInfo) 50 | console.log('Done') 51 | } 52 | 53 | test().catch(console.error); 54 | -------------------------------------------------------------------------------- /sample/src/sample-stream.ts: -------------------------------------------------------------------------------- 1 | import { proxmoxApi, Proxmox } from "proxmox-api"; 2 | import * as fs from "fs"; 3 | // import { ReadableStream } from "stream/web"; 4 | import { ReadableStream } from 'stream/web' 5 | import { getAuth } from "./common.js"; 6 | 7 | 8 | async function test() { 9 | // authorize self signed cert 10 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 11 | // load sample authentification info 12 | const auth = await getAuth(); 13 | // connect to proxmox 14 | const promox = proxmoxApi(auth); 15 | // liste nodes 16 | const nodes = await promox.nodes.$get(); 17 | // iterate cluster nodes 18 | const memory = 1536; 19 | 20 | for (const node of nodes) { 21 | const theNode = promox.nodes.$(node.node); 22 | let storages = await theNode.storage.$get(); 23 | // keep only Proxmox Backup Server 24 | storages = storages.filter(s => s.type === "pbs") 25 | if (!storages.length) { 26 | console.log('No Proxmox Backup Server available'); 27 | continue; 28 | } 29 | console.log("storage:", storages[0]); 30 | const storageApi = theNode.storage.$(storages[0].storage); 31 | const contents = await storageApi.content.$get(); 32 | console.log("content:", contents); 33 | if (!contents.length) { 34 | console.log(`No Backup available in ${storages[0].storage}`); 35 | continue; 36 | } 37 | // const status = await storageApi.status.$get(); 38 | // console.log("status:", status); 39 | // const status = await storageApi["download-url"].$post({}) 40 | // console.log("status:", status); 41 | 42 | const list = await storageApi["file-restore"].list.$get({ volume: contents[0].volid, filepath: "/" }); 43 | // console.log("list:", list); 44 | if (!list.length) { 45 | console.log(`No File available in volunme ${contents[0].volid}`); 46 | continue; 47 | } 48 | const download = storageApi["file-restore"].download.$get; 49 | const stream = await download({ volume: contents[0].volid, filepath: list[0].filepath }) as ReadableStream; 50 | console.log(stream); 51 | 52 | // try { 53 | // const reader = stream.getReader(); 54 | // const blk = await reader.read(); 55 | // console.log(blk); 56 | // // for await (const value of stream) { 57 | // // console.log(value); 58 | // // } 59 | // } catch (e) { 60 | // console.log("reading stream failed:", e); 61 | // } 62 | console.log("Done"); 63 | 64 | // var savePixels = require("save-pixels"); 65 | // const myFile = fs.createWriteStream("myOutput.tgz"); 66 | // stream.pipeTo(myFile); 67 | // console.log(Object.keys(stream)); 68 | // stream.on("data", d => console.log("rcv", d)); 69 | // stream.on("end", d => console.log("end")); 70 | 71 | } 72 | } 73 | 74 | test().catch(console.error); 75 | -------------------------------------------------------------------------------- /sample/src/sample.ts: -------------------------------------------------------------------------------- 1 | import proxmoxApi from "proxmox-api"; 2 | import { getAuth } from "./common.js"; 3 | 4 | // authorize self signed cert 5 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 6 | 7 | const delay = (t: number) => new Promise(r => setTimeout(r, t)); 8 | 9 | async function test() { 10 | // load sample authentification info 11 | const opt = await getAuth(); 12 | // connect to proxmox 13 | const promox = proxmoxApi(opt); 14 | // liste nodes 15 | const nodes = await promox.nodes.$get(); 16 | // iterate cluster nodes 17 | for (const node of nodes) { 18 | const theNode = promox.nodes.$(node.node); 19 | // list Qemu VMS 20 | const qemus = await theNode.qemu.$get({ full: true }); 21 | // iterate Qemu VMS 22 | for (const qemu of qemus) { 23 | try { 24 | const vminfo = await theNode.qemu.$(qemu.vmid).config.$get() 25 | if (vminfo.template) { 26 | console.log(`vm: ${String(qemu.vmid).padStart(5, ' ')} is a template`); 27 | continue 28 | } 29 | } catch (e) { } 30 | // do some suff. 31 | // const agents = await theNode.qemu.$(qemu.vmid).agent.$get(); 32 | // const info = await theNode.qemu.$(qemu.vmid).agent.info.$get() 33 | // console.log(info); 34 | // await theNode.qemu.$(qemu.vmid).agent["exec-status"].$get() 35 | try { 36 | const mtd1 = theNode.qemu.$(qemu.vmid).agent.exec.$post; 37 | // const process = await exec.$post({ command: "powershell.exe ping 127.0.0.1" }) 38 | const process = await mtd1({ command: "C:/Windows/System32/ping.exe 127.0.0.1" }) 39 | console.log('pid:', process.pid) 40 | const getter = theNode.qemu.$(qemu.vmid).agent["exec-status"].$get; 41 | let exited = false; 42 | let waitcnd = 0; 43 | while (!exited) { 44 | const status = await getter({ pid: process.pid }) 45 | console.log(++waitcnd + ' status:', status) 46 | exited = !!status.exited; 47 | await delay(500); 48 | } 49 | // { exited: 0 } 50 | // { exitcode: 0, exited: 1, out-data: 'stdout'} 51 | } catch (e) { 52 | console.log(e); 53 | } 54 | // const users = await theNode.qemu.$(qemu.vmid).agent["get-users"].$get() 55 | // console.log('users:', users); 56 | // // const osinfo = await theNode.qemu.$(qemu.vmid).agent["get-host-name"].$get() 57 | // // console.log(osinfo); 58 | // const inets = await theNode.qemu.$(qemu.vmid).agent["network-get-interfaces"].$get() 59 | // console.log('inets:', inets); 60 | // const execStatus = theNode.qemu.$(qemu.vmid).agent["exec-status"]; 61 | // const info = await execStatus.$get({ pid: 1 }) 62 | try { 63 | const config = await theNode.qemu.$(qemu.vmid).config.$get(); 64 | const name = `(${(config.name || '')})`; 65 | console.log(`vm: ${String(qemu.vmid).padStart(5, ' ')} ${name.padEnd(18, ' ')}, memory: ${config.memory}`); 66 | } catch (e) { } 67 | // const vnc = await theNode.qemu.$(qemu.vmid).vncproxy.$post(); 68 | // console.log('vnc:', vnc); 69 | // const spice = await theNode.qemu.$(qemu.vmid).spiceproxy.$post(); 70 | // console.log('spice:', spice); 71 | } 72 | } 73 | } 74 | 75 | test().catch(console.error); 76 | -------------------------------------------------------------------------------- /sample/src/sample2.ts: -------------------------------------------------------------------------------- 1 | import { proxmoxApi, Proxmox } from "proxmox-api"; 2 | import { getAuth } from "./common"; 3 | 4 | async function test() { 5 | // authorize self signed cert 6 | process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 7 | // load sample authentification info 8 | const auth = await getAuth(); 9 | // connect to proxmox 10 | const promox = proxmoxApi(auth); 11 | // liste nodes 12 | const nodes = await promox.nodes.$get(); 13 | // iterate cluster nodes 14 | const memory = 1536; 15 | 16 | for (const node of nodes) { 17 | const theNode = promox.nodes.$(node.node); 18 | // list Qemu VMS 19 | let qemus = await theNode.qemu.$get(); 20 | 21 | const indexed = {} as { [key: number]: Proxmox.nodesQemuVm }; // ret_nodes_node_qemuGET 22 | for (const qemu of qemus) { 23 | indexed[qemu.vmid] = qemu; 24 | } 25 | 26 | qemus = qemus.filter(q => q.vmid >= 100 && q.vmid < 200).sort((a, b) => b.vmid - a.vmid); 27 | if (!qemus.length) 28 | throw Error('no VM id 100..200'); 29 | const base = qemus[0] 30 | console.log(`using VM: ${base.vmid} (${base.name})`); 31 | 32 | // GET /nodes/{node}/qemu/{vmid}/status/current 33 | const vmExtra = await theNode.qemu.$(base.vmid).status.current.$get(); 34 | if (!vmExtra.template) { 35 | // convert to template 36 | await theNode.qemu.$(base.vmid).template.$post(); 37 | } 38 | // 39 | //console.log(JSON.stringify(vmExtra, undefined, 2)); 40 | for (let id = 1; id <= 17; id++) { 41 | const newid = 1000 + id; 42 | if (indexed[newid]) { 43 | console.log(`vmID ${newid} exists`); 44 | } else { 45 | // target: node.node, 46 | await theNode.qemu.$(base.vmid).clone.$post({ newid, name: `VM-${node.node}-${id}`, bwlimit: 128 }); 47 | await theNode.qemu.$(newid).config.$post({ net0: `model=virtio,bridge=vmbr${id}`, onboot: true, memory }) 48 | await theNode.qemu.$(newid).status.start.$post(); 49 | } 50 | 51 | } 52 | // for ID in $(seq 1 $CNT) 53 | // do 54 | // NEWID=$((1000+ID)) 55 | // echo creating VM${ID} NEWID = ${NEWID} 56 | // qm clone ${SRCID} ${NEWID} --target $HOSTNAME --name VM-${HOSTNAME}-${ID} && \ 57 | // qm set ${NEWID} -net0 model=virtio,bridge=vmbr${ID} -onboot ${ONBOOT} -memory ${MEMORY} && \ 58 | // qm start $((1000+ID)) 59 | // done 60 | 61 | } 62 | } 63 | 64 | test().catch(console.error); 65 | -------------------------------------------------------------------------------- /sample/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "ES2022", 5 | "lib": ["ES2020"], 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true 10 | } 11 | } -------------------------------------------------------------------------------- /sample/usage.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrielCh/proxmox-api/7db0cc6500c9e49d7b9e73f090fe5fccdd57d3e1/sample/usage.gif --------------------------------------------------------------------------------