From 6d584cc06382e6b3a3c689f956c29647eed684b1 Mon Sep 17 00:00:00 2001 From: Guilherme Werner Date: Sun, 13 Apr 2025 11:10:45 -0300 Subject: [PATCH] Create basic api client --- .env.example | 3 + .gitattributes | 1 + .gitignore | 12 ++ Directory.Packages.props | 10 + LICENSE.txt | 21 +++ ProxmoxSharp.sln | 26 +++ README.md | 21 ++- src/Tribufu.Proxmox.Tests/Program.cs | 27 +++ .../Tribufu.Proxmox.Tests.csproj | 14 ++ .../Interfaces/IProxmoxClient.cs | 38 ++++ .../Models/ProxmoxBalloonInfo.cs | 37 ++++ .../Models/ProxmoxBlockStats.cs | 107 +++++++++++ src/Tribufu.Proxmox/Models/ProxmoxBootInfo.cs | 16 ++ .../Models/ProxmoxContainer.cs | 61 ++++++ src/Tribufu.Proxmox/Models/ProxmoxCpuInfo.cs | 34 ++++ .../Models/ProxmoxHighAvailability.cs | 13 ++ .../Models/ProxmoxKernelInfo.cs | 22 +++ src/Tribufu.Proxmox/Models/ProxmoxKsmInfo.cs | 13 ++ .../Models/ProxmoxMemoryStats.cs | 19 ++ src/Tribufu.Proxmox/Models/ProxmoxNicStats.cs | 16 ++ src/Tribufu.Proxmox/Models/ProxmoxNode.cs | 49 +++++ .../Models/ProxmoxNodeStatus.cs | 52 +++++ .../Models/ProxmoxStorageStats.cs | 22 +++ .../Models/ProxmoxSupportInfo.cs | 34 ++++ .../Models/ProxmoxVirtualMachine.cs | 55 ++++++ .../Models/ProxmoxVirtualMachineStatus.cs | 89 +++++++++ src/Tribufu.Proxmox/ProxmoxClient.cs | 177 ++++++++++++++++++ src/Tribufu.Proxmox/README.md | 1 + src/Tribufu.Proxmox/Tribufu.Proxmox.csproj | 19 ++ 29 files changed, 1008 insertions(+), 1 deletion(-) create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Directory.Packages.props create mode 100644 LICENSE.txt create mode 100644 ProxmoxSharp.sln create mode 100644 src/Tribufu.Proxmox.Tests/Program.cs create mode 100644 src/Tribufu.Proxmox.Tests/Tribufu.Proxmox.Tests.csproj create mode 100644 src/Tribufu.Proxmox/Interfaces/IProxmoxClient.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxBalloonInfo.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxBlockStats.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxBootInfo.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxContainer.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxCpuInfo.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxHighAvailability.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxKernelInfo.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxKsmInfo.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxMemoryStats.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxNicStats.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxNode.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxNodeStatus.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxStorageStats.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxSupportInfo.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxVirtualMachine.cs create mode 100644 src/Tribufu.Proxmox/Models/ProxmoxVirtualMachineStatus.cs create mode 100644 src/Tribufu.Proxmox/ProxmoxClient.cs create mode 120000 src/Tribufu.Proxmox/README.md create mode 100644 src/Tribufu.Proxmox/Tribufu.Proxmox.csproj diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..18ff42a --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +PROXMOX_CLUSTER_URL="" +PROXMOX_TOKEN_ID="" +PROXMOX_TOKEN_SECRET="" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..921dd89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.vs/ +.vscode/* +bin/ +obj/ + +!.vscode/settings.json +.DS_Store +.DS_Store +.env +*.filters +*.user +desktop.ini diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..d7cf64a --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,10 @@ + + + true + + + + + + + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..292d951 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Tribufu. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ProxmoxSharp.sln b/ProxmoxSharp.sln new file mode 100644 index 0000000..86a497a --- /dev/null +++ b/ProxmoxSharp.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Proxmox", "src\Tribufu.Proxmox\Tribufu.Proxmox.csproj", "{A2756937-8D07-4164-9CB7-2C94E9FE0A08}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tribufu.Proxmox.Tests", "src\Tribufu.Proxmox.Tests\Tribufu.Proxmox.Tests.csproj", "{0A59E867-1A89-4E07-ABE7-E7DC2FEE9317}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2756937-8D07-4164-9CB7-2C94E9FE0A08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2756937-8D07-4164-9CB7-2C94E9FE0A08}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2756937-8D07-4164-9CB7-2C94E9FE0A08}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2756937-8D07-4164-9CB7-2C94E9FE0A08}.Release|Any CPU.Build.0 = Release|Any CPU + {0A59E867-1A89-4E07-ABE7-E7DC2FEE9317}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A59E867-1A89-4E07-ABE7-E7DC2FEE9317}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A59E867-1A89-4E07-ABE7-E7DC2FEE9317}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A59E867-1A89-4E07-ABE7-E7DC2FEE9317}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index 2494351..e7923c6 100644 --- a/README.md +++ b/README.md @@ -1 +1,20 @@ -# proxmox-sharp \ No newline at end of file +# Proxmox Sharp + +Proxmox C# API Client. + +[![MIT License][mit-badge]][mit-url] +[![Discord Chat][discord-badge]][discord-url] + +[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg +[mit-url]: https://github.com/tribufu/proxmox-shar/blob/main/LICENSE.txt +[discord-badge]: https://img.shields.io/discord/276504514616623104.svg?logo=discord&style=flat-square +[discord-url]: https://www.tribufu.com/discord + +[Website](https://www.tribufu.com) | +[Discord](https://www.tribufu.com/discord) + +## License + +This project is licensed under the [MIT License]. + +[MIT License]: https://github.com/tribufu/proxmox-sharp/blob/main/LICENSE.txt diff --git a/src/Tribufu.Proxmox.Tests/Program.cs b/src/Tribufu.Proxmox.Tests/Program.cs new file mode 100644 index 0000000..8cb798a --- /dev/null +++ b/src/Tribufu.Proxmox.Tests/Program.cs @@ -0,0 +1,27 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: UNLICENSED + +using dotenv.net; + +namespace Tribufu.Proxmox.Tests +{ + public static class Program + { + public static async Task Main(string[] args) + { + DotEnv.Load(new DotEnvOptions(ignoreExceptions: true, envFilePaths: [".env", "../../.env"])); + + var clusterUrl = Environment.GetEnvironmentVariable("PROXMOX_CLUSTER_URL"); + var tokenId = Environment.GetEnvironmentVariable("PROXMOX_TOKEN_ID"); + var tokenSecret = Environment.GetEnvironmentVariable("PROXMOX_TOKEN_SECRET"); + + var proxmox = new ProxmoxClient(clusterUrl, tokenId, tokenSecret); + + var nodes = await proxmox.ListNodesAsync(); + foreach (var node in nodes) + { + Console.WriteLine($"- {node.Name}"); + } + } + } +} diff --git a/src/Tribufu.Proxmox.Tests/Tribufu.Proxmox.Tests.csproj b/src/Tribufu.Proxmox.Tests/Tribufu.Proxmox.Tests.csproj new file mode 100644 index 0000000..f24d82e --- /dev/null +++ b/src/Tribufu.Proxmox.Tests/Tribufu.Proxmox.Tests.csproj @@ -0,0 +1,14 @@ + + + Exe + net8.0 + enable + enable + + + + + + + + diff --git a/src/Tribufu.Proxmox/Interfaces/IProxmoxClient.cs b/src/Tribufu.Proxmox/Interfaces/IProxmoxClient.cs new file mode 100644 index 0000000..cc05ad2 --- /dev/null +++ b/src/Tribufu.Proxmox/Interfaces/IProxmoxClient.cs @@ -0,0 +1,38 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using System.Collections.Generic; +using System.Threading.Tasks; +using Tribufu.Proxmox.Models; + +namespace Tribufu.Proxmox.Interfaces +{ + public interface IProxmoxClient + { + Task LoginAsync(string username, string password); + + Task> ListNodesAsync(); + + Task GetNodeStatusAsync(string node); + + Task> ListVirtualMachinesAsync(string node); + + Task GetVirtualMachineStatusAsync(string node, int vmid); + + Task StartVirtualMachineAsync(string node, int vmid); + + Task RebootVirtualMachineAsync(string node, int vmid); + + Task ResetVirtualMachineAsync(string node, int vmid); + + Task SuspendVirtualMachineAsync(string node, int vmid); + + Task ResumeVirtualMachineAsync(string node, int vmid); + + Task ShutdownVirtualMachineAsync(string node, int vmid); + + Task StopVirtualMachineAsync(string node, int vmid); + + Task> ListContainersAsync(string node); + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxBalloonInfo.cs b/src/Tribufu.Proxmox/Models/ProxmoxBalloonInfo.cs new file mode 100644 index 0000000..c222377 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxBalloonInfo.cs @@ -0,0 +1,37 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxBalloonInfo + { + [JsonProperty("major_page_faults")] + public long MajorPageFaults { get; set; } + + [JsonProperty("total_mem")] + public long TotalMem { get; set; } + + [JsonProperty("mem_swapped_out")] + public long MemSwappedOut { get; set; } + + [JsonProperty("minor_page_faults")] + public long MinorPageFaults { get; set; } + + [JsonProperty("mem_swapped_in")] + public long MemSwappedIn { get; set; } + + [JsonProperty("max_mem")] + public long MaxMem { get; set; } + + [JsonProperty("last_update")] + public long LastUpdate { get; set; } + + [JsonProperty("actual")] + public long Actual { get; set; } + + [JsonProperty("free_mem")] + public long FreeMem { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxBlockStats.cs b/src/Tribufu.Proxmox/Models/ProxmoxBlockStats.cs new file mode 100644 index 0000000..da9599d --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxBlockStats.cs @@ -0,0 +1,107 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxBlockStats + { + [JsonProperty("rd_operations")] + public long RdOperations { get; set; } + + [JsonProperty("wr_total_time_ns")] + public long WrTotalTimeNs { get; set; } + + [JsonProperty("wr_highest_offset")] + public long WrHighestOffset { get; set; } + + [JsonProperty("flush_operations")] + public long FlushOperations { get; set; } + + [JsonProperty("failed_zone_append_operations")] + public long FailedZoneAppendOperations { get; set; } + + [JsonProperty("invalid_wr_operations")] + public long InvalidWrOperations { get; set; } + + [JsonProperty("invalid_rd_operations")] + public long InvalidRdOperations { get; set; } + + [JsonProperty("rd_bytes")] + public long RdBytes { get; set; } + + [JsonProperty("invalid_zone_append_operations")] + public long InvalidZoneAppendOperations { get; set; } + + [JsonProperty("account_invalid")] + public bool AccountInvalid { get; set; } + + [JsonProperty("failed_rd_operations")] + public long FailedRdOperations { get; set; } + + [JsonProperty("timed_stats")] + public List TimedStats { get; set; } + + [JsonProperty("zone_append_operations")] + public long ZoneAppendOperations { get; set; } + + [JsonProperty("failed_flush_operations")] + public long FailedFlushOperations { get; set; } + + [JsonProperty("zone_append_merged")] + public long ZoneAppendMerged { get; set; } + + [JsonProperty("flush_total_time_ns")] + public long FlushTotalTimeNs { get; set; } + + [JsonProperty("invalid_unmap_operations")] + public long InvalidUnmapOperations { get; set; } + + [JsonProperty("invalid_flush_operations")] + public long InvalidFlushOperations { get; set; } + + [JsonProperty("rd_merged")] + public long RdMerged { get; set; } + + [JsonProperty("unmap_bytes")] + public long UnmapBytes { get; set; } + + [JsonProperty("unmap_operations")] + public long UnmapOperations { get; set; } + + [JsonProperty("zone_append_bytes")] + public long ZoneAppendBytes { get; set; } + + [JsonProperty("wr_operations")] + public long WrOperations { get; set; } + + [JsonProperty("rd_total_time_ns")] + public long RdTotalTimeNs { get; set; } + + [JsonProperty("wr_bytes")] + public long WrBytes { get; set; } + + [JsonProperty("zone_append_total_time_ns")] + public long ZoneAppendTotalTimeNs { get; set; } + + [JsonProperty("account_failed")] + public bool AccountFailed { get; set; } + + [JsonProperty("failed_unmap_operations")] + public long FailedUnmapOperations { get; set; } + + [JsonProperty("unmap_merged")] + public long UnmapMerged { get; set; } + + [JsonProperty("unmap_total_time_ns")] + public long UnmapTotalTimeNs { get; set; } + + [JsonProperty("wr_merged")] + public long WrMerged { get; set; } + + [JsonProperty("idle_time_ns")] + public long? IdleTimeNs { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxBootInfo.cs b/src/Tribufu.Proxmox/Models/ProxmoxBootInfo.cs new file mode 100644 index 0000000..6634b70 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxBootInfo.cs @@ -0,0 +1,16 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxBootInfo + { + [JsonProperty("secureboot")] + public int SecureBoot { get; set; } + + [JsonProperty("mode")] + public string Mode { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxContainer.cs b/src/Tribufu.Proxmox/Models/ProxmoxContainer.cs new file mode 100644 index 0000000..01c5e15 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxContainer.cs @@ -0,0 +1,61 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxContainer + { + [JsonProperty("vmid")] + public int VmId { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("uptime")] + public long Uptime { get; set; } + + [JsonProperty("cpu")] + public double Cpu { get; set; } + + [JsonProperty("cpus")] + public int CpuCount { get; set; } + + [JsonProperty("mem")] + public long MemoryUsed { get; set; } + + [JsonProperty("maxmem")] + public long MemoryTotal { get; set; } + + [JsonProperty("swap")] + public long SwapUsed { get; set; } + + [JsonProperty("maxswap")] + public long SwapTotal { get; set; } + + [JsonProperty("disk")] + public long DiskUsed { get; set; } + + [JsonProperty("maxdisk")] + public long DiskTotal { get; set; } + + [JsonProperty("diskread")] + public long DiskRead { get; set; } + + [JsonProperty("diskwrite")] + public long DiskWrite { get; set; } + + [JsonProperty("netin")] + public long NetworkIn { get; set; } + + [JsonProperty("netout")] + public long NetworkOut { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxCpuInfo.cs b/src/Tribufu.Proxmox/Models/ProxmoxCpuInfo.cs new file mode 100644 index 0000000..ebe1bb8 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxCpuInfo.cs @@ -0,0 +1,34 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxCpuInfo + { + [JsonProperty("cores")] + public int Cores { get; set; } + + [JsonProperty("mhz")] + public string MHz { get; set; } + + [JsonProperty("cpus")] + public int Cpus { get; set; } + + [JsonProperty("sockets")] + public int Sockets { get; set; } + + [JsonProperty("model")] + public string Model { get; set; } + + [JsonProperty("flags")] + public string Flags { get; set; } + + [JsonProperty("user_hz")] + public int UserHz { get; set; } + + [JsonProperty("hvm")] + public string Hvm { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxHighAvailability.cs b/src/Tribufu.Proxmox/Models/ProxmoxHighAvailability.cs new file mode 100644 index 0000000..96945c6 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxHighAvailability.cs @@ -0,0 +1,13 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxHighAvailability + { + [JsonProperty("managed")] + public int Managed { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxKernelInfo.cs b/src/Tribufu.Proxmox/Models/ProxmoxKernelInfo.cs new file mode 100644 index 0000000..9695a45 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxKernelInfo.cs @@ -0,0 +1,22 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxKernelInfo + { + [JsonProperty("version")] + public string Version { get; set; } + + [JsonProperty("sysname")] + public string SysName { get; set; } + + [JsonProperty("release")] + public string Release { get; set; } + + [JsonProperty("machine")] + public string Machine { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxKsmInfo.cs b/src/Tribufu.Proxmox/Models/ProxmoxKsmInfo.cs new file mode 100644 index 0000000..f375c43 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxKsmInfo.cs @@ -0,0 +1,13 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxKsmInfo + { + [JsonProperty("shared")] + public long Shared { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxMemoryStats.cs b/src/Tribufu.Proxmox/Models/ProxmoxMemoryStats.cs new file mode 100644 index 0000000..6fbe4db --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxMemoryStats.cs @@ -0,0 +1,19 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxMemoryStats + { + [JsonProperty("total")] + public long Total { get; set; } + + [JsonProperty("used")] + public long Used { get; set; } + + [JsonProperty("free")] + public long Free { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxNicStats.cs b/src/Tribufu.Proxmox/Models/ProxmoxNicStats.cs new file mode 100644 index 0000000..bd1e89f --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxNicStats.cs @@ -0,0 +1,16 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxNicStats + { + [JsonProperty("netin")] + public long NetIn { get; set; } + + [JsonProperty("netout")] + public long NetOut { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxNode.cs b/src/Tribufu.Proxmox/Models/ProxmoxNode.cs new file mode 100644 index 0000000..f8f3a11 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxNode.cs @@ -0,0 +1,49 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxNode + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("node")] + public string Name { get; set; } + + [JsonProperty("uptime")] + public long Uptime { get; set; } + + [JsonProperty("ssl_fingerprint")] + public string SslFingerprint { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("cpu")] + public double Cpu { get; set; } + + [JsonProperty("maxcpu")] + public int MaxCpu { get; set; } + + [JsonProperty("disk")] + public long Disk { get; set; } + + [JsonProperty("maxmem")] + public long MaxMemory { get; set; } + + [JsonProperty("mem")] + public long Memory { get; set; } + + [JsonProperty("maxdisk")] + public long MaxDisk { get; set; } + + [JsonProperty("level")] + public string Level { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxNodeStatus.cs b/src/Tribufu.Proxmox/Models/ProxmoxNodeStatus.cs new file mode 100644 index 0000000..5afff13 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxNodeStatus.cs @@ -0,0 +1,52 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxNodeStatus + { + [JsonProperty("boot-info")] + public ProxmoxBootInfo BootInfo { get; set; } + + [JsonProperty("swap")] + public ProxmoxMemoryStats Swap { get; set; } + + [JsonProperty("rootfs")] + public ProxmoxStorageStats RootFs { get; set; } + + [JsonProperty("pveversion")] + public string PveVersion { get; set; } + + [JsonProperty("cpuinfo")] + public ProxmoxCpuInfo CpuInfo { get; set; } + + [JsonProperty("memory")] + public ProxmoxMemoryStats Memory { get; set; } + + [JsonProperty("ksm")] + public ProxmoxKsmInfo Ksm { get; set; } + + [JsonProperty("idle")] + public double Idle { get; set; } + + [JsonProperty("cpu")] + public double Cpu { get; set; } + + [JsonProperty("loadavg")] + public string[] LoadAverage { get; set; } + + [JsonProperty("current-kernel")] + public ProxmoxKernelInfo CurrentKernel { get; set; } + + [JsonProperty("kversion")] + public string KernelVersion { get; set; } + + [JsonProperty("wait")] + public double Wait { get; set; } + + [JsonProperty("uptime")] + public long Uptime { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxStorageStats.cs b/src/Tribufu.Proxmox/Models/ProxmoxStorageStats.cs new file mode 100644 index 0000000..918c117 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxStorageStats.cs @@ -0,0 +1,22 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxStorageStats + { + [JsonProperty("total")] + public long Total { get; set; } + + [JsonProperty("avail")] + public long Available { get; set; } + + [JsonProperty("used")] + public long Used { get; set; } + + [JsonProperty("free")] + public long Free { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxSupportInfo.cs b/src/Tribufu.Proxmox/Models/ProxmoxSupportInfo.cs new file mode 100644 index 0000000..6d03c81 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxSupportInfo.cs @@ -0,0 +1,34 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxSupportInfo + { + [JsonProperty("backup-max-workers")] + public bool BackupMaxWorkers { get; set; } + + [JsonProperty("pbs-library-version")] + public string PbsLibraryVersion { get; set; } + + [JsonProperty("pbs-masterkey")] + public bool PbsMasterkey { get; set; } + + [JsonProperty("backup-fleecing")] + public bool BackupFleecing { get; set; } + + [JsonProperty("query-bitmap-info")] + public bool QueryBitmapInfo { get; set; } + + [JsonProperty("pbs-dirty-bitmap")] + public bool PbsDirtyBitmap { get; set; } + + [JsonProperty("pbs-dirty-bitmap-savevm")] + public bool PbsDirtyBitmapSaveVm { get; set; } + + [JsonProperty("pbs-dirty-bitmap-migration")] + public bool PbsDirtyBitmapMigration { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxVirtualMachine.cs b/src/Tribufu.Proxmox/Models/ProxmoxVirtualMachine.cs new file mode 100644 index 0000000..aabb31d --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxVirtualMachine.cs @@ -0,0 +1,55 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxVirtualMachine + { + [JsonProperty("vmid")] + public int VmId { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("uptime")] + public long Uptime { get; set; } + + [JsonProperty("cpu")] + public double Cpu { get; set; } + + [JsonProperty("cpus")] + public int CpuCount { get; set; } + + [JsonProperty("mem")] + public long MemoryUsed { get; set; } + + [JsonProperty("maxmem")] + public long MemoryTotal { get; set; } + + [JsonProperty("disk")] + public long DiskUsed { get; set; } + + [JsonProperty("maxdisk")] + public long DiskTotal { get; set; } + + [JsonProperty("diskread")] + public long DiskRead { get; set; } + + [JsonProperty("diskwrite")] + public long DiskWrite { get; set; } + + [JsonProperty("netin")] + public long NetworkIn { get; set; } + + [JsonProperty("netout")] + public long NetworkOut { get; set; } + + [JsonProperty("pid")] + public int Pid { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/Models/ProxmoxVirtualMachineStatus.cs b/src/Tribufu.Proxmox/Models/ProxmoxVirtualMachineStatus.cs new file mode 100644 index 0000000..45437f5 --- /dev/null +++ b/src/Tribufu.Proxmox/Models/ProxmoxVirtualMachineStatus.cs @@ -0,0 +1,89 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Tribufu.Proxmox.Models +{ + public class ProxmoxVirtualMachineStatus + { + [JsonProperty("vmid")] + public int VmId { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("nics")] + public Dictionary Nics { get; set; } + + [JsonProperty("blockstat")] + public Dictionary BlockStat { get; set; } + + [JsonProperty("maxdisk")] + public long MaxDisk { get; set; } + + [JsonProperty("cpu")] + public double Cpu { get; set; } + + [JsonProperty("mem")] + public long Mem { get; set; } + + [JsonProperty("netin")] + public long NetIn { get; set; } + + [JsonProperty("pid")] + public int Pid { get; set; } + + [JsonProperty("qmpstatus")] + public string QmpStatus { get; set; } + + [JsonProperty("uptime")] + public long Uptime { get; set; } + + [JsonProperty("agent")] + public int Agent { get; set; } + + [JsonProperty("running-qemu")] + public string RunningQemu { get; set; } + + [JsonProperty("proxmox-support")] + public ProxmoxSupportInfo ProxmoxSupport { get; set; } + + [JsonProperty("maxmem")] + public long MaxMem { get; set; } + + [JsonProperty("disk")] + public double Disk { get; set; } + + [JsonProperty("ballooninfo")] + public ProxmoxBalloonInfo BalloonInfo { get; set; } + + [JsonProperty("netout")] + public long NetOut { get; set; } + + [JsonProperty("freemem")] + public long FreeMem { get; set; } + + [JsonProperty("diskread")] + public long DiskRead { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("diskwrite")] + public long DiskWrite { get; set; } + + [JsonProperty("running-machine")] + public string RunningMachine { get; set; } + + [JsonProperty("balloon")] + public long Balloon { get; set; } + + [JsonProperty("ha")] + public ProxmoxHighAvailability Ha { get; set; } + + [JsonProperty("cpus")] + public int Cpus { get; set; } + } +} diff --git a/src/Tribufu.Proxmox/ProxmoxClient.cs b/src/Tribufu.Proxmox/ProxmoxClient.cs new file mode 100644 index 0000000..60c10e0 --- /dev/null +++ b/src/Tribufu.Proxmox/ProxmoxClient.cs @@ -0,0 +1,177 @@ +// Copyright (c) Tribufu. All Rights Reserved. +// SPDX-License-Identifier: MIT + +using Newtonsoft.Json.Linq; +using RestSharp; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Tribufu.Proxmox.Interfaces; +using Tribufu.Proxmox.Models; + +namespace Tribufu.Proxmox +{ + public class ProxmoxClient : IProxmoxClient + { + private readonly RestClient _client; + + private string _ticket; + + private string _csrfToken; + + public ProxmoxClient(string baseUrl, string tokenId, string secret) + { + var options = new RestClientOptions(baseUrl.TrimEnd('/') + "/api2/json/") + { + RemoteCertificateValidationCallback = (sender, cert, chain, sslPolicyErrors) => true + }; + + _client = new RestClient(options); + + if (!string.IsNullOrEmpty(tokenId) && !string.IsNullOrEmpty(secret)) + { + _client.AddDefaultHeader("Authorization", $"PVEAPIToken={tokenId}={secret}"); + } + } + + public async Task LoginAsync(string username, string password) + { + var request = new RestRequest("access/ticket", Method.Post); + request.AddParameter("username", username); + request.AddParameter("password", password); + + var response = await _client.ExecuteAsync(request); + if (response.IsSuccessful) + { + var json = JObject.Parse(response.Content); + var data = json["data"]; + _ticket = data["ticket"].ToString(); + _csrfToken = data["CSRFPreventionToken"].ToString(); + + _client.AddDefaultHeader("Cookie", $"PVEAuthCookie={_ticket}"); + _client.AddDefaultHeader("CSRFPreventionToken", _csrfToken); + } + + return response.IsSuccessful; + } + + public async Task> ListNodesAsync() + { + var request = new RestRequest($"nodes", Method.Get); + + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessful) + { + throw new Exception($"Failed to list nodes: {response.StatusCode}"); + } + + var json = JObject.Parse(response.Content); + return json["data"].ToObject>(); + } + + public async Task GetNodeStatusAsync(string node) + { + var request = new RestRequest($"nodes/{node}/status", Method.Get); + + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessful) + { + throw new Exception($"Failed to get node {node}: {response.StatusCode}"); + } + + var json = JObject.Parse(response.Content); + return json["data"].ToObject(); + } + + public async Task> ListVirtualMachinesAsync(string node) + { + var request = new RestRequest($"nodes/{node}/qemu", Method.Get); + + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessful) + { + throw new Exception($"Failed to list VMs: {response.StatusCode}"); + } + + var json = JObject.Parse(response.Content); + return json["data"].ToObject>(); + } + + public async Task GetVirtualMachineStatusAsync(string node, int vmid) + { + var request = new RestRequest($"nodes/{node}/qemu/{vmid}/status/current", Method.Get); + + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessful) + { + throw new Exception($"Failed to start VM {vmid}: {response.StatusCode}"); + } + + var json = JObject.Parse(response.Content); + return json["data"].ToObject(); + } + + public async Task StartVirtualMachineAsync(string node, int vmid) + { + var request = new RestRequest($"nodes/{node}/qemu/{vmid}/status/start", Method.Post); + var response = await _client.ExecuteAsync(request); + return response.IsSuccessful; + } + + public async Task RebootVirtualMachineAsync(string node, int vmid) + { + var request = new RestRequest($"nodes/{node}/qemu/{vmid}/status/reboot", Method.Post); + var response = await _client.ExecuteAsync(request); + return response.IsSuccessful; + } + + public async Task ResetVirtualMachineAsync(string node, int vmid) + { + var request = new RestRequest($"nodes/{node}/qemu/{vmid}/status/reset", Method.Post); + var response = await _client.ExecuteAsync(request); + return response.IsSuccessful; + } + + public async Task SuspendVirtualMachineAsync(string node, int vmid) + { + var request = new RestRequest($"nodes/{node}/qemu/{vmid}/status/suspend", Method.Post); + var response = await _client.ExecuteAsync(request); + return response.IsSuccessful; + } + + public async Task ResumeVirtualMachineAsync(string node, int vmid) + { + var request = new RestRequest($"nodes/{node}/qemu/{vmid}/status/resume", Method.Post); + var response = await _client.ExecuteAsync(request); + return response.IsSuccessful; + } + + public async Task ShutdownVirtualMachineAsync(string node, int vmid) + { + var request = new RestRequest($"nodes/{node}/qemu/{vmid}/status/shutdown", Method.Post); + var response = await _client.ExecuteAsync(request); + return response.IsSuccessful; + } + + public async Task StopVirtualMachineAsync(string node, int vmid) + { + var request = new RestRequest($"nodes/{node}/qemu/{vmid}/status/stop", Method.Post); + var response = await _client.ExecuteAsync(request); + return response.IsSuccessful; + } + + public async Task> ListContainersAsync(string node) + { + var request = new RestRequest($"nodes/{node}/lxc", Method.Get); + + var response = await _client.ExecuteAsync(request); + if (!response.IsSuccessful) + { + throw new Exception($"Failed to list LXC containers: {response.StatusCode}"); + } + + var json = JObject.Parse(response.Content); + return json["data"].ToObject>(); + } + } +} diff --git a/src/Tribufu.Proxmox/README.md b/src/Tribufu.Proxmox/README.md new file mode 120000 index 0000000..fe84005 --- /dev/null +++ b/src/Tribufu.Proxmox/README.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/src/Tribufu.Proxmox/Tribufu.Proxmox.csproj b/src/Tribufu.Proxmox/Tribufu.Proxmox.csproj new file mode 100644 index 0000000..6efa116 --- /dev/null +++ b/src/Tribufu.Proxmox/Tribufu.Proxmox.csproj @@ -0,0 +1,19 @@ + + + Tribufu.Proxmox + Proxmox API Client + README.md + + + true + Library + netstandard2.0;net471;net6.0 + + + + + + + + +