proxsync 2.0.8__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {proxsync-2.0.8/proxsync.egg-info → proxsync-2.1.0}/PKG-INFO +1 -1
- {proxsync-2.0.8 → proxsync-2.1.0/proxsync.egg-info}/PKG-INFO +1 -1
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync.py +59 -11
- {proxsync-2.0.8 → proxsync-2.1.0}/pyproject.toml +1 -1
- {proxsync-2.0.8 → proxsync-2.1.0}/MANIFEST.in +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/README.md +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/config.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pbs_sync.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/proxsync.egg-info/SOURCES.txt +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/proxsync.egg-info/dependency_links.txt +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/proxsync.egg-info/requires.txt +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/proxsync.egg-info/top_level.txt +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/__init__.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/api/__init__.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/api/serializers.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/api/urls.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/api/views.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/apps.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/choices.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/filtersets.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/forms.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/management/__init__.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/management/commands/__init__.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/management/commands/pve_sync.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/management/commands/run_scheduled_syncs.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0001_initial.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0002_pvepluginsettings.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0003_netboxmodel_timestamps.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0004_model_verbose_names.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0005_align_netboxmodel_timestamps.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0006_pbsserverconfig_and_more.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0007_rename_verbose_names_to_proxmox_sync.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0008_pbsserverconfig_sync_schedule.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0009_add_every_3h_schedule.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0010_pvedriftevent.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0011_pveclusterconfig_notify_on_sync.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0012_pvedriftevent_tags_and_index_renames.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0013_pluginsettings_log_retention.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0014_vmprovisioninglog.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0015_pvevmtasklog.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0016_pvevmtasklog_status_textfield.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/__init__.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/models.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/navigation.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/signals.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/sync/__init__.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/sync/config_bridge.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/sync/engine.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/tables.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/tasks.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/template_content.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/dashboard.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/inc/vm_sync_button.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/inc/vm_task_log_panel.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/pbsserverconfig.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/pvebackupstatus.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/pveclusterconfig.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/pvedriftevent.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/pvesyncjob.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/pvevmtasklog_list.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/pvewebhookevent.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/settings.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/vm_provisioning_combined.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/vm_provisioning_log.html +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templatetags/__init__.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templatetags/pve_sync_tags.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/urls.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/utils.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/view_helpers.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/views.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/setup.cfg +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/state_db.py +0 -0
- {proxsync-2.0.8 → proxsync-2.1.0}/tests/test_plugin_contracts.py +0 -0
|
@@ -177,12 +177,32 @@ class OptimizedPVEToNetBoxSync:
|
|
|
177
177
|
🔌 MAC 位址: {mac_address}
|
|
178
178
|
❌ 錯誤: {error_message}
|
|
179
179
|
|
|
180
|
-
⚠️ 該網卡的 MAC 位址與另一台已同步的 VM 重複(通常是複製 VM 時未重新產生 MAC
|
|
181
|
-
IP 位址仍會照常同步,但 MAC 關聯需要手動處理
|
|
180
|
+
⚠️ 該網卡的 MAC 位址與另一台已同步的 VM 重複(通常是複製 VM 時未重新產生 MAC)
|
|
182
181
|
"""
|
|
183
182
|
self.send_telegram_notification(message)
|
|
184
183
|
print(f"📧 已發送 MAC 衝突通知: {vm_name} - {mac_address}")
|
|
185
184
|
|
|
185
|
+
def log_vm_sync_error(self, vm_name: str, vm_id: int, error_message: str):
|
|
186
|
+
error_info = {
|
|
187
|
+
'timestamp': time.strftime("%Y-%m-%d %H:%M:%S"),
|
|
188
|
+
'vm_name': vm_name,
|
|
189
|
+
'ip_address': f"vmid {vm_id}",
|
|
190
|
+
'error': error_message
|
|
191
|
+
}
|
|
192
|
+
self.error_log.append(error_info)
|
|
193
|
+
message = f"""
|
|
194
|
+
🚨 <b>PVE-NetBox 同步 VM 失敗警告</b>
|
|
195
|
+
|
|
196
|
+
📅 時間: {error_info['timestamp']}
|
|
197
|
+
🖥️ 虛擬機: {vm_name} (vmid {vm_id})
|
|
198
|
+
🔀 叢集: {self.cluster_name}
|
|
199
|
+
❌ 錯誤: {error_message}
|
|
200
|
+
|
|
201
|
+
⚠️ 此 VM 本次同步未完成,下次同步會自動重試
|
|
202
|
+
"""
|
|
203
|
+
self.send_telegram_notification(message)
|
|
204
|
+
print(f"📧 已發送 VM 同步失敗通知: {vm_name} - {error_message}")
|
|
205
|
+
|
|
186
206
|
def send_enhanced_summary(self):
|
|
187
207
|
if not hasattr(self, 'stats'):
|
|
188
208
|
return
|
|
@@ -657,7 +677,8 @@ class OptimizedPVEToNetBoxSync:
|
|
|
657
677
|
return None
|
|
658
678
|
|
|
659
679
|
# ---------- IP 分配(改進版) ----------
|
|
660
|
-
def assign_ip_to_interface(self, interface, ip_address: str, dns_name: str = None,
|
|
680
|
+
def assign_ip_to_interface(self, interface, ip_address: str, dns_name: str = None,
|
|
681
|
+
is_vm_interface: bool = False, owner_name: str = None) -> Optional[Any]:
|
|
661
682
|
try:
|
|
662
683
|
if '/' not in ip_address:
|
|
663
684
|
ip_with_prefix = f"{ip_address}/24"
|
|
@@ -705,16 +726,24 @@ class OptimizedPVEToNetBoxSync:
|
|
|
705
726
|
except Exception as e:
|
|
706
727
|
error_msg = str(e)
|
|
707
728
|
print(f" 分配 IP 位址失敗 {ip_address}: {error_msg}")
|
|
708
|
-
|
|
709
|
-
|
|
729
|
+
# Any IP assignment failure needs a human to look at it, not just the
|
|
730
|
+
# one specific "reassign primary IP" error — otherwise conflicts with a
|
|
731
|
+
# different underlying cause (VRF/prefix validation, overlapping range,
|
|
732
|
+
# etc.) fail silently with nothing but a stdout line.
|
|
733
|
+
owner = owner_name or "Unknown"
|
|
734
|
+
if owner == "Unknown":
|
|
710
735
|
try:
|
|
711
736
|
if is_vm_interface:
|
|
712
737
|
vm_info = self.nb_api.virtualization.interfaces.get(interface.id)
|
|
713
738
|
if vm_info and vm_info.virtual_machine:
|
|
714
|
-
|
|
715
|
-
|
|
739
|
+
owner = vm_info.virtual_machine.name
|
|
740
|
+
else:
|
|
741
|
+
dev_info = self.nb_api.dcim.interfaces.get(interface.id)
|
|
742
|
+
if dev_info and dev_info.device:
|
|
743
|
+
owner = dev_info.device.name
|
|
744
|
+
except Exception:
|
|
716
745
|
pass
|
|
717
|
-
|
|
746
|
+
self.log_ip_conflict_error(owner, ip_address, error_msg)
|
|
718
747
|
return None
|
|
719
748
|
|
|
720
749
|
# ---------- 節點網路介面同步 ----------
|
|
@@ -770,7 +799,8 @@ class OptimizedPVEToNetBoxSync:
|
|
|
770
799
|
continue
|
|
771
800
|
cidr = iface_data.get('cidr')
|
|
772
801
|
if cidr:
|
|
773
|
-
self.assign_ip_to_interface(nb_iface, cidr, dns_name=f"{node_name}.local",
|
|
802
|
+
self.assign_ip_to_interface(nb_iface, cidr, dns_name=f"{node_name}.local",
|
|
803
|
+
is_vm_interface=False, owner_name=device.name)
|
|
774
804
|
|
|
775
805
|
# ---------- 節點同步 ----------
|
|
776
806
|
def sync_pve_nodes_to_netbox(self) -> Tuple[bool, Dict[str, Any], Dict]:
|
|
@@ -1046,7 +1076,8 @@ class OptimizedPVEToNetBoxSync:
|
|
|
1046
1076
|
prefix_len = ip_info['prefix']
|
|
1047
1077
|
full_addr = f"{ip_addr}/{prefix_len}"
|
|
1048
1078
|
ip_obj = self.assign_ip_to_interface(
|
|
1049
|
-
vm_interface, full_addr, f"{vm.name}.local",
|
|
1079
|
+
vm_interface, full_addr, f"{vm.name}.local",
|
|
1080
|
+
is_vm_interface=True, owner_name=vm.name
|
|
1050
1081
|
)
|
|
1051
1082
|
if ip_obj and not primary_ip:
|
|
1052
1083
|
primary_ip = ip_obj
|
|
@@ -1770,13 +1801,29 @@ class OptimizedPVEToNetBoxSync:
|
|
|
1770
1801
|
print(f" ✓ VM {original_vm_name} 電源狀態更新: {current_status} → {status}")
|
|
1771
1802
|
except Exception as e:
|
|
1772
1803
|
print(f" ✗ 更新 VM {original_vm_name} 關聯失敗: {e}")
|
|
1804
|
+
# MAC/IP 衝突檢查不受增量同步影響:即使 PVE 配置沒變,仍要每次
|
|
1805
|
+
# 重新檢查介面的 MAC/IP 位址,衝突就再次通知,而不是只在第一次配置
|
|
1806
|
+
# 變更時才發現。process_vm_interfaces 本身是冪等的(已存在的 MAC/IP
|
|
1807
|
+
# 不會重複建立),所以在跳過分支呼叫它不會產生多餘的 NetBox 寫入。
|
|
1808
|
+
_, skip_primary_ip, _ = self.process_vm_interfaces(
|
|
1809
|
+
cached_vm, vm_config, agent_interfaces, mac_to_interface, device
|
|
1810
|
+
)
|
|
1811
|
+
if skip_primary_ip:
|
|
1812
|
+
try:
|
|
1813
|
+
current_primary_ip4_id = getattr(getattr(cached_vm, 'primary_ip4', None), 'id', None)
|
|
1814
|
+
if current_primary_ip4_id != skip_primary_ip.id:
|
|
1815
|
+
cached_vm.primary_ip4 = skip_primary_ip.id
|
|
1816
|
+
cached_vm.save()
|
|
1817
|
+
except Exception as e:
|
|
1818
|
+
print(f" 設定 VM 主 IP 失敗: {e}")
|
|
1773
1819
|
print(f" ℹ️ VM {original_vm_name} 無配置變更,跳過同步")
|
|
1774
1820
|
if self.state_db:
|
|
1775
1821
|
try:
|
|
1776
1822
|
config_hash = compute_config_hash(vm_config, tag_names, network_interfaces)
|
|
1777
1823
|
memory = int(vm_config.get('memory', 0))
|
|
1778
1824
|
vcpus_save = int(vm_config.get('vcpus', vcpus))
|
|
1779
|
-
cur_ip = getattr(
|
|
1825
|
+
cur_ip = getattr(skip_primary_ip, 'address', None) \
|
|
1826
|
+
or getattr(getattr(cached_vm, 'primary_ip4', None), 'address', None) or ''
|
|
1780
1827
|
self.state_db.save_vm_config_snapshot(
|
|
1781
1828
|
vm_id=int(vm_id), cluster_name=self.cluster_name, config_hash=config_hash,
|
|
1782
1829
|
memory=memory, vcpus=vcpus_save, tags=tag_names,
|
|
@@ -1901,6 +1948,7 @@ class OptimizedPVEToNetBoxSync:
|
|
|
1901
1948
|
except Exception as e:
|
|
1902
1949
|
error_msg = str(e)
|
|
1903
1950
|
print(f" 處理虛擬機失敗: {error_msg}")
|
|
1951
|
+
self.log_vm_sync_error(original_vm_name, int(vm_id), error_msg)
|
|
1904
1952
|
# 即使 NetBox 同步失敗,仍需更新 snapshot 的非 hash 欄位(vm_name, node,
|
|
1905
1953
|
# disk_summary, description, tags, memory, vcpus),並保留舊 config_hash,
|
|
1906
1954
|
# 使下一輪不重複發送漂移通知,同時 should_sync_vm() 仍會返回 True 繼續重試。
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/management/commands/run_scheduled_syncs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0005_align_netboxmodel_timestamps.py
RENAMED
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0006_pbsserverconfig_and_more.py
RENAMED
|
File without changes
|
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0008_pbsserverconfig_sync_schedule.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0011_pveclusterconfig_notify_on_sync.py
RENAMED
|
File without changes
|
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0013_pluginsettings_log_retention.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/migrations/0016_pvevmtasklog_status_textfield.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/inc/vm_sync_button.html
RENAMED
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/inc/vm_task_log_panel.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/vm_provisioning_combined.html
RENAMED
|
File without changes
|
{proxsync-2.0.8 → proxsync-2.1.0}/pve_sync_plugin/templates/pve_sync/vm_provisioning_log.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|