toolkit/Toolkit/SqlManagedInstanceToolkit.cs (177 lines of code) (raw):
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: SqlManagedInstanceToolkit.cs
//
// @Owner: Dejan Dundjerski
//
// Purpose:
// SQL MI Toolkit for customers to debug various customer side issues.
//
// Notes:
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.NetworkInformation;
using System.IO;
using System.Data.SqlTypes;
using System.Net;
using System.Net.Sockets;
using Microsoft.SqlServer.Server;
/// <summary>
/// SQL MI Toolkit class.
/// </summary>
public partial class SqlManagedInstanceToolkit
{
/// <summary>
/// Check IP and return output.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <returns></returns>
private static List<string> CheckIp(string ipAddress, int port = 443)
{
List<string> result = new List<string>();
// Check tcp connection to port
//
if (ipAddress != null)
{
try
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ipAddress, port);
if (socket.Connected)
{
result.Add(string.Format("TCP connect to {0}:{1} was successful.", ipAddress, port));
}
}
catch (SocketException se)
{
if (se.ErrorCode == 10061)
{
result.Add(string.Format("TCP connect to {0}:{1} failed because there were no listener. However the port is open.", ipAddress, port));
}
else
{
result.Add(string.Format("TCP connect to {0}:{1} failed with {2}.", ipAddress, port, se.Message));
}
}
catch (Exception ex)
{
result.Add(string.Format("TCP connect to {0}:{1} failed with {2}.", ipAddress, port, ex.Message));
}
}
return result;
}
/// <summary>
/// Check FQDN and return ipAddress and output.
/// </summary>
/// <param name="fqdn"></param>
/// <param name="ipAddress"></param>
/// <returns></returns>
private static List<string> CheckFqdn(string fqdn, out string ipAddress)
{
ipAddress = null;
List<string> result = new List<string>();
// Resolve fqdn
//
try
{
int cnt = 0;
string addresses = string.Empty;
IPHostEntry destination = Dns.GetHostEntry(fqdn);
foreach (var addr in destination.AddressList)
{
addresses += addr + ";";
if (cnt++ == 0)
{
ipAddress = addr.ToString();
}
}
result.Add(string.Format("Hostname {0} was successfully resolved to: {1}.", fqdn, addresses));
}
catch (SocketException se)
{
if (se.ErrorCode == 11001)
{
result.Add(string.Format("Hostname {0} could not be found", fqdn));
}
else
{
result.Add(string.Format("DNS resolution of {0} thrown following error {1}.", fqdn, se.ErrorCode));
}
}
catch (Exception ex)
{
result.Add(string.Format("DNS resolution of {0} thrown following exc {1}.", fqdn, ex.Message));
}
// Check TCP ping
//
Ping ping = new Ping();
try
{
PingReply reply = ping.Send(fqdn);
result.Add(string.Format("PING to {0} reported status {1}.", fqdn, reply.Status));
}
catch (Exception ex)
{
result.Add(string.Format("PING to {0} thrown following exc {1}.", fqdn, ex.Message));
}
return result;
}
/// <summary>
/// Check IP address accessibility.
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
[Microsoft.SqlServer.Server.SqlProcedure]
public static void CheckIpAddressAccessibility(SqlString ipAddress, SqlInt32 port)
{
List<string> result = CheckIp(ipAddress.Value, port.Value);
// Expose results.
//
foreach (var row in result)
{
SqlContext.Pipe.Send(row);
}
}
/// <summary>
/// Check FQDN accessibility.
/// </summary>
/// <param name="fqdn"></param>
/// <param name="port"></param>
[Microsoft.SqlServer.Server.SqlProcedure]
public static void CheckFqdnAccessibility(SqlString fqdn, SqlInt32 port)
{
string ipAddress;
List<string> result = CheckFqdn(fqdn.Value, out ipAddress);
result.AddRange(CheckIp(ipAddress, port.Value));
// Expose results.
//
foreach (var row in result)
{
SqlContext.Pipe.Send(row);
}
}
/// <summary>
/// Check storage account accessibility.
/// </summary>
/// <param name="storageAccount"></param>
/// <param name="sasToken"></param>
/// <param name="executeWriteOperation"></param>
[Microsoft.SqlServer.Server.SqlProcedure]
public static void CheckStorageAccountAccessibility(SqlString storageAccount, SqlString sasToken, SqlBoolean executeWriteOperation)
{
List<string> result = new List<string>();
Uri uri = null;
try
{
uri = new Uri(storageAccount.Value);
}
catch (UriFormatException)
{
result.Add("URI is in the wrong format");
}
if (uri != null && uri.Port != 443)
{
result.Add("Only https is allowed");
}
if (uri != null)
{
string hostname = uri.Host;
string ipAddress;
result.AddRange(CheckFqdn(hostname, out ipAddress));
result.AddRange(CheckIp(ipAddress, 443));
if (executeWriteOperation.Value)
{
// Try writing to the file
//
string requestUri = string.Format("{0}/checktestblob_{1}_{2}?{3}", storageAccount.Value, DateTime.UtcNow.ToString("yyyyMMd_HHmmss"), Guid.NewGuid().ToString("N"), sasToken);
HttpWebRequest request = HttpWebRequest.CreateHttp(requestUri);
request.Method = "PUT";
request.ContentType = "text/plain; charset=UTF-8";
request.ContentLength = 0;
request.Headers["x-ms-blob-type"] = "BlockBlob";
request.Headers["x-ms-version"] = "2020-04-08";
request.Headers["x-ms-date"] = DateTime.UtcNow.ToString();
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
var encoding = string.IsNullOrEmpty(response.CharacterSet) ? Encoding.ASCII : Encoding.GetEncoding(response.CharacterSet);
using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding))
{
string responseBody = reader != null ? reader.ReadToEnd() : null;
result.Add(string.Format("Put request to account {0} returned {1}. Description: {2}; Response: {3};", storageAccount.Value, response.StatusCode, response.StatusDescription, responseBody));
}
}
}
catch (WebException wex)
{
string returnedResponse = string.Empty;
HttpWebResponse response = (HttpWebResponse)wex.Response;
if (response != null)
{
var encoding = string.IsNullOrEmpty(response.CharacterSet) ? Encoding.ASCII : Encoding.GetEncoding(response.CharacterSet);
using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding))
{
returnedResponse = reader != null ? reader.ReadToEnd() : string.Empty;
}
response.Dispose();
}
result.Add(string.Format("Put request to account {0} throw an exception {1}. Http response: {2}.", storageAccount.Value, wex.Message, returnedResponse));
}
}
}
// Expose results.
//
foreach (var row in result)
{
SqlContext.Pipe.Send(row);
}
}
}