For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Forum Discussion

David_Alfonso_2's avatar
David_Alfonso_2
Icon for Nimbostratus rankNimbostratus
May 05, 2015

DICOM Echo Monitor

I have been searching for hours for a DICOM Echo iRule or custom monitor with no success. Several other load balancer appliances support DICOM checks but so far not F5. Any advice on how to monitor DICOM server? BTW..TCP and TCP half-open are NOT good for DICOM server. They produce association errors on DICOM/PACS server every 5 seconds which creates unneeded CPU/IO load.

 

8 Replies

  • dubdub's avatar
    dubdub
    Icon for Nimbostratus rankNimbostratus

    Hi David,

     

    I created a .NET web service that wraps an echoscu call to create/verify an assocation between the web server and a DICOM server. The web service takes the node name as a parameter (along with some other ones), and returns a simple "UP"/"DOWN" string. Individual custom HTTP monitors on the F5's point at the web server/port and pass the node name to the web service, then use the Receive string to mark the pool members up or down.

     

    Thanks, Jen

     

    • David_Alfonso_2's avatar
      David_Alfonso_2
      Icon for Nimbostratus rankNimbostratus
      Excellent. Would you be able to share the solution with me? It would really help us out.
  • Hi David,

     

    I created a .NET web service that wraps an echoscu call to create/verify an assocation between the web server and a DICOM server. The web service takes the node name as a parameter (along with some other ones), and returns a simple "UP"/"DOWN" string. Individual custom HTTP monitors on the F5's point at the web server/port and pass the node name to the web service, then use the Receive string to mark the pool members up or down.

     

    Thanks, Jen

     

    • David_Alfonso_2's avatar
      David_Alfonso_2
      Icon for Nimbostratus rankNimbostratus
      Excellent. Would you be able to share the solution with me? It would really help us out.
  • dubdub's avatar
    dubdub
    Icon for Nimbostratus rankNimbostratus

    This is the web service code itself, that invokes echoscu. The web server has to be provisioned with the DICOM server so it accepts associations from the web server. Also, the echoscu executable has to be locally available on the web server.

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.Services;
    using System.Diagnostics;
    using System.IO;
    using System.Security;
    using System.Security.Principal;
    using System.Text;
    using System.Text.RegularExpressions;
    
    namespace MonitorService {
        [WebService(Namespace = "http://tempuri.org/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        [System.ComponentModel.ToolboxItem(false)]
        // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
        // [System.Web.Script.Services.ScriptService]
        public class MonitorService : System.Web.Services.WebService {
    
            private bool m_bDebug = false;
            private string m_szLogFile;
    
            public MonitorService() {
                // Determine if we're in debug mode
                try {
                    string szValue = System.Configuration.ConfigurationManager.AppSettings["DebugMode"];
                    if (szValue == "true") {
                        m_bDebug = true;
                        m_szLogFile = System.Configuration.ConfigurationManager.AppSettings["MSLogFile"];
                    }
                } catch {}
            }
    
            [WebMethod(Description = "Runs the echoscu command against the given host and port to determine availability.")]
            public string DicomMonitorAETitle(string szHost, string szPort, string szAEC, bool bAET)
            {
                StreamWriter sw = null;
                Process proc = null;
                ProcessStartInfo procInfo = new ProcessStartInfo();
                string szArguments = "";
                string szCommand = "";
                string szResult = "";
    
                try
                {
                    // Default to down
                    szResult = "DOWN";
    
                    // Open the log file if needed
                    if (m_bDebug)
                    {
                        sw = new StreamWriter(m_szLogFile, true);
                    }
    
                    szCommand = Environment.GetEnvironmentVariable("COMSPEC");
                    if (!bAET)
                    {
                        szArguments = String.Format("/c {0} -v -aec {1} {2} {3}", @"d:\apps\echoscu.exe", szAEC, szHost, szPort);
                    }
                    else
                    {
                        szArguments = String.Format("/c {0} -v -aec {1} -aet {2} {3} {4}", @"d:\apps\echoscu.exe", szAEC, System.Net.Dns.GetHostName(), szHost, szPort);
                    }
                    if (m_bDebug)
                    {
                        sw.WriteLine(String.Format("{0}: DicomMonitor command is [{1}]", System.DateTime.Now, szCommand));
                        sw.WriteLine(String.Format("{0}: DicomMonitor arguments are [{1}]", System.DateTime.Now, szArguments));
                    }
                    procInfo.FileName = szCommand;
                    procInfo.Arguments = szArguments;
                    procInfo.UseShellExecute = false;
                    proc = Process.Start(procInfo);
                    proc.WaitForExit();
                    if (proc.ExitCode != 0)
                    {
                        if (m_bDebug)
                        {
                            sw.WriteLine(String.Format("{0}: DicomMonitor got exit code: {1}", System.DateTime.Now, proc.ExitCode));
                        }
                    }
                    else
                    {
                        if (m_bDebug)
                        {
                            sw.WriteLine(String.Format("{0}: DicomMonitor echoscu completed successfully", System.DateTime.Now));
                        }
                        szResult = "UP";
                    }
    
                }
                catch (Exception e)
                {
                    if (m_bDebug)
                    {
                        sw.WriteLine(String.Format("{0}: Caught exception: {1}", System.DateTime.Now, e.Message));
                    }
                    szResult = "DOWN";
                }
                finally
                {
                    if (sw != null)
                    {
                        sw.Close();
                    }
                }
    
                return szResult;
            } // end DicomMonitor
    
        } // end class
    } // end namespace

    Then, create a simple web page that consumes the web service:

     

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    
    namespace MonitorPage {
        public partial class _Default : System.Web.UI.Page {
    
            protected void Page_Load(object sender, EventArgs e) {
                string szResponse = "";
                string szMethod = "";
                string szHost = "";
                string szPort = "";
                string szAEC = "";
                string szAET = "";
                bool bAET = false;
    
                try {
                    // Get the parameters from the URL
                    szMethod = Request.Params["method"];
                    szHost = Request.Params["host"];
                    szPort = Request.Params["port"];
                    szAEC = Request.Params["aec"];
                    szAET = Request.Params["aet"];
                    if (szMethod == null || szMethod.Length == 0) {
                        throw new Exception("You must specify a method.");
                    }
                    if (szHost == null || szHost.Length == 0) {
                        throw new Exception("You must specify a host.");
                    }
                    if (szPort == null || szPort.Length == 0) {
                        throw new Exception("You must specify a port.");
                    }
                    if (szAEC == null || szAEC.Length == 0) {
                        throw new Exception("You must specify an AEC value.");
                    }
                    if (szAET != null && szAET.Length > 0 && szAET == "1")
                    {
                        bAET = true;
                    }
    
                    // Establish the web service object
                    using (myWebServer.MonitorService mService = new myWebServer.MonitorService()) {
                        // Invoke the correct service
                        if (szMethod == "DicomMonitor") {
                            szResponse = mService.DicomMonitorAETitle(szHost, szPort, szAEC, bAET);
                        }
                    }
    
                    // Return the response
                    Label1.Text = szResponse;
                } catch (Exception e1) {
                    Label1.Text = e1.Message;
                }
            }
        }
    }

    Then create a monitor that calls the web page on the web server (1.1.1.1 in this example, which hosts both the web service and the web page itself) for each DICOM server in your pool, and associate the monitors at the pool member level (not the pool itself):

     

    ltm monitor http Dicom_server1 {
        defaults-from http
        destination 1.1.1.1:http
        interval 30
        recv UP
        send "GET http://myWebServer.domain.com/MonitorPage/Default.aspx\?method=DicomMonitor&host=server1&port=105&aec=SERVER1_AE HTTP/1.1\\r\\nHOST: myWebServer\\r\\nConnection: Close\\r\\n\\r\\n"
        time-until-up 0
        timeout 91
    }
    • David_Alfonso_2's avatar
      David_Alfonso_2
      Icon for Nimbostratus rankNimbostratus
      Thank you very much. I have obtained a windows binary of echoscu and will work with our team to see if we can implement this improvement.
  • This is the web service code itself, that invokes echoscu. The web server has to be provisioned with the DICOM server so it accepts associations from the web server. Also, the echoscu executable has to be locally available on the web server.

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.Services;
    using System.Diagnostics;
    using System.IO;
    using System.Security;
    using System.Security.Principal;
    using System.Text;
    using System.Text.RegularExpressions;
    
    namespace MonitorService {
        [WebService(Namespace = "http://tempuri.org/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        [System.ComponentModel.ToolboxItem(false)]
        // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
        // [System.Web.Script.Services.ScriptService]
        public class MonitorService : System.Web.Services.WebService {
    
            private bool m_bDebug = false;
            private string m_szLogFile;
    
            public MonitorService() {
                // Determine if we're in debug mode
                try {
                    string szValue = System.Configuration.ConfigurationManager.AppSettings["DebugMode"];
                    if (szValue == "true") {
                        m_bDebug = true;
                        m_szLogFile = System.Configuration.ConfigurationManager.AppSettings["MSLogFile"];
                    }
                } catch {}
            }
    
            [WebMethod(Description = "Runs the echoscu command against the given host and port to determine availability.")]
            public string DicomMonitorAETitle(string szHost, string szPort, string szAEC, bool bAET)
            {
                StreamWriter sw = null;
                Process proc = null;
                ProcessStartInfo procInfo = new ProcessStartInfo();
                string szArguments = "";
                string szCommand = "";
                string szResult = "";
    
                try
                {
                    // Default to down
                    szResult = "DOWN";
    
                    // Open the log file if needed
                    if (m_bDebug)
                    {
                        sw = new StreamWriter(m_szLogFile, true);
                    }
    
                    szCommand = Environment.GetEnvironmentVariable("COMSPEC");
                    if (!bAET)
                    {
                        szArguments = String.Format("/c {0} -v -aec {1} {2} {3}", @"d:\apps\echoscu.exe", szAEC, szHost, szPort);
                    }
                    else
                    {
                        szArguments = String.Format("/c {0} -v -aec {1} -aet {2} {3} {4}", @"d:\apps\echoscu.exe", szAEC, System.Net.Dns.GetHostName(), szHost, szPort);
                    }
                    if (m_bDebug)
                    {
                        sw.WriteLine(String.Format("{0}: DicomMonitor command is [{1}]", System.DateTime.Now, szCommand));
                        sw.WriteLine(String.Format("{0}: DicomMonitor arguments are [{1}]", System.DateTime.Now, szArguments));
                    }
                    procInfo.FileName = szCommand;
                    procInfo.Arguments = szArguments;
                    procInfo.UseShellExecute = false;
                    proc = Process.Start(procInfo);
                    proc.WaitForExit();
                    if (proc.ExitCode != 0)
                    {
                        if (m_bDebug)
                        {
                            sw.WriteLine(String.Format("{0}: DicomMonitor got exit code: {1}", System.DateTime.Now, proc.ExitCode));
                        }
                    }
                    else
                    {
                        if (m_bDebug)
                        {
                            sw.WriteLine(String.Format("{0}: DicomMonitor echoscu completed successfully", System.DateTime.Now));
                        }
                        szResult = "UP";
                    }
    
                }
                catch (Exception e)
                {
                    if (m_bDebug)
                    {
                        sw.WriteLine(String.Format("{0}: Caught exception: {1}", System.DateTime.Now, e.Message));
                    }
                    szResult = "DOWN";
                }
                finally
                {
                    if (sw != null)
                    {
                        sw.Close();
                    }
                }
    
                return szResult;
            } // end DicomMonitor
    
        } // end class
    } // end namespace

    Then, create a simple web page that consumes the web service:

     

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    
    namespace MonitorPage {
        public partial class _Default : System.Web.UI.Page {
    
            protected void Page_Load(object sender, EventArgs e) {
                string szResponse = "";
                string szMethod = "";
                string szHost = "";
                string szPort = "";
                string szAEC = "";
                string szAET = "";
                bool bAET = false;
    
                try {
                    // Get the parameters from the URL
                    szMethod = Request.Params["method"];
                    szHost = Request.Params["host"];
                    szPort = Request.Params["port"];
                    szAEC = Request.Params["aec"];
                    szAET = Request.Params["aet"];
                    if (szMethod == null || szMethod.Length == 0) {
                        throw new Exception("You must specify a method.");
                    }
                    if (szHost == null || szHost.Length == 0) {
                        throw new Exception("You must specify a host.");
                    }
                    if (szPort == null || szPort.Length == 0) {
                        throw new Exception("You must specify a port.");
                    }
                    if (szAEC == null || szAEC.Length == 0) {
                        throw new Exception("You must specify an AEC value.");
                    }
                    if (szAET != null && szAET.Length > 0 && szAET == "1")
                    {
                        bAET = true;
                    }
    
                    // Establish the web service object
                    using (myWebServer.MonitorService mService = new myWebServer.MonitorService()) {
                        // Invoke the correct service
                        if (szMethod == "DicomMonitor") {
                            szResponse = mService.DicomMonitorAETitle(szHost, szPort, szAEC, bAET);
                        }
                    }
    
                    // Return the response
                    Label1.Text = szResponse;
                } catch (Exception e1) {
                    Label1.Text = e1.Message;
                }
            }
        }
    }

    Then create a monitor that calls the web page on the web server (1.1.1.1 in this example, which hosts both the web service and the web page itself) for each DICOM server in your pool, and associate the monitors at the pool member level (not the pool itself):

     

    ltm monitor http Dicom_server1 {
        defaults-from http
        destination 1.1.1.1:http
        interval 30
        recv UP
        send "GET http://myWebServer.domain.com/MonitorPage/Default.aspx\?method=DicomMonitor&host=server1&port=105&aec=SERVER1_AE HTTP/1.1\\r\\nHOST: myWebServer\\r\\nConnection: Close\\r\\n\\r\\n"
        time-until-up 0
        timeout 91
    }
    • David_Alfonso_2's avatar
      David_Alfonso_2
      Icon for Nimbostratus rankNimbostratus
      Thank you very much. I have obtained a windows binary of echoscu and will work with our team to see if we can implement this improvement.