pyATS/Genie自作スクリプトで作業前後のConfigの差分取ってみた

最近わたしの記事はpyATSばっかり

# pyATSってなあに

Ciscoが開発している、検証用のPythonライブラリです。
細かいところは端折らせてください、、
詳しくは↓
[公式ドキュメント](https://developer.cisco.com/docs/pyats/)
[私の紹介記事](https://qiita.com/snamon/items/6f5f67a0162d4dd6fb5e)
最近のQiitaで特に自動化界隈で何かと話題（と私は思っている）

## 作成したもの
何を作成したかというと、用意されたConfigを対象の機器に正しく投入して、
投入された結果がshow running-configで差分として表示され、さらにgenie learnで取得した構造化された出力をDiffして差分をわかりやすく表示すると言った内容になります。
今回は開始前のConfigに戻すような作りにしてあります。


```
├── check_acl.py   ←スクリプトの本体
├── testbed
│   └── devnet_always.yml　←testbedふぁいる
└── vars
    ├── csr1000v-1.yml　←testbedに記載しているNodeのVars
    ├── iosxr1.yml
    └── sbx-n9kv-ao.yml
```


### testbed
接続先の情報がまとまっているファイル、今回は下記のものを用意
ちなみにこちらの説明は[前回の記事](https://qiita.com/snamon/items/6f5f67a0162d4dd6fb5e)出させていただいているので割愛させてください。

```devnet_always.yml
---
testbed:
  name: SandBox

devices:
  sbx-n9kv-ao:
    alias: uut
    credentials:
      default:
        username: admin
        password: Admin_1234!
    connections:
      vty:
        protocol: ssh
        ip: sbx-nxos-mgmt.cisco.com
        port: 8181
    os: nxos
    type: nxos

  iosxr1:
    alias: rt
    credentials:
      default:
        username: admin
        password: C1sco12345
    connections:
      vty:
        protocol: ssh
        ip: sbx-iosxr-mgmt.cisco.com
        port: 8181
    os: iosxr
    type: iosxr

  csr1000v-1:
      alias: xe1
      credentials:
        default:
          username: developer
          password: C1sco12345
      connections:
        vty:
          protocol: ssh
          ip: ios-xe-mgmt-latest.cisco.com
          port: 8181
      os: iosxe
      type: iosxe

  csr1000v:
      alias: xe0
      credentials:
        default:
          username: developer
          password: C1sco12345
      connections:
        vty:
          protocol: ssh
          ip: ios-xe-mgmt.cisco.com
          port: 8181
      os: iosxe
      type: iosxe

```

### Varsファイル（機器ごとに用意）
中身を説明すると`command`には設定投入に必要なConfig、`clear`には設定戻しに必要なConfigを記入しています。


IOSXEのcsr1000v-1にはStaticRouteとACLを追加

```csr1000v-1.yml
---
command: |
  ip route 8.8.8.8 255.255.255.255 GigabitEthernet1 10.10.20.254
  ip access-list extended TEST
  permit ip any 8.8.8.8 0.0.0.0 log
  remark ****TEST****

clear: |
  no ip route 8.8.8.8 255.255.255.255 GigabitEthernet1 10.10.20.254
  no ip access-list extended TEST

```

IOSXRのiosxr1はファイルのみ用意して、中身は空っぽです。

```iosxr1.yml
---
command: |

clear: |

```

Testbedにあるcsr1000vのVarsはあえて用意していません。
続いて自作したScriptの紹介をしますPythonで書いてあります、
醜い、イケテナイ！と感じた方はぜひ教えてください、業務で使っているわけでもなく検証で使っているので
バリバリのプログラマの方々にはへっぽこコードと感じるかもしれませんが、ご容赦ください。
### Script

```check_acl.py

import logging
import yaml

#Genieモジュールをインポート
from genie.conf import Genie
from genie.utils.diff import Diff

class setupDevices(object):
  def __init__(self):
    #Device格納用
    self.listDevice = list()

    #running-config格納用
    self.beforeConfig = list()
    self.afterConfig = list()

    #varsファイルがあるDeviceのみ格納
    self.targetDevice = list()
    self.config = dict()

    #routing格納用
    self.beforeRouting = list()
    self.afterRouting = list()

    #acl格納用
    self.beforeAcl = list()
    self.afterAcl = list()
  #テストベッドを初期化
    try:
      self.testbed = Genie.init('testbed/devnet_always.yml')
      if self.testbed :
        logging.info('testbed init:OK')
      for keys in self.testbed.devices:
          self.listDevice.append(keys)
    except TypeError:
      logging.warning("Testbedファイルがおかしい")

  #Varファイルがあるか確認
    for device in self.listDevice:
      try:
        with open('vars/{}.yml'.format(device), 'r') as yml:
          device_config = { device + "_config" :yaml.safe_load(yml)}
          self.config.update(device_config)
          self.targetDevice.append(device)
      except FileNotFoundError:
        logging.warning("{}のファイルない".format(device))
        continue

    #loginの処理に入る
    self.login()

  #ログイン
  def login(self):
    logging.info("*"*32 + "ログイン" + "*"*32)

    for device in self.targetDevice:
      self.testbed.devices[device].connect(log_stdout=False)
      if self.testbed.devices[device].is_connected():
        logging.info("{}: connected".format(device))
      else:
        logging.info("{}: disconnected".format(device))

  # 事前ログ取得
  def getBeforeAcl(self):
    logging.info("*"*32 + "事前ACL確認" + "*"*32)
    for device in self.targetDevice:
      self.beforeAcl.append(self.testbed.devices[device].learn('acl'))
      logging.info("{}: beforeAcl get".format(device))
  def getBeforeConfig(self):
    logging.info("*"*32 + "事前ログ取得"+ "*"*32)
    for device in self.targetDevice:
      self.beforeConfig.append(self.testbed.devices[device].learn('config'))
      logging.info("{}: beforeConfig get".format(device))
  def getBeforeRoute(self):
    logging.info("*"*32 + "事前RoutingTabel確認" + "*"*32)
    for device in self.targetDevice:
      self.beforeRouting.append(self.testbed.devices[device].learn('routing'))
      logging.info("{}: beforeRouting get".format(device))

  #Config投入
  def inputConfig(self):
    logging.info("*"*32 + "Config投入" + "*"*32)
    for device in self.targetDevice:
      self.testbed.devices[device].configure(self.config[device+"_config"]['command'])
      logging.info("{}: input_config".format(device))
      
  #投入したConfig削除
  def claerConfig(self):
    logging.info("*"*32 + "Config戻し" + "*"*32)
    for device in self.targetDevice:
      self.testbed.devices[device].configure(self.config[device+"_config"]['clear'])
      logging.info("{}: clear_config".format(device))

  #事後Log取得
  def getAfterConfig(self):
    logging.info("*"*32 + "事後ログ取得" + "*"*32)
    for device in self.targetDevice:
      self.afterConfig.append(self.testbed.devices[device].learn('config'))
      logging.info("{}: afterConfig get".format(device))
  def getAfterAcl(self):
    logging.info("*"*32 + "事後ACL確認" + "*"*32)
    for device in self.targetDevice:
      self.afterAcl.append(self.testbed.devices[device].learn('acl'))
      logging.info("{}: afterAcl get".format(device))
  def getAfterRoute(self):
    logging.info("*"*32 + "事後RoutingTabel確認" + "*"*32)
    for device in self.targetDevice:
      self.afterRouting.append(self.testbed.devices[device].learn('routing'))
      logging.info("{}: afterRouting get".format(device))

  #ログアウト
  def logout(self):
    logging.info("*"*32 + "ログアウト" + "*"*32)
    for device in self.targetDevice:
      self.testbed.devices[device].disconnect()
      if self.testbed.devices[device].is_connected():
        logging.info("{}: connected".format(device))
      else:
        logging.info("{}: disconnected".format(device))

  #ConfigをDiff
  def diffConfig(self):
    logging.info("*"*32 + "Diff" + "*"*32)
    if self.beforeConfig and self.afterConfig:
      for config_b,config_a,device in zip(self.beforeConfig,self.afterConfig,self.self.targetDevice):
        config_diff = Diff(config_b,config_a)
        config_diff.findDiff()
        print("\n====={}_ConfigDiff=====".format(device))
        print(config_diff)
    if self.beforeRouting and self.afterRouting:
      for config_b,config_a,device in zip(self.beforeRouting,self.afterRouting,self.self.targetDevice):
        config_diff = Diff(config_b,config_a)
        config_diff.findDiff()
        print("\n====={}_RouteDiff=====".format(device))
        print(config_diff)
    if self.beforeAcl and self.afterAcl:
      for config_b,config_a,device in zip(self.beforeAcl,self.afterAcl,self.self.targetDevice):
        config_diff = Diff(config_b,config_a)
        config_diff.findDiff()
        print("\n====={}_AclDiff=====".format(device))
        print(config_diff)

if __name__ == "__main__":
  #ログレベルを info に変更
  logging.basicConfig(level=logging.INFO)
  logging.info("*"*32 + "処理開始" + "*"*32)
  devnet = setupDevices()
  devnet.getBeforeConfig()
  devnet.getBeforeRoute()
  devnet.getBeforeAcl()
  devnet.inputConfig()
  devnet.getAfterConfig()
  devnet.getAfterRoute()
  devnet.getAfterAcl()
  devnet.claerConfig()
  devnet.logout()
  devnet.diffConfig()
  logging.info("*"*32 + "終了" + "*"*32)

```

# 実行結果の説明

一気に貼るとLogがぼうだいなので、小分けにして説明します
`python check_acl.py`でスクリプト実行します。

起動時の処理として、testbedファイルを読み込みます。
続いて、testbedにあるDevice名＋_config.ymlファイルを読み込みます。
この時、対象のyamlファイルがない場合、「〇〇〇のファイルがない」とメッセージを出しています。

```
(nw_automate) 00:26:33 my Script $python check_acl.py
INFO : ********************************処理開始********************************
INFO : testbed init:OK
WARNING : sbx-n9kv-aoのファイルない
WARNING : csr1000vのファイルない
```

続いてログインの処理ですが、
`devices[device].connect(log_stdout=False)`でログインをします。
この時、`log_stdout=False`を指定しているので、ログイン時のログが表示されません。

```
INFO : ********************************ログイン********************************
INFO : iosxr1: connected
INFO : csr1000v-1: connected
```

ログインが完了したら、各種事前ログを取得します。
取得後に各種事前の状態をListに格納していきます。

| 確認したい項目 | 取得できるOps |
|:-------------|:------------|
| RunningConfig | device['デバイス名'].learn('config') |
| ACL | device['デバイス名'].learn('acl') |
| Routing | device['デバイス名'].learn('routing') |

※ログが大量に出ましたのでいい具合に省略しています。

```
INFO : ********************************事前ログ取得********************************
INFO : iosxr1: beforeConfig get
INFO : csr1000v-1: beforeConfig get
INFO : ********************************事前RoutingTabel確認********************************
INFO : Could not learn <class 'genie.libs.parser.iosxr.show_routing.ShowRouteIpv6Distributor'>                                                                                                           'genie.libs.parser.iosxr.show_routing.ShowRouteIpDistributor'>                                                                       'genie.libs.parser.iosxr.show_routing.ShowRouteIpv6Distributor'>                                                                     
INFO : iosxr1: beforeRouting get
INFO : Could not learn <class 'genie.libs.parser.iosxe.show_routing.ShowIpv6RouteDistributor'>
'genie.libs.parser.iosxe.show_routing.ShowIpRouteDistributor'>                                                                       'genie.libs.parser.iosxe.show_routing.ShowIpv6RouteDistributor'>                                                                     
INFO : csr1000v-1: beforeRouting get
INFO : ********************************事前ACL確認********************************
INFO : Could not learn <class 'genie.libs.parser.iosxr.show_acl.ShowAclAfiAll'>
Parser Output is empty
INFO : Could not learn <class'genie.libs.parser.iosxr.show_acl.ShowAclEthernetServices'>
INFO : | Commands for learning feature 'Acl'                                                                                                               
INFO : | - Commands with empty output                                                                                                                       
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_acl.ShowAclAfiAll'>  
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_acl.ShowAclEthernetServices'>                                                                          
INFO : iosxr1: beforeAcl get
INFO : | Commands for learning feature 'Acl'                                                                                                               
INFO : |   cmd: <class 'genie.libs.parser.iosxe.show_acl.ShowAccessLists'>                                                                                  
INFO : csr1000v-1: beforeAcl get
```

続いて、Varsファイルの`[command]`に記載されているConfigを投入します。
投入後に事後ログとして事前に取ったログと同様のものを取得します。（省略・・・）

```
INFO : ********************************Config投入********************************
INFO : iosxr1: input_config
INFO : csr1000v-1: input_config
INFO : ********************************事後ログ取得********************************
INFO : iosxr1: afterConfig get
INFO : csr1000v-1: afterConfig get
INFO : ********************************事後RoutingTabel確認********************************
```

事後ログを取得後に、Configも元に戻します。
varsファイルの`clear`に記載されている内容で戻します。
※今気付いたんですが、戻しを実施した後、本当に戻ったのか？を確認するための処理がなかったですね。。

その後、ログアウトを実施します。
ログアウト後に現在のコネクションステータスを確認して状態を表示させています。

```
INFO : ********************************Config戻し********************************
INFO : iosxr1: clear_config
INFO : csr1000v-1: clear_config
INFO : ********************************ログアウト********************************
INFO : iosxr1: disconnected
INFO : csr1000v-1: disconnected
```

最後に、取得した事前Confgiと事後ConfigをDiffします。
文の先頭に＋かーの符号がついていますが、
＋が追加で
ーが削除です。

例えば、iosxr1のConfigDiffでは、階層をいい具合に表示してくれます。
これにより、単純にTextを比較するよりかなりわかりやすいのではないでしょうか？

```
INFO : ********************************Diff********************************

=====iosxr1_ConfigDiff=====
+Wed Dec 18 16:57:14.949 UTC:
-Wed Dec 18 16:56:58.216 UTC:
router static:
 address-family ipv4 unicast:
+  8.8.8.8/32 10.10.20.254:

=====csr1000v-1_ConfigDiff=====
+Current configuration : 7620 bytes:
+ip route 8.8.8.8 255.255.255.255 GigabitEthernet1 10.10.20.254:
-Current configuration : 7557 bytes:

=====iosxr1_RouteDiff=====
info:
 vrf:
  default:
   address_family:
    ipv4:
     routes:
+      8.8.8.8/32:
+       active: True
+       metric: 0
+       next_hop:
+        next_hop_list:
+         1:
+          index: 1
+          next_hop: 10.10.20.254
+          updated: 00:00:08
+       route: 8.8.8.8/32
+       route_preference: 1
+       source_protocol: static
+       source_protocol_codes: S

=====csr1000v-1_RouteDiff=====
info:
 vrf:
  default:
   address_family:
    ipv4:
     routes:
+      8.8.8.8/32:
+       active: True
+       metric: 0
+       next_hop:
+        next_hop_list:
+         1:
+          index: 1
+          next_hop: 10.10.20.254
+          outgoing_interface: GigabitEthernet1
+       route: 8.8.8.8/32
+       route_preference: 1
+       source_protocol: static
+       source_protocol_codes: S

=====iosxr1_AclDiff=====


=====csr1000v-1_AclDiff=====

INFO : ********************************終了********************************
```

# まとめ
コードばかりで、読みづらい記事になってしまって申し訳ありませんでした、
ただ、Diffの結果をご覧いただいた通り、確認は簡単にすることができました。

Pythonを一生懸命書きましたが、実はコードを書かずとも今回と同じようなことができます。
参考になるURLを貼っておきます。
[pyATS|Genieの隠れ必殺技Blitzを用いて高速自動化テスト作成](https://qiita.com/tahigash/items/fa00872a1ced946da23b)
[pyATS/GenieとRobot Framework で簡単にマルチベンダーネットワークをチェックする]
(https://qiita.com/tahigash/items/ceb0809a4a9464c521b3)

今回のコードは0から書いたわけではなく、ネットに落ちている情報を拾い集めてなんとか形にしました。
[CiscoTestAutomation](https://github.com/CiscoTestAutomationというGithubに参考になるコードがちょくちょくあります。

## おまけ

今回実施した、Log全部

```
(nw_automate) 00:26:33 my Script $python check_acl.py
INFO : ********************************処理開始********************************
INFO : testbed init:OK
WARNING : sbx-n9kv-aoのファイルない
WARNING : csr1000vのファイルない
INFO : ********************************ログイン********************************
INFO : iosxr1: connected
INFO : csr1000v-1: connected
INFO : ********************************事前ログ取得********************************
INFO : iosxr1: beforeConfig get
INFO : csr1000v-1: beforeConfig get
INFO : ********************************事前RoutingTabel確認********************************
INFO : Could not learn <class 'genie.libs.parser.iosxr.show_routing.ShowRouteIpv6Distributor'>
Parser Output is empty
INFO : +====================================================================================================================================================+
INFO : | Commands for learning feature 'Routing'                                                                                                            |
INFO : +====================================================================================================================================================+
INFO : | - Parsed commands                                                                                                                                  |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_routing.ShowRouteIpDistributor'>                                                                       |
INFO : |====================================================================================================================================================|
INFO : | - Commands with empty output                                                                                                                       |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_routing.ShowRouteIpv6Distributor'>                                                                     |
INFO : |====================================================================================================================================================|
INFO : iosxr1: beforeRouting get
INFO : Could not learn <class 'genie.libs.parser.iosxe.show_routing.ShowIpv6RouteDistributor'>
Parser Output is empty
INFO : +====================================================================================================================================================+
INFO : | Commands for learning feature 'Routing'                                                                                                            |
INFO : +====================================================================================================================================================+
INFO : | - Parsed commands                                                                                                                                  |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxe.show_routing.ShowIpRouteDistributor'>                                                                       |
INFO : |====================================================================================================================================================|
INFO : | - Commands with empty output                                                                                                                       |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxe.show_routing.ShowIpv6RouteDistributor'>                                                                     |
INFO : |====================================================================================================================================================|
INFO : csr1000v-1: beforeRouting get
INFO : ********************************事前ACL確認********************************
INFO : Could not learn <class 'genie.libs.parser.iosxr.show_acl.ShowAclAfiAll'>
Parser Output is empty
INFO : Could not learn <class 'genie.libs.parser.iosxr.show_acl.ShowAclEthernetServices'>
Parser Output is empty
INFO : +====================================================================================================================================================+
INFO : | Commands for learning feature 'Acl'                                                                                                                |
INFO : +====================================================================================================================================================+
INFO : | - Commands with empty output                                                                                                                       |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_acl.ShowAclAfiAll'>                                                                                    |
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_acl.ShowAclEthernetServices'>                                                                          |
INFO : |====================================================================================================================================================|
INFO : iosxr1: beforeAcl get
INFO : +====================================================================================================================================================+
INFO : | Commands for learning feature 'Acl'                                                                                                                |
INFO : +====================================================================================================================================================+
INFO : | - Parsed commands                                                                                                                                  |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxe.show_acl.ShowAccessLists'>                                                                                  |
INFO : |====================================================================================================================================================|
INFO : csr1000v-1: beforeAcl get
INFO : ********************************Config投入********************************
INFO : iosxr1: input_config
INFO : csr1000v-1: input_config
INFO : ********************************事後ログ取得********************************
INFO : iosxr1: afterConfig get
INFO : csr1000v-1: afterConfig get
INFO : ********************************事後RoutingTabel確認********************************
INFO : Could not learn <class 'genie.libs.parser.iosxr.show_routing.ShowRouteIpv6Distributor'>
Parser Output is empty
INFO : +====================================================================================================================================================+
INFO : | Commands for learning feature 'Routing'                                                                                                            |
INFO : +====================================================================================================================================================+
INFO : | - Parsed commands                                                                                                                                  |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_routing.ShowRouteIpDistributor'>                                                                       |
INFO : |====================================================================================================================================================|
INFO : | - Commands with empty output                                                                                                                       |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_routing.ShowRouteIpv6Distributor'>                                                                     |
INFO : |====================================================================================================================================================|
INFO : iosxr1: afterRouting get
INFO : Could not learn <class 'genie.libs.parser.iosxe.show_routing.ShowIpv6RouteDistributor'>
Parser Output is empty
INFO : +====================================================================================================================================================+
INFO : | Commands for learning feature 'Routing'                                                                                                            |
INFO : +====================================================================================================================================================+
INFO : | - Parsed commands                                                                                                                                  |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxe.show_routing.ShowIpRouteDistributor'>                                                                       |
INFO : |====================================================================================================================================================|
INFO : | - Commands with empty output                                                                                                                       |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxe.show_routing.ShowIpv6RouteDistributor'>                                                                     |
INFO : |====================================================================================================================================================|
INFO : csr1000v-1: afterRouting get
INFO : ********************************事後ACL確認********************************
INFO : Could not learn <class 'genie.libs.parser.iosxr.show_acl.ShowAclAfiAll'>
Parser Output is empty
INFO : Could not learn <class 'genie.libs.parser.iosxr.show_acl.ShowAclEthernetServices'>
Parser Output is empty
INFO : +====================================================================================================================================================+
INFO : | Commands for learning feature 'Acl'                                                                                                                |
INFO : +====================================================================================================================================================+
INFO : | - Commands with empty output                                                                                                                       |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_acl.ShowAclAfiAll'>                                                                                    |
INFO : |   cmd: <class 'genie.libs.parser.iosxr.show_acl.ShowAclEthernetServices'>                                                                          |
INFO : |====================================================================================================================================================|
INFO : iosxr1: afterAcl get
INFO : +====================================================================================================================================================+
INFO : | Commands for learning feature 'Acl'                                                                                                                |
INFO : +====================================================================================================================================================+
INFO : | - Parsed commands                                                                                                                                  |
INFO : |----------------------------------------------------------------------------------------------------------------------------------------------------|
INFO : |   cmd: <class 'genie.libs.parser.iosxe.show_acl.ShowAccessLists'>                                                                                  |
INFO : |====================================================================================================================================================|
INFO : csr1000v-1: afterAcl get
INFO : ********************************Config戻し********************************
INFO : iosxr1: clear_config
INFO : csr1000v-1: clear_config
INFO : ********************************ログアウト********************************
INFO : iosxr1: disconnected
INFO : csr1000v-1: disconnected
INFO : ********************************Diff********************************

=====iosxr1_ConfigDiff=====
+Wed Dec 18 16:57:14.949 UTC:
-Wed Dec 18 16:56:58.216 UTC:
router static:
 address-family ipv4 unicast:
+  8.8.8.8/32 10.10.20.254:

=====csr1000v-1_ConfigDiff=====
+Current configuration : 7620 bytes:
+ip route 8.8.8.8 255.255.255.255 GigabitEthernet1 10.10.20.254:
-Current configuration : 7557 bytes:

=====iosxr1_RouteDiff=====
info:
 vrf:
  default:
   address_family:
    ipv4:
     routes:
+      8.8.8.8/32:
+       active: True
+       metric: 0
+       next_hop:
+        next_hop_list:
+         1:
+          index: 1
+          next_hop: 10.10.20.254
+          updated: 00:00:08
+       route: 8.8.8.8/32
+       route_preference: 1
+       source_protocol: static
+       source_protocol_codes: S

=====csr1000v-1_RouteDiff=====
info:
 vrf:
  default:
   address_family:
    ipv4:
     routes:
+      8.8.8.8/32:
+       active: True
+       metric: 0
+       next_hop:
+        next_hop_list:
+         1:
+          index: 1
+          next_hop: 10.10.20.254
+          outgoing_interface: GigabitEthernet1
+       route: 8.8.8.8/32
+       route_preference: 1
+       source_protocol: static
+       source_protocol_codes: S

=====iosxr1_AclDiff=====


=====csr1000v-1_AclDiff=====

INFO : ********************************終了********************************
(nw_automate) 00:28:00 my Script $

```