1
0
Fork 0
mirror of https://github.com/Corsinvest/cv4pve-pepper.git synced 2025-03-09 15:39:57 +00:00

Proxmox VE 8.2

This commit is contained in:
daniele.corsini@corsinvest.it 2024-05-02 12:20:45 +02:00
parent 732d539ddc
commit f3ebcd0e6a
5 changed files with 134 additions and 153 deletions

19
.vscode/launch.json vendored
View file

@ -1,29 +1,26 @@
{ {
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
// Usare IntelliSense per individuare gli attributi esistenti per il debug C#
// Usa il passaggio del mouse per la descrizione degli attributi esistenti
// Per ulteriori informazioni, visitare https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
"name": ".NET Core Launch (console)", "name": ".NET Core Launch (console)",
"type": "coreclr", "type": "coreclr",
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // Se i framework di destinazione sono stati modificati, assicurarsi di aggiornare il percorso del programma.
"program": "${workspaceFolder}/src/Corsinvest.ProxmoxVE.Pepper/bin/Debug/net6.0/cv4pve-pepper.dll", "program": "${workspaceFolder}/src/Corsinvest.ProxmoxVE.Pepper/bin/Debug/net8.0/cv4pve-pepper.dll",
"args": [ "args": [],
"@TestParm.parm"
],
"cwd": "${workspaceFolder}/src/Corsinvest.ProxmoxVE.Pepper", "cwd": "${workspaceFolder}/src/Corsinvest.ProxmoxVE.Pepper",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console // Per ulteriori informazioni sul campo 'console', vedere https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole", "console": "internalConsole",
"stopAtEntry": false "stopAtEntry": false
}, },
{ {
"name": ".NET Core Attach", "name": ".NET Core Attach",
"type": "coreclr", "type": "coreclr",
"request": "attach", "request": "attach"
"processId": "${command:pickProcess}"
} }
] ]
} }

13
.vscode/tasks.json vendored
View file

@ -7,9 +7,9 @@
"type": "process", "type": "process",
"args": [ "args": [
"build", "build",
"${workspaceFolder}/src/Corsinvest.ProxmoxVE.Pepper/Corsinvest.ProxmoxVE.Pepper.csproj", "${workspaceFolder}/Corsinvest.ProxmoxVE.Pepper.sln",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary;ForceNoAlign"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
@ -19,9 +19,9 @@
"type": "process", "type": "process",
"args": [ "args": [
"publish", "publish",
"${workspaceFolder}/src/Corsinvest.ProxmoxVE.Pepper/Corsinvest.ProxmoxVE.Pepper.csproj", "${workspaceFolder}/Corsinvest.ProxmoxVE.Pepper.sln",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary;ForceNoAlign"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
}, },
@ -32,9 +32,8 @@
"args": [ "args": [
"watch", "watch",
"run", "run",
"${workspaceFolder}/src/Corsinvest.ProxmoxVE.Pepper/Corsinvest.ProxmoxVE.Pepper.csproj", "--project",
"/property:GenerateFullPaths=true", "${workspaceFolder}/Corsinvest.ProxmoxVE.Pepper.sln"
"/consoleloggerparameters:NoSummary"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
} }

View file

@ -3,8 +3,6 @@
[![License](https://img.shields.io/github/license/Corsinvest/cv4pve-pepper.svg)](LICENSE.md) [![License](https://img.shields.io/github/license/Corsinvest/cv4pve-pepper.svg)](LICENSE.md)
```text ```text
Description:
______ _ __ ______ _ __
/ ____/___ __________(_)___ _ _____ _____/ /_ / ____/___ __________(_)___ _ _____ _____/ /_
/ / / __ \/ ___/ ___/ / __ \ | / / _ \/ ___/ __/ / / / __ \/ ___/ ___/ / __ \ | / / _ \/ ___/ __/
@ -12,33 +10,30 @@ Description:
\____/\____/_/ /____/_/_/ /_/|___/\___/____/\__/ \____/\____/_/ /____/_/_/ /_/|___/\___/____/\__/
Launching SPICE on Proxmox VE (Made in Italy) Launching SPICE remote-viewer for Proxmox VE (Made in Italy)
cv4pve-pepper is a part of suite cv4pve. cv4pve-pepper is a part of suite cv4pve.
For more information visit https://www.cv4pve-tools.com For more information visit https://www.corsinvest.it/cv4pve
Usage: Usage:
cv4pve-pepper [command] [options] cv4pve-pepper [options]
Options: Options:
--host <host> (REQUIRED) The host name host[:port],host1[:port],host2[:port]
--api-token <api-token> Api token format 'USER@REALM!TOKENID=UUID'. Require Proxmox VE 6.2 or later --api-token <api-token> Api token format 'USER@REALM!TOKENID=UUID'. Require Proxmox VE 6.2 or later
--username <username> User name <username>@<realm> --username <username> User name <username>@<realm>
--password <password> The password. Specify 'file:path_file' to store password in file. --password <password> The password. Specify 'file:path_file' to store password in file.
--validate-certificate Validate SSL Certificate Proxmox VE node.
--host <host> (REQUIRED) The host name host[:port],host1[:port],host2[:port]
--vmid <vmid> The id or name VM/CT --vmid <vmid> The id or name VM/CT
--proxy <proxy> SPICE proxy server. This can be used by the client to specify the proxy server. All nodes in a cluster runs --proxy <proxy> SPICE proxy server. This can be used by the client to specify the proxy server. All nodes in a cluster runs 'spiceproxy', so it is up to
'spiceproxy', so it is up to the client to choose one. By default, we return the node to connect. If specify the client to choose one. By default, we return the node to connect. If specify http(s)://[host]:[port] then replace proxy option in file
http(s)://[host]:[port] then replace proxy option in file .vv. E.g. for reverse proxy. .vv. E.g. for reverse proxy.
--viewer <viewer> (REQUIRED) Executable SPICE client remote viewer. --viewer <viewer> (REQUIRED) Executable SPICE client remote viewer.
--viewer-options <viewer-options> Send options directly SPICE Viewer (quote value). --viewer-options <viewer-options> Send options directly SPICE Viewer (quote value).
--start-or-resume Run stopped or paused VM --start-or-resume Run stopped or paused VM
--wait-for-startup <wait-for-startup> Wait sec. for startup VM [default: 5] --wait-for-startup <wait-for-startup> Wait sec. for startup VM [default: 5]
--version Show version information --version Show version information
-?, -h, --help Show help and usage information -?, -h, --help Show help and usage information
Commands:
app-check-update Check update application
app-upgrade Upgrade application
``` ```
## Copyright and License ## Copyright and License
@ -48,7 +43,7 @@ For licensing details please visit [LICENSE.md](LICENSE.md)
## Commercial Support ## Commercial Support
This software is part of a suite of tools called cv4pve-tools. If you want commercial support, visit the [site](https://www.cv4pve-tools.com) This software is part of a suite of tools called cv4pve-tools. If you want commercial support, visit the [site](https://www.corisnvest.it/cv4pve)
## Introduction ## Introduction
@ -76,6 +71,7 @@ this software aims to simplify run SPICE client from Proxmox VE using command li
* Use Api token --api-token parameter * Use Api token --api-token parameter
* Send options directly to viewer * Send options directly to viewer
* Execution with file parameter e.g. @FileParameter.parm * Execution with file parameter e.g. @FileParameter.parm
* Validate certificate SSL, default not validate
## Api token ## Api token

View file

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<Version>1.6.1</Version> <Version>1.7.0</Version>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<AssemblyName>cv4pve-pepper</AssemblyName> <AssemblyName>cv4pve-pepper</AssemblyName>
<Company>Corsinvest Srl</Company> <Company>Corsinvest Srl</Company>
<Authors>Daniele Corsini</Authors> <Authors>Daniele Corsini</Authors>
@ -15,13 +15,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<!-- Fix RedHat, Centos,Fedora -->
<RuntimeHostConfigurationOption Include="System.Globalization.Invariant" Value="true" />
<TrimmerRootAssembly Include="System.Net.WebClient" />
<!-- <ProjectReference Include="..\..\..\cv4pve-api-dotnet\src\Corsinvest.ProxmoxVE.Api.Shell\Corsinvest.ProxmoxVE.Api.Shell.csproj" /> --> <!-- <ProjectReference Include="..\..\..\cv4pve-api-dotnet\src\Corsinvest.ProxmoxVE.Api.Shell\Corsinvest.ProxmoxVE.Api.Shell.csproj" /> -->
<PackageReference Include="Corsinvest.ProxmoxVE.Api.Shell" Version="7.3.7" /> <PackageReference Include="Corsinvest.ProxmoxVE.Api.Shell" Version="8.2.0" />
</ItemGroup> </ItemGroup>
<Target Name="SpicNSpan" AfterTargets="Clean"> <Target Name="SpicNSpan" AfterTargets="Clean">

View file

@ -5,143 +5,137 @@
using System; using System;
using System.CommandLine; using System.CommandLine;
using System.CommandLine.Invocation;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Corsinvest.ProxmoxVE.Api.Extension; using Corsinvest.ProxmoxVE.Api.Extension;
using Corsinvest.ProxmoxVE.Api.Extension.Utils; using Corsinvest.ProxmoxVE.Api.Extension.Utils;
using Corsinvest.ProxmoxVE.Api.Shared.Models.Vm; using Corsinvest.ProxmoxVE.Api.Shared.Models.Vm;
using Corsinvest.ProxmoxVE.Api.Shell.Helpers; using Corsinvest.ProxmoxVE.Api.Shell.Helpers;
using Microsoft.Extensions.Logging;
namespace Corsinvest.ProxmoxVE.Pepper var app = ConsoleHelper.CreateApp("cv4pve-pepper", "Launching SPICE remote-viewer for Proxmox VE");
var loggerFactory = ConsoleHelper.CreateLoggerFactory<Program>(app.GetLogLevelFromDebug());
var optVmId = app.VmIdOrNameOption();
var optProxy = app.AddOption<string>("--proxy",
@"SPICE proxy server. This can be used by the client to specify the proxy server." +
" All nodes in a cluster runs 'spiceproxy', so it is up to the client to choose one." +
" By default, we return the node to connect." +
" If specify http(s)://[host]:[port] then replace proxy option in file .vv. E.g. for reverse proxy.");
var optRemoteViewer = app.AddOption<string>("--viewer", "Executable SPICE client remote viewer.")
.AddValidatorExistFile();
optRemoteViewer.IsRequired = true;
var optViewerOptions = app.AddOption<string>("--viewer-options", "Send options directly SPICE Viewer (quote value).");
var optStartOrResume = app.AddOption<bool>("--start-or-resume", "Run stopped or paused VM");
var optWaitForStartup = app.AddOption<int>("--wait-for-startup", "Wait sec. for startup VM");
optWaitForStartup.SetDefaultValue(5);
app.SetHandler(async (ctx) =>
{ {
class Program var client = await app.ClientTryLoginAsync(loggerFactory);
var proxy = ctx.ParseResult.GetValueForOption(optProxy);
if (string.IsNullOrWhiteSpace(proxy)) { proxy = client.Host; }
var vmId = ctx.ParseResult.GetValueForOption(optVmId);
var vm = await client.GetVmAsync(vmId);
if (ctx.ParseResult.GetValueForOption(optStartOrResume) && (vm.IsStopped || vm.IsPaused))
{ {
static async Task<int> Main(string[] args) var status = vm.IsStopped
? VmStatus.Start
: VmStatus.Resume;
if (app.DebugIsActive())
{ {
var app = ConsoleHelper.CreateApp("cv4pve-pepper", "Launching SPICE on Proxmox VE"); await Console.Out.WriteLineAsync($"VM is {(vm.IsStopped ? "stopped" : "paused")}. {status} now!");
var optVmId = app.VmIdOrNameOption(); }
var optProxy = app.AddOption("--proxy", //start VM
@"SPICE proxy server. This can be used by the client to specify the proxy server." + var result = await VmHelper.ChangeStatusVmAsync(client, vm.Node, vm.VmType, vm.VmId, status);
" All nodes in a cluster runs 'spiceproxy', so it is up to the client to choose one." + await client.WaitForTaskToFinishAsync(result, timeout: ctx.ParseResult.GetValueForOption(optWaitForStartup) * 1000);
" By default, we return the node to connect." +
" If specify http(s)://[host]:[port] then replace proxy option in file .vv. E.g. for reverse proxy.");
var optRemoteViewer = app.AddOption("--viewer", "Executable SPICE client remote viewer.").AddValidatorExistFile(); //check VM is running
optRemoteViewer.IsRequired = true; vm = await client.GetVmAsync(vmId);
if (app.DebugIsActive()) { await Console.Out.WriteLineAsync($"VM is {vm.Status}."); }
}
var optViewerOptions = app.AddOption("--viewer-options", "Send options directly SPICE Viewer (quote value)."); var (success, reasonPhrase, content) = await client.Nodes[vm.Node].Qemu[vm.Id].Spiceproxy.GetSpiceFileVVAsync(proxy);
if (success)
var optStartOrResume = app.AddOption<bool>("--start-or-resume", "Run stopped or paused VM"); {
//proxy force
var optWaitForStartup = app.AddOption<int>("--wait-for-startup", "Wait sec. for startup VM"); if (new Regex(@"^(http|https|)://.*$").IsMatch(proxy))
optWaitForStartup.SetDefaultValue(5); {
var lines = content.Split("\n");
app.SetHandler(async (InvocationContext ctx) => for (int i = 0; i < lines.Length; i++)
{ {
var loggerFactory = ConsoleHelper.CreateLoggerFactory<Program>(app.GetLogLevelFromDebug()); if (lines[i].StartsWith("proxy="))
var client = await app.ClientTryLogin(loggerFactory);
var proxy = optProxy.GetValue();
if (string.IsNullOrWhiteSpace(proxy)) { proxy = client.Host; }
var vm = await client.GetVm(optVmId.GetValue());
if (optStartOrResume.GetValue() && (vm.IsStopped || vm.IsPaused))
{ {
var status = vm.IsStopped ? VmStatus.Start : VmStatus.Resume; lines[i] = $"proxy={proxy}";
break;
if (app.DebugIsActive())
{
await Console.Out.WriteLineAsync($"VM is {(vm.IsStopped ? "stopped" : "paused")}. {status} now!");
}
//start VM
var result = await VmHelper.ChangeStatusVm(client, vm.Node, vm.VmType, vm.VmId, status);
await client.WaitForTaskToFinish(result, timeout: optWaitForStartup.GetValue() * 1000);
//check VM is running
vm = await client.GetVm(optVmId.GetValue());
if (app.DebugIsActive()) { await Console.Out.WriteLineAsync($"VM is {vm.Status}."); }
} }
}
content = string.Join("\n", lines);
var (success, reasonPhrase, content) = await VmHelper.GetQemuSpiceFileVV(client, vm.Node, vm.VmId, proxy); if (app.DebugIsActive())
if (success) {
{ await Console.Out.WriteLineAsync($"Replace Proxy: {proxy}");
//proxy force await Console.Out.WriteLineAsync(content);
if (new Regex(@"^(http|https|)://.*$").IsMatch(proxy)) }
{ }
var lines = content.Split("\n");
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].StartsWith("proxy="))
{
lines[i] = $"proxy={proxy}";
break;
}
}
content = string.Join("\n", lines);
if (app.DebugIsActive()) var fileName = Path.GetTempFileName().Replace(".tmp", ".vv");
{ File.WriteAllText(fileName, content);
await Console.Out.WriteLineAsync($"Replace Proxy: {proxy}"); var startInfo = new ProcessStartInfo
await Console.Out.WriteLineAsync(content); {
} UseShellExecute = false,
} CreateNoWindow = true,
RedirectStandardOutput = false,
};
var fileName = Path.GetTempFileName().Replace(".tmp", ".vv"); var viewerOptions = ctx.ParseResult.GetValueForOption(optViewerOptions);
File.WriteAllText(fileName, content); var remoteViewer = ctx.ParseResult.GetValueForOption(optRemoteViewer);
var startInfo = new ProcessStartInfo
{
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = false,
};
var viewerOpts = optViewerOptions.GetValue(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
startInfo.FileName = "/bin/bash";
startInfo.Arguments = $"-c \"{remoteViewer} {fileName} {viewerOptions}\"";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
startInfo.FileName = $"\"{remoteViewer}\"";
startInfo.Arguments = $"\"{fileName}\" {viewerOptions}";
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) var process = new Process
{ {
startInfo.FileName = "/bin/bash"; StartInfo = startInfo
startInfo.Arguments = $"-c \"{optRemoteViewer.GetValue()} {fileName} {viewerOpts}\""; };
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
startInfo.FileName = $"\"{optRemoteViewer.GetValue()}\"";
startInfo.Arguments = $"\"{fileName}\" {viewerOpts}";
}
var process = new Process if (app.DebugIsActive())
{ {
StartInfo = startInfo await Console.Out.WriteLineAsync($"Run FileName: {process.StartInfo.FileName}");
}; await Console.Out.WriteLineAsync($"Run Arguments: {process.StartInfo.Arguments}");
}
if (app.DebugIsActive()) if (!app.DryRunIsActive())
{ {
await Console.Out.WriteLineAsync($"Run FileName: {process.StartInfo.FileName}"); process.Start();
await Console.Out.WriteLineAsync($"Run Arguments: {process.StartInfo.Arguments}"); ctx.ExitCode = !process.HasExited || process.ExitCode == 0
} ? 0
: 1;
if (!app.DryRunIsActive())
{
process.Start();
ctx.ExitCode = !process.HasExited || process.ExitCode == 0
? 0
: 1;
}
}
else
{
await Console.Out.WriteLineAsync($"Error: {reasonPhrase}");
ctx.ExitCode = 1;
}
});
return await app.ExecuteApp(args);
} }
} }
} else
{
await Console.Out.WriteLineAsync($"Error: {reasonPhrase}");
ctx.ExitCode = 1;
}
});
return await app.ExecuteAppAsync(args, loggerFactory.CreateLogger(typeof(Program)));