Currently Being Moderated

I start a few skunk works projects over the last 6 months using C# and the Data ONTAP PowerShell Toolkit and just never got around to completely finishing them or blogging about them at all. So I'm finally taking the step to get some details out there. When I first started this effort there was no Proxy for the Data ONTAP PowerShell Toolkit and as I've seen via asking my own questions there are not a lot of developers (scripters) out there looking to build full fledged managed code apps (C#/others). My main goal developing these samples was to just show what can be done using our toolkit. My own personal pet peeve was always logging into System Manager, using the CLI, and even going to PowerShell to query a controller(s) to get a status, etc. So I set out to develop what I called NaNotify, a simple Windows System tray icon app that allowed you to connect to a controller(s) and get some simple status by just hovering over the icon.

 

The core code is written but there is still a lot of work to do and I figured if I blogged about it this would only force me tidy the backyard up a bit more and getting all of the features implemented that I want to do, such as:

  • Intervals - Ability to set intervals for how you want calls to happen. Right now it takes a manual "go get the data" approach.
  • Multiple controllers - Build a list of controllers through the simple GUI or create your own XML file and import.
  • Visualization - Some simple graphs on the hover over to get a quick visual vs having to read text.
  • Save Results As... - Scheduled results savings so you can get a report at the end of the day.
  • Multiple instances - The ability to run several separate instances for different grouping options.
  • Convert to use the Data ONTAP Proxy - I have some of this started but just not far enough to post.

 

This is what the GUI looks like and how it behaves:

SystemTray.png

This is the application running in a Windows Server 2010 system tray.

 

ContextMenu.png

By right-clicking you can access the context menu to connect (or disconnect) from a controller.

 

Login.png

Simple login dialog.

 

Connected.png

Once connected the system tray icon, which is a NetApp Controller icon, then gets a secured connection with a lock.

 

Here is the source for the project. Unfortunately I cannot post a ZIP file containing the source project entirely because of an issue with my account on NetApp Communities so I have to provide via a cut-n-paste model. As soon as the issue is resolved with my account I will be sure to attach the full Microsoft Visual Studio 2010 project solution.

 

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace NaNotify
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new fNaSysMon());
         }
    }
}

 

NaSysMon.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NaNotify
{
    class NaSysMon
    {
        private static string m_ConnectionStatus = ""; 
        public static string ConnectionStatus 
        {
            get { return m_ConnectionStatus; }
            set { m_ConnectionStatus = value; } 
        }
    }
}

 

fMain.cs

namespace NaNotify
{
    partial class fNaSysMon
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fNaSysMon));
            this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
            this.cntxtNaSysMon = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.connectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.disconnectToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
            this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
            this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.cntxtNaSysMon.SuspendLayout();
            this.SuspendLayout();
            // 
            // notifyIcon
            // 
            this.notifyIcon.BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info;
            this.notifyIcon.ContextMenuStrip = this.cntxtNaSysMon;
            this.notifyIcon.Visible = true;
            // 
            // cntxtNaSysMon
            // 
            this.cntxtNaSysMon.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.connectToolStripMenuItem,
            this.disconnectToolStripMenuItem,
            this.toolStripSeparator1,
            this.aboutToolStripMenuItem,
            this.toolStripSeparator2,
            this.exitToolStripMenuItem});
            this.cntxtNaSysMon.Name = "contextMenuStrip1";
            this.cntxtNaSysMon.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
            this.cntxtNaSysMon.Size = new System.Drawing.Size(153, 126);
            this.cntxtNaSysMon.Opening += new System.ComponentModel.CancelEventHandler(this.cntxtNaSysMon_Opening);
            // 
            // connectToolStripMenuItem
            // 
            this.connectToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("connectToolStripMenuItem.Image")));
            this.connectToolStripMenuItem.ImageAlign = System.Drawing.ContentAlignment.TopLeft;
            this.connectToolStripMenuItem.ImageTransparentColor = System.Drawing.Color.White;
            this.connectToolStripMenuItem.Name = "connectToolStripMenuItem";
            this.connectToolStripMenuItem.ShowShortcutKeys = false;
            this.connectToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
            this.connectToolStripMenuItem.Text = "Connect";
            this.connectToolStripMenuItem.Click += new System.EventHandler(this.connectToolStripMenuItem_Click);
            // 
            // disconnectToolStripMenuItem
            // 
            this.disconnectToolStripMenuItem.Enabled = false;
            this.disconnectToolStripMenuItem.Name = "disconnectToolStripMenuItem";
            this.disconnectToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
            this.disconnectToolStripMenuItem.Text = "Disconnect";
            this.disconnectToolStripMenuItem.Click += new System.EventHandler(this.disconnectToolStripMenuItem_Click);
            // 
            // toolStripSeparator1
            // 
            this.toolStripSeparator1.Name = "toolStripSeparator1";
            this.toolStripSeparator1.Size = new System.Drawing.Size(149, 6);
            // 
            // aboutToolStripMenuItem
            // 
            this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
            this.aboutToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
            this.aboutToolStripMenuItem.Text = "About...";
            this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click);
            // 
            // toolStripSeparator2
            // 
            this.toolStripSeparator2.Name = "toolStripSeparator2";
            this.toolStripSeparator2.Size = new System.Drawing.Size(149, 6);
            // 
            // exitToolStripMenuItem
            // 
            this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
            this.exitToolStripMenuItem.RightToLeft = System.Windows.Forms.RightToLeft.No;
            this.exitToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
            this.exitToolStripMenuItem.Text = "Exit";
            this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
            // 
            // fNaSysMon
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
            this.Name = "fNaSysMon";
            this.Opacity = 0D;
            this.ShowInTaskbar = false;
            this.Text = "NetApp System Monitor";
            this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
            this.Load += new System.EventHandler(this.fNaSysMon_Load);
            this.cntxtNaSysMon.ResumeLayout(false);
            this.ResumeLayout(false);
        }

        #endregion

        private System.Windows.Forms.ToolStripMenuItem connectToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem;
        private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
        public System.Windows.Forms.NotifyIcon notifyIcon;
        public System.Windows.Forms.ContextMenuStrip cntxtNaSysMon;
        private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
        private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
        private System.Windows.Forms.ToolStripMenuItem disconnectToolStripMenuItem;
    }
}

 

fConnect.cs (the meat)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Security;
using System.Collections.ObjectModel;

namespace NaNotify
{
    public partial class fConnect : Form
    {
        private Runspace _runSpace;

        public fConnect()
        {
            InitializeComponent();
        }
        private void btnConnect_Click(object sender, EventArgs e)
        {
            //Change cursor while performing connection to NetApp storage controller
            this.Cursor = Cursors.WaitCursor;

            var state = InitialSessionState.CreateDefault();

            //Get the full path to the PowerShell Toolkit
            var modulePath = System.IO.Path.Combine(
                                    System.AppDomain.CurrentDomain.BaseDirectory,
                                    @"DataONTAP\DataONTAP.psd1");
  
            //Import the Toolkit into the default runspace
            //This will fail if the app.config file doesn't include an assembly probing path
            state.ImportPSModule(new String[] { modulePath });

            _runSpace = RunspaceFactory.CreateRunspace(state);
            _runSpace.Open();
            Runspace.DefaultRunspace = _runSpace;

            string Controller = txtSystem.Text.ToString();
            string User = txtUID.Text.ToString();
            string Password = txtPWD.Text.ToString();
  
            //Create a command for Connect-Na/NcController. Use NaController by default to figure out what type 
            //of cmdlets to use either Na cmdlets or Nc cmdlets. Connecting to a Data ONTAP 8.1 Cluster-Mode 
            //system using Connect-NaController will work but return but return a WARNING saying to use Nc cmdlets.
            var connectCmd = CreateCommand("Connect-NaController");
            connectCmd.AddArgument(Controller);

            //If user credentials empty use RPC, if user/password then use HTTP/HTTPS
            if (!String.IsNullOrEmpty(User))
            {
                //Convert the clear password to a SecureString
                var securePass = new SecureString();
                foreach (var c in Password)
                {
                    securePass.AppendChar(c);
                }
                securePass.MakeReadOnly();

                //Add the credentials to the Connect-NcController command
                var cred = new PSCredential(User, securePass);
                connectCmd.AddParameter("Credential", cred);
            }

            try
            {
                foreach (PSObject result in connectCmd.Invoke())
                {
                    NaSysMon.ConnectionStatus = "Connected to " + result.ToString();
                }
  
                //Change cursor while performing connection to NetApp storage controller
                this.Cursor = Cursors.Arrow;

                //Close the Connect dialog
                this.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "NetApp Storage Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #region PowerShell Commands
        private PowerShell CreateCommand(string commandText)
        {
            var cmd = PowerShell.Create().AddCommand(commandText);
            cmd.Runspace = _runSpace;
            return cmd;
        }

        private PowerShell CreateScript(string commandText)
        {
            var cmd = PowerShell.Create().AddScript(commandText);
            cmd.Runspace = _runSpace;


            return cmd;
        }
        #endregion
  
        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }

fAbout.cs (it's an about dialog who cares)

 

References

System

System.Core

System.Data

System.Data.DataSetExtensions

System.Deployment

System.Drawing

System.Management.Automation

System.Windows.Forms

System.Xml

System.Xml.Linq

 

 

Like I mentioned at the beginning this sample is a bit more challenging because of the need to handle Runspace management but with the new Data ONTAP Proxy this is all handled behind the scenes, hence why I am working to port the code over and get rid of all the ugliness that you really don't need to worry about. Well enough said and I guess I'm committed now!

 

Cheers,

Barkz

Comments

Filter Blog

By author:
By date:
By tag: