diff --git a/src/corelib/Providers/Rackspace/CloudLoadBalancerProvider.cs b/src/corelib/Providers/Rackspace/CloudLoadBalancerProvider.cs index be4a18221..865ebdf43 100644 --- a/src/corelib/Providers/Rackspace/CloudLoadBalancerProvider.cs +++ b/src/corelib/Providers/Rackspace/CloudLoadBalancerProvider.cs @@ -14,6 +14,7 @@ using net.openstack.Providers.Rackspace.Objects.LoadBalancers.Request; using net.openstack.Providers.Rackspace.Objects.LoadBalancers.Response; using net.openstack.Providers.Rackspace.Validators; + using Newtonsoft.Json.Linq; using CancellationToken = System.Threading.CancellationToken; using JsonRestServices = JSIStudios.SimpleRESTServices.Client.Json.JsonRestServices; @@ -945,25 +946,119 @@ public Task> ListAllowedDomainsAsync(CancellationToken cance /// public Task> ListBillableLoadBalancersAsync(DateTimeOffset? startTime, DateTimeOffset? endTime, int? offset, int? limit, CancellationToken cancellationToken) { - throw new NotImplementedException(); + if (endTime < startTime) + throw new ArgumentOutOfRangeException("endTime"); + if (offset < 0) + throw new ArgumentOutOfRangeException("offset"); + if (limit <= 0) + throw new ArgumentOutOfRangeException("limit"); + + UriTemplate template = new UriTemplate("/loadbalancers/billable?startTime={startTime}&endTime={endTime}&offset={offset}&limit={limit}"); + var parameters = new Dictionary(); + if (startTime != null) + parameters.Add("startTime", startTime.Value.ToString("yyyy-MM-dd")); + if (endTime != null) + parameters.Add("endTime", endTime.Value.ToString("yyyy-MM-dd")); + if (offset != null) + parameters.Add("offset", offset.ToString()); + if (limit != null) + parameters.Add("limit", limit.ToString()); + + Func>, HttpWebRequest> prepareRequest = + PrepareRequestAsyncFunc(HttpMethod.GET, template, parameters); + + Func, Task> requestResource = + GetResponseAsyncFunc(cancellationToken); + + Func, IEnumerable> resultSelector = + task => (task.Result != null ? task.Result.LoadBalancers : null) ?? Enumerable.Empty(); + + return AuthenticateServiceAsync(cancellationToken) + .ContinueWith(prepareRequest) + .ContinueWith(requestResource).Unwrap() + .ContinueWith(resultSelector); } /// public Task> ListAccountLevelUsageAsync(DateTimeOffset? startTime, DateTimeOffset? endTime, CancellationToken cancellationToken) { - throw new NotImplementedException(); + if (endTime < startTime) + throw new ArgumentOutOfRangeException("endTime"); + + UriTemplate template = new UriTemplate("/loadbalancers/usage?startTime={startTime}&endTime={endTime}"); + var parameters = new Dictionary(); + if (startTime != null) + parameters.Add("startTime", startTime.Value.ToString("yyyy-MM-dd")); + if (endTime != null) + parameters.Add("endTime", endTime.Value.ToString("yyyy-MM-dd")); + + Func>, HttpWebRequest> prepareRequest = + PrepareRequestAsyncFunc(HttpMethod.GET, template, parameters); + + Func, Task> requestResource = + GetResponseAsyncFunc(cancellationToken); + + Func, IEnumerable> resultSelector = + task => (task.Result != null ? task.Result.UsageRecords : null) ?? Enumerable.Empty(); + + return AuthenticateServiceAsync(cancellationToken) + .ContinueWith(prepareRequest) + .ContinueWith(requestResource).Unwrap() + .ContinueWith(resultSelector); } /// - public Task> ListHistoricalUsageAsync(LoadBalancerId loadBalancerId, DateTimeOffset? startTime, DateTimeOffset? endTime, CancellationToken cancellationToken1) + public Task> ListHistoricalUsageAsync(LoadBalancerId loadBalancerId, DateTimeOffset? startTime, DateTimeOffset? endTime, CancellationToken cancellationToken) { - throw new NotImplementedException(); + if (loadBalancerId == null) + throw new ArgumentNullException("loadBalancerId"); + if (endTime < startTime) + throw new ArgumentOutOfRangeException("endTime"); + + UriTemplate template = new UriTemplate("/loadbalancers/{loadBalancerId}/usage?startTime={startTime}&endTime={endTime}"); + var parameters = new Dictionary { { "loadBalancerId", loadBalancerId.Value } }; + if (startTime != null) + parameters.Add("startTime", startTime.Value.ToString("yyyy-MM-dd")); + if (endTime != null) + parameters.Add("endTime", endTime.Value.ToString("yyyy-MM-dd")); + + Func>, HttpWebRequest> prepareRequest = + PrepareRequestAsyncFunc(HttpMethod.GET, template, parameters); + + Func, Task> requestResource = + GetResponseAsyncFunc(cancellationToken); + + Func, IEnumerable> resultSelector = + task => (task.Result != null ? task.Result.UsageRecords : null) ?? Enumerable.Empty(); + + return AuthenticateServiceAsync(cancellationToken) + .ContinueWith(prepareRequest) + .ContinueWith(requestResource).Unwrap() + .ContinueWith(resultSelector); } /// public Task> ListCurrentUsageAsync(LoadBalancerId loadBalancerId, CancellationToken cancellationToken) { - throw new NotImplementedException(); + if (loadBalancerId == null) + throw new ArgumentNullException("loadBalancerId"); + + UriTemplate template = new UriTemplate("/loadbalancers/{loadBalancerId}/usage/current"); + var parameters = new Dictionary { { "loadBalancerId", loadBalancerId.Value } }; + + Func>, HttpWebRequest> prepareRequest = + PrepareRequestAsyncFunc(HttpMethod.GET, template, parameters); + + Func, Task> requestResource = + GetResponseAsyncFunc(cancellationToken); + + Func, IEnumerable> resultSelector = + task => (task.Result != null ? task.Result.UsageRecords : null) ?? Enumerable.Empty(); + + return AuthenticateServiceAsync(cancellationToken) + .ContinueWith(prepareRequest) + .ContinueWith(requestResource).Unwrap() + .ContinueWith(resultSelector); } /// @@ -1243,19 +1338,108 @@ public Task ClearAccessListAsync(LoadBalancerId loadBalancerId, AsyncCompletionO /// public Task GetHealthMonitorAsync(LoadBalancerId loadBalancerId, CancellationToken cancellationToken) { - throw new NotImplementedException(); + if (loadBalancerId == null) + throw new ArgumentNullException("loadBalancerId"); + + UriTemplate template = new UriTemplate("/loadbalancers/{loadBalancerId}/healthmonitor"); + var parameters = new Dictionary() + { + { "loadBalancerId", loadBalancerId.Value } + }; + + Func>, HttpWebRequest> prepareRequest = + PrepareRequestAsyncFunc(HttpMethod.GET, template, parameters); + + Func, Task> requestResource = + GetResponseAsyncFunc(cancellationToken); + + Func, HealthMonitor> resultSelector = + task => + { + if (task.Result == null) + return null; + + JObject healthMonitorObject = task.Result["healthMonitor"] as JObject; + if (healthMonitorObject == null) + return null; + + return HealthMonitor.FromJObject(healthMonitorObject); + }; + + return AuthenticateServiceAsync(cancellationToken) + .ContinueWith(prepareRequest) + .ContinueWith(requestResource).Unwrap() + .ContinueWith(resultSelector); } /// public Task SetHealthMonitorAsync(LoadBalancerId loadBalancerId, HealthMonitor monitor, AsyncCompletionOption completionOption, CancellationToken cancellationToken, IProgress progress) { - throw new NotImplementedException(); + if (loadBalancerId == null) + throw new ArgumentNullException("loadBalancerId"); + if (monitor == null) + throw new ArgumentNullException("monitor"); + + UriTemplate template = new UriTemplate("/loadbalancers/{loadBalancerId}/healthmonitor"); + var parameters = new Dictionary() + { + { "loadBalancerId", loadBalancerId.Value } + }; + + Func>, Task> prepareRequest = + PrepareRequestAsyncFunc(HttpMethod.PUT, template, parameters, monitor); + + Func, Task> requestResource = + GetResponseAsyncFunc(cancellationToken); + + Func, Task> resultSelector = + task => + { + task.PropagateExceptions(); + if (completionOption == AsyncCompletionOption.RequestCompleted) + return WaitForLoadBalancerToLeaveStateAsync(loadBalancerId, LoadBalancerStatus.PendingUpdate, cancellationToken, progress); + + return InternalTaskExtensions.CompletedTask(default(LoadBalancer)); + }; + + return AuthenticateServiceAsync(cancellationToken) + .ContinueWith(prepareRequest).Unwrap() + .ContinueWith(requestResource).Unwrap() + .ContinueWith(resultSelector).Unwrap(); } /// public Task RemoveHealthMonitorAsync(LoadBalancerId loadBalancerId, AsyncCompletionOption completionOption, CancellationToken cancellationToken, IProgress progress) { - throw new NotImplementedException(); + if (loadBalancerId == null) + throw new ArgumentNullException("loadBalancerId"); + + UriTemplate template = new UriTemplate("/loadbalancers/{loadBalancerId}/healthmonitor"); + var parameters = new Dictionary() + { + { "loadBalancerId", loadBalancerId.Value }, + }; + + Func>, HttpWebRequest> prepareRequest = + PrepareRequestAsyncFunc(HttpMethod.DELETE, template, parameters); + + Func, Task> requestResource = + GetResponseAsyncFunc(cancellationToken); + + Func, Task> resultSelector = + task => + { + task.PropagateExceptions(); + if (completionOption == AsyncCompletionOption.RequestCompleted) + return WaitForLoadBalancerToLeaveStateAsync(loadBalancerId, LoadBalancerStatus.PendingUpdate, cancellationToken, progress); + + return InternalTaskExtensions.CompletedTask(default(LoadBalancer)); + }; + + return AuthenticateServiceAsync(cancellationToken) + .ContinueWith(prepareRequest) + .ContinueWith(requestResource).Unwrap() + .ContinueWith(resultSelector).Unwrap(); } /// diff --git a/src/corelib/Providers/Rackspace/Objects/LoadBalancers/CustomHealthMonitor.cs b/src/corelib/Providers/Rackspace/Objects/LoadBalancers/CustomHealthMonitor.cs new file mode 100644 index 000000000..a9a41df7e --- /dev/null +++ b/src/corelib/Providers/Rackspace/Objects/LoadBalancers/CustomHealthMonitor.cs @@ -0,0 +1,22 @@ +namespace net.openstack.Providers.Rackspace.Objects.LoadBalancers +{ + using Newtonsoft.Json; + + /// + /// This class models the JSON object used to represent a custom . + /// + /// + /// + [JsonObject(MemberSerialization.OptIn)] + public class CustomHealthMonitor : HealthMonitor + { + /// + /// Initializes a new instance of the class + /// during JSON deserialization. + /// + [JsonConstructor] + protected CustomHealthMonitor() + { + } + } +} diff --git a/src/corelib/Providers/Rackspace/Objects/LoadBalancers/HealthMonitor.cs b/src/corelib/Providers/Rackspace/Objects/LoadBalancers/HealthMonitor.cs index e104fdca1..767f8e7e7 100644 --- a/src/corelib/Providers/Rackspace/Objects/LoadBalancers/HealthMonitor.cs +++ b/src/corelib/Providers/Rackspace/Objects/LoadBalancers/HealthMonitor.cs @@ -2,6 +2,7 @@ { using System; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// This is the base class for load balancer health monitoring configurations. @@ -13,7 +14,6 @@ /// /// [JsonObject(MemberSerialization.OptIn)] - [JsonConverter(typeof(HealthMonitor.Converter))] public abstract class HealthMonitor { /// @@ -131,26 +131,28 @@ public int? AttemptsBeforeDeactivation } } - /// - protected class Converter : JsonConverter + /// + /// Deserializes a JSON object to a instance of the proper type. + /// + /// The JSON object representing the health monitor. + /// A object corresponding to the JSON object. + /// If is null. + public static HealthMonitor FromJObject(JObject jsonObject) { - /// - public override bool CanConvert(Type objectType) - { - throw new NotImplementedException(); - } - - /// - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - - /// - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new NotImplementedException(); - } + if (jsonObject == null) + throw new ArgumentNullException("jsonObject"); + + JValue typeValue = jsonObject["type"] as JValue; + if (typeValue == null) + return null; + + HealthMonitorType type = typeValue.ToObject(); + if (type == HealthMonitorType.Connect) + return jsonObject.ToObject(); + else if (type == HealthMonitorType.Http || type == HealthMonitorType.Https) + return jsonObject.ToObject(); + else + return jsonObject.ToObject(); } } } diff --git a/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerConfiguration`1.cs b/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerConfiguration`1.cs index 05ffa2a20..3747cfa51 100644 --- a/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerConfiguration`1.cs +++ b/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerConfiguration`1.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using System.Linq; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; /// /// Represents a load balancer configuration. @@ -86,7 +87,7 @@ public class LoadBalancerConfiguration /// This is the backing field for the property. /// [JsonProperty("healthMonitor", DefaultValueHandling = DefaultValueHandling.Ignore)] - private HealthMonitor _healthMonitor; + private JObject _healthMonitor; /// /// This is the backing field for the property. @@ -190,7 +191,7 @@ public LoadBalancerConfiguration(string name, LoadBalancingProtocol protocol, IE if (contentCaching.HasValue) _contentCaching = contentCaching.Value ? LoadBalancerEnabledFlag.True : LoadBalancerEnabledFlag.False; _connectionThrottle = connectionThrottle; - _healthMonitor = healthMonitor; + _healthMonitor = healthMonitor != null ? JObject.FromObject(healthMonitor) : null; _metadata = metadata != null ? metadata.ToArray() : null; _timeout = timeout != null ? (int?)timeout.Value.TotalSeconds : null; _sessionPersistence = sessionPersistence; @@ -340,7 +341,10 @@ public HealthMonitor HealthMonitor { get { - return _healthMonitor; + if (_healthMonitor == null) + return null; + + return HealthMonitor.FromJObject(_healthMonitor); } } diff --git a/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancerUsageResponse.cs b/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancerUsageResponse.cs new file mode 100644 index 000000000..52a28d508 --- /dev/null +++ b/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancerUsageResponse.cs @@ -0,0 +1,47 @@ +namespace net.openstack.Providers.Rackspace.Objects.LoadBalancers.Response +{ + using System.Collections.ObjectModel; + using Newtonsoft.Json; + + /// + /// This models the JSON response used for the List Usage request. + /// + /// List Usage (Rackspace Cloud Load Balancers Developer Guide - API v1.0) + /// + /// + [JsonObject(MemberSerialization.OptIn)] + internal class ListLoadBalancerUsageResponse + { +#pragma warning disable 649 // Field 'fieldName' is never assigned to, and will always have its default value + /// + /// This is the backing field for the property. + /// + [JsonProperty("loadBalancerUsageRecords")] + private LoadBalancerUsage[] _loadBalancerUsage; +#pragma warning restore 649 + + /// + /// Initializes a new instance of the class + /// during JSON deserialization. + /// + [JsonConstructor] + protected ListLoadBalancerUsageResponse() + { + } + + /// + /// Gets a collection of objects describing the load + /// balancer usage resources in a load balancer service provider. + /// + public ReadOnlyCollection UsageRecords + { + get + { + if (_loadBalancerUsage == null) + return null; + + return new ReadOnlyCollection(_loadBalancerUsage); + } + } + } +} diff --git a/src/corelib/corelib.v3.5.csproj b/src/corelib/corelib.v3.5.csproj index 7e90cee9b..f04e8f70a 100644 --- a/src/corelib/corelib.v3.5.csproj +++ b/src/corelib/corelib.v3.5.csproj @@ -236,6 +236,7 @@ + @@ -273,6 +274,7 @@ + diff --git a/src/corelib/corelib.v4.0.csproj b/src/corelib/corelib.v4.0.csproj index 09520ff39..f9365b2b3 100644 --- a/src/corelib/corelib.v4.0.csproj +++ b/src/corelib/corelib.v4.0.csproj @@ -227,6 +227,7 @@ + @@ -287,6 +288,7 @@ + diff --git a/src/testing/integration/Providers/Rackspace/UserLoadBalancerTests.cs b/src/testing/integration/Providers/Rackspace/UserLoadBalancerTests.cs index ab960bb9b..a8e69f70c 100644 --- a/src/testing/integration/Providers/Rackspace/UserLoadBalancerTests.cs +++ b/src/testing/integration/Providers/Rackspace/UserLoadBalancerTests.cs @@ -692,33 +692,153 @@ public async Task TestListAllowedDomains() [TestMethod] [TestCategory(TestCategories.User)] [TestCategory(TestCategories.LoadBalancers)] - public void TestListBillableLoadBalancers() + public async Task TestListBillableLoadBalancers() { - Assert.Inconclusive("Not yet implemented."); + ILoadBalancerService provider = CreateProvider(); + using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TestTimeout(TimeSpan.FromSeconds(60)))) + { + IEnumerable protocols = await provider.ListProtocolsAsync(cancellationTokenSource.Token); + LoadBalancingProtocol httpProtocol = protocols.First(i => i.Name.Equals("HTTP", StringComparison.OrdinalIgnoreCase)); + + string loadBalancerName = CreateRandomLoadBalancerName(); + + LoadBalancerConfiguration configuration = new LoadBalancerConfiguration( + name: loadBalancerName, + nodes: null, + protocol: httpProtocol, + virtualAddresses: new[] { new LoadBalancerVirtualAddress(LoadBalancerVirtualAddressType.Public) }, + algorithm: LoadBalancingAlgorithm.RoundRobin); + LoadBalancer tempLoadBalancer = await provider.CreateLoadBalancerAsync(configuration, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + IEnumerable billable = await provider.ListBillableLoadBalancersAsync(DateTimeOffset.Now.Date.AddDays(-60), DateTimeOffset.Now.Date.AddDays(1), null, null, cancellationTokenSource.Token); + Assert.IsNotNull(billable); + LoadBalancer[] loadBalancers = billable.ToArray(); + if (loadBalancers.Length == 0) + Assert.Inconclusive("No billable load balancers were reported."); + + Console.WriteLine("Billable Load Balancers:"); + foreach (LoadBalancer loadBalancer in loadBalancers) + Console.WriteLine(" {0}", loadBalancer.Id); + + /* Cleanup + */ + + await provider.RemoveLoadBalancerAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + } } [TestMethod] [TestCategory(TestCategories.User)] [TestCategory(TestCategories.LoadBalancers)] - public void TestListAccountLevelUsage() + public async Task TestListAccountLevelUsage() { - Assert.Inconclusive("Not yet implemented."); + ILoadBalancerService provider = CreateProvider(); + using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TestTimeout(TimeSpan.FromSeconds(60)))) + { + IEnumerable protocols = await provider.ListProtocolsAsync(cancellationTokenSource.Token); + LoadBalancingProtocol httpProtocol = protocols.First(i => i.Name.Equals("HTTP", StringComparison.OrdinalIgnoreCase)); + + string loadBalancerName = CreateRandomLoadBalancerName(); + + LoadBalancerConfiguration configuration = new LoadBalancerConfiguration( + name: loadBalancerName, + nodes: null, + protocol: httpProtocol, + virtualAddresses: new[] { new LoadBalancerVirtualAddress(LoadBalancerVirtualAddressType.Public) }, + algorithm: LoadBalancingAlgorithm.RoundRobin); + LoadBalancer tempLoadBalancer = await provider.CreateLoadBalancerAsync(configuration, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + IEnumerable usage = await provider.ListAccountLevelUsageAsync(DateTimeOffset.Now.Date.AddDays(-60), DateTimeOffset.Now.Date.AddDays(1), cancellationTokenSource.Token); + Assert.IsNotNull(usage); + LoadBalancerUsage[] usageRecords = usage.ToArray(); + if (usageRecords.Length == 0) + Assert.Inconclusive("No account level usage was reported."); + + Console.WriteLine("Account Level Usage ({0} records)", usageRecords.Length); + foreach (LoadBalancerUsage usageRecord in usageRecords) + Console.WriteLine(JsonConvert.SerializeObject(usageRecord, Formatting.Indented)); + + /* Cleanup + */ + + await provider.RemoveLoadBalancerAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + } } [TestMethod] [TestCategory(TestCategories.User)] [TestCategory(TestCategories.LoadBalancers)] - public void TestListHistoricalUsage() + public async Task TestListHistoricalUsage() { - Assert.Inconclusive("Not yet implemented."); + ILoadBalancerService provider = CreateProvider(); + using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TestTimeout(TimeSpan.FromSeconds(60)))) + { + IEnumerable protocols = await provider.ListProtocolsAsync(cancellationTokenSource.Token); + LoadBalancingProtocol httpProtocol = protocols.First(i => i.Name.Equals("HTTP", StringComparison.OrdinalIgnoreCase)); + + string loadBalancerName = CreateRandomLoadBalancerName(); + + LoadBalancerConfiguration configuration = new LoadBalancerConfiguration( + name: loadBalancerName, + nodes: null, + protocol: httpProtocol, + virtualAddresses: new[] { new LoadBalancerVirtualAddress(LoadBalancerVirtualAddressType.Public) }, + algorithm: LoadBalancingAlgorithm.RoundRobin); + LoadBalancer tempLoadBalancer = await provider.CreateLoadBalancerAsync(configuration, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + IEnumerable usage = await provider.ListHistoricalUsageAsync(tempLoadBalancer.Id, DateTimeOffset.Now.Date.AddDays(-60), DateTimeOffset.Now.Date.AddDays(1), cancellationTokenSource.Token); + Assert.IsNotNull(usage); + LoadBalancerUsage[] usageRecords = usage.ToArray(); + if (usageRecords.Length == 0) + Assert.Inconclusive("No historical usage was reported for load balancer: {0}", tempLoadBalancer.Id); + + Console.WriteLine("Historical Load Balancer Usage ({0} records)", usageRecords.Length); + foreach (LoadBalancerUsage usageRecord in usageRecords) + Console.WriteLine(JsonConvert.SerializeObject(usageRecord, Formatting.Indented)); + + /* Cleanup + */ + + await provider.RemoveLoadBalancerAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + } } [TestMethod] [TestCategory(TestCategories.User)] [TestCategory(TestCategories.LoadBalancers)] - public void TestListCurrentUsage() + public async Task TestListCurrentUsage() { - Assert.Inconclusive("Not yet implemented."); + ILoadBalancerService provider = CreateProvider(); + using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TestTimeout(TimeSpan.FromSeconds(60)))) + { + IEnumerable protocols = await provider.ListProtocolsAsync(cancellationTokenSource.Token); + LoadBalancingProtocol httpProtocol = protocols.First(i => i.Name.Equals("HTTP", StringComparison.OrdinalIgnoreCase)); + + string loadBalancerName = CreateRandomLoadBalancerName(); + + LoadBalancerConfiguration configuration = new LoadBalancerConfiguration( + name: loadBalancerName, + nodes: null, + protocol: httpProtocol, + virtualAddresses: new[] { new LoadBalancerVirtualAddress(LoadBalancerVirtualAddressType.Public) }, + algorithm: LoadBalancingAlgorithm.RoundRobin); + LoadBalancer tempLoadBalancer = await provider.CreateLoadBalancerAsync(configuration, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + IEnumerable usage = await provider.ListCurrentUsageAsync(tempLoadBalancer.Id, cancellationTokenSource.Token); + Assert.IsNotNull(usage); + LoadBalancerUsage[] usageRecords = usage.ToArray(); + if (usageRecords.Length == 0) + Assert.Inconclusive("No current usage was reported for load balancer: {0}", tempLoadBalancer.Id); + + Console.WriteLine("Current Load Balancer Usage ({0} records)", usageRecords.Length); + foreach (LoadBalancerUsage usageRecord in usageRecords) + Console.WriteLine(JsonConvert.SerializeObject(usageRecord, Formatting.Indented)); + + /* Cleanup + */ + + await provider.RemoveLoadBalancerAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + } } [TestMethod] @@ -863,6 +983,202 @@ public async Task TestAccessLists() } } + [TestMethod] + [TestCategory(TestCategories.User)] + [TestCategory(TestCategories.LoadBalancers)] + public async Task TestHealthMonitorConnection() + { + ILoadBalancerService provider = CreateProvider(); + using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TestTimeout(TimeSpan.FromSeconds(60)))) + { + IEnumerable protocols = await provider.ListProtocolsAsync(cancellationTokenSource.Token); + LoadBalancingProtocol httpProtocol = protocols.First(i => i.Name.Equals("LDAP", StringComparison.OrdinalIgnoreCase)); + + string loadBalancerName = CreateRandomLoadBalancerName(); + + LoadBalancerConfiguration configuration = new LoadBalancerConfiguration( + name: loadBalancerName, + nodes: null, + protocol: httpProtocol, + virtualAddresses: new[] { new LoadBalancerVirtualAddress(LoadBalancerVirtualAddressType.ServiceNet) }, + algorithm: LoadBalancingAlgorithm.RoundRobin); + LoadBalancer tempLoadBalancer = await provider.CreateLoadBalancerAsync(configuration, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + // verify initially null + Assert.IsNull(tempLoadBalancer.HealthMonitor); + Assert.IsNull(await provider.GetHealthMonitorAsync(tempLoadBalancer.Id, cancellationTokenSource.Token)); + + // configure the health monitor + int attemptsBeforeDeactivation = 3; + TimeSpan timeout = TimeSpan.FromSeconds(30); + TimeSpan delay = TimeSpan.FromSeconds(30); + HealthMonitor monitor = new ConnectionHealthMonitor(attemptsBeforeDeactivation, timeout, delay); + await provider.SetHealthMonitorAsync(tempLoadBalancer.Id, monitor, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + // set the health monitor again + await provider.SetHealthMonitorAsync(tempLoadBalancer.Id, monitor, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + // verify the health monitor + HealthMonitor healthMonitor = await provider.GetHealthMonitorAsync(tempLoadBalancer.Id, cancellationTokenSource.Token); + Assert.IsInstanceOfType(healthMonitor, typeof(ConnectionHealthMonitor)); + Assert.AreEqual(HealthMonitorType.Connect, healthMonitor.Type); + Assert.AreEqual(attemptsBeforeDeactivation, healthMonitor.AttemptsBeforeDeactivation); + Assert.AreEqual(timeout, healthMonitor.Timeout); + Assert.AreEqual(delay, healthMonitor.Delay); + + // verify the information in the load balancer details + LoadBalancer loadBalancer = await provider.GetLoadBalancerAsync(tempLoadBalancer.Id, cancellationTokenSource.Token); + Assert.AreEqual(tempLoadBalancer.Id, loadBalancer.Id); + + Assert.IsInstanceOfType(loadBalancer.HealthMonitor, typeof(ConnectionHealthMonitor)); + Assert.AreEqual(HealthMonitorType.Connect, loadBalancer.HealthMonitor.Type); + Assert.AreEqual(attemptsBeforeDeactivation, loadBalancer.HealthMonitor.AttemptsBeforeDeactivation); + Assert.AreEqual(timeout, loadBalancer.HealthMonitor.Timeout); + Assert.AreEqual(delay, loadBalancer.HealthMonitor.Delay); + + // remove the health monitor + await provider.RemoveHealthMonitorAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + // remove the health monitor again + try + { + try + { + await provider.RemoveHealthMonitorAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + } + catch (WebException ex) + { + throw new AggregateException(ex); + } + } + catch (AggregateException aggregate) + { + aggregate.Flatten().Handle( + ex => + { + WebException webException = ex as WebException; + if (webException == null) + return false; + + HttpWebResponse response = webException.Response as HttpWebResponse; + if (response == null) + return false; + + Assert.AreEqual((HttpStatusCode)422, response.StatusCode); + return true; + }); + } + + /* Cleanup + */ + await provider.RemoveLoadBalancerAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + } + } + + [TestMethod] + [TestCategory(TestCategories.User)] + [TestCategory(TestCategories.LoadBalancers)] + public async Task TestHealthMonitorHttp() + { + ILoadBalancerService provider = CreateProvider(); + using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TestTimeout(TimeSpan.FromSeconds(60)))) + { + IEnumerable protocols = await provider.ListProtocolsAsync(cancellationTokenSource.Token); + LoadBalancingProtocol httpProtocol = protocols.First(i => i.Name.Equals("HTTP", StringComparison.OrdinalIgnoreCase)); + + string loadBalancerName = CreateRandomLoadBalancerName(); + + LoadBalancerConfiguration configuration = new LoadBalancerConfiguration( + name: loadBalancerName, + nodes: null, + protocol: httpProtocol, + virtualAddresses: new[] { new LoadBalancerVirtualAddress(LoadBalancerVirtualAddressType.ServiceNet) }, + algorithm: LoadBalancingAlgorithm.RoundRobin); + LoadBalancer tempLoadBalancer = await provider.CreateLoadBalancerAsync(configuration, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + // verify initially null + Assert.IsNull(tempLoadBalancer.HealthMonitor); + Assert.IsNull(await provider.GetHealthMonitorAsync(tempLoadBalancer.Id, cancellationTokenSource.Token)); + + // configure the health monitor + bool https = false; + int attemptsBeforeDeactivation = 3; + TimeSpan timeout = TimeSpan.FromSeconds(30); + TimeSpan delay = TimeSpan.FromSeconds(30); + string bodyRegex = ".*"; + string path = "/"; + string statusRegex = ".*"; + HealthMonitor monitor = new WebServerHealthMonitor(https, attemptsBeforeDeactivation, timeout, delay, bodyRegex, path, statusRegex); + await provider.SetHealthMonitorAsync(tempLoadBalancer.Id, monitor, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + // set the health monitor again + await provider.SetHealthMonitorAsync(tempLoadBalancer.Id, monitor, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + // verify the health monitor + HealthMonitor healthMonitor = await provider.GetHealthMonitorAsync(tempLoadBalancer.Id, cancellationTokenSource.Token); + Assert.IsInstanceOfType(healthMonitor, typeof(WebServerHealthMonitor)); + WebServerHealthMonitor webServerHealthMonitor = (WebServerHealthMonitor)healthMonitor; + Assert.AreEqual(monitor.Type, healthMonitor.Type); + Assert.AreEqual(attemptsBeforeDeactivation, healthMonitor.AttemptsBeforeDeactivation); + Assert.AreEqual(timeout, healthMonitor.Timeout); + Assert.AreEqual(delay, healthMonitor.Delay); + Assert.AreEqual(bodyRegex, webServerHealthMonitor.BodyRegex); + Assert.AreEqual(path, webServerHealthMonitor.Path); + Assert.AreEqual(statusRegex, webServerHealthMonitor.StatusRegex); + + // verify the information in the load balancer details + LoadBalancer loadBalancer = await provider.GetLoadBalancerAsync(tempLoadBalancer.Id, cancellationTokenSource.Token); + Assert.AreEqual(tempLoadBalancer.Id, loadBalancer.Id); + + Assert.IsInstanceOfType(loadBalancer.HealthMonitor, typeof(WebServerHealthMonitor)); + webServerHealthMonitor = (WebServerHealthMonitor)loadBalancer.HealthMonitor; + Assert.AreEqual(monitor.Type, loadBalancer.HealthMonitor.Type); + Assert.AreEqual(attemptsBeforeDeactivation, loadBalancer.HealthMonitor.AttemptsBeforeDeactivation); + Assert.AreEqual(timeout, loadBalancer.HealthMonitor.Timeout); + Assert.AreEqual(delay, loadBalancer.HealthMonitor.Delay); + Assert.AreEqual(bodyRegex, webServerHealthMonitor.BodyRegex); + Assert.AreEqual(path, webServerHealthMonitor.Path); + Assert.AreEqual(statusRegex, webServerHealthMonitor.StatusRegex); + + // remove the health monitor + await provider.RemoveHealthMonitorAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + + // remove the health monitor again + try + { + try + { + await provider.RemoveHealthMonitorAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + } + catch (WebException ex) + { + throw new AggregateException(ex); + } + } + catch (AggregateException aggregate) + { + aggregate.Flatten().Handle( + ex => + { + WebException webException = ex as WebException; + if (webException == null) + return false; + + HttpWebResponse response = webException.Response as HttpWebResponse; + if (response == null) + return false; + + Assert.AreEqual((HttpStatusCode)422, response.StatusCode); + return true; + }); + } + + /* Cleanup + */ + await provider.RemoveLoadBalancerAsync(tempLoadBalancer.Id, AsyncCompletionOption.RequestCompleted, cancellationTokenSource.Token, null); + } + } + [TestMethod] [TestCategory(TestCategories.User)] [TestCategory(TestCategories.LoadBalancers)]