うさラボ

お勉強と備忘録

CMLでUbuntu触る時初期セットアップはEDIT CONFIGでcloud-initを使うと非常に楽

CMLで構築してるいるときにあった小ネタvol.3

cloud-initは画像赤枠のEDIT CONFIGから設定が可能です

EDIT CONFIGの変更には条件があるようです

  1. サーバが停止している
  2. サーバーが初期化されている

直接書き込むことで変更が可能です

変更後は忘れずに [ save ] を実施しましょう f:id:usage_automate:20210121215740p:plain

初期化はWIPE NODEをクリックすることで可能です。 f:id:usage_automate:20210121215757p:plain

cloud-initの記載方法は下記Qiitaを参考にさせていただきました! qiita.com

実際に作成したconfigは下記です。

#cloud-config
password: PASSWORD
chpasswd: { expire: False }
hostname: intenal-sv4
ssh_pwauth: True
ssh_authorized_keys:
   - your-ssh-pubkey-line-goes-here
timezone: Asia/Tokyo
locale: ja_JP.utf8
write_files:
 - path: /etc/netplan/50-cloud-init.yaml
   content: |
    network:
      ethernets:
        ens2:
          addresses:
            - 10.0.4.2/24
          gateway4: 10.0.4.1
          dhcp4: false
          nameservers:
            addresses:
              - 192.168.100.1
      version: 2
runcmd:
  - sudo netplan apply

UbuntuのIPaddressは/etc/netplan/50-cloud-init.yamlを変更することで設定が可能です。

cloud-initは起動の度に走るわけではなく、初回起動時にのみ動作するようで、

runcmdでip add add 10.0.4.2/24 dev ens2のように設定をしてしまうと、サーバを停止→起動すると設定が飛んでしまいます。

なので、/etc/netplan/50-cloud-init.yamlを変更するようにしました。

CML-PのUbuntuでSSHにつまづいた

CMLで構築してるいるときにあった小ネタvol.2

CMLで立てたUbuntuiosSSHできなかったときにごちゃごちゃやっていたことの備忘録

検証環境を作成時に、マネジメントアクセス用でUbuntuを作成しました。

SSHの設定を終えて、IOSvにSSHしようとしたとき下記のログが出てきてアクセスできませんでした

f:id:usage_automate:20210117202320p:plain

ログを見るにかきkeyがなかったことが原因だったようです。 diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1

/etc/ssh/ssh_configにKexAlgorithms diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1を追記したところSSHが可能になりました。

iosvの設定方法がよくなかったのかもしれませんが、解決できたので書き残しておきます。

/etc/ssh/ssh_config

# This is the ssh client system-wide configuration file.  See
# ssh_config(5) for more information.  This file provides defaults for
# users, and the values can be changed in per-user configuration files
# or on the command line.

# Configuration data is parsed as follows:
#  1. command line options
#  2. user-specific file
#  3. system-wide file
(~snip~)
    KexAlgorithms diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1

CML-P Breakout-toolの使い方

CMLで構築してるいるときにあった小ネタvol.1

Breakout-toolは、ラボ環境で動作しているデバイスのコンソールポートへの接続性を提供する機能です。

イメージ図 f:id:usage_automate:20210117201118p:plain

Breakout-toolの使い方の話

インストールはCMLGUI画面から移動するサイトで可能です。

Windows/Mac/Linuxとそれぞれで実行ファイルがダウンロード可能です

f:id:usage_automate:20210117195241p:plain

Macで利用する場合は信頼されていないアプリケーションの実行がデフォルトで許可されていないので、自分で信頼ずみにする必要があります。

ダウンロードページに移動するとドキュメントがあるので基本はそちらを読んでいけば大丈夫です。

f:id:usage_automate:20210117195451p:plain

自分でもtweetしてますが、実行権限がたりたくて実行できずに結構ハマりました。

実行時にオプションでuiをつけるとGUIで起動可能です

breakout-macos-x86_amd64 ui

起動後、ページアクセスし、Configの設定をします。

f:id:usage_automate:20210117200247p:plain

最後に、対象LabのStatusをOff->Onに変更します。

f:id:usage_automate:20210117200332p:plain

対象Labをクリックし、アクセスしたいデバイスLinkをクリックするとアクセス可能です。

f:id:usage_automate:20210117200557p:plain

Cisco Certified DevNet Associate 合格体験記

Cisco Certified DevNet Associate 取得しました

2020年中になんとかDevNetの資格が欲しい!と思っていたので年内ギリギリで取得して来ました!

参考になるかわかりませんが自分がやった準備を書き残したいと思います

ちなみに、825点合格で825点でした(ギリギリすぎ)

なんとか合格できたもののもうちょっと勉強しておけばよかったなぁって気持ちもあります。

出題範囲

まず、最初に出題範囲の確認をしました。

公式サイトからざっくりと出題範囲をまとめてみました

15% ソフトウェア開発と設計
20% API の理解と使用
15% シスコ プラットフォームと開発
15% アプリケーションの展開とセキュリティ
20% インフラストラクチャと自動化
15% ネットワークの基礎

www.cisco.com

自分の知識レベル

  • Pythonは少し知ってる(Python 3 エンジニア認定基礎試験は合格してます)
  • 業務で簡単なスクリプトを書いたりしている
  • NWエンジニア歴5年目

とまぁ、目立ったスキルはありませんが数年前に勉強したPythonで空きあらば自作スクリプトを作成し、ちょっとしたことを効率化したりしてます。

勉強方法

試験の予約をしてから、「さて、どうやって勉強しようか」と迷いまくってました。

有料のコンテンツは使わないでなんとかしたいなぁと考えていたのでDevNet Associate Fundamentals Course という公式の教材がありましたが利用しませんでした(おそらくこれを利用するのが一番良いっぽい、しかし英語)。

とりあえず、APIの理解とソフトウェア開発についてを重点的に勉強をしました。

次にシスコ プラットフォームと開発とインフラストラクチャと自動化を手を動かしながら学習、Devnetのサンドボックス最高

  • Meraki,DNA,USC ManagerのAPIの叩き方も勉強(認証方式などなど)
  • Ansibleの出ると買いてあったのでIOS,ASAの設定変更系のモジュール確認
  • NETCONF,RESTCONF,gNMI,YANGのキーワードは勉強(私はちょっと勉強が足りてませんでした...)

他にもDevnetのLearning?のの日本語化されているトラックも参考にしました。

Cisco DevNet: APIs, SDKs, Sandbox, and Community for Cisco Developers

あまり参考にならないと思いますが読んだ本(さらっと読みしかしてないっす)

AmazonKidleunlimitede最高

アジャイル/ウォーターフールの説明を読んだ

APIのところだけ読みました

体系的に学ぶには何がいいのだろうか。。といまだに思う

試験中

試験が始まって、見直しができないことに気づきました。

Microsoftの試験を受けたときは見直しができたので、軽い気持ちで問題を解いてから「後で見直そ〜」と思ってませんでしたがそんなこともできずに ドキドキしながら問題を解いていきました。

日本語翻訳は若干苦しかったです。(私の読解力が足りない可能性は大)

感想

感想としては「なんとか取れた。」でした、正直試験中は「あー、これは来年再挑戦かもなぁ」と思うくらいの自信でした。

結果的に取れたから良いものの勉強不足を痛感したので、継続して勉強を続けようと思える試験になりました。

考えるのが嫌なのでIPアドレス計算ツール作ってみました

192.168.99.43/28のネットワークアドレスってなんだっけ

IPアドレスをパッとみたときにネットワークアドレスとか利用IPなどを頭の中で計算することってありませんか?

私はよくやっていて、その度に念のためググって確認しようってなっていました。

そこでPythonのipaddressモジュールを使いIPアドレスの計算をしてくれるCLIツールを作ってみました。

f:id:usage_automate:20201225183812p:plain

f:id:usage_automate:20201225183821p:plain

簡単なものですが紹介します。

構成

(ipcal) usalab!:ipcal-1.0 $ tree
.
├── ipcal
│   ├── __init__.py
│   ├── main.py
│   └── util.py
└── setup.py

main.pyはutil.pyを呼び出しのとclickで--ipのオプションで実行時にもIPセグメントの入力ができます。

from . import util
import click

@click.command()
@click.option('--ip',default='', type=str, help='ipaddress')
def main(ip):
  ipnetwork = util.input_value_cast_to_ipv4network(ip)
  util.ipnetwork_information(ipnetwork)

if __name__ == '__main__':
  main()

util.py(名前つけるセンスがない)ではipaddressモジュールを利用して、入力値をIPv4Networkオブジェクトにして返すinput_valueと計算結果を表示するipnetwork_informationの2つのメソッドを作成してます。

import ipaddress
import sys

def input_value_cast_to_ipv4network(input_ip):
  '''
  入力値をipaddressオブジェクトにキャストして戻す
  '''
  if input_ip:
    try:
      ipnetwork = ipaddress.ip_network(input_ip,strict=False)
      return ipnetwork
    except ValueError:
      print(f"Error: Value Error")
      sys.exit(1)
    except Exception as e:
      print(f"Error: {e}")
      sys.exit(1)
  else:
    value = input('IPアドレス入力[X.X.X.X/X]>> ')
    try:
      ipnetwork = ipaddress.ip_network(value,strict=False)
      return ipnetwork
    except ValueError:
      print(f"Error: Value Error")
      sys.exit(1)
    except Exception as e:
      print(f"Error: {e}")
      sys.exit(1)

def ipnetwork_information(ipnetwork):
  print(f'Prefix          : {ipnetwork.with_prefixlen}')
  print(f'SubnetMask      : {ipnetwork.netmask}')
  print(f'WildCardMask    : {ipnetwork.with_hostmask}')
  print(f'NetworkAddress  : {ipnetwork.network_address}')
  print(f'BroadCastAddress: {ipnetwork.broadcast_address}')

ipaddressモジュールのメソッドを呼び出すだけで簡単にネットワークアドレスやブロードキャストアドレスを表示することができます。

どうやって公開するのがいいのかよくわからない。。

python setup sdistを実施してpip install dist/ipcal-1.0.tar.gzすることでipcalコマンドで実行することができます。

ググる時間が減ったのでヨシ!!

参考にさせていただいたサイト

qiita.com

blog.amedama.jp

buildersbox.corp-sansan.com

docs.python.org

Microsoft Azure AZ-104取得しました

AZ-104合格できました

私がした勉強方法を紹介したいと思います。

少しでも参考になるとこがあれば幸いです

自分の知識レベル

  • AZ-900は合格
  • 業務でAzureはいっさい触ってない

やったこと

速習Azure Administratorを一気読みしました。

試験の内容がまとまっていて非常に参考になりました

しかし、これを読めばOKといった本ではなかったです。

Azureをちょびっと触る、、

無料枠を無駄に使い切っていたのでほんとに少しだけ、、

vNETの作成やピアリングなどをちょびっと試した程度です

MS LearnとDocを読む

特に苦手なストレージ/AADは追加でMS Learnを読みました

Azure Storage アカウントをセキュリティで保護する - Learn | Microsoft Docs

VPN Gateway を使用して、ご利用のオンプレミス ネットワークを Azure に接続する - Learn | Microsoft Docs

Azure DNS のドキュメント | Microsoft Docs

などなど

模擬試験をやってみる

試験前々日くらいか流石に焦りだし、情報を漁ってると模擬試験があることを知りました。

下記のQiitaを参考にさせていただき模擬試験をやってみました。

Azure Administrator Associate模擬試験問題サイトまとめ - Qiita

いろいろとあるようですが、私はJPN試験 (日本語)のみやりました。

受験してみての感想

受験中に、「こりゃダメかな。。」と思うくらい手応えはなかったですw

結果的には723点(700点が合格ライン)とギリギリでしたが合格できました。

模擬試験で試験の雰囲気をなんとなく理解できていたのが良かったのかもしれません。

NSG/AAD/WebApp/DNS/VPNなど試験範囲は結構広かったので大変でしたが取得できてよかったです。

pyATS/genieを組み込んだスクリプト作成してみた

本記事はエーピーコミュニケーションズ Advent Calendar 2020の11日目の記事です。

qiita.com

pyATSについて

pyATSはCisco社製のテスト自動化ソリューションです。

pythonで書かれたOSSです。

細い情報は以下も参考にしていただければと思います。

インストール編 usage-automate.hatenablog.com

Testbed編 usage-automate.hatenablog.com

pyATS/genieを組み込んだスクリプトを作成してみました

勉強のために、作ったスクリプトを2つほどお見せいたします。(スクリプト部分が多いです)

pyATS利用イメージの助けになれば幸いです(あまりスクリプトに組み込むことはないかもですが)

Testbedファイルは以下を作成し利用しました(CiscoのSnadboxで常時解放されているIOS-XE,IOS-XR)

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

[1] Routing取得(Leanの利用)

Testbedファイルに定義したデバイスのRoutingを取得し表示させます。

え?普通に機器入ってコマンド打てば良いじゃんって?それは言わないお約束

実行結果 f:id:usage_automate:20201211002202p:plain

作成した、Scriptの解説をしていきます。

内容としてはpyATS/genieのLeanメソッドを利用して、Routing情報を取得と整形、表示をやっているだけになります。

まずpyATS/genieを利用するためにgenie.conf.Genieスクリプト内にimportします。

#Genie import
from genie.conf import Genie

まず最初にGenie.initを呼び出しTestbedをLoadします。

  try:
    #####テストベッドを初期化####
    testbed = Genie.init(testbed)
    print(">>> Load Testbed >>>")
  except TypeError:
    print('Testbed Load Error[001]')
    sys.exit(1)

testbed.connectでDeviceに接続します。

接続にはUniconライラブリを利用しています。

  #####接続#####
  try:
    testbed.connect(log_stdout=False)
    for device in testbed.devices.keys():
      if testbed.devices[device].is_connected():
        print(f">>> {device} Connected >>>")
  except unicon.core.errors.ConnectionError:
    print('Connection Error[002]')
    sys.exit(1)

pyATS/genieのleanメソッドを利用して、Routing情報を取得します。 f:id:usage_automate:20201211123320p:plain Cisco DevNet: APIs, SDKs, Sandbox, and Community for Cisco Developers

今回対象のIOS-XEでは下記コマンド

  • show ip route
  • show ip route vrf
  • show ipv6 route updated
  • show ipv6 route vrf updated

IOS-XRでは下記コマンドを実行し、実行結果が.infoに格納されます。

  • show route ipv4
  • show route vrf all ipv4 show route ipv6
  • show route vrf all ipv6

XEとXRで微妙にRoutingを確認するコマンドが違いますが、learn('routing')をすると、.infoには同様の構成の辞書(dict)で情報が格納されます。

    #####Routing取得####
    print(f">>> {device} Learn Routing >>>")
    lean_routing = testbed.devices[device].learn('routing')
    routes = lean_routing.info['vrf']['default']['address_family']['ipv4']['routes']

learn('routing')の実行結果から、Route/NextHop/Interfaceの情報のみ抜き出し、tabulateを利用し表として出力させます。

NexthopがないRouteなど、実行結果によって微妙に値に引っ張りかたが変わったのがつまづきポイントでした。

    #####整形#####
    for route in routes.keys():
      if 'next_hop_list' in list(routes[route]['next_hop'].keys()):
        routes_list.append(routes[route]['route'])
        next_hops_list.append(routes[route]['next_hop']['next_hop_list'][1]['next_hop'])
        if 'outgoing_interface' in list(routes[route]['next_hop']['next_hop_list'][1].keys()):
          outgoing_interface_list.append(routes[route]['next_hop']['next_hop_list'][1]['outgoing_interface'])
        else:
          outgoing_interface_list.append('-')
      else:
        for interface in list(routes[route]['next_hop']['outgoing_interface'].keys()):
          routes_list.append(routes[route]['route'])
          next_hops_list.append('-')
          outgoing_interface_list.append(routes[route]['next_hop']['outgoing_interface'][interface]['outgoing_interface'])

    headers = ["route", "next_hops","outgoing_interface"]
    table = [routes_list, next_hops_list, outgoing_interface_list]
    result = tabulate.tabulate(np.array(table).transpose(), headers,tablefmt="grid")
    print(result)

最後にDeviceからログアウトして処理を終了させています。

    ####切断####
    print(f"<<< {device} Disconnected <<<")
    testbed.devices[device].disconnect()

作成したpythonスクリプト(全行)

#Genie import
from genie.conf import Genie

import unicon
import tabulate
import click
import sys
import numpy as np

@click.command()
@click.option('-t','--testbed',type=str,required=True)
def main(testbed):
  #####事前処理#####
  print(">>>>> Start >>>>>>")

  try:
    #####テストベッドを初期化####
    testbed = Genie.init(testbed)
    print(">>> Load Testbed >>>")
  except TypeError:
    print('Testbed Load Error[001]')
    sys.exit(1)

  #####接続#####
  try:
    testbed.connect(log_stdout=False)
    for device in testbed.devices.keys():
      if testbed.devices[device].is_connected():
        print(f">>> {device} Connected >>>")
  except unicon.core.errors.ConnectionError:
    print('Connection Error[002]')
    sys.exit(1)

  for device in testbed.devices.keys():
    ####List初期化####
    routes_list = list()
    next_hops_list = list()
    outgoing_interface_list = list()

    #####Routing取得####
    print(f">>> {device} Learn Routing >>>")
    lean_routing = testbed.devices[device].learn('routing')
    routes = lean_routing.info['vrf']['default']['address_family']['ipv4']['routes']

    #####整形#####
    for route in routes.keys():
      if 'next_hop_list' in list(routes[route]['next_hop'].keys()):
        routes_list.append(routes[route]['route'])
        next_hops_list.append(routes[route]['next_hop']['next_hop_list'][1]['next_hop'])
        if 'outgoing_interface' in list(routes[route]['next_hop']['next_hop_list'][1].keys()):
          outgoing_interface_list.append(routes[route]['next_hop']['next_hop_list'][1]['outgoing_interface'])
        else:
          outgoing_interface_list.append('-')
      else:
        for interface in list(routes[route]['next_hop']['outgoing_interface'].keys()):
          routes_list.append(routes[route]['route'])
          next_hops_list.append('-')
          outgoing_interface_list.append(routes[route]['next_hop']['outgoing_interface'][interface]['outgoing_interface'])

    headers = ["route", "next_hops","outgoing_interface"]
    table = [routes_list, next_hops_list, outgoing_interface_list]
    result = tabulate.tabulate(np.array(table).transpose(), headers,tablefmt="grid")
    print(result)

    ####切断####
    print(f"<<< {device} Disconnected <<<")
    testbed.devices[device].disconnect()
  print("<<<<<<< End <<<<<<<")

if __name__ == '__main__':
  main()

[2] ACL確認用Script

続いて、SourceIPとDestination IPを入力すると該当のACLがHitするScriptを作成しました。

こちらはlearn('acl')を利用しています。

learn('acl')は現状はXEのみ対応しています。

実行結果① f:id:usage_automate:20201211121354p:plain

実行結果② f:id:usage_automate:20201211121359p:plain

pythonの標準モジュールのipaddressを利用して、検索対象が含まれるIPを抽出しHitしたACLのみを表として表示させました。

leanメソッドで辞書になっているため、値の取り出し方を覚えれば応用は簡単にできます。

今回はlearnを利用しましたが、parseを利用する場合は特定の値を抜き出すためにDqと呼ばれるメソッドも用意されているようです。

ACLが適当すぎたので、少し反省しつつも想定どおりに動いてくれました。

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

import re
import unicon
import tabulate
import click
import sys
import numpy as np
import ipaddress

def format_ipaddress(ipv4_net):
  if ipv4_net == 'any':
    ipv4_net = ipaddress.ip_network('0.0.0.0/0')
  elif re.search('host', str(ipv4_net)) is not None:
    ipv4_net = ipaddress.ip_network(ipv4_net.replace('host ',''))
  else:
    ipv4_net = ipaddress.ip_network(ipv4_net.replace(' ','/'))
  return ipv4_net

@click.command()
@click.option('-t','--testbed',type=str,required=True)
@click.option('-sip','--source_ip_address',type=str,prompt='Source IP>>')
@click.option('-dip','--destination_ip_address',type=str,prompt='Destination IP>>')
def main(testbed,source_ip_address,destination_ip_address):
  #####事前処理#####
  #メッセージ
  print(">>>>> Start >>>>>>")
  try:
    if source_ip_address == 'any':
      search_source_ipaddress = ipaddress.ip_network('0.0.0.0/0')
    else:
      search_source_ipaddress = ipaddress.ip_network(source_ip_address,strict=False)
    if destination_ip_address == 'any':
      search_destination_ipaddress = ipaddress.ip_network('0.0.0.0/0')
    else:
      search_destination_ipaddress = ipaddress.ip_network(destination_ip_address,strict=False)
  except TypeError or ValueError:
    print('IPaddress Load Error[001]')
    sys.exit(1)
  try:
    #テストベッドを初期化
    testbed = Genie.init(testbed)
    print(">>> Load Testbed >>>")
  except TypeError:
    print('Testbed Load Error[002]')
    sys.exit(1)
  
  try:
    for device in testbed.devices.keys():
      if testbed.devices[device].os == 'iosxe':
        testbed.connect(log_stdout=False)
        if testbed.devices[device].is_connected():
          print(f">>> {device} Connected >>>")
      else:
        print(f"<< {device} skkiped {testbed.devices[device].os} is not support <<")
  except unicon.core.errors.ConnectionError:
    print('Connection Error[003]')
    sys.exit(1)

  for device in testbed.devices.keys():
    # List初期化
    acl_list = list()
    source_list = list()
    destination_list = list()
    src_port_list = list()
    dst_port_list =list()
    action_list =list()

    if testbed.devices[device].os == 'iosxe':
    #ACL取得
      print(f">>> {device} Learn ACL >>>")
      lean_acls = testbed.devices[device].learn('acl')
      acl_name_list = list(lean_acls.info['acls'].keys())
      for acl in acl_name_list:
        if 'aces' in lean_acls.info['acls'][acl].keys():
          aces = lean_acls.info['acls'][acl]['aces']
          for ace in aces.keys():
            destination_ipv4_network = aces[ace]['matches']['l3']['ipv4']['destination_ipv4_network']
            source_ipv4_network = aces[ace]['matches']['l3']['ipv4']['source_ipv4_network']
            for dst_ipv4_net,src_ipv4_net in zip(destination_ipv4_network.keys(),source_ipv4_network.keys()):
              dst_ipv4_net = format_ipaddress(dst_ipv4_net)
              src_ipv4_net = format_ipaddress(src_ipv4_net)

              if search_destination_ipaddress.subnet_of(dst_ipv4_net) or search_source_ipaddress.subnet_of(src_ipv4_net):
                acl_list.append(lean_acls.info['acls'][acl]['name'])
                source_list.append(src_ipv4_net)
                src_port_list.append('-')
                destination_list.append(dst_ipv4_net)
                dst_port_list.append('-')
                action_list.append(aces[ace]['actions']['forwarding'])
      headers = ["acl", "src_ip","src_port","dst_ip","dst_port","action"]
      table = [acl_list, source_list, src_port_list,destination_list,dst_port_list,action_list]
      result = tabulate.tabulate(np.array(table).transpose(), headers,tablefmt="grid")
      print(f">>> {device} Hit ACL >>>")
      print(result)

      print(f"<<< {device} Disconnected <<<")
      testbed.devices[device].disconnect()
  print("<<<<<<< End <<<<<<<")


if __name__ == '__main__':
  main()

ACLプロトコルを全部IPにしてしまったので、source_portやdestination_portの情報が取れずなかったため、、 プロトコルTCP/UDPに設定しているACLで検索する場合は改修が必要です(いつかやります)

振り返りと次回

今回、自作ScriptにpyATS/genieを組み込んでみました。

欲しい値をピンポイントで取得するには多少時間がかかってしまいましたが、半日も掛からずScriptができました。

物凄い簡単!とは言えませんでしたが、pythonを嗜んでいる方ならばそこまで時間がかからずにキャッチアップできると思います。

今度こそ、次回でJob書きます!