Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Router mass-configuration with Visual Script development

5.00/5 (3 votes)
19 Jan 2016Apache11 min read 9.7K  
Visual Scripting with Pretty Good Temrinal

Intended Audience

You may find this article interesting if network automation software is in your point of interest, you need to work with large number of network devices and are also an experienced programmer.

Introduction

In this article I want to show you the Visual Scripting capability of Pretty Good Terminal. I am creating this post here, because the Visual Script is actually a runtime generated c# code which is compiled and executed by PGT on the fly.

Although simple scripts does not require any programming skills, vScripts provide a general infrastructure to include user defined c# classes, variables and code, even referencing and using external assemblies, should someone exploit the full power of .NET to create a complex script.

In essence, I want to show you the object model used and the operation of the Visual Script execution engine.

Background

In my previous article - How to mass Configure Cisco routers - I discussed how to develop custom scripts using Pretty Good Terminal to solve complex device scripting tasks.

The method described there was rather suitable for developers than network engineers as it required some programming skills and installation / usage of Visual Studio. Since that time Pretty Good Terminal has changed a lot and now a new feature, a Visual Script Editor was introduced.

So why am I writing this article here on Code Project and how does a Visual Script Editor correspond to coding ?

In fact, visual scripts in PGT are nothing else than CustomActionHandlers (discussed in my previous article) built and compiled at runtime by PGT. Under the hood, the vScript execution engine is a c# compiler using CodeDOM and Reflection to provide an intuitive code editor where users can enter their own c# code.

The code editor is built using the popular ScintillaNET component.

Concept

Most of the time scripts - and humans, too :-) - repeat the following basic steps:
- check a device configuration element by issuing show commands
- analyse the response
- based on the result construct a configuration command
 
Of course, one step is rarely enough to decide what to do and more configuration checks are required until
enough information is collected to build the final configuration change.

The best way to design a script is to represent the required steps visually and organizing them to a flowchart.
This is exactly what you can do with PGT's Visual Script Editor. You can add visual script elements and connect them to create a flowchart. Then each element will have its own code executed at runtime, using its own local variables or script global variables to memorize command results. Then connectors again have their own code which is evaluated to decide about the control flow direction, that is, which is the next step to be taken.

A visual script - or vScript - must have a single start element and may have many stop elements. Execution of the vScript starts whenever PGT made a successful connection to a device as specified in a script (not the vScript, but the legacy script which is actually a list a devices along with connection parameters). Then PGT will pass the execution to the vScript.

Let me show you what a vScript is.

Creating a simple script

The best way to understand what a vScript is and how it operates is to discuss a simple example. For this reason let us assume we have a list of routers and we need to update the dialer interface only if :

  • It is a Cisco router
  • Belongs to a specific BGP AS
  • The dialer if bandwidth equals to 128

Without vScript, using only the conventional, CLI commands driven simple scripts this would be a challenging task. However, with vScript it is very simple, straightforward and does not even involve any programming. 

Now let’s see how it works, how to build and use the script.

Just open a new Visual Script Editor from the Actions menu of PGT. If there is any default script presented, select all elements by pressing Ctrl-A - or select with the mouse - and press delete to clear the workspace.

First of all, we need a Start Element. Right click the workspace and from the Add elements menu select the Start element:

In its simplest form, there is nothing to configure on the Start element, so we can continue with adding a Simple Decision element for checking if we are on a Cisco router.

For this purpose, select Simple Decision element from the context menu shown above.

When the Simple Decision Editor appears, enter the following data to the editor :

  • Name : IsCisco
  • Label: Is Cisco ?
  • Command : show version | i [cC][iI][sS][cC][oO]
  • Text to check in answer : Cisco

This will work exactly as one would expect: sends the command “sh version” to the connected device and checks whether the received answer contains the word “Cisco”. This is a simple decision: the answer can be “yes” or “no”. Please note, that this is a case-sensitive operation both at the router operating system (in case of Cisco IOS and also when parsing the response text. In other words, “cisco” is not equal to “Cisco” when evaluating the response. For the command, we can use the syntax show version | i [cC][iI][sS][cC][oO] to match any letter case but the decision element will still evaluate the response in a case sensitive way.

We also need to connect the Start element to this Simple Decision. To do so, go to Start, and select Add Connector from the context menu. The Visual Script designer gets into connection mode and as the mouse is moved over a possible connection target, the element will be highlighted.

From the IsCisco decision element, we have two options: in case the answer was “yes”, continue to the next check, if “no”, stop the script and report back the results.

Let’s start with the “no” branch: first add a Stop element to the script from the context menu. Then go back to the simple decision element, right click on it, and select Add Connector and connect it to the newly added Stop element. 

Now it must be decided whether the added connector will represent the “No” or the “Yes” branch of the decision. For this example select “No” as the goal is to stop the script if we are not on a Cisco device.

At this point we may want to report the result with text and also with a logical expression if the configuration was successful or not. To do so, go to the added Stop element and open its editor by double clicking on it. You may want to assign a display label to this Stop element to better visualize its function. Then go to the Main tab, and enter the following :

As you start typing you will notice that the editor will bring up a popup window for auto completion of the entered text. This way syntax errors can be avoided and you also do not need to remember the correct wording of variables.

The text assigned to the ActionResult variable will be the one appearing in the PGT script window as the Command result. Depending the value of ScriptSuccess, the line in the PGT script window will be coloured green or red.

Following the steps described above, you can build the complete script to get the final one as :

 

Okay, we have now visually built a simple script, we can run it, debug it, and deploy it. Fine. Later I will show you how to do this, but at this point I want to switch to the underlying code and the object model used because this is the main reason I am writing this article here on Code Project.

The vScript object model

Each visual element of the script represent an individual class. More precisely a nested, public class under a class named as ScriptProxy.  The ScriptProxy is the implementation of the IScriptProxy interface which defines all the members required for PGT to interact with the script elements. That is, PGT does not interact directly with the visual script elements, but through the ScriptProxy as it provides a well-defined interface independent of the actual classes inside it.

Let's see how the above simple script appears at code level. Below is the code as generated by PGT internally from the visually constructed script. I know the code seems long to read, but actually very simple and the interesting point is its structure, not what it does:

C#
#define DEBUG
using PGT.ExtensionInterfaces;
using PGT.VisualScripts;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;

namespace PGT.VisualScripts.vs_DialerUpdate4SGT
{
    public partial class ScriptProxy:IScriptProxy
    {
        public static string Name = "vs_DialerUpdate4SGT";
        public static bool BreakExecution = false;
        public static string ActionResult = "vScript <vs_DialerUpdate4SGT> processed successfully";
        public static bool ConnectionDropped = false;
        public static bool ScriptSuccess = true;
        public static IScriptExecutorBase Executor;
        public static IScriptableTerminal STerminal;
        public static DeviceConnectionInfo ConnectionInfo;
        private List<RuntimeScriptElement> Elements;

        public static TStart Start;
        public static TIsCisco IsCisco;
        public static TStop_0 Stop_0;
        public static TCheckBGP CheckBGP;
        public static TStop_1 Stop_1;
        public static TDialerBandwidth DialerBandwidth;
        public static TStop_2 Stop_2;
        public static TBW128 BW128;
        public static TStop_3 Stop_3;
        public static TStart_IsCisco Start_IsCisco;
        public static TIsCisco_Stop_0 IsCisco_Stop_0;
        public static TIsCisco_CheckBGP IsCisco_CheckBGP;
        public static TCheckBGP_Stop_1 CheckBGP_Stop_1;
        public static TCheckBGP_DialerBandwidth CheckBGP_DialerBandwidth;
        public static TDialerBandwidth_BW128 DialerBandwidth_BW128;
        public static TDialerBandwidth_Stop_2 DialerBandwidth_Stop_2;
        public static TBW128_Stop_3 BW128_Stop_3;

        #region ScriptProxy members
        public ScriptProxy()
        {
            Elements = new List<RuntimeScriptElement>();

            Start = new TStart();
            Elements.Add(Start);
            IsCisco = new TIsCisco();
            Elements.Add(IsCisco);
            Stop_0 = new TStop_0();
            Elements.Add(Stop_0);
            CheckBGP = new TCheckBGP();
            Elements.Add(CheckBGP);
            Stop_1 = new TStop_1();
            Elements.Add(Stop_1);
            DialerBandwidth = new TDialerBandwidth();
            Elements.Add(DialerBandwidth);
            Stop_2 = new TStop_2();
            Elements.Add(Stop_2);
            BW128 = new TBW128();
            Elements.Add(BW128);
            Stop_3 = new TStop_3();
            Elements.Add(Stop_3);
            Start_IsCisco = new TStart_IsCisco();
            Elements.Add(Start_IsCisco);
            IsCisco_Stop_0 = new TIsCisco_Stop_0();
            Elements.Add(IsCisco_Stop_0);
            IsCisco_CheckBGP = new TIsCisco_CheckBGP();
            Elements.Add(IsCisco_CheckBGP);
            CheckBGP_Stop_1 = new TCheckBGP_Stop_1();
            Elements.Add(CheckBGP_Stop_1);
            CheckBGP_DialerBandwidth = new TCheckBGP_DialerBandwidth();
            Elements.Add(CheckBGP_DialerBandwidth);
            DialerBandwidth_BW128 = new TDialerBandwidth_BW128();
            Elements.Add(DialerBandwidth_BW128);
            DialerBandwidth_Stop_2 = new TDialerBandwidth_Stop_2();
            Elements.Add(DialerBandwidth_Stop_2);
            BW128_Stop_3 = new TBW128_Stop_3();
            Elements.Add(BW128_Stop_3);

        }

        public class TStart : RuntimeScriptCommand
        {      

            public TStart()
            {
                ID = Guid.Parse("6753d40b-e34d-4108-8b89-d1dcec192fe0");
            }
            public override void Run()
            {

            }
            public override string CommandProvider()
            {
                return "";
            }

        }

        public class TIsCisco : RuntimeScriptCommand
        {      

            public TIsCisco()
            {
                ID = Guid.Parse("b40fbd34-bdee-4c92-beb0-540166176ff5");
            }
            public override void Run()
            {

            }
            public override string CommandProvider()
            {
                return "sh version";
            }

        }

        public class TStop_0 : RuntimeScriptCommand
        {      

            public TStop_0()
            {
                ID = Guid.Parse("071e0418-fc1d-4a26-8083-0987cef8be42");
            }
            public override void Run()
            {
                ActionResult = "Not a Cisco device";
                ScriptSuccess = false;
            }
            public override string CommandProvider()
            {
                return "";
            }

        }

        public class TCheckBGP : RuntimeScriptCommand
        {      

            public TCheckBGP()
            {
                ID = Guid.Parse("045319ba-65ea-4c5e-8c23-4e323281e9a2");
            }
            public override void Run()
            {

            }
            public override string CommandProvider()
            {
                return "sh run | in router bgp";
            }

        }

        public class TStop_1 : RuntimeScriptCommand
        {      

            public TStop_1()
            {
                ID = Guid.Parse("7042f2f5-3087-4fcf-8c35-d96c398f23ea");
            }
            public override void Run()
            {
                ActionResult = "Wrong AS number";
                ScriptSuccess = false;
            }
            public override string CommandProvider()
            {
                return "";
            }

        }

        public class TDialerBandwidth : RuntimeScriptCommand
        {      

            public TDialerBandwidth()
            {
                ID = Guid.Parse("38022f47-a25e-4756-b466-98a2eae7ca1b");
            }
            public override void Run()
            {

            }
            public override string CommandProvider()
            {
                return "sh run int dialer 1 | inc bandwidth";
            }

        }

        public class TStop_2 : RuntimeScriptCommand
        {      

            public TStop_2()
            {
                ID = Guid.Parse("98b449df-1131-4874-902f-e1fc86e85202");
            }
            public override void Run()
            {
                ActionResult = "Other BW";
                ScriptSuccess = false;
            }
            public override string CommandProvider()
            {
                return "";
            }

        }

        public class TBW128 : RuntimeScriptCommand
        {      

            public TBW128()
            {
                ID = Guid.Parse("e83bb686-125f-4f34-b5eb-529d09f62be0");
            }
            public override void Run()
            {

            }
            public override string CommandProvider()
            {
                return "dialer load-threshold 100 either";
            }

        }

        public class TStop_3 : RuntimeScriptCommand
        {      

            public TStop_3()
            {
                ID = Guid.Parse("761fbbc6-c029-482a-b0c8-5614ca1e6830");
            }
            public override void Run()
            {
                ActionResult = "Dialer IF updated successfully";
                ScriptSuccess = true;
            }
            public override string CommandProvider()
            {
                return "";
            }

        }

        public class TStart_IsCisco : RuntimeScriptConnector
        {

            public TStart_IsCisco()
            {
                ID = Guid.Parse("56f137bc-3b69-4c9d-ba04-8c2d7c76f934");
            }
            public override bool EvaluateCondition()
            {
                 return true;;
            }
        }

        public class TIsCisco_Stop_0 : RuntimeScriptConnector
        {

            public TIsCisco_Stop_0()
            {
                ID = Guid.Parse("75c0be8d-cebe-421c-b008-429291d03118");
            }
            public override bool EvaluateCondition()
            {
                 return IsCisco.CommandResult.IndexOf("Cisco") < 0;;
            }
        }

        public class TIsCisco_CheckBGP : RuntimeScriptConnector
        {

            public TIsCisco_CheckBGP()
            {
                ID = Guid.Parse("b4938aec-f5cc-4006-8fe2-5ef85e0a179f");
            }
            public override bool EvaluateCondition()
            {
                 return IsCisco.CommandResult.IndexOf("Cisco") >= 0;;
            }
        }

        public class TCheckBGP_Stop_1 : RuntimeScriptConnector
        {

            public TCheckBGP_Stop_1()
            {
                ID = Guid.Parse("1cb0c89d-5ddc-4420-ba88-3b7ee645e828");
            }
            public override bool EvaluateCondition()
            {
                 return CheckBGP.CommandResult.IndexOf("65100") < 0;;
            }
        }

        public class TCheckBGP_DialerBandwidth : RuntimeScriptConnector
        {

            public TCheckBGP_DialerBandwidth()
            {
                ID = Guid.Parse("d9948aaf-d569-4b76-a291-a821a25b7671");
            }
            public override bool EvaluateCondition()
            {
                 return CheckBGP.CommandResult.IndexOf("65100") >= 0;;
            }
        }

        public class TDialerBandwidth_BW128 : RuntimeScriptConnector
        {

            public TDialerBandwidth_BW128()
            {
                ID = Guid.Parse("5a71de56-3f39-42fa-82b6-565b8d10dc0d");
            }
            public override bool EvaluateCondition()
            {
                 return DialerBandwidth.CommandResult.IndexOf("128") >= 0;;
            }
        }

        public class TDialerBandwidth_Stop_2 : RuntimeScriptConnector
        {

            public TDialerBandwidth_Stop_2()
            {
                ID = Guid.Parse("f992b6a5-7a78-496b-9666-3dad51bd4b61");
            }
            public override bool EvaluateCondition()
            {
                 return DialerBandwidth.CommandResult.IndexOf("128") < 0;;
            }
        }

        public class TBW128_Stop_3 : RuntimeScriptConnector
        {

            public TBW128_Stop_3()
            {
                ID = Guid.Parse("a0c54fb8-94d3-45f3-9c30-e18ccd9de7c3");
            }
            public override bool EvaluateCondition()
            {
                 return true;;
            }
        }
       #endregion

    }
}
It can be seen that each visual script element corresponds to a runtime class nested into ScriptProxy class. ScriptProxy implements the IScriptProxy interface (no need to go into details of it) and provides a means of interaction between the vScript execution engine and the runtime generated script elements. The important point is that each script element have its own unique which is the link between the runtime generated class and the visually designed element. All of them are instantiated in the ScriptProxy constructor and are added to the list of known elements which list is used internally by ScriptProxy to provide interaction.
 
The runtime generated classes descend either from RuntimeScriptCommand or RuntimeScriptConnector, while their common ancestor is RuntimeScriptElement as shown below:

The key to script execution is that both RuntimeScriptCommand and RuntimeScriptConnector classes have abstract methods which PGT can call as the script executes. 

For example, look at TStart:

C#
public class TStart : RuntimeScriptCommand
{      
    public TStart()
    {
        ID = Guid.Parse("6753d40b-e34d-4108-8b89-d1dcec192fe0");
    }
    public override void Run()
    {
    }
    public override string CommandProvider()
    {
        return "";
    }
}

CommandProvider is used to get the CLI command that will be sent to a device, and Run() contains the code entered in the Main block of a Command element. PGT will call these two methods when the Start element is executed.

Connectors, on the other hand, are more simple stuff. They have just one method, the EvaluateCondition() which returns a boolean value. If the returned value is True, PGT assumes execution should flow to the connected element of this connector. For instance the connector pointing from Check BGP AS 6500? to Dialer BW 128? element evaluates the CommandResult variable of CheckBGP if the answer contained the string 65000:

C#
public class TCheckBGP_DialerBandwidth : RuntimeScriptConnector
{  
    public TCheckBGP_DialerBandwidth()
    {
        ID = Guid.Parse("d9948aaf-d569-4b76-a291-a821a25b7671");
    }
    public override bool EvaluateCondition()
    {
        return CheckBGP.CommandResult.IndexOf("65100") >= 0;;
    }
}

As Start actually does not do anything interesting in this example, lets see another script which is about adding a new static route to a host.

First see the script in the Visual Script Editor, focusing on the "Static route add" command element first:

As you can see, this command element has some variables and a Main code block to process the CommandResult variable of the start element - will show you what it is - and extract the next hop address of a route to store it in its local variable next_hop_ip. Then in the Commands block uses this variable to construct the CLI command to be sent to the router.

Let's see the generated code of the vScript:

C#
using PGT.ExtensionInterfaces;
using PGT.VisualScripts;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;

namespace PGT.VisualScripts.new_vScript
{
  public partial class ScriptProxy:IScriptProxy
  {
    public static string Name = "new_vScript";
    public static bool BreakExecution = false;
    public static string ActionResult = "vScript <new_vScript> processed successfully";
    public static bool ConnectionDropped = false;
    public static bool ScriptSuccess = true;
    public static IScriptExecutorBase Executor;
    public static IScriptableTerminal STerminal;
    public static DeviceConnectionInfo ConnectionInfo;
    private List<RuntimeScriptElement> Elements;

    public static TStart Start;
    public static TCommand Command;
    public static TStop_0 Stop_0;
    public static TStop_1 Stop_1;
    public static TKnownGW KnownGW;
    public static TStart_CommandOnNo Start_CommandOnNo;
    public static TNoGW NoGW;


    #region ScriptProxy members
    public ScriptProxy()
    {
      Elements = new List<RuntimeScriptElement>();

      Start = new TStart();
      Elements.Add(Start);
      Command = new TCommand();
      Elements.Add(Command);
      Stop_0 = new TStop_0();
      Elements.Add(Stop_0);
      Stop_1 = new TStop_1();
      Elements.Add(Stop_1);
      KnownGW = new TKnownGW();
      Elements.Add(KnownGW);
      Start_CommandOnNo = new TStart_CommandOnNo();
      Elements.Add(Start_CommandOnNo);
      NoGW = new TNoGW();
      Elements.Add(NoGW);

    }

    public class TStart : RuntimeScriptCommand
    {

      public TStart()
      {
        ID = Guid.Parse("6442f81e-1ce9-4148-9bdc-9707f475ae8c");
      }
      public override void Run()
      {

      }
      public override string CommandProvider()
      {
        return "sh run | i ip route 192.168.10.0";
      }

    }

    public class TCommand : RuntimeScriptCommand
    {
      public string[] fields;
      public string next_hop_ip;
      public TCommand()
      {
        ID = Guid.Parse("5e054322-8eb4-40dd-b754-2949933bc6de");
      }
      public override void Run()
      {
        fields = Start.CommandResult.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
        if(fields.Length>0) next_hop_ip = fields[4].Replace("\r", "").Replace("\n", "");
      }
      public override string CommandProvider()
      {
        if (string.IsNullOrEmpty(next_hop_ip)) return "";
      else return "conf t;ip route 10.1.1.1 255.255.255.255 " + next_hop_ip +";exit";
      }

    }

    public class TStop_0 : RuntimeScriptCommand
    {

      public TStop_0()
      {
        ID = Guid.Parse("dade9f45-0a91-4daa-b1e1-4c422e814b51");
      }
      public override void Run()
      {
        ActionResult = "Static route configured";
        ScriptSuccess = true;
      }
      public override string CommandProvider()
      {
        return "";
      }

    }

    public class TStop_1 : RuntimeScriptCommand
    {

      public TStop_1()
      {
        ID = Guid.Parse("b0d144b8-56d2-4e7a-8672-f8c904d7c4f0");
      }
      public override void Run()
      {
        ActionResult = "Could find gateway";
        ScriptSuccess = false;
      }
      public override string CommandProvider()
      {
        return "";
      }

    }

    public class TKnownGW : RuntimeScriptConnector
    {

      public TKnownGW()
      {
        ID = Guid.Parse("22537898-4633-4b25-8940-791cff434f45");
      }
      public override bool EvaluateCondition()
      {
         return true;;
      }
    }

    public class TStart_CommandOnNo : RuntimeScriptConnector
    {

      public TStart_CommandOnNo()
      {
        ID = Guid.Parse("7a781dbf-4e46-4448-8f5f-b770472a1be0");
      }
      public override bool EvaluateCondition()
      {
         return true;;
      }
    }

    public class TNoGW : RuntimeScriptConnector
    {

      public TNoGW()
      {
        ID = Guid.Parse("e209a3c1-80d1-4c68-986d-732c4eb80642");
      }
      public override bool EvaluateCondition()
      {
        return  string.IsNullOrEmpty(Command.next_hop_ip);
      }
    }


    #endregion
  }
}

First check the Start element code. The commandProvider() method will now returna valid command to check for an existing route, so it will be sent to the connected router. All runtime script elements has a variable named CommandResult which will store the response received from the connected device. We will use this in TCommand's Run() method to extract the next hop ip from the response - which is the fourth word - and store it in the local variable next_hop_ip:

C#
public class TCommand : RuntimeScriptCommand
{      
    public string[] fields;
    public string next_hop_ip;
    public TCommand()
    {
        ID = Guid.Parse("5e054322-8eb4-40dd-b754-2949933bc6de");
    }
    public override void Run()
    {
        fields = Start.CommandResult.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
        if(fields.Length>4) next_hop_ip = fields[4].Replace("\r", "").Replace("\n", "");
    }
    public override string CommandProvider()
    {
        if (string.IsNullOrEmpty(next_hop_ip)) return "";
        else return "conf t;ip route 10.1.1.1 255.255.255.255 " + next_hop_ip +";exit";
    }    
}

Then in CommandProvider() method we check the variable, and if it is not empty, we build the configuration commands we want to send to the router.

A step further

For the vScript to successfully execute, PGT requires that a runtime command element has the Run() and CommandProvider() methods. But why limit the class to only these two methods ? To make a runtime command element more versatile, you can define a custom code block of the element, which code is injected to the class as is. That is, the code block should be formatted to syntactically fit into a class. Let's see an example how to extend our TCommand class from the above example:

C#
public class TCommand : RuntimeScriptCommand
{      
    public string[] fields;
    public string next_hop_ip;
    public TCommand()
    {
        ID = Guid.Parse("5e054322-8eb4-40dd-b754-2949933bc6de");
    }
    public override void Run()
    {
        fields = Start.CommandResult.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
        if(fields.Length>4) next_hop_ip = fields[4].Replace("\r", "").Replace("\n", "");
    }
    public override string CommandProvider()
    {
        if (string.IsNullOrEmpty(next_hop_ip)) return "";
        else return "conf t;ip route 10.1.1.1 255.255.255.255 " + next_hop_ip +";exit";
    }    

    //<This is the placeholder of a custom code block>

}

As you see, the custom code block can contain any number of methods or variables.  Although PGT will not directly call these methods, you can then can do so in either the Main code block or the Commands code block. Consider the below example, where we add two class methods:

Then we can make use of them from Main:

And the code generated in the background is:

C#
public class TCommand : RuntimeScriptCommand
{      
    public string[] fields;
    public string next_hop_ip;
    public TCommand()
    {
        ID = Guid.Parse("5e054322-8eb4-40dd-b754-2949933bc6de");
    }
    public override void Run()
    {
        fields = Start.CommandResult.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
        if(fields.Length>0) next_hop_ip = fields[4].Replace("\r", "").Replace("\n", "");
        Foo();
    }

    public override string CommandProvider()
    {
        if (string.IsNullOrEmpty(next_hop_ip)) return "";
    else return "conf t;ip route 10.1.1.1 255.255.255.255 " + next_hop_ip +";exit";
    }

    public void Foo()
    {
        if (DummyCheck(3)) Debug.Write("wow, 3 is greater than 0");
        else Debug.Write("I am confused about math...");
    }
    
    public bool DummyCheck(int i)
    {
        return (i > 0);
    }    
}

To make things even more elastic, you can add custom usings and external assembly references to the vScript.

For instance, if you may have a file or database containing information about devices that you need to access to sucessfully update a device,. To do this you probably want to use the File class which is not accessible by default from a vScript. In order to use the File class you only need to add System.IO to the usings section of the code for a particulare vScript. Go to vScript Parameters editor by clicking the Parameters button in the Visual Script Editor:

And so the File class gets known to the code editor:

Conclusion

In this short tutorial I demonstrated how easy to develop a Visal Script with Pretty Good Terminal is. With some level of programming skills a Visual Script can simply be tuned to include custom c# code and variables necessary to do any computation that might be required for a more complex task.

If you found this article interesing, you can downlad the software on PGT website.

Should you have any questions, feel free to cantact me.

 

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0