Skip to content

Commit f4d27e3

Browse files
committed
Add ViewConsole fake command (no API call exists for this!)
1 parent f85fb9e commit f4d27e3

File tree

10 files changed

+170
-32
lines changed

10 files changed

+170
-32
lines changed

assets/CommonAssemblyInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using System;
22
using System.Reflection;
33

4-
[assembly: AssemblyVersion("4.8.0.10")]
5-
[assembly: AssemblyFileVersion("4.8.0.10")]
6-
[assembly: AssemblyInformationalVersion("4.8.0.10")]
4+
[assembly: AssemblyVersion("4.8.0.12")]
5+
[assembly: AssemblyFileVersion("4.8.0.12")]
6+
[assembly: AssemblyInformationalVersion("4.8.0.12")]
77

88
[assembly: CLSCompliant(true)]

build.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
param(
22
[String] $majorMinor = "4.8.0", # 1.4
3-
[String] $patch = "10", # $env:APPVEYOR_BUILD_VERSION
3+
[String] $patch = "12", # $env:APPVEYOR_BUILD_VERSION
44
[String] $branch = "master", # $env:APPVEYOR_REPO_BRANCH
55
[String] $customLogger = "", # C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll
66
[Switch] $notouch

src/CloudStack.Net/APIRequest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public APIRequest(string command) : this(command, false)
3434

3535
public bool SupportsImpersonation { get; }
3636

37+
/// <summary>
38+
/// If non-null, /api will be removed from the endpoint and this value will be appended
39+
/// </summary>
40+
internal virtual string OverrideEndpoint => null;
41+
internal virtual bool OverrideDecodeResponse => false;
42+
3743
protected IList<T> GetList<T>(string name)
3844
{
3945
if (!Parameters.ContainsKey(name))

src/CloudStack.Net/CloudStack.Net.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
<Compile Include="CloudStackAPIClient.cs" />
5757
<Compile Include="CloudStackAPIProxy.cs" />
5858
<Compile Include="CloudStackException.cs" />
59+
<Compile Include="CustomResponse.cs" />
5960
<Compile Include="Generated\AccountResponse.cs" />
6061
<Compile Include="Generated\AccountState.cs" />
6162
<Compile Include="Generated\ActivateProject.cs" />
@@ -792,6 +793,7 @@
792793
<Compile Include="ListResponse.cs" />
793794
<Compile Include="Properties\AssemblyInfo.cs" />
794795
<Compile Include="ProxyExtensions.cs" />
796+
<Compile Include="ViewConsole.cs" />
795797
</ItemGroup>
796798
<ItemGroup>
797799
<None Include="CloudStack.Net.nuspec" />

src/CloudStack.Net/CloudStackAPIProxy.cs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ private CloudStackAPIProxy(string serviceUrl)
5555
/// Calculates a HMAC SHA-1 hash of the supplied string.
5656
/// </summary>
5757
/// <param name="toSign">String to sign</param>
58-
/// <param name="ssoKey">Signing private key</param>
58+
/// <param name="secretKey">Signing private key</param>
5959
/// <returns>HMAC SHA-1 signature</returns>
6060
public static string CalcSignature(string toSign, string secretKey)
6161
{
@@ -74,7 +74,7 @@ public static string CalcSignature(string toSign, string secretKey)
7474
/// Produces a query string with an optional signature from arguments in key/value string form.
7575
/// </summary>
7676
/// <param name="arguments">Command in terms of key/value pairs</param>
77-
/// <param name="ssoKey">Optional secret key if the query is to be signed</param>
77+
/// <param name="secretKey">Optional secret key if the query is to be signed</param>
7878
/// <returns>Http query string including the signature.</returns>
7979
/// <remarks>
8080
/// Reference:
@@ -207,6 +207,8 @@ public static string SerialiseValue(string name, object value)
207207
public TResponse Request<TResponse>(APIRequest request) where TResponse : new()
208208
{
209209
HttpWebRequest webRequest = CreateRequest(request);
210+
webRequest.Timeout = (int)this.HttpRequestTimeout.TotalMilliseconds;
211+
webRequest.ReadWriteTimeout = (int)this.HttpRequestTimeout.TotalMilliseconds;
210212

211213
try
212214
{
@@ -218,14 +220,30 @@ public static string SerialiseValue(string name, object value)
218220
using (StreamReader streamReader = new StreamReader(respStrm, Encoding.UTF8))
219221
{
220222
string responseText = streamReader.ReadToEnd();
221-
try
223+
TResponse response;
224+
if (request.OverrideDecodeResponse)
222225
{
223-
return DecodeResponse<TResponse>(responseText);
226+
response = new TResponse();
227+
CustomResponse customResponse = response as CustomResponse;
228+
if (customResponse == null)
229+
{
230+
throw new InvalidOperationException($"{nameof(request.OverrideDecodeResponse)} has been selected, but result does not derive from {nameof(CustomResponse)}");
231+
}
232+
customResponse.DecodeResponse(responseText);
224233
}
225-
catch (FormatException ex)
234+
else
226235
{
227-
throw new FormatException("Could not decode CloudStack API Response", ex);
236+
try
237+
{
238+
response = DecodeResponse<TResponse>(responseText);
239+
}
240+
catch (FormatException ex)
241+
{
242+
throw new FormatException("Could not decode CloudStack API Response", ex);
243+
}
228244
}
245+
246+
return response;
229247
}
230248
}
231249
}
@@ -336,7 +354,12 @@ private CloudStackException CreateCloudStackException(WebException we, Uri fullU
336354
private HttpWebRequest CreateRequest(APIRequest request)
337355
{
338356
string queryString = CreateQuery(request.Parameters, ApiKey, SecretKey, SessionKey);
339-
var fullUri = new Uri(ServiceUrl + "?" + queryString);
357+
string serviceUrlBase = ServiceUrl;
358+
if (!String.IsNullOrEmpty(request.OverrideEndpoint))
359+
{
360+
serviceUrlBase = serviceUrlBase.Substring(0, serviceUrlBase.Length - "api".Length) + request.OverrideEndpoint;
361+
}
362+
var fullUri = new Uri(serviceUrlBase + "?" + queryString);
340363

341364
HttpWebRequest webRequest = WebRequest.CreateHttp(fullUri);
342365
webRequest.Accept = "application/json;charset=UTF-8";
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace CloudStack.Net
8+
{
9+
public abstract class CustomResponse
10+
{
11+
public abstract void DecodeResponse(string response);
12+
}
13+
}

src/CloudStack.Net/ViewConsole.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Linq;
3+
using System.Text.RegularExpressions;
4+
using System.Threading.Tasks;
5+
6+
namespace CloudStack.Net
7+
{
8+
public class ViewConsoleRequest : APIRequest
9+
{
10+
// client/console?cmd=access&vm=vmid
11+
12+
public ViewConsoleRequest() : base("-ViewConsole-")
13+
{
14+
Parameters.Clear();
15+
Parameters.Add("cmd", "access");
16+
}
17+
18+
/// <summary>
19+
/// the name of the physical network
20+
/// </summary>
21+
public Guid VirtualMachineId
22+
{
23+
get { return GetParameterValue<Guid>("vm"); }
24+
set { SetParameterValue("vm", value); }
25+
}
26+
27+
internal override string OverrideEndpoint => "console";
28+
internal override bool OverrideDecodeResponse => true;
29+
}
30+
31+
public class ViewConsoleResponse : CustomResponse
32+
{
33+
private static Regex _rxResponse = new Regex(@"^<html><title>(?<title>.+)</title><frameset><frame src=""(?<url>.*)""></frame></frameset></html>$", RegexOptions.ExplicitCapture | RegexOptions.Compiled);
34+
35+
public string Name { get; set; }
36+
public string Url { get; set; }
37+
38+
public override void DecodeResponse(string response)
39+
{
40+
Match m = _rxResponse.Match(response);
41+
if (!m.Success)
42+
{
43+
throw new FormatException("Console response was not of the expected format:\n\n" + response);
44+
}
45+
46+
Name = m.Groups["name"].Value;
47+
Url = m.Groups["url"].Value;
48+
}
49+
}
50+
51+
public partial interface ICloudStackAPIClient
52+
{
53+
ViewConsoleResponse ViewConsole(ViewConsoleRequest request);
54+
Task<ViewConsoleResponse> ViewConsoleAsync(ViewConsoleRequest request);
55+
}
56+
57+
public partial class CloudStackAPIClient : ICloudStackAPIClient
58+
{
59+
public ViewConsoleResponse ViewConsole(ViewConsoleRequest request) => _proxy.Request<ViewConsoleResponse>(request);
60+
public Task<ViewConsoleResponse> ViewConsoleAsync(ViewConsoleRequest request) => _proxy.RequestAsync<ViewConsoleResponse>(request);
61+
}
62+
}

test/CloudStack.Net.TestClient/MainWindow.Designer.cs

Lines changed: 34 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/CloudStack.Net.TestClient/MainWindow.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,5 +267,11 @@ private void ButtonCreateDomain_Click(object sender, EventArgs e)
267267
tests.CreateDomain(domainName);
268268
}
269269
}
270+
271+
private void ButtonViewConsole_Click(object sender, EventArgs e)
272+
{
273+
Tests tests = new Tests(this.WriteToLogBox);
274+
tests.ViewConsole(this.TextBoxVMId.Text);
275+
}
270276
}
271277
}

test/CloudStack.Net.TestClient/Tests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,19 @@ internal void StopVirtualMachine(string id, bool force)
321321
}
322322
}
323323

324+
internal void ViewConsole(string id)
325+
{
326+
try
327+
{
328+
var result = _client.ViewConsole(new ViewConsoleRequest { VirtualMachineId = Guid.Parse(id) });
329+
_logWriter($"Console for {result.Name} is available at {result.Url}");
330+
}
331+
catch (System.Exception ex)
332+
{
333+
_logWriter("Error viewing console:" + ex.Message);
334+
}
335+
}
336+
324337
#endregion
325338

326339
#region Volume tests

0 commit comments

Comments
 (0)