LoginSignup
9
1

More than 1 year has passed since last update.

RESTCONFとかChatOpsでIOS-XEを設定するのってなんかかっこいいよね。

Last updated at Posted at 2022-12-23

はじめに

この記事はシスコシステムズ合同会社の社員有志による「Cisco Systems Japan Advent Calendar 2022」の 2枚目の 24日目 として投稿しています。

過去のカレンダーはこちらから
|| 2021年-1枚目 / 2021年-2枚目 || 2020年-1枚目 / 2020年-2枚目 || 2019年 || 2018年 || 2017年 ||

DevNet Class of 2020

私はシスコに2020年に新卒として入社しましたが、同年シスコではDevnet認定が新しく始まりました。そのおかげもあり、新卒研修中にDevnet AssociateおよびDevnet Professionalのトレーニングを受ける事ができ、DevNetの資格を取得することができました。2020年にDevNet資格を取得したことで、DevNet Class of 2020というような称号もいただきました。そんなDevNetの勉強をしてた頃、個人的に好きだったRESTCONFとそれのChatOpsへの応用を今回紹介したいと思います。

RESTCONFとは

RESTCONFはシスコのプログラマビリティ コンフィグレーションガイドによると

構造化データ(XML または JSON)および YANG を使用して REST ライクな API を提供します。これによりさまざまなネットワーク デバイスにプログラムを使用してアクセスできます。RESTCONF API は HTTPs メソッドを使用します。

らしいです。難しいことは分かりませんが、REST APIみたいな感じでHTTPsメソッドを使ってネットワーク機器を設定できるものだと認識しています。データはYANGを使用しています。YANGの構造についてはこちらを参照してください。

RESTCONFの準備をする

RESTCONFを使用するにはネットワーク機器にAAAでの認証と、HTTPサービスを有効にしなければなりません。

AAAの有効化

en
conf t
aaa new-model
aaa authentication login default local
aaa authorization exec default local
username cisco privilege 15 secret cisco
enable secret cisco
line vty 0 15
  transport input ssh
end

HTTPサービスとRESTCONFの有効化

en
conf t
ip http secure-server
restconf
end

準備はこれだけです。簡単ですね。

RESTCONFを使ってみる

簡単な検証環境を用意しました。
image.png

curl

まずcurlを使ってUbuntuからRESTCONF APIを叩きます。試しにI/Fの設定の確認をしてみます。

$ curl --insecure --location --request GET 'https://{hostname}:443/restconf/data/native/interface' --header 'Accept: application/yang-data+json' --header 'Authorization: Basic <base64エンコードした"user:password">'

{
  "Cisco-IOS-XE-native:interface": {
    "GigabitEthernet": [
      {
        "name": "1",
        "ip": {
          "address": {
            "primary": {
              "address": "172.16.100.12",
              "mask": "255.255.255.0"
            }
          }
        },
        "Cisco-IOS-XE-ethernet:negotiation": {
          "auto": true
        }
      },
      {
        "name": "2",
        "ip": {
          "address": {
            "primary": {
              "address": "10.0.0.20",
              "mask": "255.255.255.0"
            }
          }
        },
        "Cisco-IOS-XE-ethernet:negotiation": {
          "auto": true
        }
      },
      {
        "name": "3",
        "ip": {
          "address": {
            "primary": {
              "address": "192.168.11.200",
              "mask": "255.255.255.0"
            }
          }
        },
        "Cisco-IOS-XE-ethernet:negotiation": {
          "auto": true
        }
      }
    ]
  }
}

YANG形式でC8000vの各I/Fの情報が出力されました。

Python

もちろんPythonのRequestsモジュールを使用してRESTCONF APIを叩くことも可能です。以下のPythonスクリプトでcurlと同様の結果が得られます。

get_interface.py
import sys, requests, urllib3, json

url = 'https://192.168.11.200:443/restconf/data/native/interface'

payload = {}
headers = {
        'Content-Type': 'application/yang-data+json',
        'Accept': 'application/yang-data+json',
        'Authorization': 'Basic <base64エンコードした"user:password">'
    }

urllib3.disable_warnings()

response = requests.request("GET", url, verify=False, headers=headers, data=payload)

print(response.text)

Webex ChatBotとRESTCONFを組み合わせてChatOpsしてみる

PythonでRESTCONF APIが叩けるのであれば、WebexのChatBotと組み合わせてChatOps的な事をすることもできます。こちらを参考にしてWebex ChatBotを作り、コードを書いていきました。検証環境は以下になります。

image.png

ChatBotのコード

Webex ChatBotに対して「/c8kv」で始まるメッセージを送った場合、特定の操作をネットワーク機器に対してRESTCONF APIを叩いて行います。今回はI/Fの設定確認とIPアドレスを変更するコマンドを用意してみました。

chatbot.py
from flask import Flask, request, json
import requests
import sys, urllib3, json
import c8kv_ops

app = Flask(__name__)
port = 5005
base_url = 'https://webexapis.com/v1/'
api_key = '<bot_token>'
headers = {
    "Authorization": f"Bearer {api_key}",
    "Content-Type": "application/json"
}

@app.route('/', methods=['POST'])
def index():
    data = request.get_json()
    bot_id = requests.get(f'{base_url}/people/me', headers=headers).json().get('id')
    # 自分のメッセージは無視する
    if bot_id == data.get('data').get('personId'):
        return 'Message from self ignored'
    else:
        # メッセージを受け取った部屋の情報を収集する
        message_id = data.get('data').get('id')
        message_url = f'{base_url}/messages/{message_id}'
        message_text = requests.get(message_url, headers=headers).json().get('text')
        room_id = data.get('data').get('roomId')
        # /c8kvで始まるメッセージを解読する
        if message_text.startswith('/c8kv'):
            try:
                action = message_text.split()[1]
            except IndexError:
                action = 'nothing'
            # /c8kv get_int int_name
            if action == 'get_int':
                try:
                    int_name = message_text.split()[2]
                except IndexError:
                    reply = 'Incorrect syntax.'
                reply = c8kv_ops.get_int_conf(int_name)
            # /c8kv set_int_ip int_name ip_addr mask
            elif action == 'set_int_ip':
                try:
                    int_name = msg.message_text.split()[2]
                    ip_addr = msg.message_text.split()[3]
                    mask = msg.message_text.split()[4]
                except IndexError:
                    reply = 'Incorrect syntax.'
                reply = csr1kv_ops.set_int_ip(int_name, ip_addr, mask)
            # /c8kv
            else:
                reply = 'Did nothing.'
        # どの操作にも引っかからなかった場合、メッセージをそのまま返す
        else:
            reply = f'You said: "{message_text}"'

        my_msg_data = {
            "roomId": room_id,
            "text": reply,
            }
        post_message_url = f'{base_url}/messages'
        post_message_data = requests.post(post_message_url,headers=headers,data=json.dumps(my_msg_data))

    return data

app.run(host="0.0.0.0", port=port, debug=True)
c8kv_ops.py
import requests, sys, urllib3, json

base_url = 'https://192.168.11.200:443/'
headers = {
    'Content-Type': 'application/yang-data+json',
    'Accept': 'application/yang-data+json',
    'Authorization': 'Basic <base64エンコードした"user:password">'
}

# Gets configuration of specific interface.
def get_int_conf(int_name):
    req_url = base_url + f'restconf/data/Cisco-IOS-XE-native:native/interface/{int_name}'
    response = requests.get(req_url,
                            headers=headers,
                            verify=False)
    return response.text

# Sets ip address of specified interface.
def set_int_ip(int_name, ip_addr, mask):
    req_url = base_url + f'restconf/data/Cisco-IOS-XE-native:native/interface/{int_name}/ip/address/primary/address'
    ip_address = { 'address': f'{ip_addr}', 'mask': f'{mask}' }
    response = requests.put(req_url,
                            headers=headers,
                            verify=False,
                            data = json.dumps(ip_address))
    if response.status_code == 204:
        return 'Successfully configured ip address.'
    else:
        return 'Failed to configure ip address.'

ChatBotの準備と起動

ChatBotを使用するためにはWebhookの設定が必要になります。curlを使用してWebhookを作成します。今回は楽をしてngrokも使用しています。

最後にChatBotのアプリを起動して準備完了です。

$ ngrok http -region jp 5005
$ curl -X POST https://webexapis.com/v1/webhooks -H 'Authorization: Bearer <bot_token>' -H 'Content-Type: application/json' -d '{"name": "Webhook to Chatbot", "resource": "all", "event": "all", "targetUrl": "<ngrok_url>"}'
$ python3 chatbot.py
 * Serving Flask app "chatbot" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://0.0.0.0:5005/ (Press CTRL+C to quit)
 * Restarting with inotify reloader
 * Debugger is active!
 * Debugger PIN: 319-610-158

実際に使ってみる

ChatBotからI/F情報の取得

コマンド:/c8kv get_int [インターフェイス名]
image.png

ChatBotからI/FのIPアドレスを変更

コマンド:/c8kv set_int_ip [IPアドレスを変更したいI/F名] [IPアドレス] [サブネットマスク]
image.png
ちゃんとIPアドレスが変わっている事が確認できます!

参考情報

Cisco IOS XE Bengaluru 17.6.x プログラマビリティ コンフィギュレーションガイド: RESTCONFプロトコル
Using a Bot to Operate Your Network!? – Getting Started with ChatOps, Part 1
Defining Your Bot Logic – Getting Started with ChatOps, Part 2
Message Details – Getting Started with ChatOps, Part 3
Integration with Chuck Norris and Meraki APIs – Getting Started with ChatOps, Part 4
上記シスコブログシリーズで参照されているGitHub Repo

免責事項

本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1