65.9K
CodeProject 正在变化。 阅读更多。
Home

使用可视化脚本开发进行路由器批量配置

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2016 年 1 月 18 日

Apache

11分钟阅读

viewsIcon

9943

使用 Pretty Good Terminal 进行可视化脚本

目标受众

如果网络自动化软件是您感兴趣的领域,您需要处理大量网络设备,并且您是一位经验丰富的程序员,那么您可能会觉得本文很有趣。

引言

在本文中,我想向您展示 Pretty Good Terminal 的可视化脚本功能。我在这里发布这篇文章,因为可视化脚本实际上是运行时生成的 C# 代码,由 PGT 动态编译和执行。

虽然简单的脚本不需要任何编程技能,但 vScripts 提供了一个通用的基础设施来包含用户定义的 C# 类、变量和代码,甚至可以引用和使用外部程序集,如果有人能够利用 .NET 的全部功能来创建复杂的脚本。

本质上,我想向您展示所使用的对象模型以及可视化脚本执行引擎的运行方式。

背景

在我之前的文章 - 如何批量配置 Cisco 路由器 - 我讨论了如何使用 Pretty Good Terminal 开发自定义脚本来解决复杂的设备脚本任务。

那里描述的方法更适合开发人员而非网络工程师,因为它需要一些编程技能以及 Visual Studio 的安装/使用。从那时起,Pretty Good Terminal 发生了很大变化,现在引入了一个新功能:可视化脚本编辑器。

那么,我为什么要在 Code Project 上写这篇文章,可视化脚本编辑器又与编码有什么关系呢?

事实上,PGT 中的可视化脚本只不过是在运行时由 PGT 构建和编译的 CustomActionHandlers(在我之前的文章中讨论过)。在底层,vScript 执行引擎是一个 C# 编译器,它使用 CodeDOM 和 Reflection 来提供一个直观的代码编辑器,用户可以在其中输入自己的 C# 代码。

代码编辑器是使用流行的 ScintillaNET 组件构建的。

概念

脚本(以及人类 :-))大多数时候会重复以下基本步骤:
- 通过发出 show 命令检查设备配置元素
- 分析响应
- 根据结果构造配置命令
 
当然,一个步骤很少足以决定该做什么,并且需要更多的配置检查,直到
收集到足够的信息来构建最终的配置更改。

设计脚本的最佳方法是直观地表示所需的步骤,并将它们组织成流程图。
这正是您可以使用 PGT 的可视化脚本编辑器所能做到的。您可以添加可视化脚本元素并将它们连接起来创建流程图。然后,每个元素将在运行时执行自己的代码,使用其自己的局部变量或脚本全局变量来记住命令结果。然后连接器本身的代码将被评估,以决定控制流方向,即下一步要采取的步骤。

可视化脚本(或 vScript)必须有一个开始元素,并且可以有多个停止元素。每当 PGT 成功连接到脚本(不是 vScript,而是传统的脚本,实际上是设备列表以及连接参数)中指定的设备时,vScript 就会开始执行。然后 PGT 会将执行交给 vScript。

让我向您展示 vScript 是什么。

创建简单的脚本

理解 vScript 是什么以及它是如何工作的最佳方法是讨论一个简单的例子。为此,让我们假设我们有一个路由器列表,并且我们需要更新拨号接口,前提是:

  • 它是一台 Cisco 路由器
  • 属于特定的 BGP AS
  • 拨号接口带宽等于 128

没有 vScript,仅使用传统的、由 CLI 命令驱动的简单脚本,这将是一项艰巨的任务。然而,有了 vScript,它就非常简单、直接,甚至不需要任何编程。

现在让我们看看它是如何工作的,如何构建和使用脚本。

只需从 PGT 的“操作”菜单中打开一个新的“可视化脚本编辑器”。如果存在任何默认脚本,请按 Ctrl-A 选择所有元素 - 或用鼠标选择 - 然后按 Delete 清除工作区。

首先,我们需要一个“开始”元素。右键单击工作区,然后从“添加元素”菜单中选择“开始”元素。

最简单的形式是,“开始”元素没有任何需要配置的内容,因此我们可以继续添加一个“简单决策”元素来检查我们是否在 Cisco 路由器上。

为此,请从上面的上下文菜单中选择“简单决策”元素。

当出现“简单决策编辑器”时,向编辑器输入以下数据:

  • 名称:IsCisco
  • 标签:是 Cisco 吗?
  • 命令:show version | i [cC][iI][sS][cC][oO]
  • 在答案中要检查的文本:Cisco

这将按预期工作:向连接的设备发送“sh version”命令,并检查收到的答案是否*包含*“Cisco”一词。这是一个简单的决策:答案可以是“是”或“否”。请注意,在路由器操作系统(对于 Cisco IOS)以及解析响应文本时,这是一个区分大小写的操作。换句话说,“cisco”与“Cisco”在评估响应时是不同的。对于命令,我们可以使用语法 show version | i [cC][iI][sS][cC][oO] 来匹配任何字母大小写,但决策元素仍然会区分大小写地评估响应。

我们还需要将“开始”元素连接到这个“简单决策”。为此,请转到“开始”,然后从上下文菜单中选择“添加连接器”。可视化脚本设计器将进入连接模式,并且当鼠标悬停在可能的连接目标上时,元素将被高亮显示。

从 IsCisco 决策元素,我们有两个选项:如果答案是“是”,则继续下一步检查;如果答案是“否”,则停止脚本并报告结果。

让我们从“否”分支开始:首先从上下文菜单中向脚本添加一个“停止”元素。然后返回到简单决策元素,右键单击它,选择“添加连接器”,然后将其连接到新添加的“停止”元素。 

现在必须决定添加的连接器将代表决策的“否”还是“是”分支。在此示例中,选择“否”,因为目标是在非 Cisco 设备时停止脚本。

此时,我们可能希望通过文本和逻辑表达式来报告配置是否成功。为此,请转到已添加的“停止”元素,然后通过双击打开其编辑器。您可能希望为该“停止”元素分配一个显示标签,以更好地可视化其功能。然后转到“主”选项卡,然后输入以下内容:

当您开始输入时,您会注意到编辑器会弹出一个窗口,用于自动完成输入的文本。这样可以避免语法错误,您也不需要记住变量的正确措辞。

分配给 ActionResult 变量的文本将显示在 PGT 脚本窗口中作为命令结果。根据 ScriptSuccess 的值,PGT 脚本窗口中的行将显示为绿色或红色。

按照上述步骤,您可以构建完整的脚本,最终脚本如下:

 

好的,我们现在已经可视化地构建了一个简单的脚本,我们可以运行它、调试它并部署它。很好。稍后我将向您展示如何执行此操作,但此时我想切换到底层代码和所使用的对象模型,因为这是我在这里 Code Project 上写这篇文章的主要原因。

vScript 对象模型

脚本的每个可视化元素都代表一个单独的类。更确切地说,它是 ScriptProxy 类下的一个嵌套的公共类。ScriptProxy 是 IScriptProxy 接口的实现,该接口定义了 PGT 与脚本元素交互所需的所有成员。也就是说,PGT 不直接与可视化脚本元素交互,而是通过 ScriptProxy 交互,因为它提供了一个明确定义的接口,独立于其内部的实际类。

让我们看看上面简单的脚本在代码级别上是如何显示的。下面是 PGT 从可视化构建的脚本内部生成的代码。我知道这段代码看起来很长,但实际上很简单,有趣的是它的结构,而不是它的功能。

#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

    }
}
可以看出,每个可视化脚本元素都对应一个嵌套在 ScriptProxy 类中的运行时类。ScriptProxy 实现 IScriptProxy 接口(无需详细介绍),并提供了 vScript 执行引擎与运行时生成的脚本元素之间的交互方式。重要的是,每个脚本元素都有其唯一的标识,这是运行时生成的类与可视化设计的元素之间的链接。所有这些都在 ScriptProxy 构造函数中实例化,并添加到已知元素列表中,ScriptProxy 内部使用此列表来提供交互。
 
运行时生成的类要么继承自 RuntimeScriptCommand,要么继承自 RuntimeScriptConnector,而它们的共同祖先是 RuntimeScriptElement,如下所示:

脚本执行的关键在于,RuntimeScriptCommand 和 RuntimeScriptConnector 类都有 PGT 在脚本执行时可以调用的抽象方法。

例如,看看 TStart

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

CommandProvider 用于获取将发送到设备的 CLI 命令,而 Run() 包含在 Command 元素的主块中输入的代码。PGT 在执行 Start 元素时将调用这两个方法。

另一方面,连接器则更简单。它们只有一个方法,即 EvaluateCondition(),它返回一个布尔值。如果返回值是 True,PGT 将假定执行应该流向该连接器的连接元素。例如,指向 Check BGP AS 6500? 到 Dialer BW 128? 元素的连接器在答案包含字符串 65000 时评估 CheckBGP 的 CommandResult 变量。

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;;
    }
}
 

由于 Start 在此示例中实际上并没有做任何有趣的事情,让我们看看另一个关于向主机添加新静态路由的脚本。

首先在可视化脚本编辑器中查看脚本,重点关注“静态路由添加”命令元素。

如您所见,此命令元素包含一些变量和一个 Main 代码块,用于处理 Start 元素的 CommandResult 变量(稍后会向您展示是什么),并提取路由的下一跳地址以存储在其局部变量 next_hop_ip 中。然后在 Commands 块中使用此变量来构造要发送到路由器的 CLI 命令。

让我们看看 vScript 生成的代码。

      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
        }
      }
      

首先检查 Start 元素代码。commandProvider() 方法现在将返回一个有效的命令来检查现有路由,因此它将被发送到连接的路由器。所有运行时脚本元素都有一个名为 CommandResult 的变量,该变量将存储从连接设备接收到的响应。我们将在 TCommand 的 Run() 方法中使用它来从响应中提取下一跳 IP(它是第四个单词),并将其存储在局部变量 next_hop_ip 中。

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";
    }    
}

然后在 CommandProvider() 方法中,我们检查该变量,如果它不为空,我们就构建要发送到路由器的配置命令。

更进一步

为了使 vScript 能够成功执行,PGT 要求运行时命令元素具有 Run() 和 CommandProvider() 方法。但为什么只将类限制在这两个方法上呢?为了使运行时命令元素更具通用性,您可以定义元素的自定义代码块,该代码块会按原样注入到类中。也就是说,代码块的格式应在语法上适合类。让我们看一个如何扩展上面示例中的 TCommand 类的示例。

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>

}

如您所见,自定义代码块可以包含任意数量的方法或变量。尽管 PGT 不会直接调用这些方法,但您可以在 Main 代码块或 Commands 代码块中调用它们。考虑下面的示例,我们在其中添加了两个类方法:

然后我们可以在 Main 中使用它们:

以及后台生成的代码是:

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);
    }    
}

为了使事情更加灵活,您可以为 vScript 添加自定义 using 和外部程序集引用。

例如,您可能有一个包含设备信息的ファイル或数据库,您需要访问这些信息才能成功更新设备。要做到这一点,您可能希望使用 File 类,而该类默认情况下无法从 vScript 访问。为了使用 File 类,您只需为您特定的 vScript 添加代码的 using 部分的 System.IO。通过单击可视化脚本编辑器中的“参数”按钮,转到 vScript 参数编辑器。

这样,File 类就可以被代码编辑器识别了。

结论

在这个简短的教程中,我演示了使用 Pretty Good Terminal 开发可视化脚本有多么容易。通过一定程度的编程技能,可视化脚本可以简单地进行调整,以包含自定义 C# 代码和变量,以执行更复杂的任务所需的任何计算。

如果您发现本文有趣,您可以在 PGT 网站上下载该软件。

如果您有任何疑问,请随时与我联系。

 

© . All rights reserved.