├── PVE ├── .gitignore ├── Jobs │ ├── Makefile │ └── VZDump.pm ├── API2 │ ├── Hardware │ │ ├── Makefile │ │ └── USB.pm │ ├── Ceph │ │ └── Makefile │ ├── Cluster │ │ ├── BulkAction │ │ │ └── Makefile │ │ ├── Mapping │ │ │ └── Makefile │ │ ├── Makefile │ │ ├── BulkAction.pm │ │ └── Mapping.pm │ ├── Makefile │ ├── Hardware.pm │ └── HAConfig.pm ├── Ceph │ └── Makefile ├── Service │ └── Makefile ├── Status │ └── Makefile ├── CLI │ ├── Makefile │ └── vzdump.pm ├── pvecfg.pm.in └── Makefile ├── debian ├── docs ├── source │ ├── format │ └── lintian-overrides ├── triggers ├── links ├── tmpfiles ├── rules ├── postrm ├── lintian-overrides ├── prerm └── copyright ├── templates ├── default │ ├── test-subject.txt.hbs │ ├── replication-subject.txt.hbs │ ├── vzdump-subject.txt.hbs │ ├── package-updates-subject.txt.hbs │ ├── test-body.html.hbs │ ├── test-body.txt.hbs │ ├── package-updates-body.txt.hbs │ ├── vzdump-body.txt.hbs │ ├── replication-body.txt.hbs │ ├── replication-body.html.hbs │ ├── package-updates-body.html.hbs │ └── vzdump-body.html.hbs └── Makefile ├── services ├── ceph-after-pve-cluster.conf ├── pve-daily-update.timer ├── pve-daily-update.service ├── pve-storage.target ├── pvebanner.service ├── pve-firewall-commit.service ├── pve-sdn-commit.service ├── pvestatd.service ├── spiceproxy.service ├── pvenetcommit.service ├── pvedaemon.service ├── pvescheduler.service ├── pveproxy.service ├── pve-guests.service └── Makefile ├── bin ├── .gitignore ├── pvesh ├── pveam ├── pvesr ├── pve8to9 ├── pveceph ├── pvenode ├── vzdump ├── pvesubscription ├── pve-network-interface-pinning ├── pvereport ├── pve-startall-delay ├── pvedaemon ├── pvescheduler ├── pvestatd ├── pve-rbd-storage-configure-keyring ├── pvebanner ├── pve-firewall-commit ├── pveproxy └── spiceproxy ├── www ├── images │ ├── favicon.ico │ ├── icon-cd.xcf │ ├── logo-128.png │ ├── proxmox_logo.png │ ├── icon-sdn.dot │ ├── icon-cloud.svg │ ├── icon-cd-drive.svg │ ├── xtermjs.svg │ ├── icon-fa-network-wired.svg │ ├── icon-pci.svg │ ├── icon-memory.svg │ ├── spinner.svg │ ├── icon-cpu.svg │ ├── Makefile │ └── icon-die.svg ├── manager6 │ ├── sdn │ │ ├── fabrics │ │ │ ├── ospf │ │ │ │ ├── InterfacePanel.js │ │ │ │ ├── NodeEdit.js │ │ │ │ └── FabricEdit.js │ │ │ ├── Common.js │ │ │ ├── openfabric │ │ │ │ ├── NodeEdit.js │ │ │ │ └── InterfacePanel.js │ │ │ └── FabricEdit.js │ │ ├── OptionsPanel.js │ │ ├── ipams │ │ │ ├── PVEIpamEdit.js │ │ │ ├── NetboxEdit.js │ │ │ ├── PhpIpamEdit.js │ │ │ └── Base.js │ │ ├── zones │ │ │ ├── VlanEdit.js │ │ │ ├── SimpleEdit.js │ │ │ └── QinQEdit.js │ │ ├── Status.js │ │ ├── VnetPanel.js │ │ ├── FirewallPanel.js │ │ ├── ZoneContentPanel.js │ │ ├── dns │ │ │ ├── PowerdnsEdit.js │ │ │ └── Base.js │ │ ├── controllers │ │ │ └── IsisEdit.js │ │ └── IpamEdit.js │ ├── panel │ │ ├── TagConfig.js │ │ ├── HealthWidget.js │ │ └── StatusPanel.js │ ├── form │ │ ├── VNCKeyboardSelector.js │ │ ├── NotificationModeSelector.js │ │ ├── NotificationPolicySelector.js │ │ ├── FirewallPolicySelector.js │ │ ├── BackupModeSelector.js │ │ ├── iScsiProviderSelector.js │ │ ├── PermPathSelector.js │ │ ├── DiskFormatSelector.js │ │ ├── PreallocationSelector.js │ │ ├── BackupCompressionSelector.js │ │ ├── NetworkCardSelector.js │ │ ├── UserSelector.js │ │ ├── HashAlgorithmSelector.js │ │ ├── CacheTypeSelector.js │ │ ├── QemuBiosSelector.js │ │ ├── ACMEPluginSelector.js │ │ ├── ACMEAccountSelector.js │ │ ├── ScsiHwSelector.js │ │ ├── ContentTypeSelector.js │ │ ├── BusTypeSelector.js │ │ ├── DayOfWeekSelector.js │ │ ├── PrivilegesSelector.js │ │ ├── StorageScanNodeSelector.js │ │ ├── VLanField.js │ │ ├── CephFSSelector.js │ │ ├── ACMEAPISelector.js │ │ ├── CephPoolSelector.js │ │ ├── SDNDnsSelector.js │ │ ├── SDNIpamSelector.js │ │ ├── SDNZoneSelector.js │ │ ├── DirMapSelector.js │ │ ├── SDNControllerSelector.js │ │ ├── NotificationTargetSelector.js │ │ ├── PoolSelector.js │ │ ├── GroupSelector.js │ │ ├── BridgeSelector.js │ │ ├── SnapshotSelector.js │ │ ├── SecurityGroupSelector.js │ │ ├── HotplugFeatureSelector.js │ │ ├── TagFieldSet.js │ │ └── SDNVnetSelector.js │ ├── qemu │ │ ├── ScsiHwEdit.js │ │ ├── KeyboardEdit.js │ │ └── QemuBiosEdit.js │ ├── controller │ │ └── StorageEdit.js │ ├── ceph │ │ ├── Log.js │ │ ├── Monitor.js │ │ └── Crush.js │ ├── ha │ │ ├── rules │ │ │ ├── ResourceAffinityRuleEdit.js │ │ │ ├── ResourceAffinityRules.js │ │ │ └── NodeAffinityRules.js │ │ ├── Status.js │ │ ├── RuleErrorsModal.js │ │ └── Fencing.js │ ├── pool │ │ ├── StatusView.js │ │ ├── Summary.js │ │ └── Config.js │ ├── Toolkit.js │ ├── dc │ │ ├── ACMEClusterView.js │ │ ├── DirMapView.js │ │ ├── GroupEdit.js │ │ ├── PoolEdit.js │ │ └── RoleEdit.js │ ├── storage │ │ ├── BTRFSEdit.js │ │ ├── DirEdit.js │ │ └── StatusView.js │ ├── window │ │ ├── SafeDestroyStorage.js │ │ ├── ConfirmRemoveResource.js │ │ ├── BackupConfig.js │ │ ├── FirewallEnableEdit.js │ │ └── SafeDestroyGuest.js │ ├── button │ │ └── Revert.js │ ├── menu │ │ └── MenuItem.js │ ├── container │ │ └── TwoColumnContainer.js │ └── data │ │ └── model │ │ └── RRDModels.js ├── css │ └── Makefile └── Makefile ├── aplinfo ├── proxmox-release-trixie.gpg ├── proxmox-release-bookworm.gpg ├── apltest.pl ├── Makefile └── README.format ├── configs ├── pve-initramfs.conf ├── virtual-function-pinning.rules ├── pve-sources.sources ├── pve-blacklist.conf ├── pve.logrotate ├── proxmox-ve-default.link ├── vzdump.conf ├── Makefile └── virtual-function-pinning-helper ├── .gitignore ├── network-hooks ├── mtu ├── Makefile ├── bridgevlanport ├── vlan-down ├── bridgevlan └── vlan ├── test ├── replication_test6.log ├── perftest2.pl ├── Makefile ├── replication_test1.pl ├── replication_test3.pl ├── replication_test4.pl ├── replication_test6.pl ├── replication_test4.log ├── perftest3.pl └── OSD_test.pl ├── defines.mk └── spice-example-sh /PVE/.gitignore: -------------------------------------------------------------------------------- 1 | pvecfg.pm 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | debian/SOURCE 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /templates/default/test-subject.txt.hbs: -------------------------------------------------------------------------------- 1 | Test notification 2 | -------------------------------------------------------------------------------- /services/ceph-after-pve-cluster.conf: -------------------------------------------------------------------------------- 1 | [Unit] 2 | After=pve-cluster.service 3 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | docinfo.xml 2 | *.1 3 | *.adoc 4 | *.1.pod 5 | *.8 6 | pvemailforward -------------------------------------------------------------------------------- /templates/default/replication-subject.txt.hbs: -------------------------------------------------------------------------------- 1 | Replication Job: '{{job-id}}' failed 2 | -------------------------------------------------------------------------------- /templates/default/vzdump-subject.txt.hbs: -------------------------------------------------------------------------------- 1 | vzdump backup status ({{fqdn}}): {{status-text}} 2 | -------------------------------------------------------------------------------- /debian/triggers: -------------------------------------------------------------------------------- 1 | interest-noawait pve-api-updates 2 | interest-noawait /usr/share/perl5/PVE 3 | -------------------------------------------------------------------------------- /templates/default/package-updates-subject.txt.hbs: -------------------------------------------------------------------------------- 1 | New software packages available ({{fqdn}}) 2 | -------------------------------------------------------------------------------- /templates/default/test-body.html.hbs: -------------------------------------------------------------------------------- 1 | This is a test of the notification target '{{target}}'. 2 | -------------------------------------------------------------------------------- /templates/default/test-body.txt.hbs: -------------------------------------------------------------------------------- 1 | This is a test of the notification target '{{ target }}'. 2 | -------------------------------------------------------------------------------- /www/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-manager/HEAD/www/images/favicon.ico -------------------------------------------------------------------------------- /www/images/icon-cd.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-manager/HEAD/www/images/icon-cd.xcf -------------------------------------------------------------------------------- /www/images/logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-manager/HEAD/www/images/logo-128.png -------------------------------------------------------------------------------- /debian/links: -------------------------------------------------------------------------------- 1 | usr/libexec/proxmox/pve-network-interface-pinning usr/bin/pve-network-interface-pinning 2 | 3 | -------------------------------------------------------------------------------- /debian/tmpfiles: -------------------------------------------------------------------------------- 1 | #Type Path Mode User Group Age Argument 2 | d /run/pve 0750 root www-data - - 3 | -------------------------------------------------------------------------------- /www/images/proxmox_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-manager/HEAD/www/images/proxmox_logo.png -------------------------------------------------------------------------------- /aplinfo/proxmox-release-trixie.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-manager/HEAD/aplinfo/proxmox-release-trixie.gpg -------------------------------------------------------------------------------- /configs/pve-initramfs.conf: -------------------------------------------------------------------------------- 1 | # disable suspend-to-disk, as it delays boot on systems with root on ZFS 2 | RESUME=none 3 | -------------------------------------------------------------------------------- /aplinfo/proxmox-release-bookworm.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/proxmox/pve-manager/HEAD/aplinfo/proxmox-release-bookworm.gpg -------------------------------------------------------------------------------- /templates/default/package-updates-body.txt.hbs: -------------------------------------------------------------------------------- 1 | The following updates are available: 2 | 3 | {{table available-updates}} 4 | -------------------------------------------------------------------------------- /bin/pvesh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pvesh; 7 | 8 | PVE::CLI::pvesh->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /configs/virtual-function-pinning.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="net", ACTION=="add", PROGRAM=="/usr/lib/udev/virtual-function-naming-helper", NAME="%c" 2 | -------------------------------------------------------------------------------- /bin/pveam: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -T 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pveam; 7 | 8 | PVE::CLI::pveam->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /bin/pvesr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -T 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pvesr; 7 | 8 | PVE::CLI::pvesr->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /bin/pve8to9: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pve8to9; 7 | 8 | PVE::CLI::pve8to9->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /bin/pveceph: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -T 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pveceph; 7 | 8 | PVE::CLI::pveceph->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /bin/pvenode: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -T 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pvenode; 7 | 8 | PVE::CLI::pvenode->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /bin/vzdump: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -T 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::vzdump; 7 | 8 | PVE::CLI::vzdump->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /www/manager6/sdn/fabrics/ospf/InterfacePanel.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Fabric.Ospf.InterfacePanel', { 2 | extend: 'PVE.sdn.Fabric.InterfacePanel', 3 | }); 4 | -------------------------------------------------------------------------------- /bin/pvesubscription: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pvesubscription; 7 | 8 | PVE::CLI::pvesubscription->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /www/manager6/panel/TagConfig.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.panel.TagConfig', { 2 | extend: 'PVE.panel.Config', 3 | alias: 'widget.pveTagConfig', 4 | 5 | onlineHelp: 'gui_tags', 6 | }); 7 | -------------------------------------------------------------------------------- /configs/pve-sources.sources: -------------------------------------------------------------------------------- 1 | Types: deb 2 | URIs: https://enterprise.proxmox.com/debian/pve 3 | Suites: trixie 4 | Components: pve-enterprise 5 | Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg 6 | -------------------------------------------------------------------------------- /bin/pve-network-interface-pinning: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::CLI::pve_network_interface_pinning; 7 | 8 | PVE::CLI::pve_network_interface_pinning->run_cli_handler(); 9 | -------------------------------------------------------------------------------- /configs/pve-blacklist.conf: -------------------------------------------------------------------------------- 1 | # This file contains a list of modules which are not supported by Proxmox VE 2 | 3 | # nvidiafb see bugreport https://bugzilla.proxmox.com/show_bug.cgi?id=701 4 | blacklist nvidiafb 5 | -------------------------------------------------------------------------------- /services/pve-daily-update.timer: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Daily PVE download activities 3 | 4 | [Timer] 5 | OnCalendar=*-*-* 1:00 6 | RandomizedDelaySec=5h 7 | Persistent=true 8 | 9 | [Install] 10 | WantedBy=timers.target 11 | -------------------------------------------------------------------------------- /www/css/Makefile: -------------------------------------------------------------------------------- 1 | include ../../defines.mk 2 | 3 | all: 4 | 5 | .PHONY: install 6 | install: ext6-pve.css 7 | install -d $(WWWCSSDIR) 8 | install -m 0644 $? $(WWWCSSDIR) 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -rf *~ 13 | -------------------------------------------------------------------------------- /templates/default/vzdump-body.txt.hbs: -------------------------------------------------------------------------------- 1 | {{error}} 2 | Details 3 | ======= 4 | {{table guest-table}} 5 | Total running time: {{duration total-time}} 6 | Total size: {{human-bytes total-size}} 7 | 8 | Logs 9 | ==== 10 | {{logs}} 11 | -------------------------------------------------------------------------------- /www/manager6/form/VNCKeyboardSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.VNCKeyboardSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.VNCKeyboardSelector'], 4 | comboItems: Object.entries(PVE.Utils.kvm_keymaps), 5 | }); 6 | -------------------------------------------------------------------------------- /www/manager6/sdn/fabrics/ospf/NodeEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Fabric.Ospf.Node.Edit', { 2 | extend: 'PVE.sdn.Fabric.Node.Edit', 3 | protocol: 'ospf', 4 | 5 | extraRequestParams: { 6 | protocol: 'ospf', 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /www/images/icon-sdn.dot: -------------------------------------------------------------------------------- 1 | graph sdn { 2 | bgcolor="transparent"; 3 | node[label="",shape=circle,fillcolor=black,style=filled]; 4 | edge[penwidth=6]; 5 | a -- b -- d; 6 | a -- c -- d; 7 | a -- d; 8 | a -- e; 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | country.dat 2 | dest/ 3 | *.deb 4 | *.buildinfo 5 | *.changes 6 | 7 | /www/manager6/OnlineHelpInfo.js 8 | /www/manager6/pvemanagerlib.js 9 | /www/mobile/pvemanager-mobile.js 10 | /www/touch/touch-[0-9]*/ 11 | /pve-manager-[0-9]*/ 12 | /test/.mocked_* 13 | /test/*.tmp 14 | -------------------------------------------------------------------------------- /services/pve-daily-update.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Daily PVE download activities 3 | After=network-online.target 4 | Wants=network-online.target 5 | Wants=pve-cluster.service 6 | After=pve-cluster.service 7 | 8 | [Service] 9 | Type=oneshot 10 | ExecStart=/usr/bin/pveupdate 11 | -------------------------------------------------------------------------------- /debian/source/lintian-overrides: -------------------------------------------------------------------------------- 1 | # for now we manage those ourselves, with modern dh_system it might be possible to avoid that though 2 | pve-manager source: maintainer-script-lacks-debhelper-token [debian/postinst] 3 | pve-manager source: maintainer-script-lacks-debhelper-token [debian/prerm] 4 | 5 | -------------------------------------------------------------------------------- /PVE/Jobs/Makefile: -------------------------------------------------------------------------------- 1 | include ../../defines.mk 2 | 3 | PERLSOURCE = \ 4 | VZDump.pm \ 5 | 6 | all: 7 | 8 | .PHONY: clean 9 | clean: 10 | rm -rf *~ 11 | 12 | .PHONY: install 13 | install: $(PERLSOURCE) 14 | install -d $(PERLLIBDIR)/PVE/Jobs 15 | install -m 0644 $(PERLSOURCE) $(PERLLIBDIR)/PVE/Jobs 16 | -------------------------------------------------------------------------------- /services/pve-storage.target: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PVE Storage Target 3 | Wants=remote-fs.target 4 | After=remote-fs.target 5 | After=ceph.target 6 | After=ceph-mon.target 7 | After=ceph-osd.target 8 | After=ceph-mds.target 9 | After=ceph-mgr.target 10 | After=glusterd.service 11 | After=open-iscsi.service 12 | -------------------------------------------------------------------------------- /network-hooks/mtu: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | case "$IFACE" in 4 | # Ignore any alias (#272891) which uses : 5 | *:*) 6 | exit 0 7 | ;; 8 | esac 9 | 10 | if [ "$METHOD" != manual ]; then 11 | exit 0 12 | fi 13 | 14 | if [ -n "$IF_MTU" ]; then 15 | ip link set $IFACE mtu $IF_MTU 16 | fi 17 | -------------------------------------------------------------------------------- /www/manager6/form/NotificationModeSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.NotificationModeSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveNotificationModeSelector'], 4 | comboItems: [ 5 | ['notification-target', gettext('Target')], 6 | ['mailto', gettext('E-Mail')], 7 | ], 8 | }); 9 | -------------------------------------------------------------------------------- /www/manager6/form/NotificationPolicySelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.EmailNotificationSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveEmailNotificationSelector'], 4 | comboItems: [ 5 | ['always', gettext('Always')], 6 | ['failure', gettext('On failure only')], 7 | ], 8 | }); 9 | -------------------------------------------------------------------------------- /www/manager6/form/FirewallPolicySelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.FirewallPolicySelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveFirewallPolicySelector'], 4 | comboItems: [ 5 | ['ACCEPT', 'ACCEPT'], 6 | ['REJECT', 'REJECT'], 7 | ['DROP', 'DROP'], 8 | ], 9 | }); 10 | -------------------------------------------------------------------------------- /PVE/API2/Hardware/Makefile: -------------------------------------------------------------------------------- 1 | include ../../../defines.mk 2 | 3 | PERLSOURCE= \ 4 | PCI.pm \ 5 | USB.pm \ 6 | 7 | all: 8 | 9 | .PHONY: clean 10 | clean: 11 | rm -rf *~ 12 | 13 | .PHONY: install 14 | install: 15 | install -d $(PERLLIBDIR)/PVE/API2/Hardware 16 | install -m 0644 $(PERLSOURCE) $(PERLLIBDIR)/PVE/API2/Hardware 17 | -------------------------------------------------------------------------------- /PVE/Ceph/Makefile: -------------------------------------------------------------------------------- 1 | include ../../defines.mk 2 | 3 | PERLSOURCE = \ 4 | Releases.pm \ 5 | Services.pm \ 6 | Tools.pm \ 7 | 8 | all: 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -rf *~ 13 | 14 | .PHONY: install 15 | install: $(PERLSOURCE) 16 | install -d $(PERLLIBDIR)/PVE/Ceph 17 | install -m 0644 $(PERLSOURCE) $(PERLLIBDIR)/PVE/Ceph 18 | -------------------------------------------------------------------------------- /PVE/Service/Makefile: -------------------------------------------------------------------------------- 1 | include ../../defines.mk 2 | 3 | SOURCES=pvestatd.pm pveproxy.pm pvedaemon.pm spiceproxy.pm pvescheduler.pm 4 | 5 | all: 6 | 7 | .PHONY: install 8 | install: $(SOURCES) 9 | install -d -m 0755 $(PERLLIBDIR)/PVE/Service 10 | for i in $(SOURCES); do install -D -m 0644 $$i $(PERLLIBDIR)/PVE/Service/$$i; done 11 | 12 | clean: 13 | -------------------------------------------------------------------------------- /services/pvebanner.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Proxmox VE Login Banner 3 | ConditionPathExists=/usr/bin/pvebanner 4 | DefaultDependencies=no 5 | After=local-fs.target 6 | Before=console-getty.service 7 | 8 | [Service] 9 | ExecStart=/usr/bin/pvebanner 10 | Type=oneshot 11 | RemainAfterExit=yes 12 | 13 | [Install] 14 | WantedBy=getty.target 15 | -------------------------------------------------------------------------------- /www/manager6/form/BackupModeSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.BackupModeSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveBackupModeSelector'], 4 | comboItems: [ 5 | ['snapshot', gettext('Snapshot')], 6 | ['suspend', gettext('Suspend')], 7 | ['stop', gettext('Stop')], 8 | ], 9 | }); 10 | -------------------------------------------------------------------------------- /www/manager6/form/iScsiProviderSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.iScsiProviderSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveiScsiProviderSelector'], 4 | comboItems: [ 5 | ['comstar', 'Comstar'], 6 | ['istgt', 'istgt'], 7 | ['iet', 'IET'], 8 | ['LIO', 'LIO'], 9 | ], 10 | }); 11 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | include debian/rules.env 4 | export REPOID=${REPOID_GENERATED} 5 | 6 | %: 7 | dh $@ 8 | 9 | override_dh_compress: 10 | dh_compress -Xaplinfo.dat -Xtrustedkeys.gpg 11 | 12 | override_dh_strip_nondeterminism: 13 | dh_strip_nondeterminism -X.png 14 | 15 | override_dh_fixperms: 16 | dh_fixperms -Xvar/log/pveproxy 17 | -------------------------------------------------------------------------------- /services/pve-firewall-commit.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Commit Proxmox VE Firewall changes 3 | DefaultDependencies=no 4 | Wants=pve-cluster.service 5 | After=corosync.service 6 | 7 | [Service] 8 | ExecStart=/usr/share/pve-manager/helpers/pve-firewall-commit 9 | Type=oneshot 10 | RemainAfterExit=yes 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /configs/pve.logrotate: -------------------------------------------------------------------------------- 1 | /var/log/pveproxy/access.log { 2 | rotate 7 3 | daily 4 | missingok 5 | compress 6 | delaycompress 7 | notifempty 8 | create 640 www-data www-data 9 | sharedscripts 10 | postrotate 11 | /bin/systemctl try-reload-or-restart pveproxy.service 12 | /bin/systemctl try-reload-or-restart spiceproxy.service 13 | endscript 14 | } 15 | -------------------------------------------------------------------------------- /PVE/Status/Makefile: -------------------------------------------------------------------------------- 1 | include ../../defines.mk 2 | 3 | PERLSOURCE = \ 4 | Graphite.pm \ 5 | InfluxDB.pm \ 6 | OpenTelemetry.pm \ 7 | Plugin.pm 8 | 9 | all: 10 | 11 | .PHONY: clean 12 | clean: 13 | rm -rf *~ 14 | 15 | .PHONY: install 16 | install: $(PERLSOURCE) 17 | install -d $(PERLLIBDIR)/PVE/Status 18 | install -m 0644 $(PERLSOURCE) $(PERLLIBDIR)/PVE/Status 19 | -------------------------------------------------------------------------------- /www/manager6/form/PermPathSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.PermPathSelector', { 2 | extend: 'Ext.form.field.ComboBox', 3 | xtype: 'pvePermPathSelector', 4 | 5 | valueField: 'value', 6 | displayField: 'value', 7 | typeAhead: true, 8 | queryMode: 'local', 9 | width: 380, 10 | 11 | store: { 12 | type: 'pvePermPath', 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /services/pve-sdn-commit.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Commit Proxmox VE SDN changes 3 | DefaultDependencies=no 4 | Wants=pve-cluster.service network.target 5 | After=frr.service network.target corosync.service 6 | 7 | [Service] 8 | ExecStart=/usr/share/pve-manager/helpers/pve-sdn-commit 9 | Type=oneshot 10 | RemainAfterExit=yes 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /www/images/icon-cloud.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /www/manager6/form/DiskFormatSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.DiskFormatSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: 'widget.pveDiskFormatSelector', 4 | comboItems: [ 5 | ['raw', gettext('Raw disk image') + ' (raw)'], 6 | ['qcow2', gettext('QEMU image format') + ' (qcow2)'], 7 | ['vmdk', gettext('VMware image format') + ' (vmdk)'], 8 | ], 9 | }); 10 | -------------------------------------------------------------------------------- /PVE/API2/Ceph/Makefile: -------------------------------------------------------------------------------- 1 | include ../../../defines.mk 2 | 3 | PERLSOURCE= \ 4 | Cfg.pm \ 5 | MGR.pm \ 6 | MON.pm \ 7 | OSD.pm \ 8 | FS.pm \ 9 | Pool.pm \ 10 | MDS.pm 11 | 12 | all: 13 | 14 | .PHONY: clean 15 | clean: 16 | rm -rf *~ 17 | 18 | .PHONY: install 19 | install: $(PERLSOURCE) 20 | install -d $(PERLLIBDIR)/PVE/API2/Ceph 21 | install -m 0644 $(PERLSOURCE) $(PERLLIBDIR)/PVE/API2/Ceph 22 | -------------------------------------------------------------------------------- /test/replication_test6.log: -------------------------------------------------------------------------------- 1 | 1000 job_900_to_node1: new job next_sync => 1 2 | 1000 job_900_to_node1: start replication job 3 | 1000 job_900_to_node1: guest => VM 900, running => 0 4 | 1000 job_900_to_node1: volumes => local-zfs:vm-900-disk-1 5 | 1000 job_900_to_node1: start job removal - mode 'full' 6 | 1000 job_900_to_node1: job removed 7 | 1000 job_900_to_node1: end replication job 8 | 1000 job_900_to_node1: vanished job 9 | -------------------------------------------------------------------------------- /bin/pvereport: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::Report; 7 | 8 | ($> == 0) || die "please run as root\n"; 9 | 10 | print PVE::Report::generate(); 11 | 12 | __END__ 13 | 14 | =head1 NAME 15 | 16 | pvereport - Proxmox VE Report tool 17 | 18 | =head1 SYNOPSIS 19 | 20 | pvereport 21 | 22 | =head1 DESCRIPTION 23 | 24 | Display various information related to a Proxmox VE system 25 | -------------------------------------------------------------------------------- /services/pvestatd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PVE Status Daemon 3 | ConditionPathExists=/usr/bin/pvestatd 4 | Wants=pve-cluster.service 5 | After=pve-cluster.service pvenetcommit.service 6 | 7 | [Service] 8 | ExecStart=/usr/bin/pvestatd start 9 | ExecStop=/usr/bin/pvestatd stop 10 | ExecReload=/usr/bin/pvestatd restart 11 | PIDFile=/run/pvestatd.pid 12 | Type=forking 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /services/spiceproxy.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PVE SPICE Proxy Server 3 | ConditionPathExists=/usr/bin/spiceproxy 4 | Wants=pveproxy.service 5 | After=pveproxy.service 6 | 7 | [Service] 8 | ExecStart=/usr/bin/spiceproxy start 9 | ExecStop=/usr/bin/spiceproxy stop 10 | ExecReload=/usr/bin/spiceproxy restart 11 | PIDFile=/run/pveproxy/spiceproxy.pid 12 | Type=forking 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /www/manager6/form/PreallocationSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.preallocationSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pvePreallocationSelector'], 4 | comboItems: [ 5 | ['__default__', Proxmox.Utils.defaultText], 6 | ['off', 'Off'], 7 | ['metadata', 'Metadata'], 8 | ['falloc', 'Full (posix_fallocate)'], 9 | ['full', 'Full'], 10 | ], 11 | }); 12 | -------------------------------------------------------------------------------- /services/pvenetcommit.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Commit Proxmox VE network changes 3 | DefaultDependencies=no 4 | After=local-fs.target 5 | Before=sysinit.target 6 | 7 | [Service] 8 | ExecStartPre=-/bin/rm -f /etc/openvswitch/conf.db 9 | Environment="FN=/etc/network/interfaces" 10 | ExecStart=sh -c 'if [ -f ${FN}.new ]; then mv ${FN}.new ${FN}; fi' 11 | Type=oneshot 12 | RemainAfterExit=yes 13 | 14 | [Install] 15 | WantedBy=sysinit.target 16 | -------------------------------------------------------------------------------- /templates/default/replication-body.txt.hbs: -------------------------------------------------------------------------------- 1 | Replication job '{{job-id}}' with target '{{job-target}}' and schedule '{{job-schedule}}' failed! 2 | 3 | Last successful sync: {{timestamp last-sync}} 4 | Next sync try: {{timestamp next-sync}} 5 | Failure count: {{failure-count}} 6 | 7 | {{#if (eq failure-count 3)}} 8 | Note: The system will now reduce the frequency of error reports, as the job 9 | appears to be stuck. 10 | {{/if}} 11 | Error: 12 | {{error}} 13 | -------------------------------------------------------------------------------- /www/manager6/form/BackupCompressionSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.BackupCompressionSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveBackupCompressionSelector'], 4 | comboItems: [ 5 | ['0', Proxmox.Utils.noneText], 6 | ['lzo', 'LZO (' + gettext('fast') + ')'], 7 | ['gzip', 'GZIP (' + gettext('good') + ')'], 8 | ['zstd', 'ZSTD (' + gettext('fast and good') + ')'], 9 | ], 10 | }); 11 | -------------------------------------------------------------------------------- /www/manager6/form/NetworkCardSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.NetworkCardSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: 'widget.pveNetworkCardSelector', 4 | comboItems: [ 5 | ['e1000', 'Intel E1000'], 6 | ['e1000e', 'Intel E1000E'], 7 | ['virtio', 'VirtIO (' + gettext('paravirtualized') + ')'], 8 | ['rtl8139', 'Realtek RTL8139'], 9 | ['vmxnet3', 'VMware vmxnet3'], 10 | ], 11 | }); 12 | -------------------------------------------------------------------------------- /services/pvedaemon.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PVE API Daemon 3 | ConditionPathExists=/usr/bin/pvedaemon 4 | Wants=corosync.service pve-cluster.service 5 | After=corosync.service pve-cluster.service 6 | 7 | [Service] 8 | ExecStart=/usr/bin/pvedaemon start 9 | ExecStop=/usr/bin/pvedaemon stop 10 | ExecReload=/usr/bin/pvedaemon restart 11 | PIDFile=/run/pvedaemon.pid 12 | Type=forking 13 | Restart=on-failure 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /PVE/CLI/Makefile: -------------------------------------------------------------------------------- 1 | include ../../defines.mk 2 | 3 | SOURCES = \ 4 | vzdump.pm \ 5 | pvesubscription.pm \ 6 | pveceph.pm \ 7 | pveam.pm \ 8 | pvesr.pm \ 9 | pvenode.pm \ 10 | pvesh.pm \ 11 | pve8to9.pm \ 12 | pve_network_interface_pinning.pm \ 13 | 14 | all: 15 | 16 | .PHONY: install 17 | install: $(SOURCES) 18 | install -d -m 0755 $(PERLLIBDIR)/PVE/CLI 19 | for i in $(SOURCES); do install -D -m 0644 $$i $(PERLLIBDIR)/PVE/CLI/$$i; done 20 | 21 | 22 | clean: 23 | -------------------------------------------------------------------------------- /debian/postrm: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Abort if any command returns an error value 4 | set -e 5 | 6 | case "$1" in 7 | purge) 8 | rm -rf /var/log/pveproxy 9 | rm -rf /var/lib/pve-manager 10 | ;; 11 | 12 | remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear) 13 | rm -f /etc/cron.d/pveupdate 14 | ;; 15 | 16 | *) 17 | echo "postrm called with unknown argument \`$1'" >&2 18 | exit 1 19 | ;; 20 | esac 21 | 22 | #DEBHELPER# 23 | -------------------------------------------------------------------------------- /www/images/icon-cd-drive.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /PVE/API2/Cluster/BulkAction/Makefile: -------------------------------------------------------------------------------- 1 | include ../../../../defines.mk 2 | 3 | # for node independent, cluster-wide applicable, API endpoints 4 | # ensure we do not conflict with files shipped by pve-cluster!! 5 | PERLSOURCE= \ 6 | Guest.pm 7 | 8 | all: 9 | 10 | .PHONY: clean 11 | clean: 12 | rm -rf *~ 13 | 14 | .PHONY: install 15 | install: ${PERLSOURCE} 16 | install -d ${PERLLIBDIR}/PVE/API2/Cluster/BulkAction 17 | install -m 0644 ${PERLSOURCE} ${PERLLIBDIR}/PVE/API2/Cluster/BulkAction 18 | -------------------------------------------------------------------------------- /bin/pve-startall-delay: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::INotify; 7 | use PVE::NodeConfig; 8 | 9 | my $local_node = PVE::INotify::nodename(); 10 | 11 | my $node_config = eval { PVE::NodeConfig::load_config($local_node) } // {}; 12 | 13 | if (my $delay = $node_config->{'startall-onboot-delay'}) { 14 | warn "Delaying on-boot 'startall' command for $delay second(s).\n"; 15 | sleep($delay); # don't care for interrupts, best effort only 16 | } 17 | 18 | exit 0; 19 | -------------------------------------------------------------------------------- /services/pvescheduler.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Proxmox VE scheduler 3 | ConditionPathExists=/usr/bin/pvescheduler 4 | Wants=pve-cluster.service 5 | After=pve-cluster.service 6 | After=pve-guests.service 7 | After=pve-storage.target 8 | 9 | [Service] 10 | ExecStart=/usr/bin/pvescheduler start 11 | ExecStop=/usr/bin/pvescheduler stop 12 | ExecReload=/usr/bin/pvescheduler restart 13 | PIDFile=/run/pvescheduler.pid 14 | KillMode=process 15 | Type=forking 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /www/manager6/form/UserSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('pmx-users', { 2 | extend: 'Ext.data.Model', 3 | fields: [ 4 | 'userid', 5 | 'firstname', 6 | 'lastname', 7 | 'email', 8 | 'comment', 9 | { type: 'boolean', name: 'enable' }, 10 | { type: 'date', dateFormat: 'timestamp', name: 'expire' }, 11 | ], 12 | proxy: { 13 | type: 'proxmox', 14 | url: '/api2/json/access/users?full=1', 15 | }, 16 | idProperty: 'userid', 17 | }); 18 | -------------------------------------------------------------------------------- /PVE/API2/Cluster/Mapping/Makefile: -------------------------------------------------------------------------------- 1 | include ../../../../defines.mk 2 | 3 | # for node independent, cluster-wide applicable, API endpoints 4 | # ensure we do not conflict with files shipped by pve-cluster!! 5 | PERLSOURCE= \ 6 | Dir.pm \ 7 | PCI.pm \ 8 | USB.pm 9 | 10 | all: 11 | 12 | .PHONY: clean 13 | clean: 14 | rm -rf *~ 15 | 16 | .PHONY: install 17 | install: ${PERLSOURCE} 18 | install -d ${PERLLIBDIR}/PVE/API2/Cluster/Mapping 19 | install -m 0644 ${PERLSOURCE} ${PERLLIBDIR}/PVE/API2/Cluster/Mapping 20 | -------------------------------------------------------------------------------- /www/manager6/form/HashAlgorithmSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.hashAlgorithmSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveHashAlgorithmSelector'], 4 | config: { 5 | deleteEmpty: false, 6 | }, 7 | comboItems: [ 8 | ['__default__', 'None'], 9 | ['md5', 'MD5'], 10 | ['sha1', 'SHA-1'], 11 | ['sha224', 'SHA-224'], 12 | ['sha256', 'SHA-256'], 13 | ['sha384', 'SHA-384'], 14 | ['sha512', 'SHA-512'], 15 | ], 16 | }); 17 | -------------------------------------------------------------------------------- /network-hooks/Makefile: -------------------------------------------------------------------------------- 1 | include ../defines.mk 2 | 3 | all: 4 | 5 | .PHONY: install 6 | install: mtu bridgevlan bridgevlanport vlan vlan-down 7 | install -D -m 0755 mtu $(DESTDIR)/etc/network/if-up.d/mtu 8 | install -D -m 0755 bridgevlan $(DESTDIR)/etc/network/if-up.d/bridgevlan 9 | install -D -m 0755 bridgevlanport $(DESTDIR)/etc/network/if-up.d/bridgevlanport 10 | install -D -m 0755 vlan $(DESTDIR)/etc/network/if-pre-up.d/vlan 11 | install -D -m 0755 vlan-down $(DESTDIR)/etc/network/if-post-down.d/vlan 12 | 13 | clean: 14 | -------------------------------------------------------------------------------- /network-hooks/bridgevlanport: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -x /sbin/bridge ] 4 | then 5 | exit 0 6 | fi 7 | 8 | if [ "$MODE" = "start" ] ; then 9 | case "$IFACE" in 10 | *.[0-9]*) 11 | VLANID="${IFACE##*.}" 12 | IF_VLAN_RAW_DEVICE="${IFACE%.*}" 13 | ;; 14 | esac 15 | 16 | if [ -n "$IF_VLAN_RAW_DEVICE" ]; then 17 | if [ -e "/sys/class/net/$IF_VLAN_RAW_DEVICE/bridge/vlan_filtering" ]; then 18 | bridge vlan add dev $IF_VLAN_RAW_DEVICE vid $VLANID self 19 | fi 20 | fi 21 | fi 22 | 23 | 24 | -------------------------------------------------------------------------------- /www/Makefile: -------------------------------------------------------------------------------- 1 | include ../defines.mk 2 | SUBDIRS = images css manager6 3 | 4 | all: 5 | 6 | .PHONY: install 7 | install: 8 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done 9 | install -m 0644 index.html.tpl $(WWWBASEDIR) 10 | install -d $(WWWJSDIR) 11 | install -m 0644 u2f-api.js $(WWWJSDIR) 12 | 13 | .PHONY: check 14 | check: 15 | $(MAKE) -C manager6 $@ 16 | 17 | .PHONY: tidy 18 | tidy: 19 | $(MAKE) -C manager6 $@ 20 | 21 | .PHONY: clean 22 | clean: 23 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done 24 | -------------------------------------------------------------------------------- /www/manager6/form/CacheTypeSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.CacheTypeSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.CacheTypeSelector'], 4 | comboItems: [ 5 | ['__default__', Proxmox.Utils.defaultText + ' (' + gettext('No cache') + ')'], 6 | ['directsync', 'Direct sync'], 7 | ['writethrough', 'Write through'], 8 | ['writeback', 'Write back'], 9 | ['unsafe', 'Write back (' + gettext('unsafe') + ')'], 10 | ['none', gettext('No cache')], 11 | ], 12 | }); 13 | -------------------------------------------------------------------------------- /www/manager6/form/QemuBiosSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.QemuBiosSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveQemuBiosSelector'], 4 | 5 | initComponent: function () { 6 | var me = this; 7 | 8 | me.comboItems = [ 9 | ['__default__', PVE.Utils.render_qemu_bios('')], 10 | ['seabios', PVE.Utils.render_qemu_bios('seabios')], 11 | ['ovmf', PVE.Utils.render_qemu_bios('ovmf')], 12 | ]; 13 | 14 | me.callParent(); 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /configs/proxmox-ve-default.link: -------------------------------------------------------------------------------- 1 | [Match] 2 | OriginalName=* 3 | 4 | [Link] 5 | # Fixes two issues for Proxmox VE systems: 6 | # 1. inheriting MAC from the first slave, instead of using a random one, avoids 7 | # that locked down network environments (e.g., at most hosting providers) 8 | # will block traffic due to a unexpected MAC in the outgoing network packets 9 | # 2. Avoids that systemd keeps bridge offline if there are no slaves connected, 10 | # failing, e.g., setting up s-NAT if no guest is (yet) started. 11 | MACAddressPolicy=none 12 | -------------------------------------------------------------------------------- /www/images/xtermjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /www/manager6/form/ACMEPluginSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.ACMEPluginSelector', { 2 | extend: 'Ext.form.field.ComboBox', 3 | alias: 'widget.pveACMEPluginSelector', 4 | 5 | fieldLabel: gettext('Plugin'), 6 | displayField: 'plugin', 7 | valueField: 'plugin', 8 | 9 | store: { 10 | model: 'pve-acme-plugins', 11 | autoLoad: true, 12 | filters: (item) => item.data.type === 'dns', 13 | }, 14 | 15 | triggerAction: 'all', 16 | queryMode: 'local', 17 | allowBlank: false, 18 | editable: false, 19 | }); 20 | -------------------------------------------------------------------------------- /configs/vzdump.conf: -------------------------------------------------------------------------------- 1 | # vzdump default settings 2 | 3 | #tmpdir: DIR 4 | #dumpdir: DIR 5 | #storage: STORAGE_ID 6 | #mode: snapshot|suspend|stop 7 | #bwlimit: KBPS 8 | #performance: [max-workers=N][,pbs-entries-max=N] 9 | #ionice: PRI 10 | #lockwait: MINUTES 11 | #stopwait: MINUTES 12 | #stdexcludes: BOOLEAN 13 | #mailto: ADDRESSLIST 14 | #prune-backups: keep-INTERVAL=N[,...] 15 | #script: FILENAME 16 | #exclude-path: PATHLIST 17 | #pigz: N 18 | #notes-template: {{guestname}} 19 | #pbs-change-detection-mode: legacy|data|metadata 20 | #fleecing: enabled=BOOLEAN,storage=STORAGE_ID 21 | -------------------------------------------------------------------------------- /debian/lintian-overrides: -------------------------------------------------------------------------------- 1 | pve-manager: mail-transport-agent-dependency-does-not-specify-default-mta * 2 | pve-manager: no-manual-page [usr/bin/pvebanner] 3 | pve-manager: no-manual-page [usr/bin/pveupdate] 4 | pve-manager: non-standard-dir-perm 0700 != 0755 [var/log/pveproxy/] 5 | pve-manager: systemd-service-file-refers-to-unusual-wantedby-target getty.target [usr/lib/systemd/system/pvebanner.service] 6 | pve-manager: package-installs-apt-sources [etc/apt/sources.list.d/pve-enterprise.sources] 7 | pve-manager: privacy-breach-generic usr/share/pve-manager/touch/sencha-touch-all-debug.js * 8 | -------------------------------------------------------------------------------- /www/manager6/sdn/fabrics/ospf/FabricEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Fabric.Ospf.Fabric.Edit', { 2 | extend: 'PVE.sdn.Fabric.Fabric.Edit', 3 | 4 | subject: 'OSPF', 5 | onlineHelp: 'pvesdn_ospf_fabric', 6 | 7 | extraRequestParams: { 8 | protocol: 'ospf', 9 | }, 10 | 11 | additionalItems: [ 12 | { 13 | xtype: 'textfield', 14 | fieldLabel: gettext('Area'), 15 | labelWidth: 120, 16 | name: 'area', 17 | emptyText: '0', 18 | allowBlank: false, 19 | }, 20 | ], 21 | }); 22 | -------------------------------------------------------------------------------- /www/manager6/qemu/ScsiHwEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.qemu.ScsiHwEdit', { 2 | extend: 'Proxmox.window.Edit', 3 | 4 | initComponent: function () { 5 | var me = this; 6 | 7 | Ext.applyIf(me, { 8 | subject: gettext('SCSI Controller Type'), 9 | items: { 10 | xtype: 'pveScsiHwSelector', 11 | name: 'scsihw', 12 | value: '__default__', 13 | fieldLabel: gettext('Type'), 14 | }, 15 | }); 16 | 17 | me.callParent(); 18 | 19 | me.load(); 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /www/manager6/sdn/fabrics/Common.js: -------------------------------------------------------------------------------- 1 | Ext.define('Pve.sdn.Fabric', { 2 | extend: 'Ext.data.Model', 3 | idProperty: 'name', 4 | fields: ['id', 'protocol', 'ip_prefix', 'ip6_prefix'], 5 | }); 6 | 7 | Ext.define('Pve.sdn.Node', { 8 | extend: 'Ext.data.Model', 9 | idProperty: 'name', 10 | fields: ['fabric_id', 'node_id', 'protocol', 'ip', 'ip6', 'area'], 11 | }); 12 | 13 | Ext.define('Pve.sdn.Interface', { 14 | extend: 'Ext.data.Model', 15 | idProperty: 'name', 16 | fields: ['name', 'ip', 'ip6', 'hello_interval', 'hello_multiplier', 'csnp_interval'], 17 | }); 18 | -------------------------------------------------------------------------------- /PVE/pvecfg.pm.in: -------------------------------------------------------------------------------- 1 | package PVE::pvecfg; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | sub package { 7 | return '@PACKAGE@'; 8 | } 9 | 10 | sub version { 11 | return '@VERSION@'; 12 | } 13 | 14 | sub release { 15 | return '@PVERELEASE@'; 16 | } 17 | 18 | sub repoid { 19 | return '@REPOID@'; 20 | } 21 | 22 | sub version_text { 23 | return '@VERSION@/@REPOID@'; 24 | } 25 | 26 | # this is returned by the API 27 | sub version_info { 28 | return { 29 | 'version' => '@VERSION@', 30 | 'release' => '@PVERELEASE@', 31 | 'repoid' => '@REPOID@', 32 | } 33 | } 34 | 35 | 1; 36 | -------------------------------------------------------------------------------- /www/manager6/controller/StorageEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.controller.StorageEdit', { 2 | extend: 'Ext.app.ViewController', 3 | alias: 'controller.storageEdit', 4 | control: { 5 | 'field[name=content]': { 6 | change: function (field, value) { 7 | const hasImages = Ext.Array.contains(value, 'images'); 8 | const prealloc = field.up('form').getForm().findField('preallocation'); 9 | if (prealloc) { 10 | prealloc.setDisabled(!hasImages); 11 | } 12 | }, 13 | }, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /www/manager6/form/ACMEAccountSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.ACMEAccountSelector', { 2 | extend: 'Ext.form.field.ComboBox', 3 | alias: 'widget.pveACMEAccountSelector', 4 | 5 | displayField: 'name', 6 | valueField: 'name', 7 | 8 | store: { 9 | model: 'pve-acme-accounts', 10 | autoLoad: true, 11 | }, 12 | 13 | triggerAction: 'all', 14 | queryMode: 'local', 15 | allowBlank: false, 16 | editable: false, 17 | forceSelection: true, 18 | 19 | isEmpty: function () { 20 | return this.getStore().getData().length === 0; 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /www/manager6/qemu/KeyboardEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.qemu.KeyboardEdit', { 2 | extend: 'Proxmox.window.Edit', 3 | 4 | initComponent: function () { 5 | var me = this; 6 | 7 | Ext.applyIf(me, { 8 | subject: gettext('Keyboard Layout'), 9 | items: { 10 | xtype: 'VNCKeyboardSelector', 11 | name: 'keyboard', 12 | value: '__default__', 13 | fieldLabel: gettext('Keyboard Layout'), 14 | }, 15 | }); 16 | 17 | me.callParent(); 18 | 19 | me.load(); 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /bin/pvedaemon: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -T 2 | 3 | $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin'; 4 | 5 | delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; 6 | 7 | use strict; 8 | use warnings; 9 | 10 | use PVE::SafeSyslog; 11 | use PVE::Service::pvedaemon; 12 | 13 | $SIG{'__WARN__'} = sub { 14 | my $err = $@; 15 | my $t = $_[0]; 16 | chomp $t; 17 | print STDERR "$t\n"; 18 | syslog('warning', "%s", $t); 19 | $@ = $err; 20 | }; 21 | 22 | my $prepare = sub { 23 | # create dir for dtach sockets 24 | mkdir "/var/run/dtach"; 25 | }; 26 | 27 | PVE::Service::pvedaemon->run_cli_handler(prepare => $prepare); 28 | -------------------------------------------------------------------------------- /test/perftest2.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use lib '../../'; 4 | use strict; 5 | use warnings; 6 | use Time::HiRes qw( usleep ualarm gettimeofday tv_interval ); 7 | use PVE::INotify; 8 | use PVE::AccessControl; 9 | 10 | my $hostname = PVE::INotify::read_file("hostname"); 11 | 12 | # normally you use username/password, 13 | # but we can simply create a ticket if we are root 14 | my $ticket = PVE::AccessControl::assemble_ticket('root@pam'); 15 | 16 | my $cmd = "ab -c 10 -n 1000 -k -C 'PVEAuthCookie=$ticket' https://$hostname:8006/api2/json"; 17 | print "$cmd\n"; 18 | system($cmd) == 0 || die "command failed - $!\n"; 19 | -------------------------------------------------------------------------------- /www/manager6/ceph/Log.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.ceph.Log', { 2 | extend: 'Proxmox.panel.LogView', 3 | xtype: 'cephLogView', 4 | 5 | nodename: undefined, 6 | 7 | failCallback: function (response) { 8 | var me = this; 9 | var msg = response.htmlStatus; 10 | var windowShow = PVE.Utils.showCephInstallOrMask(me, msg, me.nodename, function (win) { 11 | me.mon(win, 'cephInstallWindowClosed', function () { 12 | me.loadTask.delay(200); 13 | }); 14 | }); 15 | if (!windowShow) { 16 | Proxmox.Utils.setErrorMask(me, msg); 17 | } 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /www/manager6/form/ScsiHwSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.ScsiHwSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveScsiHwSelector'], 4 | comboItems: [ 5 | ['__default__', PVE.Utils.render_scsihw('')], 6 | ['lsi', PVE.Utils.render_scsihw('lsi')], 7 | ['lsi53c810', PVE.Utils.render_scsihw('lsi53c810')], 8 | ['megasas', PVE.Utils.render_scsihw('megasas')], 9 | ['virtio-scsi-pci', PVE.Utils.render_scsihw('virtio-scsi-pci')], 10 | ['virtio-scsi-single', PVE.Utils.render_scsihw('virtio-scsi-single')], 11 | ['pvscsi', PVE.Utils.render_scsihw('pvscsi')], 12 | ], 13 | }); 14 | -------------------------------------------------------------------------------- /www/manager6/sdn/fabrics/openfabric/NodeEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Fabric.OpenFabric.Node.Edit', { 2 | extend: 'PVE.sdn.Fabric.Node.Edit', 3 | protocol: 'openfabric', 4 | 5 | extraRequestParams: { 6 | protocol: 'openfabric', 7 | }, 8 | 9 | additionalItems: [ 10 | { 11 | xtype: 'proxmoxtextfield', 12 | fieldLabel: gettext('IPv6'), 13 | labelWidth: 120, 14 | name: 'ip6', 15 | allowBlank: true, 16 | skipEmptyText: true, 17 | cbind: { 18 | deleteEmpty: '{!isCreate}', 19 | }, 20 | }, 21 | ], 22 | }); 23 | -------------------------------------------------------------------------------- /bin/pvescheduler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::Service::pvescheduler; 7 | 8 | use PVE::RPCEnvironment; 9 | use PVE::SafeSyslog; 10 | 11 | $SIG{'__WARN__'} = sub { 12 | my $err = $@; 13 | my $t = $_[0]; 14 | chomp $t; 15 | print STDERR "$t\n"; 16 | syslog('warning', "%s", $t); 17 | $@ = $err; 18 | }; 19 | 20 | my $prepare = sub { 21 | my $rpcenv = PVE::RPCEnvironment->init('priv'); 22 | 23 | $rpcenv->init_request(); 24 | $rpcenv->set_language($ENV{LANG}); 25 | $rpcenv->set_user('root@pam'); 26 | }; 27 | 28 | PVE::Service::pvescheduler->run_cli_handler(prepare => $prepare); 29 | -------------------------------------------------------------------------------- /templates/default/replication-body.html.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | Replication job '{{job-id}}' with target '{{job-target}}' and schedule '{{job-schedule}}' failed!

4 | 5 | Last successful sync: {{timestamp last-sync}}
6 | Next sync try: {{timestamp next-sync}}
7 | Failure count: {{failure-count}}
8 | 9 | {{#if (eq failure-count 3)}} 10 | Note: The system will now reduce the frequency of error reports, as the job appears to be stuck. 11 | {{/if}} 12 |
13 | Error:
14 |
15 | {{error}}
16 |         
17 | 18 | 19 | -------------------------------------------------------------------------------- /bin/pvestatd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::INotify; 7 | use PVE::RPCEnvironment; 8 | use PVE::SafeSyslog; 9 | use PVE::Service::pvestatd; 10 | 11 | $SIG{'__WARN__'} = sub { 12 | my $err = $@; 13 | my $t = $_[0]; 14 | chomp $t; 15 | print STDERR "$t\n"; 16 | syslog('warning', "%s", $t); 17 | $@ = $err; 18 | }; 19 | 20 | my $prepare = sub { 21 | my $rpcenv = PVE::RPCEnvironment->init('priv'); 22 | 23 | $rpcenv->init_request(); 24 | $rpcenv->set_language($ENV{LANG}); 25 | $rpcenv->set_user('root@pam'); 26 | }; 27 | 28 | PVE::Service::pvestatd->run_cli_handler(prepare => $prepare); 29 | -------------------------------------------------------------------------------- /bin/pve-rbd-storage-configure-keyring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::RPCEnvironment; 7 | use PVE::Storage; 8 | 9 | use PVE::CLI::pve8to9; 10 | 11 | sub main { 12 | PVE::RPCEnvironment->setup_default_cli_env(); 13 | 14 | my $cfg = PVE::Storage::config(); 15 | 16 | print "INFO: Starting with PVE 9, externally managed RBD storages require that the 'keyring'" 17 | . " option is configured in the storage's Ceph configuration. This script creates and" 18 | . " updates the storage's Ceph configurations.\n"; 19 | 20 | PVE::CLI::pve8to9::check_rbd_storage_keyring($cfg, 0); 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /bin/pvebanner: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use PVE::INotify; 5 | use PVE::Cluster; 6 | 7 | my $nodename = PVE::INotify::nodename(); 8 | my $localip = PVE::Cluster::remote_node_ip($nodename, 1); 9 | 10 | my $xline = '-' x 78; 11 | 12 | my $banner = ''; 13 | 14 | if ($localip) { 15 | $banner .= <<__EOBANNER; 16 | 17 | $xline 18 | 19 | Welcome to the Proxmox Virtual Environment. Please use your web browser to 20 | configure this server - connect to: 21 | 22 | https://${localip}:8006/ 23 | 24 | $xline 25 | 26 | __EOBANNER 27 | 28 | } 29 | 30 | open(ISSUE, ">/etc/issue"); 31 | 32 | print ISSUE $banner; 33 | 34 | close(ISSUE); 35 | 36 | exit(0); 37 | -------------------------------------------------------------------------------- /www/manager6/form/ContentTypeSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.ContentTypeSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveContentTypeSelector'], 4 | 5 | cts: undefined, 6 | 7 | initComponent: function () { 8 | var me = this; 9 | 10 | me.comboItems = []; 11 | 12 | if (me.cts === undefined) { 13 | me.cts = ['images', 'iso', 'vztmpl', 'backup', 'rootdir', 'snippets', 'import']; 14 | } 15 | 16 | Ext.Array.each(me.cts, function (ct) { 17 | me.comboItems.push([ct, PVE.Utils.format_content_types(ct)]); 18 | }); 19 | 20 | me.callParent(); 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /aplinfo/apltest.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::APLInfo; 7 | use Data::Dumper; 8 | 9 | my $pkglist = PVE::APLInfo::load_data(); 10 | 11 | my $err = 0; 12 | 13 | foreach my $k (keys %{ $pkglist->{'all'} }) { 14 | next if $k eq 'pve-web-news'; 15 | my $res = $pkglist->{all}->{$k}; 16 | 17 | # heuristic only.. 18 | my $template = "$res->{package}_$res->{version}_$res->{architecture}.tar"; 19 | 20 | if ($k !~ m/^($res->{os}-)?\Q$template\E\.(gz|xz|zst)$/) { 21 | print "ERROR: $k != $template\n"; 22 | #print Dumper($res) . "\n"; 23 | $err = 1; 24 | } 25 | } 26 | 27 | $err ? exit(-11) : exit(0); 28 | 29 | -------------------------------------------------------------------------------- /services/pveproxy.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PVE API Proxy Server 3 | ConditionPathExists=/usr/bin/pveproxy 4 | Wants=pve-cluster.service 5 | Wants=pvedaemon.service 6 | Wants=ssh.service 7 | Wants=pve-storage.target 8 | After=pve-storage.target 9 | After=pve-cluster.service 10 | After=pvedaemon.service 11 | After=ssh.service 12 | 13 | [Service] 14 | ExecStartPre=-/usr/bin/pvecm updatecerts --silent 15 | ExecStart=/usr/bin/pveproxy start 16 | ExecStartPost=-sh -c '[ ! -e /var/log/pveam.log ] && /usr/bin/pveupdate' 17 | ExecStop=/usr/bin/pveproxy stop 18 | ExecReload=/usr/bin/pveproxy restart 19 | PIDFile=/run/pveproxy/pveproxy.pid 20 | Type=forking 21 | Restart=on-failure 22 | 23 | [Install] 24 | WantedBy=multi-user.target 25 | -------------------------------------------------------------------------------- /PVE/CLI/vzdump.pm: -------------------------------------------------------------------------------- 1 | package PVE::CLI::vzdump; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::RPCEnvironment; 7 | use PVE::CLIHandler; 8 | use PVE::API2::VZDump; 9 | 10 | use base qw(PVE::CLIHandler); 11 | 12 | sub setup_environment { 13 | PVE::RPCEnvironment->setup_default_cli_env(); 14 | } 15 | 16 | # Note: use string 'vmid' as $arg_param option, to allow vmid lists 17 | our $cmddef = [ 18 | 'PVE::API2::VZDump', 19 | 'vzdump', 20 | 'vmid', 21 | undef, 22 | sub { 23 | my $upid = shift; 24 | exit(0) if $upid eq 'OK'; 25 | my $status = PVE::Tools::upid_read_status($upid); 26 | exit(PVE::Tools::upid_status_is_error($status) ? -1 : 0); 27 | }, 28 | ]; 29 | 30 | 1; 31 | -------------------------------------------------------------------------------- /debian/prerm: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Abort if any command returns an error value 4 | set -e 5 | 6 | # This script is called as the first step in removing the package from 7 | # the system. This includes cases where the user explicitly asked for 8 | # the package to be removed, upgrade, automatic removal due to conflicts, 9 | # and deconfiguration due to temporary removal of a depended-on package. 10 | 11 | package_name=pve-manager; 12 | 13 | case "$1" in 14 | remove|deconfigure|failed-upgrade) : ;; 15 | upgrade) 16 | if [ -L /usr/doc/$package_name ]; then 17 | rm -f /usr/doc/$package_name 18 | fi 19 | ;; 20 | *) echo "$0: didn't understand being called with \`$1'" 1>&2 21 | exit 0;; 22 | esac 23 | 24 | exit 0 25 | -------------------------------------------------------------------------------- /www/manager6/form/BusTypeSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.BusTypeSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: 'widget.pveBusSelector', 4 | 5 | withVirtIO: true, 6 | withUnused: false, 7 | 8 | initComponent: function () { 9 | var me = this; 10 | 11 | me.comboItems = [ 12 | ['ide', 'IDE'], 13 | ['sata', 'SATA'], 14 | ]; 15 | 16 | if (me.withVirtIO) { 17 | me.comboItems.push(['virtio', 'VirtIO Block']); 18 | } 19 | 20 | me.comboItems.push(['scsi', 'SCSI']); 21 | 22 | if (me.withUnused) { 23 | me.comboItems.push(['unused', 'Unused']); 24 | } 25 | 26 | me.callParent(); 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /www/manager6/ha/rules/ResourceAffinityRuleEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.ha.rules.ResourceAffinityInputPanel', { 2 | extend: 'PVE.ha.RuleInputPanel', 3 | 4 | initComponent: function () { 5 | let me = this; 6 | 7 | me.column1 = []; 8 | 9 | me.column2 = [ 10 | { 11 | xtype: 'proxmoxKVComboBox', 12 | name: 'affinity', 13 | fieldLabel: gettext('Affinity'), 14 | allowBlank: false, 15 | comboItems: [ 16 | ['positive', gettext('Keep Together')], 17 | ['negative', gettext('Keep Separate')], 18 | ], 19 | }, 20 | ]; 21 | 22 | me.callParent(); 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /PVE/API2/Cluster/Makefile: -------------------------------------------------------------------------------- 1 | include ../../../defines.mk 2 | 3 | SUBDIRS=Mapping \ 4 | BulkAction 5 | 6 | # for node independent, cluster-wide applicable, API endpoints 7 | # ensure we do not conflict with files shipped by pve-cluster!! 8 | PERLSOURCE= \ 9 | BackupInfo.pm \ 10 | BulkAction.pm \ 11 | MetricServer.pm \ 12 | Mapping.pm \ 13 | Notifications.pm \ 14 | Jobs.pm \ 15 | Ceph.pm 16 | 17 | all: 18 | 19 | .PHONY: clean 20 | clean: 21 | rm -rf *~ 22 | set -e && for i in ${SUBDIRS}; do ${MAKE} -C $$i $@; done 23 | 24 | .PHONY: install 25 | install: $(PERLSOURCE) 26 | install -d $(PERLLIBDIR)/PVE/API2/Cluster 27 | install -m 0644 $(PERLSOURCE) $(PERLLIBDIR)/PVE/API2/Cluster 28 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done 29 | -------------------------------------------------------------------------------- /www/manager6/ha/rules/ResourceAffinityRules.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.ha.ResourceAffinityRulesView', { 2 | extend: 'PVE.ha.RulesBaseView', 3 | alias: 'widget.pveHAResourceAffinityRulesView', 4 | 5 | ruleType: 'resource-affinity', 6 | ruleTitle: gettext('HA Resource Affinity'), 7 | inputPanel: 'ResourceAffinityInputPanel', 8 | faIcon: 'link', 9 | 10 | stateful: true, 11 | stateId: 'grid-ha-resource-affinity-rules', 12 | 13 | columns: [ 14 | { 15 | header: gettext('Affinity'), 16 | width: 100, 17 | dataIndex: 'affinity', 18 | }, 19 | { 20 | header: gettext('HA Resources'), 21 | flex: 5, 22 | dataIndex: 'resources', 23 | }, 24 | ], 25 | }); 26 | -------------------------------------------------------------------------------- /bin/pve-firewall-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Time::HiRes qw(usleep); 7 | 8 | use PVE::Cluster; 9 | use PVE::INotify; 10 | 11 | for (my $i = 0; !PVE::Cluster::check_cfs_quorum(1); $i++) { 12 | print "waiting for pmxcfs mount to appear and get quorate...\n" 13 | if $i % 50 == 0; 14 | 15 | usleep(100 * 1000); 16 | } 17 | 18 | my $local_node = PVE::INotify::nodename(); 19 | my $current_fw_config_file = "/etc/pve/nodes/$local_node/host.fw"; 20 | my $new_fw_config_file = "/etc/pve/nodes/$local_node/host.fw.new"; 21 | 22 | if (-e $new_fw_config_file) { 23 | rename($new_fw_config_file, $current_fw_config_file) 24 | or die "failed to commit new local node firewall config '$new_fw_config_file' - $!\n"; 25 | } 26 | 27 | exit 0; 28 | -------------------------------------------------------------------------------- /templates/Makefile: -------------------------------------------------------------------------------- 1 | NOTIFICATION_TEMPLATES= \ 2 | default/test-subject.txt.hbs \ 3 | default/test-body.txt.hbs \ 4 | default/test-body.html.hbs \ 5 | default/vzdump-subject.txt.hbs \ 6 | default/vzdump-body.txt.hbs \ 7 | default/vzdump-body.html.hbs \ 8 | default/replication-subject.txt.hbs \ 9 | default/replication-body.txt.hbs \ 10 | default/replication-body.html.hbs \ 11 | default/package-updates-subject.txt.hbs \ 12 | default/package-updates-body.txt.hbs \ 13 | default/package-updates-body.html.hbs \ 14 | 15 | all: 16 | 17 | .PHONY: install 18 | install: 19 | install -dm 0755 $(DESTDIR)/usr/share/pve-manager/templates/default 20 | $(foreach i,$(NOTIFICATION_TEMPLATES), \ 21 | install -m644 $(i) $(DESTDIR)/usr/share/pve-manager/templates/$(i) ;) 22 | 23 | 24 | clean: 25 | -------------------------------------------------------------------------------- /www/manager6/panel/HealthWidget.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.widget.HealthWidget', { 2 | extend: 'Ext.Component', 3 | alias: 'widget.pveHealthWidget', 4 | 5 | data: { 6 | iconCls: PVE.Utils.get_health_icon(undefined, true), 7 | text: '', 8 | title: '', 9 | }, 10 | 11 | style: { 12 | 'text-align': 'center', 13 | }, 14 | 15 | tpl: ['

{title}

', '', '

', '{text}'], 16 | 17 | updateHealth: function (data) { 18 | var me = this; 19 | me.update(Ext.apply(me.data, data)); 20 | }, 21 | 22 | initComponent: function () { 23 | var me = this; 24 | 25 | if (me.title) { 26 | me.config.data.title = me.title; 27 | } 28 | 29 | me.callParent(); 30 | }, 31 | }); 32 | -------------------------------------------------------------------------------- /www/images/icon-fa-network-wired.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /bin/pveproxy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -T 2 | 3 | $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin'; 4 | 5 | delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; 6 | 7 | use strict; 8 | use warnings; 9 | 10 | use PVE::SafeSyslog; 11 | use PVE::Service::pveproxy; 12 | 13 | $SIG{'__WARN__'} = sub { 14 | my $err = $@; 15 | my $t = $_[0]; 16 | chomp $t; 17 | print STDERR "$t\n"; 18 | syslog('warning', "%s", $t); 19 | $@ = $err; 20 | }; 21 | 22 | my $prepare = sub { 23 | my $rundir = "/var/run/pveproxy"; 24 | if (mkdir($rundir, 0700)) { # only works at first start if we are root) 25 | my $gid = getgrnam('www-data') || die "getgrnam failed - $!\n"; 26 | my $uid = getpwnam('www-data') || die "getpwnam failed - $!\n"; 27 | chown($uid, $gid, $rundir); 28 | } 29 | }; 30 | 31 | PVE::Service::pveproxy->run_cli_handler(prepare => $prepare); 32 | -------------------------------------------------------------------------------- /bin/spiceproxy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -T 2 | 3 | $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin'; 4 | 5 | delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; 6 | 7 | use strict; 8 | use warnings; 9 | use PVE::SafeSyslog; 10 | use PVE::Service::spiceproxy; 11 | 12 | $SIG{'__WARN__'} = sub { 13 | my $err = $@; 14 | my $t = $_[0]; 15 | chomp $t; 16 | print STDERR "$t\n"; 17 | syslog('warning', "%s", $t); 18 | $@ = $err; 19 | }; 20 | 21 | my $prepare = sub { 22 | my $rundir = "/var/run/pveproxy"; 23 | if (mkdir($rundir, 0700)) { # only works at first start if we are root) 24 | my $gid = getgrnam('www-data') || die "getgrnam failed - $!\n"; 25 | my $uid = getpwnam('www-data') || die "getpwnam failed - $!\n"; 26 | chown($uid, $gid, $rundir); 27 | } 28 | }; 29 | 30 | PVE::Service::spiceproxy->run_cli_handler(prepare => $prepare); 31 | -------------------------------------------------------------------------------- /www/manager6/form/DayOfWeekSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.DayOfWeekSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | alias: ['widget.pveDayOfWeekSelector'], 4 | comboItems: [], 5 | initComponent: function () { 6 | var me = this; 7 | me.comboItems = [ 8 | ['mon', Ext.util.Format.htmlDecode(Ext.Date.dayNames[1])], 9 | ['tue', Ext.util.Format.htmlDecode(Ext.Date.dayNames[2])], 10 | ['wed', Ext.util.Format.htmlDecode(Ext.Date.dayNames[3])], 11 | ['thu', Ext.util.Format.htmlDecode(Ext.Date.dayNames[4])], 12 | ['fri', Ext.util.Format.htmlDecode(Ext.Date.dayNames[5])], 13 | ['sat', Ext.util.Format.htmlDecode(Ext.Date.dayNames[6])], 14 | ['sun', Ext.util.Format.htmlDecode(Ext.Date.dayNames[0])], 15 | ]; 16 | this.callParent(); 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /www/manager6/ha/rules/NodeAffinityRules.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.ha.NodeAffinityRulesView', { 2 | extend: 'PVE.ha.RulesBaseView', 3 | alias: 'widget.pveHANodeAffinityRulesView', 4 | 5 | ruleType: 'node-affinity', 6 | ruleTitle: gettext('HA Node Affinity'), 7 | inputPanel: 'NodeAffinityInputPanel', 8 | faIcon: 'map-pin', 9 | 10 | stateful: true, 11 | stateId: 'grid-ha-node-affinity-rules', 12 | 13 | columns: [ 14 | { 15 | header: gettext('Strict'), 16 | width: 75, 17 | dataIndex: 'strict', 18 | }, 19 | { 20 | header: gettext('HA Resources'), 21 | flex: 3, 22 | dataIndex: 'resources', 23 | }, 24 | { 25 | header: gettext('Nodes'), 26 | flex: 2, 27 | dataIndex: 'nodes', 28 | }, 29 | ], 30 | }); 31 | -------------------------------------------------------------------------------- /PVE/API2/Makefile: -------------------------------------------------------------------------------- 1 | include ../../defines.mk 2 | 3 | SUBDIRS=Hardware Ceph Cluster 4 | 5 | PERLSOURCE = \ 6 | ACME.pm \ 7 | ACMEAccount.pm \ 8 | ACMEPlugin.pm \ 9 | APT.pm \ 10 | Backup.pm \ 11 | Capabilities.pm \ 12 | Ceph.pm \ 13 | Certificates.pm \ 14 | Cluster.pm \ 15 | HAConfig.pm \ 16 | Hardware.pm \ 17 | Network.pm \ 18 | NodeConfig.pm \ 19 | Nodes.pm \ 20 | Pool.pm \ 21 | Replication.pm \ 22 | ReplicationConfig.pm \ 23 | Services.pm \ 24 | Subscription.pm \ 25 | Tasks.pm \ 26 | VZDump.pm \ 27 | 28 | all: 29 | 30 | .PHONY: clean 31 | clean: 32 | rm -rf *~ 33 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done 34 | 35 | .PHONY: install 36 | install: $(PERLSOURCE) 37 | install -d $(PERLLIBDIR)/PVE/API2 38 | install -m 0644 $(PERLSOURCE) $(PERLLIBDIR)/PVE/API2 39 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done 40 | -------------------------------------------------------------------------------- /network-hooks/vlan-down: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # If IFACE is an automagic vlan interface (without the vlan-raw-device 4 | # parameter) then let's try to discover the magic here.. Another way would be 5 | # to just probe for the right device name in /proc/net/vlan 6 | 7 | case "$IFACE" in 8 | # Ignore any alias (#272891) 9 | *:*) 10 | exit 0 11 | ;; 12 | *.[0-9]*) 13 | # Silently ignore interfaces which ifupdown handles on its own 14 | # If IF_BRIDGE_PORTS is set, probably we're called by bridge-utils 15 | [ -z "$IF_VLAN_RAW_DEVICE" -a -z "$IF_BRIDGE_PORTS" ] && exit 0 16 | IF_VLAN_RAW_DEVICE=`echo $IFACE|sed "s/\([A-Za-z0-9]*\)\..*/\1/"` 17 | ;; 18 | # Test for vlan raw device (#196890, #292648) 19 | *) 20 | [ -z "$IF_VLAN_RAW_DEVICE" ] && exit 0 21 | ;; 22 | esac 23 | 24 | if [ -e "/sys/class/net/$IFACE" ]; then 25 | ip link delete $IFACE 26 | fi 27 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | include ../defines.mk 2 | 3 | all: 4 | 5 | export PERLLIB=.. 6 | 7 | .PHONY: check 8 | check: test-replication test-balloon test-vzdump test-osd 9 | 10 | .PHONY: test-balloon 11 | test-balloon: 12 | ./balloontest.pl 13 | 14 | .PHONY: test-replication 15 | test-replication: replication1.t replication2.t replication3.t replication4.t replication5.t replication6.t 16 | 17 | replication%.t: replication_test%.pl 18 | ./$< 19 | 20 | .PHONY: test-vzdump 21 | test-vzdump: test-vzdump-guest-included test-vzdump-new 22 | 23 | .PHONY: test-vzdump-guest-included 24 | test-vzdump-guest-included: 25 | ./vzdump_guest_included_test.pl 26 | 27 | .PHONY: test-vzdump-new 28 | test-vzdump-new: 29 | ./vzdump_new_test.pl 30 | 31 | .PHONY: test-osd 32 | test-osd: 33 | ./OSD_test.pl 34 | 35 | .PHONY: install 36 | install: 37 | 38 | .PHONY: clean 39 | clean: 40 | rm -rf *~ .mocked_* *.tmp 41 | -------------------------------------------------------------------------------- /defines.mk: -------------------------------------------------------------------------------- 1 | PACKAGE=pve-manager 2 | 3 | BINDIR=$(DESTDIR)/usr/bin 4 | LIBEXECDIR=$(DESTDIR)/usr/libexec/proxmox 5 | PERLLIBDIR=$(DESTDIR)/usr/share/perl5 6 | MAN1DIR=$(DESTDIR)/usr/share/man/man1 7 | MAN8DIR=$(DESTDIR)/usr/share/man/man8 8 | CRONDAILYDIR=$(DESTDIR)/etc/cron.daily 9 | INITDBINDIR=$(DESTDIR)/etc/init.d 10 | SERVICEDIR=$(DESTDIR)/usr/lib/systemd/system 11 | BASHCOMPLDIR=$(DESTDIR)/usr/share/bash-completion/completions/ 12 | ZSHCOMPLDIR=$(DESTDIR)/usr/share/zsh/vendor-completions/ 13 | HARADIR=$(DESTDIR)/usr/share/cluster 14 | DOCDIR=$(DESTDIR)/usr/share/doc/$(PACKAGE) 15 | PODDIR=$(DESTDIR)/usr/share/doc/$(PACKAGE)/pod 16 | USRSHARE=$(DESTDIR)/usr/share/$(PACKAGE) 17 | WWWBASEDIR=$(DESTDIR)/usr/share/$(PACKAGE) 18 | WWWIMAGEDIR=$(WWWBASEDIR)/images 19 | WWWTOUCHDIR=$(WWWBASEDIR)/touch 20 | WWWCSSDIR=$(WWWBASEDIR)/css 21 | WWWFONTSDIR=$(WWWBASEDIR)/css/fonts 22 | WWWJSDIR=$(WWWBASEDIR)/js 23 | -------------------------------------------------------------------------------- /www/manager6/pool/StatusView.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.pool.StatusView', { 2 | extend: 'Proxmox.grid.ObjectGrid', 3 | alias: ['widget.pvePoolStatusView'], 4 | disabled: true, 5 | 6 | title: gettext('Status'), 7 | cwidth1: 150, 8 | interval: 30000, 9 | //height: 195, 10 | initComponent: function () { 11 | var me = this; 12 | 13 | var pool = me.pveSelNode.data.pool; 14 | if (!pool) { 15 | throw 'no pool specified'; 16 | } 17 | 18 | var rows = { 19 | comment: { 20 | header: gettext('Comment'), 21 | renderer: Ext.String.htmlEncode, 22 | required: true, 23 | }, 24 | }; 25 | 26 | Ext.apply(me, { 27 | url: '/api2/json/pools/?poolid=' + pool, 28 | rows: rows, 29 | }); 30 | 31 | me.callParent(); 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /aplinfo/Makefile: -------------------------------------------------------------------------------- 1 | 2 | DOCDIR=/usr/share/doc/pve-manager/ 3 | 4 | TRUSTED_KEYS= \ 5 | proxmox-release-bookworm.gpg \ 6 | proxmox-release-trixie.gpg \ 7 | release@turnkeylinux.com.pubkey 8 | 9 | all: 10 | 11 | .PHONY: install 12 | install: aplinfo.dat trustedkeys.gpg 13 | install -D -m 0644 aplinfo.dat $(DESTDIR)$(DOCDIR)/aplinfo.dat 14 | install -D -m 0644 trustedkeys.gpg $(DESTDIR)$(DOCDIR)/trustedkeys.gpg 15 | 16 | .PHONY: update 17 | update: 18 | rm -f aplinfo.dat 19 | wget http://download.proxmox.com/images/aplinfo-pve-9.dat -O aplinfo.dat.tmp 20 | mv aplinfo.dat.tmp aplinfo.dat 21 | 22 | trustedkeys.gpg: $(TRUSTED_KEYS) 23 | sq keyring merge --output $(basename $@).asc.tmp $(TRUSTED_KEYS) 24 | sq packet dearmor $(basename $@).asc.tmp --output $@.tmp 25 | rm $(basename $@).asc.tmp 26 | mv $@.tmp $@ 27 | 28 | .PHONY: clean 29 | clean: 30 | rm -rf *~ aplinfo.dat.gz aplinfo.dat.asc trustedkeys.gpg 31 | -------------------------------------------------------------------------------- /www/manager6/sdn/OptionsPanel.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Options', { 2 | extend: 'Ext.panel.Panel', 3 | alias: 'widget.pveSDNOptions', 4 | 5 | title: 'Options', 6 | 7 | layout: { 8 | type: 'vbox', 9 | align: 'stretch', 10 | }, 11 | 12 | onlineHelp: 'pvesdn_config_controllers', 13 | 14 | items: [ 15 | { 16 | xtype: 'pveSDNControllerView', 17 | title: gettext('Controllers'), 18 | flex: 1, 19 | padding: '0 0 20 0', 20 | border: 0, 21 | }, 22 | { 23 | xtype: 'pveSDNIpamView', 24 | title: 'IPAM', 25 | flex: 1, 26 | padding: '0 0 20 0', 27 | border: 0, 28 | }, 29 | { 30 | xtype: 'pveSDNDnsView', 31 | title: 'DNS', 32 | flex: 1, 33 | border: 0, 34 | }, 35 | ], 36 | }); 37 | -------------------------------------------------------------------------------- /www/manager6/sdn/ipams/PVEIpamEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.ipams.PVEIpamInputPanel', { 2 | extend: 'PVE.panel.SDNIpamBase', 3 | 4 | onlineHelp: 'pvesdn_ipam_plugin_pveipam', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.ipam; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | var me = this; 20 | 21 | me.items = [ 22 | { 23 | xtype: me.isCreate ? 'textfield' : 'displayfield', 24 | name: 'ipam', 25 | maxLength: 10, 26 | value: me.zone || '', 27 | fieldLabel: 'ID', 28 | allowBlank: false, 29 | }, 30 | ]; 31 | 32 | me.callParent(); 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /www/manager6/sdn/zones/VlanEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.zones.VlanInputPanel', { 2 | extend: 'PVE.panel.SDNZoneBase', 3 | 4 | onlineHelp: 'pvesdn_zone_plugin_vlan', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.zone; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | var me = this; 20 | 21 | me.items = [ 22 | { 23 | xtype: 'textfield', 24 | name: 'bridge', 25 | fieldLabel: 'Bridge', 26 | allowBlank: false, 27 | vtype: 'BridgeName', 28 | minLength: 1, 29 | maxLength: 10, 30 | }, 31 | ]; 32 | 33 | me.callParent(); 34 | }, 35 | }); 36 | -------------------------------------------------------------------------------- /services/pve-guests.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=PVE guests 3 | ConditionPathExists=/usr/bin/pvesh 4 | RefuseManualStart=true 5 | RefuseManualStop=true 6 | Wants=pvestatd.service 7 | Wants=pveproxy.service 8 | Wants=spiceproxy.service 9 | Wants=pve-firewall.service 10 | Wants=lxc.service 11 | After=pveproxy.service 12 | After=pvestatd.service 13 | After=spiceproxy.service 14 | After=pve-firewall.service 15 | After=lxc.service 16 | After=pve-ha-crm.service pve-ha-lrm.service 17 | 18 | [Service] 19 | Environment="PVE_LOG_ID=pve-guests" 20 | ExecStartPre=-/usr/share/pve-manager/helpers/pve-startall-delay 21 | ExecStart=/usr/bin/pvesh --nooutput create /nodes/localhost/startall 22 | ExecStop=-/usr/bin/vzdump -stop 23 | ExecStop=/usr/bin/pvesh --nooutput create /nodes/localhost/stopall 24 | Type=oneshot 25 | RemainAfterExit=yes 26 | TimeoutSec=infinity 27 | 28 | [Install] 29 | WantedBy=multi-user.target 30 | Alias=pve-manager.service 31 | -------------------------------------------------------------------------------- /www/manager6/form/PrivilegesSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.PrivilegesSelector', { 2 | extend: 'Proxmox.form.KVComboBox', 3 | xtype: 'pvePrivilegesSelector', 4 | 5 | multiSelect: true, 6 | 7 | initComponent: function () { 8 | let me = this; 9 | 10 | me.callParent(); 11 | 12 | Proxmox.Utils.API2Request({ 13 | url: '/access/roles/Administrator', 14 | method: 'GET', 15 | success: function (response, options) { 16 | let data = Object.keys(response.result.data).map((key) => [key, key]); 17 | 18 | me.store.setData(data); 19 | 20 | me.store.sort({ 21 | property: 'key', 22 | direction: 'ASC', 23 | }); 24 | }, 25 | failure: (response, opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus), 26 | }); 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /www/manager6/sdn/zones/SimpleEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.zones.SimpleInputPanel', { 2 | extend: 'PVE.panel.SDNZoneBase', 3 | 4 | onlineHelp: 'pvesdn_zone_plugin_simple', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.zone; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | var me = this; 20 | 21 | me.items = []; 22 | me.advancedItems = [ 23 | { 24 | xtype: 'proxmoxcheckbox', 25 | name: 'dhcp', 26 | inputValue: 'dnsmasq', 27 | uncheckedValue: null, 28 | checked: false, 29 | fieldLabel: gettext('automatic DHCP'), 30 | deleteEmpty: !me.isCreate, 31 | }, 32 | ]; 33 | 34 | me.callParent(); 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /www/manager6/form/StorageScanNodeSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.StorageScanNodeSelector', { 2 | extend: 'PVE.form.NodeSelector', 3 | xtype: 'pveStorageScanNodeSelector', 4 | 5 | name: 'storageScanNode', 6 | itemId: 'pveStorageScanNodeSelector', 7 | fieldLabel: gettext('Scan node'), 8 | allowBlank: true, 9 | disallowedNodes: undefined, 10 | autoSelect: false, 11 | submitValue: false, 12 | value: null, 13 | autoEl: { 14 | tag: 'div', 15 | 'data-qtip': gettext('Scan for available storages on the selected node'), 16 | }, 17 | triggers: { 18 | clear: { 19 | handler: function () { 20 | let me = this; 21 | me.setValue(null); 22 | }, 23 | }, 24 | }, 25 | 26 | emptyText: Proxmox.NodeName, 27 | 28 | setValue: function (value) { 29 | let me = this; 30 | me.callParent([value]); 31 | me.triggers.clear.setVisible(!!value); 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /templates/default/package-updates-body.html.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | The following updates are available: 4 | 5 | 6 | 7 | 8 | 9 | 10 | {{#each available-updates.data}} 11 | 12 | 16 | {{/each}} 17 |
Package NameInstalled VersionAvailable Version
{{this.package-name}} 13 | {{this.installed-version}} 14 | {{this.available-version}} 15 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /www/manager6/pool/Summary.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.pool.Summary', { 2 | extend: 'Ext.panel.Panel', 3 | alias: 'widget.pvePoolSummary', 4 | 5 | initComponent: function () { 6 | var me = this; 7 | 8 | var pool = me.pveSelNode.data.pool; 9 | if (!pool) { 10 | throw 'no pool specified'; 11 | } 12 | 13 | var statusview = Ext.create('PVE.pool.StatusView', { 14 | pveSelNode: me.pveSelNode, 15 | style: 'padding-top:0px', 16 | }); 17 | 18 | var rstore = statusview.rstore; 19 | 20 | Ext.apply(me, { 21 | autoScroll: true, 22 | bodyStyle: 'padding:10px', 23 | defaults: { 24 | style: 'padding-top:10px', 25 | width: 800, 26 | }, 27 | items: [statusview], 28 | }); 29 | 30 | me.on('activate', rstore.startUpdate); 31 | me.on('destroy', rstore.stopUpdate); 32 | 33 | me.callParent(); 34 | }, 35 | }); 36 | -------------------------------------------------------------------------------- /configs/Makefile: -------------------------------------------------------------------------------- 1 | include ../defines.mk 2 | 3 | all: 4 | 5 | .PHONY: install 6 | install: vzdump.conf pve-sources.sources pve-initramfs.conf pve-blacklist.conf pve.logrotate virtual-function-pinning.rules virtual-function-pinning-helper 7 | install -D -m 0644 pve.logrotate $(DESTDIR)/etc/logrotate.d/pve 8 | install -D -m 0644 pve-sources.sources $(DESTDIR)/etc/apt/sources.list.d/pve-enterprise.sources 9 | install -D -m 0644 pve-blacklist.conf $(DESTDIR)/etc/modprobe.d/pve-blacklist.conf 10 | install -D -m 0644 vzdump.conf $(DESTDIR)/etc/vzdump.conf 11 | install -D -m 0644 pve-initramfs.conf $(DESTDIR)/etc/initramfs-tools/conf.d/pve-initramfs.conf 12 | install -D -m 0644 proxmox-ve-default.link $(DESTDIR)/usr/lib/systemd/network/99-default.link.d/proxmox-mac-address-policy.conf 13 | install -D -m 0644 virtual-function-pinning.rules $(DESTDIR)/usr/lib/udev/rules.d/70-virtual-function-pinning.rules 14 | install -D -m 0755 virtual-function-pinning-helper $(DESTDIR)/usr/lib/udev/virtual-function-naming-helper 15 | 16 | clean: 17 | -------------------------------------------------------------------------------- /www/manager6/form/VLanField.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.VlanField', { 2 | extend: 'Ext.form.field.Number', 3 | alias: ['widget.pveVlanField'], 4 | 5 | deleteEmpty: false, 6 | 7 | emptyText: gettext('no VLAN'), 8 | 9 | fieldLabel: gettext('VLAN Tag'), 10 | 11 | allowBlank: true, 12 | 13 | getSubmitData: function () { 14 | var me = this, 15 | data = null, 16 | val; 17 | if (!me.disabled && me.submitValue) { 18 | val = me.getSubmitValue(); 19 | if (val) { 20 | data = {}; 21 | data[me.getName()] = val; 22 | } else if (me.deleteEmpty) { 23 | data = {}; 24 | data.delete = me.getName(); 25 | } 26 | } 27 | return data; 28 | }, 29 | 30 | initComponent: function () { 31 | var me = this; 32 | 33 | Ext.apply(me, { 34 | minValue: 1, 35 | maxValue: 4094, 36 | }); 37 | 38 | me.callParent(); 39 | }, 40 | }); 41 | -------------------------------------------------------------------------------- /PVE/API2/Cluster/BulkAction.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::Cluster::BulkAction; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::API2::Cluster::BulkAction::Guest; 7 | 8 | use base qw(PVE::RESTHandler); 9 | 10 | __PACKAGE__->register_method({ 11 | subclass => "PVE::API2::Cluster::BulkAction::Guest", 12 | path => 'guest', 13 | }); 14 | 15 | __PACKAGE__->register_method({ 16 | name => 'index', 17 | path => '', 18 | method => 'GET', 19 | description => "List resource types.", 20 | permissions => { 21 | user => 'all', 22 | }, 23 | parameters => { 24 | additionalProperties => 0, 25 | properties => {}, 26 | }, 27 | returns => { 28 | type => 'array', 29 | items => { 30 | type => "object", 31 | }, 32 | links => [{ rel => 'child', href => "{name}" }], 33 | }, 34 | code => sub { 35 | my ($param) = @_; 36 | 37 | my $result = [ 38 | { name => 'guest' }, 39 | ]; 40 | 41 | return $result; 42 | }, 43 | }); 44 | 45 | 1; 46 | -------------------------------------------------------------------------------- /PVE/Jobs/VZDump.pm: -------------------------------------------------------------------------------- 1 | package PVE::Jobs::VZDump; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::JSONSchema; 7 | 8 | use PVE::VZDump::Common; 9 | 10 | use PVE::API2::VZDump; 11 | 12 | use base qw(PVE::VZDump::JobBase); 13 | 14 | sub run { 15 | my ($class, $conf, $job_id) = @_; 16 | 17 | my $props = $class->properties(); 18 | # remove all non vzdump related options 19 | foreach my $opt (keys %$conf) { 20 | delete $conf->{$opt} if !defined($props->{$opt}); 21 | } 22 | 23 | $conf->{'job-id'} = $job_id; 24 | 25 | # Required as string parameters # FIXME why?! we could just check ref() 26 | for my $key (keys $PVE::VZDump::Common::PROPERTY_STRINGS->%*) { 27 | if ($conf->{$key} && ref($conf->{$key}) eq 'HASH') { 28 | my $format = $PVE::VZDump::Common::PROPERTY_STRINGS->{$key}; 29 | $conf->{$key} = PVE::JSONSchema::print_property_string($conf->{$key}, $format); 30 | } 31 | } 32 | 33 | $conf->{quiet} = 1; # do not write to stdout/stderr 34 | 35 | return PVE::API2::VZDump->vzdump($conf); 36 | } 37 | 38 | 1; 39 | -------------------------------------------------------------------------------- /www/manager6/Toolkit.js: -------------------------------------------------------------------------------- 1 | // ExtJS related things 2 | 3 | Proxmox.Utils.toolkit = 'extjs'; 4 | 5 | // custom PVE specific VTypes 6 | Ext.apply(Ext.form.field.VTypes, { 7 | QemuStartDate: function (v) { 8 | return /^(now|\d{4}-\d{1,2}-\d{1,2}(T\d{1,2}:\d{1,2}:\d{1,2})?)$/.test(v); 9 | }, 10 | QemuStartDateText: gettext('Format') + ': "now" or "2006-06-17T16:01:21" or "2006-06-17"', 11 | IP64AddressList: (v) => PVE.Utils.verify_ip64_address_list(v, false), 12 | IP64AddressWithSuffixList: (v) => PVE.Utils.verify_ip64_address_list(v, true), 13 | IP64AddressListText: gettext('Example') + ': 192.168.1.1,192.168.1.2', 14 | IP64AddressListMask: /[A-Fa-f0-9,:.; ]/, 15 | PciIdText: gettext('Example') + ': 0x8086', 16 | PciId: (v) => /^0x[0-9a-fA-F]{4}$/.test(v), 17 | }); 18 | 19 | Ext.define('PVE.form.field.Display', { 20 | override: 'Ext.form.field.Display', 21 | 22 | setSubmitValue: function (value) { 23 | // do nothing, this is only to allow generalized bindings for the: 24 | // `me.isCreate ? 'textfield' : 'displayfield'` cases we have. 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /www/manager6/sdn/fabrics/openfabric/InterfacePanel.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Fabric.OpenFabric.InterfacePanel', { 2 | extend: 'PVE.sdn.Fabric.InterfacePanel', 3 | 4 | additionalColumns: [ 5 | { 6 | text: gettext('IPv6'), 7 | xtype: 'widgetcolumn', 8 | dataIndex: 'ip6', 9 | flex: 1, 10 | widget: { 11 | xtype: 'proxmoxtextfield', 12 | isFormField: false, 13 | bind: { 14 | disabled: '{record.isDisabled}', 15 | }, 16 | }, 17 | }, 18 | { 19 | text: gettext('Hello Multiplier'), 20 | xtype: 'widgetcolumn', 21 | dataIndex: 'hello_multiplier', 22 | flex: 1, 23 | hidden: true, 24 | widget: { 25 | xtype: 'proxmoxintegerfield', 26 | isFormField: false, 27 | emptyText: '10', 28 | bind: { 29 | disabled: '{record.isDisabled}', 30 | }, 31 | }, 32 | }, 33 | ], 34 | }); 35 | -------------------------------------------------------------------------------- /www/manager6/dc/ACMEClusterView.js: -------------------------------------------------------------------------------- 1 | Ext.define('pve-acme-accounts', { 2 | extend: 'Ext.data.Model', 3 | fields: ['name'], 4 | proxy: { 5 | type: 'proxmox', 6 | url: '/api2/json/cluster/acme/account', 7 | }, 8 | idProperty: 'name', 9 | }); 10 | 11 | Ext.define('pve-acme-plugins', { 12 | extend: 'Ext.data.Model', 13 | fields: ['type', 'plugin', 'api'], 14 | proxy: { 15 | type: 'proxmox', 16 | url: '/api2/json/cluster/acme/plugins', 17 | }, 18 | idProperty: 'plugin', 19 | }); 20 | 21 | Ext.define('PVE.dc.ACMEClusterView', { 22 | extend: 'Ext.panel.Panel', 23 | alias: 'widget.pveACMEClusterView', 24 | 25 | onlineHelp: 'sysadmin_certificate_management', 26 | 27 | items: [ 28 | { 29 | region: 'north', 30 | border: false, 31 | xtype: 'pmxACMEAccounts', 32 | acmeUrl: '/cluster/acme', 33 | }, 34 | { 35 | region: 'center', 36 | border: false, 37 | xtype: 'pmxACMEPluginView', 38 | acmeUrl: '/cluster/acme', 39 | }, 40 | ], 41 | }); 42 | -------------------------------------------------------------------------------- /www/manager6/form/CephFSSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.CephFSSelector', { 2 | extend: 'Ext.form.field.ComboBox', 3 | alias: 'widget.pveCephFSSelector', 4 | 5 | allowBlank: false, 6 | valueField: 'name', 7 | displayField: 'name', 8 | editable: false, 9 | queryMode: 'local', 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | if (!me.nodename) { 15 | throw 'no nodename given'; 16 | } 17 | 18 | var store = Ext.create('Ext.data.Store', { 19 | fields: ['name'], 20 | sorters: 'name', 21 | proxy: { 22 | type: 'proxmox', 23 | url: '/api2/json/nodes/' + me.nodename + '/ceph/fs', 24 | }, 25 | }); 26 | 27 | Ext.apply(me, { 28 | store: store, 29 | }); 30 | 31 | me.callParent(); 32 | 33 | store.load({ 34 | callback: function (rec, op, success) { 35 | if (success && rec.length > 0) { 36 | me.select(rec[0]); 37 | } 38 | }, 39 | }); 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /www/images/icon-pci.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /www/manager6/storage/BTRFSEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.storage.BTRFSInputPanel', { 2 | extend: 'PVE.panel.StorageBase', 3 | 4 | onlineHelp: 'storage_btrfs', 5 | 6 | initComponent: function () { 7 | let me = this; 8 | 9 | me.column1 = [ 10 | { 11 | xtype: me.isCreate ? 'textfield' : 'displayfield', 12 | name: 'path', 13 | value: '', 14 | fieldLabel: gettext('Path'), 15 | allowBlank: false, 16 | }, 17 | { 18 | xtype: 'pveContentTypeSelector', 19 | name: 'content', 20 | value: ['images', 'rootdir'], 21 | multiSelect: true, 22 | fieldLabel: gettext('Content'), 23 | allowBlank: false, 24 | }, 25 | ]; 26 | 27 | me.columnB = [ 28 | { 29 | xtype: 'displayfield', 30 | userCls: 'pmx-hint', 31 | value: `BTRFS integration is currently a technology preview.`, 32 | }, 33 | ]; 34 | 35 | me.callParent(); 36 | }, 37 | }); 38 | -------------------------------------------------------------------------------- /www/manager6/dc/DirMapView.js: -------------------------------------------------------------------------------- 1 | Ext.define('pve-resource-dir-tree', { 2 | extend: 'Ext.data.Model', 3 | idProperty: 'internalId', 4 | fields: ['type', 'text', 'path', 'id', 'description', 'digest'], 5 | }); 6 | 7 | Ext.define('PVE.dc.DirMapView', { 8 | extend: 'PVE.tree.ResourceMapTree', 9 | alias: 'widget.pveDcDirMapView', 10 | 11 | editWindowClass: 'PVE.window.DirMapEditWindow', 12 | baseUrl: '/cluster/mapping/dir', 13 | mapIconCls: 'fa fa-folder', 14 | entryIdProperty: 'path', 15 | 16 | store: { 17 | sorters: 'text', 18 | model: 'pve-resource-dir-tree', 19 | data: {}, 20 | }, 21 | 22 | columns: [ 23 | { 24 | xtype: 'treecolumn', 25 | text: gettext('ID/Node'), 26 | dataIndex: 'text', 27 | width: 200, 28 | }, 29 | { 30 | header: gettext('Comment'), 31 | dataIndex: 'description', 32 | renderer: function (value, _meta, record) { 33 | return Ext.String.htmlEncode(value ?? record.data.comment); 34 | }, 35 | flex: 1, 36 | }, 37 | ], 38 | }); 39 | -------------------------------------------------------------------------------- /www/manager6/sdn/Status.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Status', { 2 | extend: 'Ext.panel.Panel', 3 | alias: 'widget.pveSDNStatus', 4 | 5 | onlineHelp: 'chapter_pvesdn', 6 | 7 | layout: { 8 | type: 'vbox', 9 | align: 'stretch', 10 | }, 11 | 12 | initComponent: function () { 13 | var me = this; 14 | 15 | me.rstore = Ext.create('Proxmox.data.ObjectStore', { 16 | interval: me.interval, 17 | model: 'pve-sdn-status', 18 | storeid: 'pve-store-' + ++Ext.idSeed, 19 | groupField: 'type', 20 | proxy: { 21 | type: 'proxmox', 22 | url: '/api2/json/cluster/resources', 23 | }, 24 | }); 25 | 26 | me.items = [ 27 | { 28 | xtype: 'pveSDNStatusView', 29 | title: gettext('Status'), 30 | rstore: me.rstore, 31 | border: 0, 32 | collapsible: true, 33 | padding: '0 0 20 0', 34 | }, 35 | ]; 36 | 37 | me.callParent(); 38 | me.on('activate', me.rstore.startUpdate); 39 | }, 40 | }); 41 | -------------------------------------------------------------------------------- /www/manager6/sdn/VnetPanel.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Vnet', { 2 | extend: 'Ext.panel.Panel', 3 | alias: 'widget.pveSDNVnet', 4 | 5 | title: 'VNet', 6 | 7 | onlineHelp: 'pvesdn_config_vnet', 8 | 9 | initComponent: function () { 10 | var me = this; 11 | 12 | var subnetview_panel = Ext.createWidget('pveSDNSubnetView', { 13 | title: gettext('Subnets'), 14 | region: 'center', 15 | border: false, 16 | }); 17 | 18 | var vnetview_panel = Ext.createWidget('pveSDNVnetView', { 19 | title: 'VNets', 20 | region: 'west', 21 | subnetview_panel: subnetview_panel, 22 | width: '50%', 23 | border: false, 24 | split: true, 25 | }); 26 | 27 | Ext.apply(me, { 28 | layout: 'border', 29 | items: [vnetview_panel, subnetview_panel], 30 | listeners: { 31 | show: function () { 32 | subnetview_panel.fireEvent('show', subnetview_panel); 33 | }, 34 | }, 35 | }); 36 | 37 | me.callParent(); 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /test/replication_test1.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Note: Test if mockup from ReplicationTestEnv works 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use JSON; 9 | 10 | use lib ('.', '../..'); 11 | 12 | use ReplicationTestEnv; 13 | use Test::More tests => 3; 14 | 15 | $ReplicationTestEnv::mocked_nodename = 'node1'; 16 | 17 | my $testjob = { 18 | 'type' => 'local', 19 | 'target' => 'node1', 20 | 'guest' => 900, 21 | }; 22 | 23 | $ReplicationTestEnv::mocked_replication_jobs = { 24 | job_900_to_node1 => $testjob, 25 | }; 26 | 27 | $ReplicationTestEnv::mocked_vm_configs = { 28 | 900 => { 29 | node => 'node1', 30 | snapshots => {}, 31 | ide0 => 'local-lvm:vm-900-disk-1,size=4G', 32 | memory => 512, 33 | ide2 => 'none,media=cdrom', 34 | }, 35 | }; 36 | 37 | ReplicationTestEnv::setup(); 38 | 39 | ok(PVE::INotify::nodename() eq 'node1'); 40 | 41 | my $list = PVE::Cluster::get_vmlist(); 42 | is_deeply($list, { ids => { 900 => { node => 'node1', type => 'qemu', version => 1 } } }); 43 | my $cfg = PVE::ReplicationConfig->new(); 44 | is_deeply($cfg, { ids => { job_900_to_node1 => $testjob } }); 45 | 46 | exit(0); 47 | -------------------------------------------------------------------------------- /www/images/icon-memory.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /configs/virtual-function-pinning-helper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | 4 | DEVICE_SYSFS_PCI_PATH=$(realpath "/sys${DEVPATH}/../.."); 5 | 6 | if [ ! -L "$DEVICE_SYSFS_PCI_PATH/physfn" ]; then 7 | exit; 8 | fi 9 | 10 | PHYSFN_SYSFS_PCI_PATH=$(realpath "${DEVICE_SYSFS_PCI_PATH}/physfn"); 11 | PHYSFN_IFACE_NAME=$(ls "${PHYSFN_SYSFS_PCI_PATH}/net") 12 | 13 | # interface is not pinned 14 | if [ ! -f "/usr/local/lib/systemd/network/50-pve-${PHYSFN_IFACE_NAME}.link" ]; then 15 | exit; 16 | fi 17 | 18 | # pin is not applied - or interface doesn't exist 19 | if ! ip link show "$PHYSFN_IFACE_NAME" > /dev/null 2>&1 ; then 20 | exit; 21 | fi 22 | 23 | DEVICE_PCI_ID=$(basename "$DEVICE_SYSFS_PCI_PATH"); 24 | 25 | for file in $(find "${PHYSFN_SYSFS_PCI_PATH=$}/" -maxdepth 1 -type l -name 'virtfn*' ); do 26 | VF_PCI_ID=$(basename "$(realpath "$file")"); 27 | 28 | if [ "$DEVICE_PCI_ID" = "$VF_PCI_ID" ]; then 29 | VF_INDEX=$(basename "$file" | grep -Eo '[[:digit:]]+$' -); 30 | echo "${PHYSFN_IFACE_NAME}v${VF_INDEX}"; 31 | exit; 32 | fi 33 | done 34 | 35 | echo "interface seems to be a VF of ${PHYSFN_IFACE_NAME}, but could not find the VF index" 1>&2; 36 | exit; 37 | 38 | -------------------------------------------------------------------------------- /PVE/Makefile: -------------------------------------------------------------------------------- 1 | include ../defines.mk 2 | 3 | SUBDIRS=API2 Status CLI Service Ceph Jobs 4 | 5 | PERLSOURCE = \ 6 | API2.pm \ 7 | API2Tools.pm \ 8 | APLInfo.pm \ 9 | AutoBalloon.pm \ 10 | CertCache.pm \ 11 | CertHelpers.pm \ 12 | ExtMetric.pm \ 13 | HTTPServer.pm \ 14 | Jobs.pm \ 15 | NodeConfig.pm \ 16 | PullMetric.pm \ 17 | Report.pm \ 18 | VZDump.pm 19 | 20 | all: pvecfg.pm $(SUBDIRS) 21 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i; done 22 | 23 | REPOID ?= $(or $(GITVERSION), $(shell git rev-parse --short=16 HEAD), unknown) 24 | pvecfg.pm: pvecfg.pm.in 25 | sed 's/@VERSION@/$(VERSION)/;s/@PVERELEASE@/$(PVERELEASE)/;s/@PACKAGE@/$(PACKAGE)/;s/@REPOID@/$(REPOID)/' $< >$@.tmp 26 | mv $@.tmp $@ 27 | 28 | %: 29 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done 30 | 31 | .PHONY: clean 32 | clean: 33 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done 34 | rm -rf *~ pvecfg.pm pvecfg.pm.tmp 35 | 36 | .PHONY: install 37 | install: pvecfg.pm $(PERLSOURCE) 38 | install -d $(PERLLIBDIR)/PVE 39 | install -m 0644 pvecfg.pm $(PERLLIBDIR)/PVE/ 40 | install -m 0644 $(PERLSOURCE) $(PERLLIBDIR)/PVE/ 41 | set -e && for i in $(SUBDIRS); do $(MAKE) -C $$i $@; done 42 | -------------------------------------------------------------------------------- /www/manager6/form/ACMEAPISelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('pve-acme-challenges', { 2 | extend: 'Ext.data.Model', 3 | fields: ['id', 'type', 'schema'], 4 | proxy: { 5 | type: 'proxmox', 6 | url: '/api2/json/cluster/acme/challenge-schema', 7 | }, 8 | idProperty: 'id', 9 | }); 10 | 11 | Ext.define('PVE.form.ACMEApiSelector', { 12 | extend: 'Ext.form.field.ComboBox', 13 | alias: 'widget.pveACMEApiSelector', 14 | 15 | fieldLabel: gettext('DNS API'), 16 | displayField: 'name', 17 | valueField: 'id', 18 | 19 | store: { 20 | model: 'pve-acme-challenges', 21 | autoLoad: true, 22 | }, 23 | 24 | triggerAction: 'all', 25 | queryMode: 'local', 26 | allowBlank: false, 27 | editable: true, 28 | forceSelection: true, 29 | anyMatch: true, 30 | selectOnFocus: true, 31 | 32 | getSchema: function () { 33 | let me = this; 34 | let val = me.getValue(); 35 | if (val) { 36 | let record = me.getStore().findRecord('id', val, 0, false, true, true); 37 | if (record) { 38 | return record.data.schema; 39 | } 40 | } 41 | return {}; 42 | }, 43 | }); 44 | -------------------------------------------------------------------------------- /network-hooks/bridgevlan: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -x /sbin/bridge ] && [ ! -f /sys/class/net/$IFACE/bridge/vlan_filtering ] 4 | then 5 | exit 0 6 | fi 7 | 8 | # Enabling vlan filtering feature 9 | if [ "$MODE" = "start" ] ; then 10 | 11 | if [ -n "$IF_BRIDGE_VLAN_AWARE" ] 12 | then 13 | echo 1 > /sys/class/net/$IFACE/bridge/vlan_filtering 14 | else 15 | exit 0 16 | fi 17 | 18 | fi 19 | 20 | . /lib/bridge-utils/bridge-utils.sh 21 | 22 | case "$IF_BRIDGE_PORTS" in 23 | "") 24 | exit 0 25 | ;; 26 | none) 27 | INTERFACES="" 28 | ;; 29 | *) 30 | INTERFACES="$IF_BRIDGE_PORTS" 31 | ;; 32 | esac 33 | 34 | all_interfaces= && 35 | unset all_interfaces && 36 | bridge_parse_ports $INTERFACES | while read i 37 | do 38 | for port in $i 39 | do 40 | if [ "$MODE" = "start" ] && [ -d /sys/class/net/$IFACE/brif/$port ]; then 41 | # we allow vlan to pass through attached interface 42 | if [ -n "$IF_BRIDGE_VIDS" ] 43 | then 44 | for VID in $IF_BRIDGE_VIDS 45 | do 46 | bridge vlan add dev $port vid $VID 47 | done 48 | else 49 | bridge vlan add dev $port vid 2-4094 50 | fi 51 | fi 52 | done 53 | done 54 | 55 | -------------------------------------------------------------------------------- /test/replication_test3.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Note: Try to run replication job to same node (should fail) 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use JSON; 9 | 10 | use lib ('.', '../..'); 11 | 12 | use Test::MockModule; 13 | use Test::More; 14 | 15 | use ReplicationTestEnv; 16 | use PVE::API2::Replication; 17 | 18 | $ReplicationTestEnv::mocked_nodename = 'node1'; 19 | 20 | my $testjob = { 21 | 'type' => 'local', 22 | 'target' => 'node1', 23 | 'guest' => 900, 24 | }; 25 | 26 | $ReplicationTestEnv::mocked_replication_jobs = { 27 | job_900_to_node1 => { 28 | 'type' => 'local', 29 | 'target' => 'node1', # local node, job should be skipped 30 | 'guest' => 900, 31 | }, 32 | }; 33 | 34 | $ReplicationTestEnv::mocked_vm_configs = { 35 | 900 => { 36 | node => 'node1', 37 | snapshots => {}, 38 | ide0 => 'local-lvm:vm-900-disk-1,size=4G', 39 | memory => 512, 40 | ide2 => 'none,media=cdrom', 41 | }, 42 | }; 43 | 44 | ReplicationTestEnv::setup(); 45 | 46 | eval { PVE::API2::Replication::run_single_job('job_900_to_node1', 1000); }; 47 | my $err = $@; 48 | 49 | is($err, "unable to sync to local node\n", "test error message"); 50 | 51 | done_testing(); 52 | -------------------------------------------------------------------------------- /www/images/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /network-hooks/vlan: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Most of this stuff is to enable vlans, it's really only needed by bridge_utils 4 | case "$IFACE" in 5 | # Ignore any alias (#272891) which uses : 6 | *:*) 7 | exit 0 8 | ;; 9 | vlan[0-9]*) 10 | VLANID=`echo $IFACE|sed "s/vlan*//"` 11 | ;; 12 | *.[0-9]*) 13 | # Silently ignore interfaces which ifupdown handles on its own 14 | # If IF_BRIDGE_PORTS is set, probably we're called by bridge-utils 15 | [ -z "$IF_VLAN_RAW_DEVICE" -a -z "$IF_BRIDGE_PORTS" ] && exit 0 16 | VLANID=`echo $IFACE|sed "s/[a-zA-Z0-9]*\.//g"` 17 | IF_VLAN_RAW_DEVICE=`echo $IFACE|sed "s/\([a-zA-Z0-9]*\)\..*/\1/"` 18 | ;; 19 | 20 | *) 21 | exit 0 22 | ;; 23 | esac 24 | 25 | if [ -n "$IF_VLAN_RAW_DEVICE" ]; then 26 | if ! ip link show dev "$IF_VLAN_RAW_DEVICE" > /dev/null; then 27 | echo "$IF_VLAN_RAW_DEVICE does not exist, unable to create $IFACE" 28 | exit 1 29 | fi 30 | if [ ! -e "/sys/class/net/$IFACE" ]; then 31 | ip link set up dev $IF_VLAN_RAW_DEVICE 32 | ip link add link $IF_VLAN_RAW_DEVICE name $IFACE type vlan id $VLANID 33 | fi 34 | 35 | fi 36 | 37 | # This is not vlan specific, and should actually go somewhere else. 38 | if [ -n "$IF_HW_MAC_ADDRESS" ]; then 39 | ip link set $IFACE address $IF_HW_MAC_ADDRESS 40 | fi 41 | -------------------------------------------------------------------------------- /services/Makefile: -------------------------------------------------------------------------------- 1 | include ../defines.mk 2 | 3 | all: 4 | 5 | SERVICES= \ 6 | pvebanner.service \ 7 | pvenetcommit.service \ 8 | pvestatd.service \ 9 | pve-guests.service \ 10 | pvedaemon.service \ 11 | pveproxy.service \ 12 | spiceproxy.service \ 13 | pve-storage.target \ 14 | pve-daily-update.service\ 15 | pve-daily-update.timer \ 16 | pvescheduler.service \ 17 | pve-sdn-commit.service \ 18 | pve-firewall-commit.service 19 | 20 | .PHONY: install 21 | install: $(SERVICES) 22 | install -d $(SERVICEDIR) 23 | install -m 0644 $(SERVICES) $(SERVICEDIR) 24 | install -d $(SERVICEDIR)/ceph-mon@.service.d 25 | install -m 0644 ceph-after-pve-cluster.conf $(SERVICEDIR)/ceph-mon@.service.d 26 | install -d $(SERVICEDIR)/ceph-mgr@.service.d 27 | install -m 0644 ceph-after-pve-cluster.conf $(SERVICEDIR)/ceph-mgr@.service.d 28 | install -d $(SERVICEDIR)/ceph-osd@.service.d 29 | install -m 0644 ceph-after-pve-cluster.conf $(SERVICEDIR)/ceph-osd@.service.d 30 | install -d $(SERVICEDIR)/ceph-volume@.service.d 31 | install -m 0644 ceph-after-pve-cluster.conf $(SERVICEDIR)/ceph-volume@.service.d 32 | install -d $(SERVICEDIR)/ceph-mds@.service.d 33 | install -m 0644 ceph-after-pve-cluster.conf $(SERVICEDIR)/ceph-mds@.service.d 34 | install -d $(DESTDIR)/usr/share/doc/$(PACKAGE)/examples/ 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *~ 39 | -------------------------------------------------------------------------------- /www/manager6/qemu/QemuBiosEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.qemu.BiosEdit', { 2 | extend: 'Proxmox.window.Edit', 3 | alias: 'widget.pveQemuBiosEdit', 4 | 5 | onlineHelp: 'qm_bios_and_uefi', 6 | subject: 'BIOS', 7 | autoLoad: true, 8 | 9 | viewModel: { 10 | data: { 11 | bios: '__default__', 12 | efidisk0: false, 13 | }, 14 | formulas: { 15 | showEFIDiskHint: (get) => get('bios') === 'ovmf' && !get('efidisk0'), 16 | }, 17 | }, 18 | 19 | items: [ 20 | { 21 | xtype: 'pveQemuBiosSelector', 22 | onlineHelp: 'qm_bios_and_uefi', 23 | name: 'bios', 24 | value: '__default__', 25 | bind: '{bios}', 26 | fieldLabel: 'BIOS', 27 | }, 28 | { 29 | xtype: 'displayfield', 30 | name: 'efidisk0', 31 | bind: '{efidisk0}', 32 | hidden: true, 33 | }, 34 | { 35 | xtype: 'displayfield', 36 | userCls: 'pmx-hint', 37 | value: gettext( 38 | 'You need to add an EFI disk for storing the EFI settings. See the online help for details.', 39 | ), 40 | bind: { 41 | hidden: '{!showEFIDiskHint}', 42 | }, 43 | }, 44 | ], 45 | }); 46 | -------------------------------------------------------------------------------- /www/manager6/window/SafeDestroyStorage.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SafeDestroy window with additional checkboxes for removing a storage on the disk level. 3 | */ 4 | Ext.define('PVE.window.SafeDestroyStorage', { 5 | extend: 'Proxmox.window.SafeDestroy', 6 | alias: 'widget.pveSafeDestroyStorage', 7 | 8 | showProgress: true, 9 | 10 | additionalItems: [ 11 | { 12 | xtype: 'proxmoxcheckbox', 13 | name: 'wipeDisks', 14 | reference: 'wipeDisksCheckbox', 15 | boxLabel: gettext('Cleanup Disks'), 16 | checked: true, 17 | autoEl: { 18 | tag: 'div', 19 | 'data-qtip': gettext('Wipe labels and other left-overs'), 20 | }, 21 | }, 22 | { 23 | xtype: 'proxmoxcheckbox', 24 | name: 'cleanupConfig', 25 | reference: 'cleanupConfigCheckbox', 26 | boxLabel: gettext('Cleanup Storage Configuration'), 27 | checked: true, 28 | }, 29 | ], 30 | 31 | getParams: function () { 32 | let me = this; 33 | 34 | me.params['cleanup-disks'] = me.lookupReference('wipeDisksCheckbox').checked ? 1 : 0; 35 | me.params['cleanup-config'] = me.lookupReference('cleanupConfigCheckbox').checked ? 1 : 0; 36 | 37 | return me.callParent(); 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /www/manager6/window/ConfirmRemoveResource.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ConfirmRemoveDialog window with additional checkboxes for removing resources 3 | */ 4 | Ext.define('PVE.window.ConfirmRemoveResource', { 5 | extend: 'Proxmox.window.ConfirmRemoveDialog', 6 | alias: 'widget.pveConfirmRemoveResource', 7 | 8 | additionalItems: [ 9 | { 10 | xtype: 'proxmoxcheckbox', 11 | name: 'purge', 12 | reference: 'purgeCheckbox', 13 | boxLabel: gettext('Purge resource from referenced HA rules'), 14 | padding: '5 0 0 0', 15 | checked: true, 16 | autoEl: { 17 | tag: 'div', 18 | 'data-qtip': gettext( 19 | 'Also removes resource from HA rules and removes rule if there are no other resources in it', 20 | ), 21 | }, 22 | }, 23 | ], 24 | 25 | getText: function () { 26 | let me = this; 27 | 28 | me.text = `Are you sure you want to remove resource '${me.getItem().id}'?`; 29 | 30 | return me.callParent(); 31 | }, 32 | 33 | getParams: function () { 34 | let me = this; 35 | 36 | const purgeCheckbox = me.lookupReference('purgeCheckbox'); 37 | me.params.purge = purgeCheckbox.checked ? 1 : 0; 38 | 39 | return me.callParent(); 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /www/manager6/button/Revert.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.button.PendingRevert', { 2 | extend: 'Proxmox.button.Button', 3 | alias: 'widget.pvePendingRevertButton', 4 | 5 | text: gettext('Revert'), 6 | disabled: true, 7 | config: { 8 | pendingGrid: null, 9 | apiurl: undefined, 10 | }, 11 | 12 | handler: function () { 13 | if (!this.pendingGrid) { 14 | this.pendingGrid = this.up('proxmoxPendingObjectGrid'); 15 | if (!this.pendingGrid) { 16 | throw 'revert button requires a pendingGrid'; 17 | } 18 | } 19 | let view = this.pendingGrid; 20 | 21 | let rec = view.getSelectionModel().getSelection()[0]; 22 | if (!rec) { 23 | return; 24 | } 25 | 26 | let rowdef = view.rows[rec.data.key] || {}; 27 | let keys = rowdef.multiKey || [rec.data.key]; 28 | 29 | Proxmox.Utils.API2Request({ 30 | url: this.apiurl || view.editorConfig.url, 31 | waitMsgTarget: view, 32 | selModel: view.getSelectionModel(), 33 | method: 'PUT', 34 | params: { 35 | revert: keys.join(','), 36 | }, 37 | callback: () => view.reload(), 38 | failure: (response) => Ext.Msg.alert('Error', response.htmlStatus), 39 | }); 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /www/manager6/ceph/Monitor.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.node.CephMonMgrList', { 2 | extend: 'Ext.container.Container', 3 | xtype: 'pveNodeCephMonMgr', 4 | 5 | mixins: ['Proxmox.Mixin.CBind'], 6 | 7 | onlineHelp: 'chapter_pveceph', 8 | 9 | defaults: { 10 | border: false, 11 | onlineHelp: 'chapter_pveceph', 12 | flex: 1, 13 | }, 14 | 15 | layout: { 16 | type: 'vbox', 17 | align: 'stretch', 18 | }, 19 | 20 | items: [ 21 | { 22 | xtype: 'pveNodeCephServiceList', 23 | cbind: { pveSelNode: '{pveSelNode}' }, 24 | type: 'mon', 25 | additionalColumns: [ 26 | { 27 | header: gettext('Quorum'), 28 | width: 70, 29 | sortable: true, 30 | renderer: Proxmox.Utils.format_boolean, 31 | dataIndex: 'quorum', 32 | }, 33 | ], 34 | stateId: 'grid-ceph-monitor', 35 | showCephInstallMask: true, 36 | title: gettext('Monitor'), 37 | }, 38 | { 39 | xtype: 'pveNodeCephServiceList', 40 | type: 'mgr', 41 | stateId: 'grid-ceph-manager', 42 | cbind: { pveSelNode: '{pveSelNode}' }, 43 | title: gettext('Manager'), 44 | }, 45 | ], 46 | }); 47 | -------------------------------------------------------------------------------- /PVE/API2/Hardware.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::Hardware; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::JSONSchema qw(get_standard_option); 7 | use PVE::RESTHandler; 8 | 9 | use PVE::API2::Hardware::PCI; 10 | use PVE::API2::Hardware::USB; 11 | 12 | use base qw(PVE::RESTHandler); 13 | 14 | __PACKAGE__->register_method({ 15 | subclass => "PVE::API2::Hardware::PCI", 16 | path => 'pci', 17 | }); 18 | 19 | __PACKAGE__->register_method({ 20 | subclass => "PVE::API2::Hardware::USB", 21 | path => 'usb', 22 | }); 23 | 24 | __PACKAGE__->register_method({ 25 | name => 'index', 26 | path => '', 27 | method => 'GET', 28 | description => "Index of hardware types", 29 | permissions => { 30 | user => 'all', 31 | }, 32 | parameters => { 33 | additionalProperties => 0, 34 | properties => { 35 | node => get_standard_option('pve-node'), 36 | }, 37 | }, 38 | returns => { 39 | type => 'array', 40 | items => { 41 | type => "object", 42 | properties => { type => { type => 'string' } }, 43 | }, 44 | links => [{ rel => 'child', href => "{type}" }], 45 | }, 46 | code => sub { 47 | my ($param) = @_; 48 | 49 | my $res = [ 50 | { type => 'pci' }, { type => 'usb' }, 51 | ]; 52 | 53 | return $res; 54 | }, 55 | }); 56 | 57 | -------------------------------------------------------------------------------- /www/manager6/storage/DirEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.storage.DirInputPanel', { 2 | extend: 'PVE.panel.StorageBase', 3 | 4 | onlineHelp: 'storage_directory', 5 | 6 | initComponent: function () { 7 | var me = this; 8 | 9 | me.column1 = [ 10 | { 11 | xtype: me.isCreate ? 'textfield' : 'displayfield', 12 | name: 'path', 13 | value: '', 14 | fieldLabel: gettext('Directory'), 15 | allowBlank: false, 16 | }, 17 | { 18 | xtype: 'pveContentTypeSelector', 19 | name: 'content', 20 | value: 'images', 21 | multiSelect: true, 22 | fieldLabel: gettext('Content'), 23 | allowBlank: false, 24 | }, 25 | ]; 26 | 27 | me.column2 = [ 28 | { 29 | xtype: 'proxmoxcheckbox', 30 | name: 'shared', 31 | uncheckedValue: 0, 32 | fieldLabel: gettext('Shared'), 33 | autoEl: { 34 | tag: 'div', 35 | 'data-qtip': gettext( 36 | 'Enable if the underlying file system is already shared between nodes.', 37 | ), 38 | }, 39 | }, 40 | ]; 41 | 42 | me.callParent(); 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /PVE/API2/Cluster/Mapping.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::Cluster::Mapping; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::API2::Cluster::Mapping::Dir; 7 | use PVE::API2::Cluster::Mapping::PCI; 8 | use PVE::API2::Cluster::Mapping::USB; 9 | 10 | use base qw(PVE::RESTHandler); 11 | 12 | __PACKAGE__->register_method({ 13 | subclass => "PVE::API2::Cluster::Mapping::Dir", 14 | path => 'dir', 15 | }); 16 | 17 | __PACKAGE__->register_method({ 18 | subclass => "PVE::API2::Cluster::Mapping::PCI", 19 | path => 'pci', 20 | }); 21 | 22 | __PACKAGE__->register_method({ 23 | subclass => "PVE::API2::Cluster::Mapping::USB", 24 | path => 'usb', 25 | }); 26 | 27 | __PACKAGE__->register_method({ 28 | name => 'index', 29 | path => '', 30 | method => 'GET', 31 | description => "List resource types.", 32 | permissions => { 33 | user => 'all', 34 | }, 35 | parameters => { 36 | additionalProperties => 0, 37 | properties => {}, 38 | }, 39 | returns => { 40 | type => 'array', 41 | items => { 42 | type => "object", 43 | }, 44 | links => [{ rel => 'child', href => "{name}" }], 45 | }, 46 | code => sub { 47 | my ($param) = @_; 48 | 49 | my $result = [ 50 | { name => 'dir' }, { name => 'pci' }, { name => 'usb' }, 51 | ]; 52 | 53 | return $result; 54 | }, 55 | }); 56 | 57 | 1; 58 | -------------------------------------------------------------------------------- /www/manager6/dc/GroupEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.dc.GroupEdit', { 2 | extend: 'Proxmox.window.Edit', 3 | alias: ['widget.pveDcGroupEdit'], 4 | 5 | initComponent: function () { 6 | var me = this; 7 | 8 | me.isCreate = !me.groupid; 9 | 10 | var url; 11 | var method; 12 | 13 | if (me.isCreate) { 14 | url = '/api2/extjs/access/groups'; 15 | method = 'POST'; 16 | } else { 17 | url = '/api2/extjs/access/groups/' + me.groupid; 18 | method = 'PUT'; 19 | } 20 | 21 | Ext.applyIf(me, { 22 | subject: gettext('Group'), 23 | url: url, 24 | method: method, 25 | items: [ 26 | { 27 | xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', 28 | fieldLabel: gettext('Name'), 29 | name: 'groupid', 30 | value: me.groupid, 31 | allowBlank: false, 32 | }, 33 | { 34 | xtype: 'textfield', 35 | fieldLabel: gettext('Comment'), 36 | name: 'comment', 37 | allowBlank: true, 38 | }, 39 | ], 40 | }); 41 | 42 | me.callParent(); 43 | 44 | if (!me.isCreate) { 45 | me.load(); 46 | } 47 | }, 48 | }); 49 | -------------------------------------------------------------------------------- /www/manager6/ha/Status.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.ha.Status', { 2 | extend: 'Ext.panel.Panel', 3 | alias: 'widget.pveHAStatus', 4 | 5 | onlineHelp: 'chapter_ha_manager', 6 | layout: { 7 | type: 'vbox', 8 | align: 'stretch', 9 | }, 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | me.rstore = Ext.create('Proxmox.data.ObjectStore', { 15 | interval: me.interval, 16 | model: 'pve-ha-status', 17 | storeid: 'pve-store-' + ++Ext.idSeed, 18 | groupField: 'type', 19 | proxy: { 20 | type: 'proxmox', 21 | url: '/api2/json/cluster/ha/status/current', 22 | }, 23 | }); 24 | 25 | me.items = [ 26 | { 27 | xtype: 'pveHAStatusView', 28 | title: gettext('Status'), 29 | rstore: me.rstore, 30 | border: 0, 31 | collapsible: true, 32 | padding: '0 0 20 0', 33 | }, 34 | { 35 | xtype: 'pveHAResourcesView', 36 | flex: 1, 37 | collapsible: true, 38 | title: gettext('Resources'), 39 | border: 0, 40 | rstore: me.rstore, 41 | }, 42 | ]; 43 | 44 | me.callParent(); 45 | me.on('activate', me.rstore.startUpdate); 46 | }, 47 | }); 48 | -------------------------------------------------------------------------------- /www/manager6/pool/Config.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.pool.Config', { 2 | extend: 'PVE.panel.Config', 3 | alias: 'widget.pvePoolConfig', 4 | 5 | onlineHelp: 'pveum_pools', 6 | 7 | initComponent: function () { 8 | var me = this; 9 | 10 | var pool = me.pveSelNode.data.pool; 11 | if (!pool) { 12 | throw 'no pool specified'; 13 | } 14 | 15 | Ext.apply(me, { 16 | title: Ext.String.format(gettext('Resource Pool') + ': ' + pool), 17 | hstateid: 'pooltab', 18 | items: [ 19 | { 20 | title: gettext('Summary'), 21 | iconCls: 'fa fa-book', 22 | xtype: 'pvePoolSummary', 23 | itemId: 'summary', 24 | }, 25 | { 26 | title: gettext('Members'), 27 | xtype: 'pvePoolMembers', 28 | iconCls: 'fa fa-th', 29 | pool: pool, 30 | itemId: 'members', 31 | }, 32 | { 33 | xtype: 'pveACLView', 34 | title: gettext('Permissions'), 35 | iconCls: 'fa fa-unlock', 36 | itemId: 'permissions', 37 | path: '/pool/' + pool, 38 | }, 39 | ], 40 | }); 41 | 42 | me.callParent(); 43 | }, 44 | }); 45 | -------------------------------------------------------------------------------- /www/images/icon-cpu.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /www/manager6/window/BackupConfig.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.window.BackupConfig', { 2 | extend: 'Ext.window.Window', 3 | title: gettext('Configuration'), 4 | width: 600, 5 | height: 400, 6 | layout: 'fit', 7 | modal: true, 8 | items: { 9 | xtype: 'component', 10 | itemId: 'configtext', 11 | autoScroll: true, 12 | style: { 13 | 'white-space': 'pre', 14 | 'font-family': 'monospace', 15 | padding: '5px', 16 | }, 17 | }, 18 | 19 | initComponent: function () { 20 | var me = this; 21 | 22 | if (!me.volume) { 23 | throw 'no volume specified'; 24 | } 25 | 26 | var nodename = me.pveSelNode.data.node; 27 | if (!nodename) { 28 | throw 'no node name specified'; 29 | } 30 | 31 | me.callParent(); 32 | 33 | Proxmox.Utils.API2Request({ 34 | url: '/nodes/' + nodename + '/vzdump/extractconfig', 35 | method: 'GET', 36 | params: { 37 | volume: me.volume, 38 | }, 39 | failure: function (response, opts) { 40 | me.close(); 41 | Ext.Msg.alert('Error', response.htmlStatus); 42 | }, 43 | success: function (response, options) { 44 | me.show(); 45 | me.down('#configtext').update(Ext.htmlEncode(response.result.data)); 46 | }, 47 | }); 48 | }, 49 | }); 50 | -------------------------------------------------------------------------------- /www/manager6/form/CephPoolSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.CephPoolSelector', { 2 | extend: 'Ext.form.field.ComboBox', 3 | alias: 'widget.pveCephPoolSelector', 4 | 5 | allowBlank: false, 6 | valueField: 'pool_name', 7 | displayField: 'pool_name', 8 | listConfig: { 9 | itemTpl: '{pool_name:htmlEncode}', 10 | }, 11 | editable: false, 12 | queryMode: 'local', 13 | 14 | initComponent: function () { 15 | var me = this; 16 | 17 | if (!me.nodename) { 18 | throw 'no nodename given'; 19 | } 20 | 21 | let onlyRBDPools = ({ data }) => 22 | !data?.application_metadata || !!data?.application_metadata?.rbd; 23 | 24 | var store = Ext.create('Ext.data.Store', { 25 | fields: ['name'], 26 | sorters: 'name', 27 | filters: [onlyRBDPools], 28 | proxy: { 29 | type: 'proxmox', 30 | url: '/api2/json/nodes/' + me.nodename + '/ceph/pool', 31 | }, 32 | }); 33 | 34 | Ext.apply(me, { 35 | store: store, 36 | }); 37 | 38 | me.callParent(); 39 | 40 | store.load({ 41 | callback: function (rec, op, success) { 42 | let filteredRec = rec.filter(onlyRBDPools); 43 | 44 | if (success && filteredRec.length > 0) { 45 | me.select(filteredRec[0]); 46 | } 47 | }, 48 | }); 49 | }, 50 | }); 51 | -------------------------------------------------------------------------------- /www/manager6/ha/RuleErrorsModal.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.ha.RuleErrorsModal', { 2 | extend: 'Ext.window.Window', 3 | alias: ['widget.pveHARulesErrorsModal'], 4 | mixins: ['Proxmox.Mixin.CBind'], 5 | 6 | modal: true, 7 | scrollable: true, 8 | resizable: false, 9 | 10 | title: gettext('HA rule errors'), 11 | 12 | initComponent: function () { 13 | let me = this; 14 | 15 | let renderHARuleErrors = (errors) => { 16 | if (!errors) { 17 | return gettext('The HA rule has no errors.'); 18 | } 19 | 20 | let errorListItemsHtml = ''; 21 | 22 | for (let [opt, messages] of Object.entries(errors)) { 23 | errorListItemsHtml += messages 24 | .map((message) => `
  • ${Ext.htmlEncode(`${opt}: ${message}`)}
  • `) 25 | .join(''); 26 | } 27 | 28 | return `
    29 |

    ${gettext('The HA rule has the following errors:')}

    30 |
      ${errorListItemsHtml}
    31 |
    `; 32 | }; 33 | 34 | Ext.apply(me, { 35 | modal: true, 36 | border: false, 37 | layout: 'fit', 38 | items: [ 39 | { 40 | xtype: 'displayfield', 41 | padding: 20, 42 | scrollable: true, 43 | value: renderHARuleErrors(me.errors), 44 | }, 45 | ], 46 | }); 47 | 48 | me.callParent(); 49 | }, 50 | }); 51 | -------------------------------------------------------------------------------- /www/manager6/ha/Fencing.js: -------------------------------------------------------------------------------- 1 | Ext.define( 2 | 'PVE.ha.FencingView', 3 | { 4 | extend: 'Ext.grid.GridPanel', 5 | alias: ['widget.pveFencingView'], 6 | 7 | onlineHelp: 'ha_manager_fencing', 8 | 9 | initComponent: function () { 10 | var me = this; 11 | 12 | var store = new Ext.data.Store({ 13 | model: 'pve-ha-fencing', 14 | data: [], 15 | }); 16 | 17 | Ext.apply(me, { 18 | store: store, 19 | stateful: false, 20 | viewConfig: { 21 | trackOver: false, 22 | deferEmptyText: false, 23 | emptyText: gettext('Use watchdog based fencing.'), 24 | }, 25 | columns: [ 26 | { 27 | header: gettext('Node'), 28 | width: 100, 29 | sortable: true, 30 | dataIndex: 'node', 31 | }, 32 | { 33 | header: gettext('Command'), 34 | flex: 1, 35 | dataIndex: 'command', 36 | }, 37 | ], 38 | }); 39 | 40 | me.callParent(); 41 | }, 42 | }, 43 | function () { 44 | Ext.define('pve-ha-fencing', { 45 | extend: 'Ext.data.Model', 46 | fields: ['node', 'command', 'digest'], 47 | }); 48 | }, 49 | ); 50 | -------------------------------------------------------------------------------- /www/manager6/sdn/FirewallPanel.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.FirewallPanel', { 2 | extend: 'Ext.panel.Panel', 3 | alias: 'widget.pveSDNFirewall', 4 | 5 | title: 'VNet', 6 | 7 | onlineHelp: 'pvesdn_firewall_integration', 8 | 9 | initComponent: function () { 10 | let me = this; 11 | 12 | let tabPanel = Ext.create('Ext.TabPanel', { 13 | fullscreen: true, 14 | region: 'center', 15 | border: false, 16 | split: true, 17 | disabled: true, 18 | flex: 2, 19 | items: [ 20 | { 21 | xtype: 'pveFirewallRules', 22 | title: gettext('Rules'), 23 | list_refs_url: '/cluster/firewall/refs', 24 | firewall_type: 'vnet', 25 | }, 26 | { 27 | xtype: 'pveFirewallOptions', 28 | title: gettext('Options'), 29 | fwtype: 'vnet', 30 | }, 31 | ], 32 | }); 33 | 34 | let vnetPanel = Ext.createWidget('pveSDNFirewallVnetView', { 35 | title: 'VNets', 36 | region: 'west', 37 | border: false, 38 | split: true, 39 | forceFit: true, 40 | flex: 1, 41 | tabPanel, 42 | }); 43 | 44 | Ext.apply(me, { 45 | layout: 'border', 46 | items: [vnetPanel, tabPanel], 47 | }); 48 | 49 | me.callParent(); 50 | }, 51 | }); 52 | -------------------------------------------------------------------------------- /www/manager6/menu/MenuItem.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.menu.Item', { 2 | extend: 'Ext.menu.Item', 3 | alias: 'widget.pveMenuItem', 4 | 5 | // set to wrap the handler callback in a confirm dialog showing this text 6 | confirmMsg: false, 7 | 8 | // set to focus 'No' instead of 'Yes' button and show a warning symbol 9 | dangerous: false, 10 | 11 | initComponent: function () { 12 | let me = this; 13 | if (me.handler) { 14 | me.setHandler(me.handler, me.scope); 15 | } 16 | me.callParent(); 17 | }, 18 | 19 | setHandler: function (fn, scope) { 20 | let me = this; 21 | me.scope = scope; 22 | me.handler = function (button, e) { 23 | if (me.confirmMsg) { 24 | Ext.MessageBox.defaultButton = me.dangerous ? 2 : 1; 25 | Ext.Msg.show({ 26 | title: gettext('Confirm'), 27 | icon: me.dangerous ? Ext.Msg.WARNING : Ext.Msg.QUESTION, 28 | msg: me.confirmMsg, 29 | buttons: Ext.Msg.YESNO, 30 | defaultFocus: me.dangerous ? 'no' : 'yes', 31 | callback: function (btn) { 32 | if (btn === 'yes') { 33 | Ext.callback(fn, me.scope, [me, e], 0, me); 34 | } 35 | }, 36 | }); 37 | } else { 38 | Ext.callback(fn, me.scope, [me, e], 0, me); 39 | } 40 | }; 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /test/replication_test4.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Note: Test replication job failure 4 | 5 | use strict; 6 | use warnings; 7 | use JSON; 8 | 9 | use lib ('.', '../..'); 10 | 11 | use Test::MockModule; 12 | use ReplicationTestEnv; 13 | 14 | use PVE::Tools; 15 | 16 | $ReplicationTestEnv::mocked_nodename = 'node1'; 17 | 18 | my $pve_replication_module = Test::MockModule->new('PVE::Replication'); 19 | $pve_replication_module->mock(replicate => sub { die "faked replication error\n"; }); 20 | 21 | my $testjob = { 22 | 'type' => 'local', 23 | 'target' => 'node1', 24 | 'guest' => 900, 25 | }; 26 | 27 | $ReplicationTestEnv::mocked_replication_jobs = { 28 | job_900_to_node2 => { 29 | 'type' => 'local', 30 | 'target' => 'node2', 31 | 'guest' => 900, 32 | }, 33 | job_900_to_node1 => { 34 | 'type' => 'local', 35 | 'target' => 'node1', # local node, job should be skipped 36 | 'guest' => 900, 37 | }, 38 | }; 39 | 40 | $ReplicationTestEnv::mocked_vm_configs = { 41 | 900 => { 42 | node => 'node1', 43 | snapshots => {}, 44 | ide0 => 'local-lvm:vm-900-disk-1,size=4G', 45 | memory => 512, 46 | ide2 => 'none,media=cdrom', 47 | }, 48 | }; 49 | 50 | ReplicationTestEnv::setup(); 51 | 52 | my $ctime = 1000; 53 | 54 | my $status; 55 | 56 | ReplicationTestEnv::openlog(); 57 | 58 | for (my $i = 0; $i < 120; $i++) { 59 | ReplicationTestEnv::track_jobs($ctime); 60 | $ctime += 60; 61 | } 62 | 63 | ReplicationTestEnv::commit_log(); 64 | 65 | exit(0); 66 | -------------------------------------------------------------------------------- /test/replication_test6.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Note: Try to delete replication job with target on same node 4 | 5 | use strict; 6 | use warnings; 7 | use JSON; 8 | 9 | use lib ('.', '../..'); 10 | 11 | use Test::MockModule; 12 | use ReplicationTestEnv; 13 | 14 | $ReplicationTestEnv::mocked_nodename = 'node1'; 15 | 16 | my $mocked_delete_job = sub { 17 | my ($jobid) = @_; 18 | 19 | delete $ReplicationTestEnv::mocked_replication_jobs->{$jobid}; 20 | }; 21 | 22 | my $pve_replication_config_module = Test::MockModule->new('PVE::ReplicationConfig'); 23 | $pve_replication_config_module->mock(delete_job => $mocked_delete_job); 24 | 25 | my $testjob = { 26 | 'type' => 'local', 27 | 'target' => 'node1', 28 | 'guest' => 900, 29 | }; 30 | 31 | $ReplicationTestEnv::mocked_replication_jobs = { 32 | job_900_to_node1 => { 33 | remove_job => 'full', 34 | type => 'local', 35 | target => 'node1', # local node, job should be skipped 36 | guest => 900, 37 | }, 38 | }; 39 | 40 | $ReplicationTestEnv::mocked_vm_configs = { 41 | 900 => { 42 | node => 'node1', 43 | snapshots => {}, 44 | ide0 => 'local-zfs:vm-900-disk-1,size=4G', 45 | memory => 512, 46 | ide2 => 'none,media=cdrom', 47 | }, 48 | }; 49 | 50 | ReplicationTestEnv::setup(); 51 | 52 | ReplicationTestEnv::openlog(); 53 | 54 | my $ctime = 1000; 55 | for (my $i = 0; $i < 15; $i++) { 56 | ReplicationTestEnv::track_jobs($ctime); 57 | $ctime += 60; 58 | } 59 | 60 | ReplicationTestEnv::commit_log(); 61 | 62 | exit(0); 63 | -------------------------------------------------------------------------------- /www/manager6/form/SDNDnsSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define( 2 | 'PVE.form.SDNDnsSelector', 3 | { 4 | extend: 'Proxmox.form.ComboGrid', 5 | alias: ['widget.pveSDNDnsSelector'], 6 | 7 | allowBlank: false, 8 | valueField: 'dns', 9 | displayField: 'dns', 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | var store = new Ext.data.Store({ 15 | model: 'pve-sdn-dns', 16 | sorters: { 17 | property: 'dns', 18 | direction: 'ASC', 19 | }, 20 | }); 21 | 22 | Ext.apply(me, { 23 | store: store, 24 | autoSelect: false, 25 | listConfig: { 26 | columns: [ 27 | { 28 | header: gettext('dns'), 29 | sortable: true, 30 | dataIndex: 'dns', 31 | flex: 1, 32 | }, 33 | ], 34 | }, 35 | }); 36 | 37 | me.callParent(); 38 | 39 | store.load(); 40 | }, 41 | }, 42 | function () { 43 | Ext.define('pve-sdn-dns', { 44 | extend: 'Ext.data.Model', 45 | fields: ['dns'], 46 | proxy: { 47 | type: 'proxmox', 48 | url: '/api2/json/cluster/sdn/dns', 49 | }, 50 | idProperty: 'dns', 51 | }); 52 | }, 53 | ); 54 | -------------------------------------------------------------------------------- /aplinfo/README.format: -------------------------------------------------------------------------------- 1 | PVE Package description format 2 | ============================== 3 | 4 | We basically use a debian like format - see: 5 | 6 | http://www.debian.org/doc/debian-policy/ch-controlfields.html 7 | 8 | The following (debian) field are used: 9 | 10 | Package, Section, Version, Maintainer, Description 11 | 12 | Additionally we define the following fields: 13 | 14 | Type: openvz | qemu | lxc 15 | OS: Operating system type 16 | Location: download url 17 | Infopage: url to additional info 18 | ManageUrl: url to connect to management interface 19 | md5sum: md5sum of the package 20 | sha512sum: sha512sum of the package 21 | 22 | Here is an example: 23 | 24 | Package: proxmox-mailgateway 25 | Version: 2.4-2 26 | Type: openvz 27 | OS: debian-4.0 28 | Section: mail 29 | Certified: yes 30 | Maintainer: Proxmox Support Team 31 | Location: http://download.proxmox.com/appliances/mail/debian-4.0-proxmox-mailgateway_2.4-2_i386.tar.gz 32 | Infopage: http://pve.proxmox.com/wiki/Proxmox_Mail_Gateway 33 | ManageUrl: https://__IPADDRESS__/ 34 | md5sum: 3b48ffe08347229a854bc8d5ee161e1f 35 | sha512sum: 85d2a87a70feb0e595dde83b487f1c549c3ef26e7338f39259c81094077479864fac537ec05152510ae7b9735c672033031f267add03410950c721a8e55a2a9c 36 | Description: Proxmox Mail Gateway 37 | A full featured mail proxy for spam an virus filtering. 38 | 39 | 40 | The filename is automatically generated from the above description: 41 | 42 | ${OS}-${Package}_${VERSION}_i386.tar.gz 43 | 44 | If ${Package} starts with "${OS}-", the filename is: 45 | 46 | ${Package}_${VERSION}_i386.tar.gz -------------------------------------------------------------------------------- /www/images/Makefile: -------------------------------------------------------------------------------- 1 | include ../../defines.mk 2 | 3 | all: 4 | 5 | # virt-viewer.svg copied from virt-viewer source (and reformatted): 6 | # https://github.com/webrulon/virt-viewer/blob/master/icons/virt-viewer.svg 7 | # 8 | # novnc.svg copied from the noVnc source: 9 | # https://github.com/novnc/noVNC/blob/master/app/images/icons/novnc-icon-sm.svg 10 | 11 | # icon-cd-drive and icon-pci 12 | # are self made (sources as .xcf) 13 | # icon-cloud 14 | # come from fontawesome (respective fa-cloud) 15 | 16 | # icon-serial is a modified version of 17 | # https://commons.wikimedia.org/wiki/File:DE9_Diagram.svg 18 | # (public domain) 19 | 20 | # logo-ceph is adapted and reformatted from Ceph_Logo.svg: 21 | # https://github.com/ceph/ceph/blob/main/src/pybind/mgr/dashboard/frontend/src/assets/Ceph_Logo.svg 22 | 23 | # xtermjs.svg was copied from the xtermjs-branding sources: 24 | # https://github.com/xtermjs/xtermjs-branding/blob/master/logo.svg 25 | 26 | IMAGES = \ 27 | virt-viewer.svg \ 28 | novnc.svg \ 29 | xtermjs.svg \ 30 | favicon.ico \ 31 | proxmox_logo.png \ 32 | logo-ceph.svg \ 33 | logo-128.png \ 34 | icon-serial.svg \ 35 | icon-cloud.svg \ 36 | icon-pci.svg \ 37 | icon-die.svg \ 38 | icon-sdn.svg \ 39 | icon-fa-network-wired.svg\ 40 | icon-cpu.svg \ 41 | icon-memory.svg \ 42 | icon-cd-drive.svg \ 43 | spinner.svg \ 44 | 45 | icon-sdn.svg: icon-sdn.dot 46 | fdp -Tsvg $< > $@ 47 | 48 | .PHONY: install 49 | install: $(IMAGES) 50 | install -d $(WWWIMAGEDIR) 51 | install -m 0644 $(IMAGES) $(WWWIMAGEDIR) 52 | 53 | .PHONY: clean 54 | clean: 55 | rm -rf *~ 56 | -------------------------------------------------------------------------------- /www/manager6/form/SDNIpamSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define( 2 | 'PVE.form.SDNIpamSelector', 3 | { 4 | extend: 'Proxmox.form.ComboGrid', 5 | alias: ['widget.pveSDNIpamSelector'], 6 | 7 | allowBlank: false, 8 | valueField: 'ipam', 9 | displayField: 'ipam', 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | var store = new Ext.data.Store({ 15 | model: 'pve-sdn-ipam', 16 | sorters: { 17 | property: 'ipam', 18 | direction: 'ASC', 19 | }, 20 | }); 21 | 22 | Ext.apply(me, { 23 | store: store, 24 | autoSelect: false, 25 | listConfig: { 26 | columns: [ 27 | { 28 | header: gettext('Ipam'), 29 | sortable: true, 30 | dataIndex: 'ipam', 31 | flex: 1, 32 | }, 33 | ], 34 | }, 35 | }); 36 | 37 | me.callParent(); 38 | 39 | store.load(); 40 | }, 41 | }, 42 | function () { 43 | Ext.define('pve-sdn-ipam', { 44 | extend: 'Ext.data.Model', 45 | fields: ['ipam'], 46 | proxy: { 47 | type: 'proxmox', 48 | url: '/api2/json/cluster/sdn/ipams', 49 | }, 50 | idProperty: 'ipam', 51 | }); 52 | }, 53 | ); 54 | -------------------------------------------------------------------------------- /www/manager6/form/SDNZoneSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define( 2 | 'PVE.form.SDNZoneSelector', 3 | { 4 | extend: 'Proxmox.form.ComboGrid', 5 | alias: ['widget.pveSDNZoneSelector'], 6 | 7 | allowBlank: false, 8 | valueField: 'zone', 9 | displayField: 'zone', 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | var store = new Ext.data.Store({ 15 | model: 'pve-sdn-zone', 16 | sorters: { 17 | property: 'zone', 18 | direction: 'ASC', 19 | }, 20 | }); 21 | 22 | Ext.apply(me, { 23 | store: store, 24 | autoSelect: false, 25 | listConfig: { 26 | columns: [ 27 | { 28 | header: gettext('Zone'), 29 | sortable: true, 30 | dataIndex: 'zone', 31 | flex: 1, 32 | }, 33 | ], 34 | }, 35 | }); 36 | 37 | me.callParent(); 38 | 39 | store.load(); 40 | }, 41 | }, 42 | function () { 43 | Ext.define('pve-sdn-zone', { 44 | extend: 'Ext.data.Model', 45 | fields: ['zone', 'type'], 46 | proxy: { 47 | type: 'proxmox', 48 | url: '/api2/json/cluster/sdn/zones', 49 | }, 50 | idProperty: 'zone', 51 | }); 52 | }, 53 | ); 54 | -------------------------------------------------------------------------------- /www/manager6/ceph/Crush.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.node.CephCrushMap', { 2 | extend: 'Ext.panel.Panel', 3 | alias: ['widget.pveNodeCephCrushMap'], 4 | bodyStyle: 'white-space:pre', 5 | bodyPadding: 5, 6 | border: false, 7 | stateful: true, 8 | stateId: 'layout-ceph-crush', 9 | scrollable: true, 10 | load: function () { 11 | var me = this; 12 | 13 | Proxmox.Utils.API2Request({ 14 | url: me.url, 15 | waitMsgTarget: me, 16 | failure: function (response, opts) { 17 | me.update(gettext('Error') + ' ' + response.htmlStatus); 18 | var msg = response.htmlStatus; 19 | PVE.Utils.showCephInstallOrMask(me.ownerCt, msg, me.pveSelNode.data.node, (win) => 20 | me.mon(win, 'cephInstallWindowClosed', () => me.load()), 21 | ); 22 | }, 23 | success: function (response, opts) { 24 | var data = response.result.data; 25 | me.update(Ext.htmlEncode(data)); 26 | }, 27 | }); 28 | }, 29 | 30 | initComponent: function () { 31 | let me = this; 32 | 33 | let nodename = me.pveSelNode.data.node; 34 | if (!nodename) { 35 | throw 'no node name specified'; 36 | } 37 | 38 | Ext.apply(me, { 39 | url: `/nodes/${nodename}/ceph/crush`, 40 | listeners: { 41 | activate: () => me.load(), 42 | }, 43 | }); 44 | 45 | me.callParent(); 46 | 47 | me.load(); 48 | }, 49 | }); 50 | -------------------------------------------------------------------------------- /www/manager6/dc/PoolEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.dc.PoolEdit', { 2 | extend: 'Proxmox.window.Edit', 3 | alias: ['widget.pveDcPoolEdit'], 4 | mixins: ['Proxmox.Mixin.CBind'], 5 | 6 | subject: gettext('Pool'), 7 | 8 | cbindData: { 9 | poolid: '', 10 | isCreate: (cfg) => !cfg.poolid, 11 | }, 12 | 13 | cbind: { 14 | url: (get) => `/api2/extjs/pools/${!get('isCreate') ? '?poolid=' + get('poolid') : ''}`, 15 | method: (get) => (get('isCreate') ? 'POST' : 'PUT'), 16 | }, 17 | 18 | items: [ 19 | { 20 | xtype: 'pmxDisplayEditField', 21 | fieldLabel: gettext('Name'), 22 | cbind: { 23 | editable: '{isCreate}', 24 | value: '{poolid}', 25 | }, 26 | name: 'poolid', 27 | allowBlank: false, 28 | }, 29 | { 30 | xtype: 'textfield', 31 | fieldLabel: gettext('Comment'), 32 | name: 'comment', 33 | allowBlank: true, 34 | }, 35 | ], 36 | 37 | initComponent: function () { 38 | let me = this; 39 | me.callParent(); 40 | if (me.poolid) { 41 | me.load({ 42 | success: function (response) { 43 | let data = response.result.data; 44 | if (Ext.isArray(data)) { 45 | me.setValues(data[0]); 46 | } else { 47 | me.setValues(data); 48 | } 49 | }, 50 | }); 51 | } 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /www/manager6/sdn/ZoneContentPanel.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.ZoneContentPanel', { 2 | extend: 'Ext.panel.Panel', 3 | alias: 'widget.pveSDNZoneContentPanel', 4 | 5 | title: 'VNet', 6 | 7 | onlineHelp: 'pvesdn_config_vnet', 8 | 9 | initComponent: function () { 10 | var me = this; 11 | 12 | var permissions_panel = Ext.createWidget('pveSDNVnetACLView', { 13 | title: gettext('VNet Permissions'), 14 | region: 'center', 15 | border: false, 16 | }); 17 | 18 | var vnetview_panel = Ext.createWidget('pveSDNZoneContentView', { 19 | title: 'VNets', 20 | region: 'west', 21 | sub_panel: permissions_panel, 22 | nodename: me.nodename, 23 | zone: me.zone, 24 | width: '50%', 25 | border: false, 26 | split: true, 27 | 28 | on_select: function (_sm, rec) { 29 | let path = `/sdn/zones/${me.zone}/${rec.data.vnet}`; 30 | permissions_panel.setPath(path); 31 | }, 32 | 33 | on_deselect: function () { 34 | permissions_panel.setPath(undefined); 35 | }, 36 | }); 37 | 38 | Ext.apply(me, { 39 | layout: 'border', 40 | items: [vnetview_panel, permissions_panel], 41 | listeners: { 42 | show: function () { 43 | permissions_panel.fireEvent('show', permissions_panel); 44 | }, 45 | }, 46 | }); 47 | 48 | me.callParent(); 49 | }, 50 | }); 51 | -------------------------------------------------------------------------------- /www/manager6/form/DirMapSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.DirMapSelector', { 2 | extend: 'Proxmox.form.ComboGrid', 3 | alias: 'widget.pveDirMapSelector', 4 | 5 | store: { 6 | fields: ['name', 'path'], 7 | filterOnLoad: true, 8 | sorters: [ 9 | { 10 | property: 'id', 11 | direction: 'ASC', 12 | }, 13 | ], 14 | }, 15 | 16 | allowBlank: false, 17 | autoSelect: false, 18 | displayField: 'id', 19 | valueField: 'id', 20 | 21 | listConfig: { 22 | columns: [ 23 | { 24 | header: gettext('Directory ID'), 25 | dataIndex: 'id', 26 | flex: 1, 27 | }, 28 | { 29 | header: gettext('Comment'), 30 | dataIndex: 'description', 31 | flex: 1, 32 | }, 33 | ], 34 | }, 35 | 36 | setNodename: function (nodename) { 37 | var me = this; 38 | 39 | if (!nodename || me.nodename === nodename) { 40 | return; 41 | } 42 | 43 | me.nodename = nodename; 44 | 45 | me.store.setProxy({ 46 | type: 'proxmox', 47 | url: `/api2/json/cluster/mapping/dir?check-node=${nodename}`, 48 | }); 49 | 50 | me.store.load(); 51 | }, 52 | 53 | initComponent: function () { 54 | var me = this; 55 | 56 | var nodename = me.nodename; 57 | me.nodename = undefined; 58 | 59 | me.callParent(); 60 | 61 | me.setNodename(nodename); 62 | }, 63 | }); 64 | -------------------------------------------------------------------------------- /www/manager6/sdn/zones/QinQEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.zones.QinQInputPanel', { 2 | extend: 'PVE.panel.SDNZoneBase', 3 | 4 | onlineHelp: 'pvesdn_zone_plugin_qinq', 5 | 6 | onGetValues: function (values) { 7 | let me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.sdn; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | let me = this; 20 | 21 | me.items = [ 22 | { 23 | xtype: 'textfield', 24 | name: 'bridge', 25 | fieldLabel: 'Bridge', 26 | allowBlank: false, 27 | vtype: 'BridgeName', 28 | minLength: 1, 29 | maxLength: 10, 30 | }, 31 | { 32 | xtype: 'proxmoxintegerfield', 33 | name: 'tag', 34 | minValue: 0, 35 | maxValue: 4096, 36 | fieldLabel: gettext('Service VLAN'), 37 | allowBlank: false, 38 | }, 39 | { 40 | xtype: 'proxmoxKVComboBox', 41 | name: 'vlan-protocol', 42 | fieldLabel: gettext('Service VLAN Protocol'), 43 | allowBlank: true, 44 | value: '802.1q', 45 | comboItems: [ 46 | ['802.1q', '802.1q'], 47 | ['802.1ad', '802.1ad'], 48 | ], 49 | }, 50 | ]; 51 | 52 | me.callParent(); 53 | }, 54 | }); 55 | -------------------------------------------------------------------------------- /www/manager6/window/FirewallEnableEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.FirewallEnableEdit', { 2 | extend: 'Proxmox.window.Edit', 3 | alias: ['widget.pveFirewallEnableEdit'], 4 | mixins: ['Proxmox.Mixin.CBind'], 5 | 6 | subject: gettext('Firewall'), 7 | cbindData: { 8 | defaultValue: 0, 9 | }, 10 | width: 350, 11 | 12 | items: [ 13 | { 14 | xtype: 'proxmoxcheckbox', 15 | name: 'enable', 16 | uncheckedValue: 0, 17 | cbind: { 18 | defaultValue: '{defaultValue}', 19 | checked: '{defaultValue}', 20 | }, 21 | deleteDefaultValue: false, 22 | fieldLabel: gettext('Firewall'), 23 | }, 24 | { 25 | xtype: 'displayfield', 26 | name: 'warning', 27 | userCls: 'pmx-hint', 28 | value: gettext('Warning: Firewall still disabled at datacenter level!'), 29 | hidden: true, 30 | }, 31 | ], 32 | 33 | beforeShow: function () { 34 | var me = this; 35 | 36 | Proxmox.Utils.API2Request({ 37 | url: '/api2/extjs/cluster/firewall/options', 38 | method: 'GET', 39 | failure: function (response, opts) { 40 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); 41 | }, 42 | success: function (response, opts) { 43 | if (!response.result.data.enable) { 44 | me.down('displayfield[name=warning]').setVisible(true); 45 | } 46 | }, 47 | }); 48 | }, 49 | }); 50 | -------------------------------------------------------------------------------- /www/manager6/sdn/ipams/NetboxEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.ipams.NetboxInputPanel', { 2 | extend: 'PVE.panel.SDNIpamBase', 3 | 4 | onlineHelp: 'pvesdn_ipam_plugin_netbox', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.ipam; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | var me = this; 20 | 21 | me.column1 = [ 22 | { 23 | xtype: me.isCreate ? 'textfield' : 'displayfield', 24 | name: 'ipam', 25 | maxLength: 10, 26 | value: me.zone || '', 27 | fieldLabel: 'ID', 28 | allowBlank: false, 29 | }, 30 | { 31 | xtype: 'textfield', 32 | name: 'token', 33 | fieldLabel: gettext('Token'), 34 | allowBlank: false, 35 | }, 36 | ]; 37 | 38 | me.column2 = [ 39 | { 40 | xtype: 'textfield', 41 | name: 'url', 42 | fieldLabel: gettext('URL'), 43 | allowBlank: false, 44 | }, 45 | ]; 46 | 47 | me.columnB = [ 48 | { 49 | xtype: 'pmxFingerprintField', 50 | name: 'fingerprint', 51 | value: me.isCreate ? null : undefined, 52 | deleteEmpty: !me.isCreate, 53 | }, 54 | ]; 55 | 56 | me.callParent(); 57 | }, 58 | }); 59 | -------------------------------------------------------------------------------- /www/manager6/form/SDNControllerSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define( 2 | 'PVE.form.SDNControllerSelector', 3 | { 4 | extend: 'Proxmox.form.ComboGrid', 5 | alias: ['widget.pveSDNControllerSelector'], 6 | 7 | allowBlank: false, 8 | valueField: 'controller', 9 | displayField: 'controller', 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | var store = new Ext.data.Store({ 15 | model: 'pve-sdn-controller', 16 | sorters: { 17 | property: 'controller', 18 | direction: 'ASC', 19 | }, 20 | }); 21 | 22 | Ext.apply(me, { 23 | store: store, 24 | autoSelect: false, 25 | listConfig: { 26 | columns: [ 27 | { 28 | header: gettext('Controller'), 29 | sortable: true, 30 | dataIndex: 'controller', 31 | flex: 1, 32 | }, 33 | ], 34 | }, 35 | }); 36 | 37 | me.callParent(); 38 | 39 | store.load(); 40 | }, 41 | }, 42 | function () { 43 | Ext.define('pve-sdn-controller', { 44 | extend: 'Ext.data.Model', 45 | fields: ['controller'], 46 | proxy: { 47 | type: 'proxmox', 48 | url: '/api2/json/cluster/sdn/controllers', 49 | }, 50 | idProperty: 'controller', 51 | }); 52 | }, 53 | ); 54 | -------------------------------------------------------------------------------- /www/manager6/form/NotificationTargetSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.NotificationTargetSelector', { 2 | extend: 'Proxmox.form.ComboGrid', 3 | alias: ['widget.pveNotificationTargetSelector'], 4 | 5 | // set default value to empty array, else it inits it with 6 | // null and after the store load it is an empty array, 7 | // triggering dirtychange 8 | value: [], 9 | valueField: 'name', 10 | displayField: 'name', 11 | deleteEmpty: true, 12 | skipEmptyText: true, 13 | 14 | store: { 15 | fields: ['name', 'type', 'comment'], 16 | proxy: { 17 | type: 'proxmox', 18 | url: '/api2/json/cluster/notifications/targets', 19 | }, 20 | sorters: [ 21 | { 22 | property: 'name', 23 | direction: 'ASC', 24 | }, 25 | ], 26 | autoLoad: true, 27 | }, 28 | 29 | listConfig: { 30 | columns: [ 31 | { 32 | header: gettext('Target'), 33 | dataIndex: 'name', 34 | sortable: true, 35 | hideable: false, 36 | flex: 1, 37 | }, 38 | { 39 | header: gettext('Type'), 40 | dataIndex: 'type', 41 | sortable: true, 42 | hideable: false, 43 | flex: 1, 44 | }, 45 | { 46 | header: gettext('Comment'), 47 | dataIndex: 'comment', 48 | sortable: true, 49 | hideable: false, 50 | flex: 2, 51 | }, 52 | ], 53 | }, 54 | }); 55 | -------------------------------------------------------------------------------- /www/manager6/sdn/fabrics/FabricEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.Fabric.Fabric.Edit', { 2 | extend: 'Proxmox.window.Edit', 3 | mixins: ['Proxmox.Mixin.CBind'], 4 | 5 | width: 400, 6 | 7 | fabricId: undefined, 8 | baseUrl: '/cluster/sdn/fabrics/fabric', 9 | 10 | items: [ 11 | { 12 | xtype: 'textfield', 13 | name: 'digest', 14 | hidden: true, 15 | allowBlank: true, 16 | }, 17 | { 18 | xtype: 'proxmoxtextfield', 19 | fieldLabel: gettext('Name'), 20 | labelWidth: 120, 21 | maxLength: 8, 22 | name: 'id', 23 | cbind: { 24 | disabled: '{!isCreate}', 25 | }, 26 | }, 27 | { 28 | xtype: 'proxmoxtextfield', 29 | fieldLabel: gettext('IPv4 Prefix'), 30 | labelWidth: 120, 31 | name: 'ip_prefix', 32 | allowBlank: true, 33 | skipEmptyText: true, 34 | cbind: { 35 | disabled: '{!isCreate}', 36 | deleteEmpty: '{!isCreate}', 37 | }, 38 | }, 39 | ], 40 | 41 | additionalItems: [], 42 | 43 | initComponent: function () { 44 | let me = this; 45 | 46 | me.isCreate = me.fabricId === undefined; 47 | me.autoLoad = !me.isCreate; 48 | me.method = me.isCreate ? 'POST' : 'PUT'; 49 | 50 | if (!me.isCreate) { 51 | me.url = `${me.baseUrl}/${me.fabricId}`; 52 | } else { 53 | me.url = me.baseUrl; 54 | } 55 | 56 | me.items.push(...me.additionalItems); 57 | 58 | me.callParent(); 59 | }, 60 | }); 61 | -------------------------------------------------------------------------------- /PVE/API2/Hardware/USB.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::Hardware::USB; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::JSONSchema qw(get_standard_option); 7 | 8 | use PVE::SysFSTools; 9 | 10 | use base qw(PVE::RESTHandler); 11 | 12 | __PACKAGE__->register_method({ 13 | name => 'usbscan', 14 | path => '', 15 | method => 'GET', 16 | description => "List local USB devices.", 17 | protected => 1, 18 | proxyto => "node", 19 | permissions => { 20 | check => ['perm', '/', ['Sys.Modify']], 21 | }, 22 | parameters => { 23 | additionalProperties => 0, 24 | properties => { 25 | node => get_standard_option('pve-node'), 26 | }, 27 | }, 28 | returns => { 29 | type => 'array', 30 | items => { 31 | type => "object", 32 | properties => { 33 | busnum => { type => 'integer' }, 34 | class => { type => 'integer' }, 35 | devnum => { type => 'integer' }, 36 | level => { type => 'integer' }, 37 | manufacturer => { type => 'string', optional => 1 }, 38 | port => { type => 'integer' }, 39 | prodid => { type => 'string' }, 40 | product => { type => 'string', optional => 1 }, 41 | serial => { type => 'string', optional => 1 }, 42 | speed => { type => 'string' }, 43 | usbpath => { type => 'string', optional => 1 }, 44 | vendid => { type => 'string' }, 45 | }, 46 | }, 47 | }, 48 | code => sub { 49 | my ($param) = @_; 50 | 51 | return PVE::SysFSTools::scan_usb(); 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /test/replication_test4.log: -------------------------------------------------------------------------------- 1 | 1000 job_900_to_node2: new job next_sync => 900 2 | 1000 job_900_to_node2: start replication job 3 | 1000 job_900_to_node2: end replication job with error: faked replication error 4 | 1000 job_900_to_node2: changed config next_sync => 1300 5 | 1000 job_900_to_node2: changed state last_node => node1, last_try => 1000, fail_count => 1, error => faked replication error 6 | 1300 job_900_to_node2: start replication job 7 | 1300 job_900_to_node2: end replication job with error: faked replication error 8 | 1300 job_900_to_node2: changed config next_sync => 1900 9 | 1300 job_900_to_node2: changed state last_try => 1300, fail_count => 2 10 | 1900 job_900_to_node2: start replication job 11 | 1900 job_900_to_node2: end replication job with error: faked replication error 12 | 1900 job_900_to_node2: changed config next_sync => 2800 13 | 1900 job_900_to_node2: changed state last_try => 1900, fail_count => 3 14 | 2800 job_900_to_node2: start replication job 15 | 2800 job_900_to_node2: end replication job with error: faked replication error 16 | 2800 job_900_to_node2: changed config next_sync => 4600 17 | 2800 job_900_to_node2: changed state last_try => 2800, fail_count => 4 18 | 4600 job_900_to_node2: start replication job 19 | 4600 job_900_to_node2: end replication job with error: faked replication error 20 | 4600 job_900_to_node2: changed config next_sync => 6400 21 | 4600 job_900_to_node2: changed state last_try => 4600, fail_count => 5 22 | 6400 job_900_to_node2: start replication job 23 | 6400 job_900_to_node2: end replication job with error: faked replication error 24 | 6400 job_900_to_node2: changed config next_sync => 8200 25 | 6400 job_900_to_node2: changed state last_try => 6400, fail_count => 6 26 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010-2025 Proxmox Server Solutions GmbH 2 | 3 | This software is written by Proxmox Server Solutions GmbH 4 | 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Affero General Public License for more details. 14 | 15 | You should have received a copy of the GNU Affero General Public License 16 | along with this program. If not, see . 17 | 18 | ------------- 19 | 20 | Ext JS - JavaScript Library 21 | Copyright (c) 2006-2021, Sencha Inc. 22 | All rights reserved. 23 | licensing@sencha.com 24 | 25 | http://www.sencha.com/license 26 | 27 | Open Source License 28 | ---------------------------------------------------------------------------- 29 | This version of Ext JS is licensed under the terms of the 30 | Open Source GPL 3.0 license. 31 | 32 | http://www.gnu.org/licenses/gpl.html (/usr/share/common-licenses/GPL-3) 33 | 34 | There are several FLOSS exceptions available for use with this release for 35 | open source applications that are distributed under a license other than GPL. 36 | 37 | * Open Source License Exception for Applications 38 | 39 | http://www.sencha.com/products/floss-exception.php 40 | 41 | * Open Source License Exception for Development 42 | 43 | http://www.sencha.com/products/ux-exception.php 44 | -------------------------------------------------------------------------------- /templates/default/vzdump-body.html.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{error}} 4 |

    Details

    5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {{#each guest-table.data}} 15 | 16 | 23 | {{/each}} 24 |
    VMIDNameStatusTimeSizeFilename
    {{this.vmid}} 17 | {{this.name}} 18 | {{this.status}} 19 | {{duration this.time}} 20 | {{human-bytes this.size}} 21 | {{this.filename}} 22 |
    25 |
    26 | Total running time: {{duration total-time}}
    27 | Total size: {{human-bytes total-size}}
    28 |

    Logs

    29 |
    {{logs}}
    30 | 31 | 32 | -------------------------------------------------------------------------------- /www/images/icon-die.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 21 | 27 | 33 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /www/manager6/window/SafeDestroyGuest.js: -------------------------------------------------------------------------------- 1 | /* 2 | * SafeDestroy window with additional checkboxes for removing guests 3 | */ 4 | Ext.define('PVE.window.SafeDestroyGuest', { 5 | extend: 'Proxmox.window.SafeDestroy', 6 | alias: 'widget.pveSafeDestroyGuest', 7 | 8 | additionalItems: [ 9 | { 10 | xtype: 'proxmoxcheckbox', 11 | name: 'purge', 12 | reference: 'purgeCheckbox', 13 | boxLabel: gettext('Purge from job configurations'), 14 | checked: false, 15 | autoEl: { 16 | tag: 'div', 17 | 'data-qtip': gettext('Remove from replication, HA and backup jobs'), 18 | }, 19 | }, 20 | { 21 | xtype: 'proxmoxcheckbox', 22 | name: 'destroyUnreferenced', 23 | reference: 'destroyUnreferencedCheckbox', 24 | boxLabel: gettext('Destroy unreferenced disks owned by guest'), 25 | checked: false, 26 | autoEl: { 27 | tag: 'div', 28 | 'data-qtip': gettext( 29 | 'Scan all enabled storages for unreferenced disks and delete them.', 30 | ), 31 | }, 32 | }, 33 | ], 34 | 35 | note: gettext('Referenced disks will always be destroyed.'), 36 | 37 | getParams: function () { 38 | let me = this; 39 | 40 | const purgeCheckbox = me.lookupReference('purgeCheckbox'); 41 | me.params.purge = purgeCheckbox.checked ? 1 : 0; 42 | 43 | const destroyUnreferencedCheckbox = me.lookupReference('destroyUnreferencedCheckbox'); 44 | me.params['destroy-unreferenced-disks'] = destroyUnreferencedCheckbox.checked ? 1 : 0; 45 | 46 | return me.callParent(); 47 | }, 48 | }); 49 | -------------------------------------------------------------------------------- /www/manager6/form/PoolSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define( 2 | 'PVE.form.PoolSelector', 3 | { 4 | extend: 'Proxmox.form.ComboGrid', 5 | alias: ['widget.pvePoolSelector'], 6 | 7 | allowBlank: false, 8 | valueField: 'poolid', 9 | displayField: 'poolid', 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | var store = new Ext.data.Store({ 15 | model: 'pve-pools', 16 | sorters: 'poolid', 17 | }); 18 | 19 | Ext.apply(me, { 20 | store: store, 21 | autoSelect: false, 22 | listConfig: { 23 | columns: [ 24 | { 25 | header: gettext('Pool'), 26 | sortable: true, 27 | dataIndex: 'poolid', 28 | flex: 1, 29 | }, 30 | { 31 | header: gettext('Comment'), 32 | sortable: false, 33 | dataIndex: 'comment', 34 | renderer: Ext.String.htmlEncode, 35 | flex: 1, 36 | }, 37 | ], 38 | }, 39 | }); 40 | 41 | me.callParent(); 42 | 43 | store.load(); 44 | }, 45 | }, 46 | function () { 47 | Ext.define('pve-pools', { 48 | extend: 'Ext.data.Model', 49 | fields: ['poolid', 'comment'], 50 | proxy: { 51 | type: 'proxmox', 52 | url: '/api2/json/pools', 53 | }, 54 | idProperty: 'poolid', 55 | }); 56 | }, 57 | ); 58 | -------------------------------------------------------------------------------- /www/manager6/sdn/dns/PowerdnsEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.dns.PowerdnsInputPanel', { 2 | extend: 'PVE.panel.SDNDnsBase', 3 | 4 | onlineHelp: 'pvesdn_dns_plugin_powerdns', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.dns; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | var me = this; 20 | 21 | me.column1 = [ 22 | { 23 | xtype: me.isCreate ? 'textfield' : 'displayfield', 24 | name: 'dns', 25 | maxLength: 10, 26 | value: me.dns || '', 27 | fieldLabel: 'ID', 28 | allowBlank: false, 29 | }, 30 | { 31 | xtype: 'textfield', 32 | name: 'key', 33 | fieldLabel: gettext('API Key'), 34 | allowBlank: false, 35 | }, 36 | ]; 37 | me.column2 = [ 38 | { 39 | xtype: 'textfield', 40 | name: 'url', 41 | fieldLabel: 'URL', 42 | allowBlank: false, 43 | }, 44 | { 45 | xtype: 'proxmoxintegerfield', 46 | name: 'ttl', 47 | fieldLabel: 'TTL', 48 | allowBlank: true, 49 | }, 50 | ]; 51 | me.columnB = [ 52 | { 53 | xtype: 'pmxFingerprintField', 54 | name: 'fingerprint', 55 | value: me.isCreate ? null : undefined, 56 | deleteEmpty: !me.isCreate, 57 | }, 58 | ]; 59 | 60 | me.callParent(); 61 | }, 62 | }); 63 | -------------------------------------------------------------------------------- /test/perftest3.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use lib '../../'; 4 | use strict; 5 | use warnings; 6 | use Time::HiRes qw( usleep ualarm gettimeofday tv_interval ); 7 | use PVE::INotify; 8 | use PVE::AccessControl; 9 | use Net::SSLeay qw(get_https post_https sslcat make_headers make_form); 10 | 11 | use Data::Dumper; 12 | 13 | my $hostname = PVE::INotify::read_file("hostname"); 14 | 15 | # normally you use username/password, 16 | # but we can simply create a ticket if we are root 17 | my $ticket = PVE::AccessControl::assemble_ticket('root@pam'); 18 | 19 | my $wcount = 10; 20 | my $qcount = 100; 21 | 22 | sub test_rpc { 23 | my ($host) = @_; 24 | 25 | for (my $i = 0; $i < $qcount; $i++) { 26 | eval { 27 | my ($page, $response, %reply_headers) = get_https( 28 | $host, 29 | 8006, 30 | '/api2/json', 31 | make_headers(Cookie => "PVEAuthCookie=$ticket"), 32 | ); 33 | die "$response\n" if $response !~ m/200 OK/; 34 | }; 35 | 36 | my $err = $@; 37 | 38 | if ($err) { 39 | 40 | print "ERROR: $err\n"; 41 | last; 42 | } 43 | } 44 | } 45 | 46 | sub run_tests { 47 | my ($host) = @_; 48 | 49 | my $workers; 50 | 51 | my $starttime = [gettimeofday]; 52 | 53 | for (my $i = 0; $i < $wcount; $i++) { 54 | if (my $pid = fork()) { 55 | $workers->{$pid} = 1; 56 | } else { 57 | test_rpc($host); 58 | exit(0); 59 | } 60 | } 61 | 62 | # wait for children 63 | 1 while (wait > 0); 64 | 65 | my $elapsed = int(tv_interval($starttime) * 1000); 66 | 67 | my $tpq = $elapsed / ($wcount * $qcount); 68 | 69 | print "$host: $tpq ms per query\n"; 70 | } 71 | 72 | run_tests($hostname); # test 'pveproxy' 73 | -------------------------------------------------------------------------------- /www/manager6/sdn/controllers/IsisEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.controllers.IsisInputPanel', { 2 | extend: 'PVE.panel.SDNControllerBase', 3 | 4 | onlineHelp: 'pvesdn_controller_plugin_evpn', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | values.controller = 'isis' + values.node; 12 | } else { 13 | delete values.controller; 14 | } 15 | 16 | return values; 17 | }, 18 | 19 | initComponent: function () { 20 | var me = this; 21 | 22 | me.items = [ 23 | { 24 | xtype: 'pveNodeSelector', 25 | name: 'node', 26 | fieldLabel: gettext('Node'), 27 | multiSelect: false, 28 | autoSelect: false, 29 | allowBlank: false, 30 | }, 31 | { 32 | xtype: 'textfield', 33 | name: 'isis-domain', 34 | fieldLabel: 'Domain', 35 | allowBlank: false, 36 | }, 37 | { 38 | xtype: 'textfield', 39 | name: 'isis-net', 40 | fieldLabel: 'Network entity title', 41 | allowBlank: false, 42 | }, 43 | { 44 | xtype: 'textfield', 45 | name: 'isis-ifaces', 46 | fieldLabel: gettext('Interfaces'), 47 | allowBlank: false, 48 | }, 49 | ]; 50 | 51 | me.advancedItems = [ 52 | { 53 | xtype: 'textfield', 54 | name: 'loopback', 55 | fieldLabel: gettext('Loopback Interface'), 56 | }, 57 | ]; 58 | 59 | me.callParent(); 60 | }, 61 | }); 62 | -------------------------------------------------------------------------------- /www/manager6/sdn/ipams/PhpIpamEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.ipams.PhpIpamInputPanel', { 2 | extend: 'PVE.panel.SDNIpamBase', 3 | 4 | onlineHelp: 'pvesdn_ipam_plugin_phpipam', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.ipam; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | var me = this; 20 | 21 | me.column1 = [ 22 | { 23 | xtype: me.isCreate ? 'textfield' : 'displayfield', 24 | name: 'ipam', 25 | maxLength: 10, 26 | value: me.zone || '', 27 | fieldLabel: 'ID', 28 | allowBlank: false, 29 | }, 30 | { 31 | xtype: 'textfield', 32 | name: 'token', 33 | fieldLabel: gettext('Token'), 34 | allowBlank: false, 35 | }, 36 | ]; 37 | me.column2 = [ 38 | { 39 | xtype: 'textfield', 40 | name: 'url', 41 | fieldLabel: gettext('URL'), 42 | allowBlank: false, 43 | }, 44 | { 45 | xtype: 'textfield', 46 | name: 'section', 47 | fieldLabel: gettext('Section'), 48 | allowBlank: false, 49 | }, 50 | ]; 51 | 52 | me.columnB = [ 53 | { 54 | xtype: 'pmxFingerprintField', 55 | name: 'fingerprint', 56 | value: me.isCreate ? null : undefined, 57 | deleteEmpty: !me.isCreate, 58 | }, 59 | ]; 60 | 61 | me.callParent(); 62 | }, 63 | }); 64 | -------------------------------------------------------------------------------- /www/manager6/form/GroupSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('pve-groups', { 2 | extend: 'Ext.data.Model', 3 | fields: ['groupid', 'comment', 'users'], 4 | proxy: { 5 | type: 'proxmox', 6 | url: '/api2/json/access/groups', 7 | }, 8 | idProperty: 'groupid', 9 | }); 10 | 11 | Ext.define('PVE.form.GroupSelector', { 12 | extend: 'Proxmox.form.ComboGrid', 13 | xtype: 'pveGroupSelector', 14 | 15 | editable: true, 16 | anyMatch: true, 17 | forceSelection: true, 18 | 19 | allowBlank: false, 20 | autoSelect: false, 21 | valueField: 'groupid', 22 | displayField: 'groupid', 23 | listConfig: { 24 | columns: [ 25 | { 26 | header: gettext('Group'), 27 | sortable: true, 28 | dataIndex: 'groupid', 29 | flex: 1, 30 | }, 31 | { 32 | header: gettext('Comment'), 33 | sortable: false, 34 | dataIndex: 'comment', 35 | renderer: Ext.String.htmlEncode, 36 | flex: 1, 37 | }, 38 | { 39 | header: gettext('Users'), 40 | sortable: false, 41 | dataIndex: 'users', 42 | renderer: Ext.String.htmlEncode, 43 | flex: 1, 44 | }, 45 | ], 46 | }, 47 | 48 | initComponent: function () { 49 | var me = this; 50 | 51 | var store = new Ext.data.Store({ 52 | model: 'pve-groups', 53 | sorters: [ 54 | { 55 | property: 'groupid', 56 | }, 57 | ], 58 | }); 59 | 60 | Ext.apply(me, { 61 | store: store, 62 | }); 63 | 64 | me.callParent(); 65 | 66 | store.load(); 67 | }, 68 | }); 69 | -------------------------------------------------------------------------------- /www/manager6/dc/RoleEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.dc.RoleEdit', { 2 | extend: 'Proxmox.window.Edit', 3 | xtype: 'pveDcRoleEdit', 4 | 5 | width: 400, 6 | 7 | initComponent: function () { 8 | var me = this; 9 | 10 | me.isCreate = !me.roleid; 11 | 12 | var url; 13 | var method; 14 | 15 | if (me.isCreate) { 16 | url = '/api2/extjs/access/roles'; 17 | method = 'POST'; 18 | } else { 19 | url = '/api2/extjs/access/roles/' + me.roleid; 20 | method = 'PUT'; 21 | } 22 | 23 | Ext.applyIf(me, { 24 | subject: gettext('Role'), 25 | url: url, 26 | method: method, 27 | items: [ 28 | { 29 | xtype: me.isCreate ? 'proxmoxtextfield' : 'displayfield', 30 | name: 'roleid', 31 | value: me.roleid, 32 | allowBlank: false, 33 | fieldLabel: gettext('Name'), 34 | }, 35 | { 36 | xtype: 'pvePrivilegesSelector', 37 | name: 'privs', 38 | value: me.privs, 39 | allowBlank: false, 40 | fieldLabel: gettext('Privileges'), 41 | }, 42 | ], 43 | }); 44 | 45 | me.callParent(); 46 | 47 | if (!me.isCreate) { 48 | me.load({ 49 | success: function (response) { 50 | var data = response.result.data; 51 | var keys = Ext.Object.getKeys(data); 52 | 53 | me.setValues({ 54 | privs: keys, 55 | roleid: me.roleid, 56 | }); 57 | }, 58 | }); 59 | } 60 | }, 61 | }); 62 | -------------------------------------------------------------------------------- /spice-example-sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # needs pve-manager >= 3.1-44 6 | 7 | usage() { 8 | echo "Usage: $0 [-u ] [-p ] vmid [node [proxy]]" 9 | echo 10 | echo "-u username. Default root@pam" 11 | echo "-p password. Default ''" 12 | echo 13 | echo "vmid: id for VM" 14 | echo "node: Proxmox cluster node name" 15 | echo "proxy: DNS or IP (use as default)" 16 | exit 1 17 | } 18 | 19 | PASSWORD="" 20 | USERNAME="" 21 | 22 | while getopts ":u:p:" o; do 23 | case "${o}" in 24 | u) 25 | USERNAME="${OPTARG}" 26 | ;; 27 | p) 28 | PASSWORD="${OPTARG}" 29 | ;; 30 | *) 31 | usage 32 | ;; 33 | esac 34 | done 35 | 36 | shift $((OPTIND-1)) 37 | 38 | if [[ -z "$PASSWORD" ]]; then 39 | PASSWORD="" 40 | fi 41 | if [[ -z "$USERNAME" ]]; then 42 | USERNAME='root@pam' 43 | fi 44 | 45 | DEFAULTHOST="$(hostname -f)" 46 | 47 | # select VM 48 | [[ -z "$1" ]] && usage 49 | VMID="$1" 50 | 51 | #[[ -z "$2" ]] && usage 52 | NODE="${2:-$DEFAULTHOST}" 53 | 54 | if [[ -z "$3" ]]; then 55 | PROXY="$NODE" 56 | else 57 | PROXY="$3" 58 | fi 59 | 60 | NODE="${NODE%%\.*}" 61 | 62 | DATA="$(curl -f -s -S -k --data-urlencode "username=$USERNAME" --data-urlencode "password=$PASSWORD" "https://$PROXY:8006/api2/json/access/ticket")" 63 | 64 | echo "AUTH OK" 65 | 66 | TICKET="${DATA//\"/}" 67 | TICKET="${TICKET##*ticket:}" 68 | TICKET="${TICKET%%,*}" 69 | TICKET="${TICKET%%\}*}" 70 | 71 | CSRF="${DATA//\"/}" 72 | CSRF="${CSRF##*CSRFPreventionToken:}" 73 | CSRF="${CSRF%%,*}" 74 | CSRF="${CSRF%%\}*}" 75 | 76 | curl -f -s -S -k -b "PVEAuthCookie=$TICKET" -H "CSRFPreventionToken: $CSRF" "https://$PROXY:8006/api2/spiceconfig/nodes/$NODE/qemu/$VMID/spiceproxy" -d "proxy=$PROXY" > spiceproxy 77 | 78 | exec remote-viewer spiceproxy 79 | -------------------------------------------------------------------------------- /www/manager6/container/TwoColumnContainer.js: -------------------------------------------------------------------------------- 1 | // This is a container intended to show a field on the first column and one on the second column. 2 | // One can set a ratio for the field sizes. 3 | // 4 | // Works around a limitation of our input panel column1/2 handling that entries are not vertically 5 | // aligned when one of them has wrapping text (like it happens sometimes with such longer 6 | // descriptions) 7 | Ext.define('PVE.container.TwoColumnContainer', { 8 | extend: 'Ext.container.Container', 9 | alias: 'widget.pveTwoColumnContainer', 10 | 11 | layout: { 12 | type: 'hbox', 13 | align: 'begin', 14 | }, 15 | 16 | // The default ratio of the start widget. It an be an integer or a floating point number 17 | startFlex: 1, 18 | 19 | // The default ratio of the end widget. It an be an integer or a floating point number 20 | endFlex: 1, 21 | 22 | // the padding between the two columns 23 | columnPadding: 20, 24 | 25 | // the config of the first widget 26 | startColumn: undefined, 27 | 28 | // the config of the second widget 29 | endColumn: undefined, 30 | 31 | // same as fields in a panel 32 | padding: '0 0 5 0', 33 | 34 | initComponent: function () { 35 | let me = this; 36 | 37 | if (!me.startColumn) { 38 | throw 'no start widget configured'; 39 | } 40 | if (!me.endColumn) { 41 | throw 'no end widget configured'; 42 | } 43 | 44 | Ext.apply(me, { 45 | items: [ 46 | Ext.applyIf({ flex: me.startFlex }, me.startColumn), 47 | { 48 | xtype: 'box', 49 | width: me.columnPadding, 50 | }, 51 | Ext.applyIf({ flex: me.endFlex }, me.endColumn), 52 | ], 53 | }); 54 | 55 | me.callParent(); 56 | }, 57 | }); 58 | -------------------------------------------------------------------------------- /PVE/API2/HAConfig.pm: -------------------------------------------------------------------------------- 1 | package PVE::API2::HAConfig; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use PVE::SafeSyslog; 7 | use PVE::Tools; 8 | use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file); 9 | use PVE::RESTHandler; 10 | use PVE::RPCEnvironment; 11 | use PVE::JSONSchema qw(get_standard_option); 12 | use PVE::Exception qw(raise_param_exc); 13 | use PVE::API2::HA::Resources; 14 | use PVE::API2::HA::Groups; 15 | use PVE::API2::HA::Rules; 16 | use PVE::API2::HA::Status; 17 | 18 | use base qw(PVE::RESTHandler); 19 | 20 | __PACKAGE__->register_method({ 21 | subclass => "PVE::API2::HA::Resources", 22 | path => 'resources', 23 | }); 24 | 25 | __PACKAGE__->register_method({ 26 | subclass => "PVE::API2::HA::Groups", 27 | path => 'groups', 28 | }); 29 | 30 | __PACKAGE__->register_method({ 31 | subclass => "PVE::API2::HA::Rules", 32 | path => 'rules', 33 | }); 34 | 35 | __PACKAGE__->register_method({ 36 | subclass => "PVE::API2::HA::Status", 37 | path => 'status', 38 | }); 39 | 40 | __PACKAGE__->register_method({ 41 | name => 'index', 42 | path => '', 43 | method => 'GET', 44 | description => "Directory index.", 45 | permissions => { 46 | check => ['perm', '/', ['Sys.Audit']], 47 | }, 48 | parameters => { 49 | additionalProperties => 0, 50 | properties => {}, 51 | }, 52 | returns => { 53 | type => 'array', 54 | items => { 55 | type => "object", 56 | properties => { 57 | id => { type => 'string' }, 58 | }, 59 | }, 60 | links => [{ rel => 'child', href => "{id}" }], 61 | }, 62 | code => sub { 63 | my ($param) = @_; 64 | 65 | my $res = [ 66 | { id => 'status' }, { id => 'resources' }, { id => 'groups' }, { id => 'rules' }, 67 | ]; 68 | 69 | return $res; 70 | }, 71 | }); 72 | 73 | 1; 74 | -------------------------------------------------------------------------------- /www/manager6/form/BridgeSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.BridgeSelector', { 2 | extend: 'Proxmox.form.ComboGrid', 3 | alias: ['widget.PVE.form.BridgeSelector'], 4 | 5 | bridgeType: 'any_bridge', // bridge, OVSBridge or any_bridge 6 | 7 | store: { 8 | fields: ['iface', 'active', 'type'], 9 | filterOnLoad: true, 10 | sorters: [ 11 | { 12 | property: 'iface', 13 | direction: 'ASC', 14 | }, 15 | ], 16 | }, 17 | valueField: 'iface', 18 | displayField: 'iface', 19 | listConfig: { 20 | columns: [ 21 | { 22 | header: gettext('Bridge'), 23 | dataIndex: 'iface', 24 | hideable: false, 25 | width: 100, 26 | }, 27 | { 28 | header: gettext('Active'), 29 | width: 60, 30 | dataIndex: 'active', 31 | renderer: Proxmox.Utils.format_boolean, 32 | }, 33 | { 34 | header: gettext('Comment'), 35 | dataIndex: 'comments', 36 | renderer: Ext.String.htmlEncode, 37 | flex: 1, 38 | }, 39 | ], 40 | }, 41 | 42 | setNodename: function (nodename) { 43 | var me = this; 44 | 45 | if (!nodename || me.nodename === nodename) { 46 | return; 47 | } 48 | 49 | me.nodename = nodename; 50 | 51 | me.store.setProxy({ 52 | type: 'proxmox', 53 | url: '/api2/json/nodes/' + me.nodename + '/network?type=' + me.bridgeType, 54 | }); 55 | 56 | me.store.load(); 57 | }, 58 | 59 | initComponent: function () { 60 | var me = this; 61 | 62 | var nodename = me.nodename; 63 | me.nodename = undefined; 64 | 65 | me.callParent(); 66 | 67 | me.setNodename(nodename); 68 | }, 69 | }); 70 | -------------------------------------------------------------------------------- /www/manager6/form/SnapshotSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.SnapshotSelector', { 2 | extend: 'Proxmox.form.ComboGrid', 3 | alias: ['widget.PVE.form.SnapshotSelector'], 4 | 5 | valueField: 'name', 6 | displayField: 'name', 7 | 8 | loadStore: function (nodename, vmid) { 9 | var me = this; 10 | 11 | if (!nodename) { 12 | return; 13 | } 14 | 15 | me.nodename = nodename; 16 | 17 | if (!vmid) { 18 | return; 19 | } 20 | 21 | me.vmid = vmid; 22 | 23 | me.store.setProxy({ 24 | type: 'proxmox', 25 | url: 26 | '/api2/json/nodes/' + 27 | me.nodename + 28 | '/' + 29 | me.guestType + 30 | '/' + 31 | me.vmid + 32 | '/snapshot', 33 | }); 34 | 35 | me.store.load(); 36 | }, 37 | 38 | initComponent: function () { 39 | var me = this; 40 | 41 | if (!me.nodename) { 42 | throw 'no node name specified'; 43 | } 44 | 45 | if (!me.vmid) { 46 | throw 'no VM ID specified'; 47 | } 48 | 49 | if (!me.guestType) { 50 | throw 'no guest type specified'; 51 | } 52 | 53 | var store = Ext.create('Ext.data.Store', { 54 | fields: ['name'], 55 | filterOnLoad: true, 56 | }); 57 | 58 | Ext.apply(me, { 59 | store: store, 60 | listConfig: { 61 | columns: [ 62 | { 63 | header: gettext('Snapshot'), 64 | dataIndex: 'name', 65 | hideable: false, 66 | flex: 1, 67 | }, 68 | ], 69 | }, 70 | }); 71 | 72 | me.callParent(); 73 | 74 | me.loadStore(me.nodename, me.vmid); 75 | }, 76 | }); 77 | -------------------------------------------------------------------------------- /www/manager6/form/SecurityGroupSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.SecurityGroupsSelector', { 2 | extend: 'Proxmox.form.ComboGrid', 3 | alias: ['widget.pveSecurityGroupsSelector'], 4 | 5 | valueField: 'group', 6 | displayField: 'group', 7 | initComponent: function () { 8 | var me = this; 9 | 10 | var store = Ext.create('Ext.data.Store', { 11 | autoLoad: true, 12 | fields: ['group', 'comment'], 13 | idProperty: 'group', 14 | proxy: { 15 | type: 'proxmox', 16 | url: '/api2/json/cluster/firewall/groups', 17 | }, 18 | sorters: { 19 | property: 'group', 20 | direction: 'ASC', 21 | }, 22 | }); 23 | 24 | Ext.apply(me, { 25 | store: store, 26 | listConfig: { 27 | columns: [ 28 | { 29 | header: gettext('Security Group'), 30 | dataIndex: 'group', 31 | hideable: false, 32 | width: 100, 33 | }, 34 | { 35 | header: gettext('Comment'), 36 | dataIndex: 'comment', 37 | renderer: function (value, metaData) { 38 | let comment = Ext.String.htmlEncode(value) || ''; 39 | if (comment.length * 12 > metaData.column.cellWidth) { 40 | let qtip = Ext.htmlEncode(comment); 41 | comment = `${comment}`; 42 | } 43 | return comment; 44 | }, 45 | flex: 1, 46 | }, 47 | ], 48 | }, 49 | }); 50 | 51 | me.callParent(); 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /test/OSD_test.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib ('.', '..'); 7 | 8 | use JSON; 9 | use Test::More; 10 | use PVE::API2::Ceph::OSD; 11 | 12 | use Data::Dumper; 13 | 14 | # NOTE: not exhaustive, reduced to actually required fields! 15 | my $tree = { 16 | nodes => [ 17 | { 18 | id => -3, 19 | name => 'pveA', 20 | children => [0, 1, 2, 3], 21 | type => 'host', 22 | }, 23 | { 24 | id => -5, 25 | name => 'pveB', 26 | children => [4, 5, 6, 7], 27 | type => 'host', 28 | }, 29 | { 30 | id => -7, 31 | name => 'pveC', 32 | children => [8, 9, 10, 11], 33 | type => 'host', 34 | }, 35 | ], 36 | }; 37 | 38 | # Check if all the grep and casts are correct 39 | my @belong_to_B = (4, 5); 40 | my @not_belong_to_B = (-1, 1, 10, 15); 41 | foreach (@belong_to_B) { 42 | is( 43 | PVE::API2::Ceph::OSD::osd_belongs_to_node($tree, 'pveB', $_), 44 | 1, 45 | "OSD $_ belongs to node pveB", 46 | ); 47 | } 48 | foreach (@not_belong_to_B) { 49 | is( 50 | PVE::API2::Ceph::OSD::osd_belongs_to_node($tree, 'pveB', $_), 51 | 0, 52 | "OSD $_ does not belong to node pveB", 53 | ); 54 | } 55 | 56 | my $double_nodes_tree = { 57 | nodes => [ 58 | { 59 | name => 'pveA', 60 | type => 'host', 61 | }, 62 | { 63 | name => 'pveA', 64 | type => 'host', 65 | }, 66 | ], 67 | }; 68 | eval { PVE::API2::Ceph::OSD::osd_belongs_to_node($double_nodes_tree, 'pveA') }; 69 | like($@, qr/duplicate host name found/, "Die if node occurs too often"); 70 | 71 | is( 72 | PVE::API2::Ceph::OSD::osd_belongs_to_node(undef), 73 | 0, 74 | "Early-return false if there's no/empty node tree", 75 | ); 76 | 77 | done_testing(@belong_to_B + @not_belong_to_B + 2); 78 | -------------------------------------------------------------------------------- /www/manager6/sdn/IpamEdit.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.sdn.IpamEditInputPanel', { 2 | extend: 'Proxmox.panel.InputPanel', 3 | mixins: ['Proxmox.Mixin.CBind'], 4 | 5 | isCreate: false, 6 | 7 | onGetValues: function (values) { 8 | let _me = this; 9 | 10 | if (!values.vmid) { 11 | delete values.vmid; 12 | } 13 | 14 | return values; 15 | }, 16 | 17 | items: [ 18 | { 19 | xtype: 'pmxDisplayEditField', 20 | name: 'vmid', 21 | fieldLabel: 'VMID', 22 | allowBlank: false, 23 | editable: false, 24 | cbind: { 25 | hidden: '{isCreate}', 26 | }, 27 | }, 28 | { 29 | xtype: 'pmxDisplayEditField', 30 | name: 'mac', 31 | fieldLabel: 'MAC', 32 | allowBlank: false, 33 | cbind: { 34 | editable: '{isCreate}', 35 | }, 36 | }, 37 | { 38 | xtype: 'proxmoxtextfield', 39 | name: 'ip', 40 | fieldLabel: gettext('IP Address'), 41 | allowBlank: false, 42 | }, 43 | ], 44 | }); 45 | 46 | Ext.define('PVE.sdn.IpamEdit', { 47 | extend: 'Proxmox.window.Edit', 48 | 49 | subject: gettext('DHCP Mapping'), 50 | width: 350, 51 | 52 | isCreate: false, 53 | mapping: {}, 54 | 55 | url: '/cluster/sdn/vnets', 56 | 57 | submitUrl: function (url, values) { 58 | return `${url}/${values.vnet}/ips`; 59 | }, 60 | 61 | initComponent: function () { 62 | var me = this; 63 | 64 | me.method = me.isCreate ? 'POST' : 'PUT'; 65 | 66 | let ipanel = Ext.create('PVE.sdn.IpamEditInputPanel', { 67 | isCreate: me.isCreate, 68 | }); 69 | 70 | Ext.apply(me, { 71 | items: [ipanel], 72 | }); 73 | 74 | me.callParent(); 75 | 76 | ipanel.setValues(me.mapping); 77 | }, 78 | }); 79 | -------------------------------------------------------------------------------- /www/manager6/form/HotplugFeatureSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.HotplugFeatureSelector', { 2 | extend: 'Ext.form.CheckboxGroup', 3 | alias: 'widget.pveHotplugFeatureSelector', 4 | 5 | columns: 1, 6 | vertical: true, 7 | 8 | defaults: { 9 | name: 'hotplugCbGroup', 10 | submitValue: false, 11 | }, 12 | items: [ 13 | { 14 | boxLabel: gettext('Disk'), 15 | inputValue: 'disk', 16 | checked: true, 17 | }, 18 | { 19 | boxLabel: gettext('Network'), 20 | inputValue: 'network', 21 | checked: true, 22 | }, 23 | { 24 | boxLabel: 'USB', 25 | inputValue: 'usb', 26 | checked: true, 27 | }, 28 | { 29 | boxLabel: gettext('Memory'), 30 | inputValue: 'memory', 31 | }, 32 | { 33 | boxLabel: gettext('CPU'), 34 | inputValue: 'cpu', 35 | }, 36 | ], 37 | 38 | setValue: function (value) { 39 | var me = this; 40 | var newVal = []; 41 | if (value === '1') { 42 | newVal = ['disk', 'network', 'usb']; 43 | } else if (value !== '0') { 44 | newVal = value.split(','); 45 | } 46 | me.callParent([{ hotplugCbGroup: newVal }]); 47 | }, 48 | 49 | // override framework function to 50 | // assemble the hotplug value 51 | getSubmitData: function () { 52 | var me = this, 53 | boxes = me.getBoxes(), 54 | data = []; 55 | Ext.Array.forEach(boxes, function (box) { 56 | if (box.getValue()) { 57 | data.push(box.inputValue); 58 | } 59 | }); 60 | 61 | /* because above is hotplug an array */ 62 | if (data.length === 0) { 63 | return { hotplug: '0' }; 64 | } else { 65 | return { hotplug: data.join(',') }; 66 | } 67 | }, 68 | }); 69 | -------------------------------------------------------------------------------- /www/manager6/panel/StatusPanel.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This class describes the bottom panel 3 | */ 4 | Ext.define('PVE.panel.StatusPanel', { 5 | extend: 'Ext.tab.Panel', 6 | alias: 'widget.pveStatusPanel', 7 | 8 | //title: "Logs", 9 | //tabPosition: 'bottom', 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | var stateid = 'ltab'; 15 | var sp = Ext.state.Manager.getProvider(); 16 | 17 | var state = sp.get(stateid); 18 | if (state && state.value) { 19 | me.activeTab = state.value; 20 | } 21 | 22 | Ext.apply(me, { 23 | listeners: { 24 | tabchange: function () { 25 | var atab = me.getActiveTab().itemId; 26 | let tabstate = { value: atab }; 27 | sp.set(stateid, tabstate); 28 | }, 29 | }, 30 | items: [ 31 | { 32 | itemId: 'tasks', 33 | title: gettext('Tasks'), 34 | xtype: 'pveClusterTasks', 35 | }, 36 | { 37 | itemId: 'clog', 38 | title: gettext('Cluster log'), 39 | xtype: 'pveClusterLog', 40 | }, 41 | ], 42 | }); 43 | 44 | me.callParent(); 45 | 46 | me.items.get(0).fireEvent('show', me.items.get(0)); 47 | 48 | var statechange = function (_, key, newstate) { 49 | if (key === stateid) { 50 | let atab = me.getActiveTab().itemId; 51 | let ntab = newstate.value; 52 | if (newstate && ntab && atab !== ntab) { 53 | me.setActiveTab(ntab); 54 | } 55 | } 56 | }; 57 | 58 | sp.on('statechange', statechange); 59 | me.on('destroy', function () { 60 | sp.un('statechange', statechange); 61 | }); 62 | }, 63 | }); 64 | -------------------------------------------------------------------------------- /www/manager6/form/TagFieldSet.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.form.TagFieldSet', { 2 | extend: 'Ext.form.FieldSet', 3 | alias: 'widget.pveTagFieldSet', 4 | mixins: ['Ext.form.field.Field'], 5 | 6 | title: gettext('Tags'), 7 | padding: '0 5 5 5', 8 | 9 | getValue: function () { 10 | let me = this; 11 | let tags = me 12 | .down('pveTagEditContainer') 13 | .getTags() 14 | .filter((t) => t !== ''); 15 | return tags.join(';'); 16 | }, 17 | 18 | setValue: function (value) { 19 | let me = this; 20 | value ??= []; 21 | if (!Ext.isArray(value)) { 22 | value = value.split(/[;, ]/).filter((t) => t !== ''); 23 | } 24 | me.down('pveTagEditContainer').loadTags(value.join(';')); 25 | }, 26 | 27 | getErrors: function (value) { 28 | value ??= []; 29 | if (!Ext.isArray(value)) { 30 | value = value.split(/[;, ]/).filter((t) => t !== ''); 31 | } 32 | if (value.some((t) => !t.match(PVE.Utils.tagCharRegex))) { 33 | return [gettext('Tags contain invalid characters.')]; 34 | } 35 | return []; 36 | }, 37 | 38 | getSubmitData: function () { 39 | let me = this; 40 | let value = me.getValue(); 41 | if (me.disabled || !me.submitValue || value === '') { 42 | return null; 43 | } 44 | let data = {}; 45 | data[me.getName()] = value; 46 | return data; 47 | }, 48 | 49 | layout: 'fit', 50 | 51 | items: [ 52 | { 53 | xtype: 'pveTagEditContainer', 54 | userCls: 'proxmox-tags-full proxmox-tag-fieldset', 55 | editOnly: true, 56 | allowBlank: true, 57 | layout: 'column', 58 | scrollable: true, 59 | }, 60 | ], 61 | 62 | initComponent: function () { 63 | let me = this; 64 | me.callParent(); 65 | me.initField(); 66 | }, 67 | }); 68 | -------------------------------------------------------------------------------- /www/manager6/sdn/dns/Base.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.panel.SDNDnsBase', { 2 | extend: 'Proxmox.panel.InputPanel', 3 | 4 | type: '', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.dns; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | var me = this; 20 | 21 | me.callParent(); 22 | }, 23 | }); 24 | 25 | Ext.define('PVE.sdn.dns.BaseEdit', { 26 | extend: 'Proxmox.window.Edit', 27 | 28 | initComponent: function () { 29 | var me = this; 30 | 31 | me.isCreate = !me.dns; 32 | 33 | if (me.isCreate) { 34 | me.url = '/api2/extjs/cluster/sdn/dns'; 35 | me.method = 'POST'; 36 | } else { 37 | me.url = '/api2/extjs/cluster/sdn/dns/' + me.dns; 38 | me.method = 'PUT'; 39 | } 40 | 41 | var ipanel = Ext.create(me.paneltype, { 42 | type: me.type, 43 | isCreate: me.isCreate, 44 | dns: me.dns, 45 | }); 46 | 47 | Ext.apply(me, { 48 | subject: PVE.Utils.format_sdndns_type(me.type), 49 | isAdd: true, 50 | items: [ipanel], 51 | }); 52 | 53 | me.callParent(); 54 | 55 | if (!me.isCreate) { 56 | me.load({ 57 | success: function (response, options) { 58 | var values = response.result.data; 59 | var ctypes = values.content || ''; 60 | 61 | values.content = ctypes.split(','); 62 | 63 | if (values.nodes) { 64 | values.nodes = values.nodes.split(','); 65 | } 66 | values.enable = values.disable ? 0 : 1; 67 | 68 | ipanel.setValues(values); 69 | }, 70 | }); 71 | } 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /www/manager6/sdn/ipams/Base.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.panel.SDNIpamBase', { 2 | extend: 'Proxmox.panel.InputPanel', 3 | 4 | type: '', 5 | 6 | onGetValues: function (values) { 7 | var me = this; 8 | 9 | if (me.isCreate) { 10 | values.type = me.type; 11 | } else { 12 | delete values.ipam; 13 | } 14 | 15 | return values; 16 | }, 17 | 18 | initComponent: function () { 19 | var me = this; 20 | 21 | me.callParent(); 22 | }, 23 | }); 24 | 25 | Ext.define('PVE.sdn.ipams.BaseEdit', { 26 | extend: 'Proxmox.window.Edit', 27 | 28 | initComponent: function () { 29 | var me = this; 30 | 31 | me.isCreate = !me.ipam; 32 | 33 | if (me.isCreate) { 34 | me.url = '/api2/extjs/cluster/sdn/ipams'; 35 | me.method = 'POST'; 36 | } else { 37 | me.url = '/api2/extjs/cluster/sdn/ipams/' + me.ipam; 38 | me.method = 'PUT'; 39 | } 40 | 41 | var ipanel = Ext.create(me.paneltype, { 42 | type: me.type, 43 | isCreate: me.isCreate, 44 | ipam: me.ipam, 45 | }); 46 | 47 | Ext.apply(me, { 48 | subject: PVE.Utils.format_sdnipam_type(me.type), 49 | isAdd: true, 50 | items: [ipanel], 51 | }); 52 | 53 | me.callParent(); 54 | 55 | if (!me.isCreate) { 56 | me.load({ 57 | success: function (response, options) { 58 | var values = response.result.data; 59 | var ctypes = values.content || ''; 60 | 61 | values.content = ctypes.split(','); 62 | 63 | if (values.nodes) { 64 | values.nodes = values.nodes.split(','); 65 | } 66 | values.enable = values.disable ? 0 : 1; 67 | 68 | ipanel.setValues(values); 69 | }, 70 | }); 71 | } 72 | }, 73 | }); 74 | -------------------------------------------------------------------------------- /www/manager6/data/model/RRDModels.js: -------------------------------------------------------------------------------- 1 | Ext.define('pve-rrd-node', { 2 | extend: 'Ext.data.Model', 3 | fields: [ 4 | { 5 | name: 'cpu', 6 | // percentage 7 | convert: function (value) { 8 | return value * 100; 9 | }, 10 | }, 11 | { 12 | name: 'iowait', 13 | // percentage 14 | convert: function (value) { 15 | return value * 100; 16 | }, 17 | }, 18 | 'loadavg', 19 | 'maxcpu', 20 | 'memtotal', 21 | 'memused', 22 | 'netin', 23 | 'netout', 24 | 'roottotal', 25 | 'rootused', 26 | 'swaptotal', 27 | 'swapused', 28 | 'memavailable', 29 | 'arcsize', 30 | 'pressurecpusome', 31 | 'pressureiosome', 32 | 'pressureiofull', 33 | 'pressurememorysome', 34 | 'pressurememoryfull', 35 | { type: 'date', dateFormat: 'timestamp', name: 'time' }, 36 | ], 37 | }); 38 | 39 | Ext.define('pve-rrd-guest', { 40 | extend: 'Ext.data.Model', 41 | fields: [ 42 | { 43 | name: 'cpu', 44 | // percentage 45 | convert: function (value) { 46 | return value * 100; 47 | }, 48 | }, 49 | 'maxcpu', 50 | 'netin', 51 | 'netout', 52 | { name: 'mem', defaultValue: null }, 53 | 'maxmem', 54 | 'disk', 55 | 'maxdisk', 56 | 'diskread', 57 | 'diskwrite', 58 | 'memhost', 59 | 'pressurecpusome', 60 | 'pressurecpufull', 61 | 'pressureiosome', 62 | 'pressurecpufull', 63 | 'pressurememorysome', 64 | 'pressurememoryfull', 65 | { type: 'date', dateFormat: 'timestamp', name: 'time' }, 66 | ], 67 | }); 68 | 69 | Ext.define('pve-rrd-storage', { 70 | extend: 'Ext.data.Model', 71 | fields: ['used', 'total', { type: 'date', dateFormat: 'timestamp', name: 'time' }], 72 | }); 73 | -------------------------------------------------------------------------------- /www/manager6/form/SDNVnetSelector.js: -------------------------------------------------------------------------------- 1 | Ext.define( 2 | 'PVE.form.SDNVnetSelector', 3 | { 4 | extend: 'Proxmox.form.ComboGrid', 5 | alias: ['widget.pveSDNVnetSelector'], 6 | 7 | allowBlank: false, 8 | valueField: 'vnet', 9 | displayField: 'vnet', 10 | 11 | initComponent: function () { 12 | var me = this; 13 | 14 | var store = new Ext.data.Store({ 15 | model: 'pve-sdn-vnet', 16 | sorters: { 17 | property: 'vnet', 18 | direction: 'ASC', 19 | }, 20 | }); 21 | 22 | Ext.apply(me, { 23 | store: store, 24 | autoSelect: false, 25 | listConfig: { 26 | columns: [ 27 | { 28 | header: gettext('VNet'), 29 | sortable: true, 30 | dataIndex: 'vnet', 31 | flex: 1, 32 | }, 33 | { 34 | header: gettext('Alias'), 35 | flex: 1, 36 | dataIndex: 'alias', 37 | }, 38 | { 39 | header: gettext('Tag'), 40 | flex: 1, 41 | dataIndex: 'tag', 42 | }, 43 | ], 44 | }, 45 | }); 46 | 47 | me.callParent(); 48 | 49 | store.load(); 50 | }, 51 | }, 52 | function () { 53 | Ext.define('pve-sdn-vnet', { 54 | extend: 'Ext.data.Model', 55 | fields: ['alias', 'tag', 'type', 'vnet', 'zone'], 56 | proxy: { 57 | type: 'proxmox', 58 | url: '/api2/json/cluster/sdn/vnets', 59 | }, 60 | idProperty: 'vnet', 61 | }); 62 | }, 63 | ); 64 | -------------------------------------------------------------------------------- /www/manager6/storage/StatusView.js: -------------------------------------------------------------------------------- 1 | Ext.define('PVE.storage.StatusView', { 2 | extend: 'Proxmox.panel.StatusView', 3 | alias: 'widget.pveStorageStatusView', 4 | 5 | height: 230, 6 | title: gettext('Status'), 7 | 8 | layout: { 9 | type: 'vbox', 10 | align: 'stretch', 11 | }, 12 | 13 | defaults: { 14 | xtype: 'pmxInfoWidget', 15 | padding: '0 30 5 30', 16 | }, 17 | items: [ 18 | { 19 | xtype: 'box', 20 | height: 30, 21 | }, 22 | { 23 | itemId: 'enabled', 24 | title: gettext('Enabled'), 25 | printBar: false, 26 | textField: 'disabled', 27 | renderer: Proxmox.Utils.format_neg_boolean, 28 | }, 29 | { 30 | itemId: 'active', 31 | title: gettext('Active'), 32 | printBar: false, 33 | textField: 'active', 34 | renderer: Proxmox.Utils.format_boolean, 35 | }, 36 | { 37 | itemId: 'content', 38 | title: gettext('Content'), 39 | printBar: false, 40 | textField: 'content', 41 | renderer: PVE.Utils.format_content_types, 42 | }, 43 | { 44 | itemId: 'type', 45 | title: gettext('Type'), 46 | printBar: false, 47 | textField: 'type', 48 | renderer: PVE.Utils.format_storage_type, 49 | }, 50 | { 51 | xtype: 'box', 52 | height: 10, 53 | }, 54 | { 55 | itemId: 'usage', 56 | title: gettext('Usage'), 57 | valueField: 'used', 58 | maxField: 'total', 59 | renderer: (val, max) => { 60 | if (max === undefined) { 61 | return val; 62 | } 63 | return Proxmox.Utils.render_size_usage(val, max, true); 64 | }, 65 | }, 66 | ], 67 | 68 | updateTitle: function () { 69 | // nothing 70 | }, 71 | }); 72 | --------------------------------------------------------------------------------