2020-07-26 09:49 AM 2021-01-18 10:57 AM 更新
Cisco DNA Center は Docker/Kubernetes 上で稼働する、各種 OSS を活用した Web アプリケーションです。
動作の概要を理解するためには API (REST API や GraphQL)、docker log, パケットキャプチャなどを組み合わせて調べることになりますが、ここでは比較的シンプルなアプリケーションである Command Runner を例に動作概要把握の流れをご紹介します。
IOS などと異なり、Cisco 独自の要素は多くありませんが、Automation については Prime Infrastructure や APIC-EM の流れを汲む実装となっており、ログなどの特徴は似ています。
Command Runner は Web ブラウザ上で一部の CLI をエミュレートするアプリケーションです。Device 360 等で Run Commands をクリックすることなどにより実行できます。
ブラウザの開発者ツールで HTTP トレースを取得しながら Command Runner を実行すると以下のような API によりこの機能が実現されていることが分かります。
network-device-poller/cli/legit-reads API にアクセスし、実行可能なコマンドを取得します。ブラウザはロードされている JavaScript により入力された文字列が実行可能コマンドであるかどうかをチェックします。
なお、network-device-poller API は DNA Center が使用している API Gateway である Kong により、command-runner-service Service (Kubernetes サービス) 経由で command-runnner-service Pod にルーティングされます。
[Thu Jul 23 20:05:59 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ magctl api routes <-- Kong の API 定義確認 (snip) { "http_if_terminated": false, "retries": 0, "name": "fusion_command-runner-service_network-device-poller", "https_only": false, "upstream_url": "http://command-runner-service.fusion.svc.cluster.local:17015/network-device-poller", "strip_uri": true, "created_at": 1594052309747, "uris": [ "/api/v1/network-device-poller" ], (snip) [Thu Jul 23 20:08:44 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ kubectl get pods -n fusion -o wide | egrep "command-runner|IP" NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE command-runner-service-87649dc96-ssgg6 1/1 Running 0 17d 10.61.3.190 192.0.2.2 [Thu Jul 23 20:09:13 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ kubectl get services -n fusion | egrep "command-runner|TYPE" NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE command-runner-service ClusterIP 10.62.1.215 17015/TCP 1y [Thu Jul 23 20:09:35 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ sudo iptables-save | grep command-runner [sudo] password for maglev: -A KUBE-SEP-5YGOMRMMC42GKZEO -s 10.61.3.190/32 -m comment --comment "fusion/command-runner-service:default" -j KUBE-MARK-MASQ -A KUBE-SEP-5YGOMRMMC42GKZEO -p tcp -m comment --comment "fusion/command-runner-service:default" -m tcp -j DNAT --to-destination 10.61.3.190:17015 -A KUBE-SERVICES ! -s 10.61.0.0/21 -d 10.62.1.215/32 -p tcp -m comment --comment "fusion/command-runner-service:default cluster IP" -m tcp --dport 17015 -j KUBE-MARK-MASQ -A KUBE-SERVICES -d 10.62.1.215/32 -p tcp -m comment --comment "fusion/command-runner-service:default cluster IP" -m tcp --dport 17015 -j KUBE-SVC-XM3YN2Z4G7FFSE5R -A KUBE-SVC-XM3YN2Z4G7FFSE5R -m comment --comment "fusion/command-runner-service:default" -j KUBE-SEP-5YGOMRMMC42GKZEO
ユーザにより入力された CLI コマンドは command-runner-service Pod の network-device-poller/read-request API へ HTTP POST されます。
ブラウザから POST される JSON には CLI コマンド (例では sh ver) だけでなく、対象とするデバイスの UUID が指定されます。POST に対するレスポンスには、DNA Center 内での処理を識別するための task ID と task ID へアクセスするための URL が含まれます。
task/{taskId} API へアクセスすると、タスクの進捗状況が分かります。最初は progress:"CLI Runner request creation" ですが、何度か API へのアクセスを繰り返す内に endTime:{unixtime} が追加され、progress には fileId が提供されることでタスクの完了が通知されます。(/api/v1/task/ API は task-service Pod へルーティングされます)
タスクが完了し、fileId が提供されるとブラウザ上で稼働する JavaScript は file API へアクセスし、CLI コマンドの出力結果を JSON として受け取ります。Device 360 からの Command Runner 実行では 1 台のデバイスに対して CLI コマンドを実行するため実行結果を一時ファイルとして保存する必要性は感じませんが、Tools > Command Runner からの CLI コマンド実行では複数デバイスに対して複数コマンドを一括実行できるため、実行結果をファイルとして扱う実装が適当です。
(file API は file-service Pod へルーティングされます)
以上が Web ブラウザでの HTTP (API) トレースから分かる動作概要ですが、これだけでは内部動作は分かりません。
magctl service logs -rf {Pod名} (docker logs -f 相当) で Pod のリアルタイムログを取得できます。このログからは REST API 通信の他に、PostgreSQL や AMQP(RabbitMQ) が関与していることが分かります。(注: magctl service logs について、Pod に複数コンテナが含まれる場合は -c オプションでコンテナの指定が必要です)
こちらは System Settings > Settings > Debugging Logs にて Logging Levelを Debug にした際の抜粋で取得時刻は HTTP トレース、パケットキャプチャとは異なります。これらと同時刻 (Logging Level = INFO) のものは添付をご参照ください) [Sat Jul 25 23:51:22 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ magctl service logs -rf command-runner // 実行可能コマンドの取得 2020-07-26 00:02:34,509 | INFO | qtp1028780142-24659 | | c.c.a.c.r.s.c.CommandRunnerController | Valid CLI keywords are: [call-home, cd, cping, crypto, dir, eping, grep, help, mediatrace, monitor, more, mping, mstat, ping, pwd, sdlc, show, sh, standby, start-chat, systat, tarp, test, traceroute, ucse, verify, where, which-route] | // デバイスのインベントリ情報を PostgreSQL から取得 2020-07-26 00:02:37,179 | INFO | qtp1028780142-24659 | | c.c.a.c.r.s.util.InventoryUtil | deviceuuids in inside[4114cd20-56b8-4f42-852d-31875a94b5e6] | 2020-07-26 00:02:37,183 | INFO | qtp1028780142-24659 | | c.c.a.c.r.s.util.InventoryUtil | nwdevice :: getWapdevicesList method {}NetworkDevice[apManagerInterfaceIp=,associatedWlcIp=,bootDateTime=2020-07-20 01:41:00,collectionInterval=Global Default,collectionStatus=Managed,description=Cisco IOS Software [Gibraltar], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 16.12.3s, RELEASE SOFTWARE (fc1) Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2020 by Cisco Systems, Inc. Compiled Tue 19-May-20 11:48 by mcpre,deviceSupportLevel=Supported,family=Switches and Hubs,hostname=tky-dna2-edge2.cisco.com,interfaceCount=0,inventoryStatusDetail=,lastUpdateTime=2020-07-26 00:00:00.581,lastUpdated=2020-07-26 00:00:00,lineCardCount=0,lineCardId=,macAddress=70:18:a7:1e:64:80,managementIpAddress=192.168.12.11,memorySize=NA,paddedMgmtIpAddress=192.168. 12. 11,platformId=C9300-48P, C9300-48P,reachabilityFailureReason=,reachabilityStatus=Reachable,role=ACCESS,roleSource=AUTO,serialNumber=FCW2244AHRR, FCW2137G0MN,series=Cisco Catalyst 9300 Series Switches,snmpContact=,snmpLocation=,softwareType=IOS-XE,softwareVersion=16.12.3s,tagCount=0,type=Cisco Catalyst 9300 Switch,upTime=5 days, 22:19:06.56,uptimeSeconds=512497,instanceUuid=4114cd20-56b8-4f42-852d-31875a94b5e6,instanceId=814565764,authEntityId=814565764,authEntityClass=-927529445,instanceTenantId=5cb85950179074004c58a503,_orderedListOEIndex=,_creationOrderIndex=,_isBeingChanged=,deployPending=,instanceVersion=0] | 2020-07-26 00:02:37,306 | INFO | qtp1028780142-24659 | | c.c.a.c.r.s.util.InventoryUtil | meiList :: ValidateDevices method [ManagedElementInterface[assignedNetworkRoles=[UNKNOWN],autoIpSwapEnabled=false,collectionInterval=-1,communicationState=REACHABLE,createTime=2020-07-20 01:44:26.32,discoverySource=UNKNOWN,entityId=814565764,featureSupportLevel=FULL_SUPPORT,inventoryCollectionTime=2020-07-26 00:00:00.581,inventoryStatusDetail=,lastBootTime=2020-07-20 01:41:00.582,lastIcmpPingTime=0,lastInventoryAttemptEndTime=2020-07-26 00:00:00.581,lastInventoryAttemptStartTime=2020-07-25 23:59:44.621,lifecycleState=MANAGED_AND_SYNCHRONIZED,managementAddress=192.168.12.11,paddedMgmtAddress=192.168. 12. 11,description=,name=,instanceUuid=4114cd20-56b8-4f42-852d-31875a94b5e6,instanceId=814565764,authEntityId=814565764,authEntityClass=-927529445,instanceTenantId=5cb85950179074004c58a503,_orderedListOEIndex=,_creationOrderIndex=,_isBeingChanged=,deployPending=,instanceVersion=0]] | // CLI コマンドと実行対象デバイスの情報を networkpoller へ送信 2020-07-26 00:02:37,308 | INFO | qtp1028780142-24659 | | c.c.a.c.r.s.util.CommandRunnerUtil | items : [sh ver], delimiter : ; | 2020-07-26 00:02:37,309 | DEBUG | qtp1028780142-24659 | | c.c.enc.audit.impl.AuditClientImpl | Sending Audit message with TaskId to queue CreateAuditTaskMessage {context=null, replyToChain=null, version=1595721757309, payload=Request [auditInstanceUUID=c56d77ec-17f6-4ad6-bf4a-0119f37bec6d, taskId=5349c1a5-0daf-4240-beb2-7372d84a38cc, auditDescription=CLI Runner request creation, auditRequestor=admin, siteName=null, deviceIp=null, deviceName=null, tag=Command Runner, auditMap={COMMANDS=sh ver, DEVICE_UUIDS=4114cd20-56b8-4f42-852d-31875a94b5e6}, createdDateTime=Sun Jul 26 00:02:37 UTC 2020]} | 2020-07-26 00:02:37,310 | DEBUG | qtp1028780142-24659 | | com.cisco.xmp.persistence.impl.DMM | @@@Calling session save for the instance:com.cisco.apicem.networkpoller.model.commandrunner.CommandRunnerRequest | 2020-07-26 00:02:37,313 | DEBUG | qtp1028780142-24659 | | c.c.enc.audit.impl.AuditClientImpl | Sending Audit message with TaskId to queue CreateAuditTaskMessage {context=null, replyToChain=null, version=1595721757313, payload=Request [auditInstanceUUID=c48f9804-a737-45f1-b355-a55d095fd963, taskId=05819539-21af-4e21-b03f-e0db40f3cc18, auditDescription=Execution of commands on device : 4114cd20-56b8-4f42-852d-31875a94b5e6STARTED, auditRequestor=admin, siteName=null, deviceIp=null, deviceName=null, tag=Command Runner, auditMap={COMMANDS=sh ver, DEVICE_UUIDS=4114cd20-56b8-4f42-852d-31875a94b5e6}, createdDateTime=Sun Jul 26 00:02:37 UTC 2020]} | // networkpoller より CLI コマンド実行結果を AMQP (RabbitMQ) で取得 2020-07-26 00:02:37,329 | DEBUG | qtp1028780142-24659 | | c.c.a.c.r.s.m.CommandRunnerMessageFactory | Constructing commandRunner message for device : 192.168.12.11 & commands : [sh ver] with timeout : 0 | 2020-07-26 00:02:37,339 | INFO | qtp1028780142-24659 | | c.c.a.c.r.s.m.CommandRunnerMessageFactory | Message is : XdeRunnerMessage {context={public={serviceType=Command Runner Service}}, replyToChain=[null://clirunner.response.exchange/clirunner.response.routing], version=0, payload=com.cisco.enc.networkpoller.api.request.xderunner.XdeRunnerMessage$Request@6e77baf2} | 2020-07-26 00:02:37,339 | DEBUG | qtp1028780142-24659 | | c.c.a.c.r.s.c.CommandRunnerController | Message successfully submitted for device : 4114cd20-56b8-4f42-852d-31875a94b5e6 | 2020-07-26 00:02:37,523 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.g.a.i.GrapevineMessageListener | Received message=com.cisco.grapevine.amqp.message.result.SuccessResultMessage, retries=0, maxRetries=5 | 2020-07-26 00:02:37,524 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.a.c.r.s.r.XdeRunnerMessageResultHandler | command response {sh ver=sh ver Cisco IOS XE Software, Version 16.12.03s Cisco IOS Software [Gibraltar], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 16.12.3s, RELEASE SOFTWARE (fc1) Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2020 by Cisco Systems, Inc. Compiled Tue 19-May-20 11:48 by mcpre ... ... Configuration register is 0x102 // CLI 実行結果を file-service へアップロード 2020-07-26 00:02:37,524 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.a.c.r.s.r.XdeRunnerMessageResultHandler | command response {} | 2020-07-26 00:02:37,524 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.a.c.r.s.r.XdeRunnerMessageResultHandler | Command output to persist {"SUCCESS":{"sh ver":"sh ver\nCisco IOS XE Software, Version 16.12.03s\nCisco IOS Software [Gibraltar], ... "},"FAILURE":{},"BLACKLISTED":{}} | 2020-07-26 00:02:37,537 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.a.c.r.s.c.f.FileServiceInteractorImpl | get internal upload url Return object: SuccessResultMessage {context={public={RBACSecurityContext=eyJ0eXA ... }}, replyToChain=null, version=0, payload=/api/v1/file/internalupload/d2796681-a970-4286-9877-6e8fa08c3d73} | 2020-07-26 00:02:37,537 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.a.c.r.s.c.f.FileServiceInteractorImpl | Unique Internal Upload id is :: d2796681-a970-4286-9877-6e8fa08c3d73 | 2020-07-26 00:02:37,556 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.e.c.c.f.ServiceInstanceManagerImpl | Endpoint URL manufactured from service instance IP 10.61.3.168 and port 16020 is http://10.61.3.168:16020 | 2020-07-26 00:02:37,556 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.e.c.c.f.FileServiceRestClientEngine | file-service port =16020 | 2020-07-26 00:02:37,801 | INFO | SimpleAsyncTaskExecutor-4 | | c.c.a.c.r.s.c.f.FileServiceInteractorImpl | File created..fileUuid d2796681-a970-4286-9877-6e8fa08c3d73 | 2020-07-26 00:02:37,801 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.a.c.r.s.r.XdeRunnerMessageResultHandler | Need to udapte the task 5349c1a5-0daf-4240-beb2-7372d84a38cc | 2020-07-26 00:02:37,802 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.g.a.i.MaglevAppContainerMessagePropertiesConverter | All done. The message is now ready to be sent to a maglev based app container. | 2020-07-26 00:02:37,806 | DEBUG | SimpleAsyncTaskExecutor-4 | | c.c.g.a.i.GrapevineMessageListener | Handler returned result: SuccessResultMessage {context={public={serviceType=Command Runner Service, RBACSecurityContext=eyJ0eXA ... , context.taskid=05819539-21af-4e21-b03f-e0db40f3cc18, COMMANDS=sh ver, isCommandRunner=true}, clirunner.workflow={deviceUuid=4114cd20-56b8-4f42-852d-31875a94b5e6, invalidDevices=, requestorUsername=admin, parentTaskId=5349c1a5-0daf-4240-beb2-7372d84a38cc, requestUuid=a61cbf44-cb80-4773-aef8-2c81e431b1e2, Processing Devices=, Blacklisted Commands=}}, replyToChain=[], version=0, payload={"SUCCESS":{"actualValue":{"sh ver":"sh ver\nCisco IOS XE Software, Version 16.12.03s\nCisco IOS Software [Gibraltar], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 16.12.3s, RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.cisco.com/techsupport\nCopyright (c) 1986-2020 by Cisco Systems, Inc.\nCompiled Tue 19-May-20 11:48 by mcpre\n\n\nCisco IOS-XE software, Copyright (c) 2005-2020 by cisco Systems, Inc. ... \n\nConfiguration register is 0x102\n\ntky-dna2-edge2#"},"valueType":{}},"FAILURE":{"actualValue":{},"valueType":{}},"BLACKLISTED":{"actualValue":{},"valueType":{}}}}) |
DNAC: tcpdump による特定 Pod のパケットキャプチャ のような手順で command-runner-service Pod についてパケットキャプチャを取得し、キャプチャファイルを HTTP トレースや docker log と組み合わせると、もう少し具体的な挙動を理解できます。関連する Pod は以下です。
[Fri Jul 24 12:50:30 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ kubectl get pods --all-namespaces -o wide | egrep "IP|kong|command-runner|postgres|network-poller|rabbitmq|maglevserver|task-service" | grep -v app-hosting NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE fusion command-runner-service-87649dc96-ssgg6 1/1 Running 0 19d 10.61.3.190 192.0.2.2 fusion network-poller-service-5b648cd98b-tkvqn 1/1 Running 0 19d 10.61.3.144 192.0.2.2 fusion postgres-0 3/3 Running 0 19d 10.61.3.26 192.0.2.2 fusion task-service-55b6d7d8fb-5t6vd 1/1 Running 0 19d 10.61.3.19 192.0.2.2 maglev-system kong-56986f66d6-9xpvc 2/2 Running 0 19d 10.61.3.18 192.0.2.2 maglev-system maglevserver-58bd6c7b67-v6jvg 1/1 Running 0 19d 10.61.3.17 192.0.2.2 maglev-system rabbitmq-0 2/2 Running 0 19d 10.61.3.46 192.0.2.2
ブラウザからの network-device-poller/cli/legit-reads API リクエストが kong Pod (10.61.3.18) 経由で command-runner-service (10.61.3.190) に届くと、command-runner-service は AMQP (RabbitMQ) 経由で network-poller-service に実行可能コマンドを問い合わせます。 network-poller-service からの応答がブランクなのはデフォルトだからです。これを受けて command-runner-service は Kong 経由でブラウザにレスポンスを返します。
(Kong が付加した HTTP X-* ヘッダよりレスポンスの宛先 API クライアント (Web ブラウザ) の IP が 10.70.232.47 でプロトコルは HTTPS、ブラウザが Mac 版 Chrome v84 といったことも分かります。)
ユーザが CLI コマンドを入力すると、Kong 経由で command-runner-service の network-device-poller/cli/read-request API へ CLI コマンド (sh ver) が POST されます。
command-runner-service は PostgreSQL へクエリを実行し、当該デバイスの情報を取得したり、
タスクの進捗管理のために task-service Pod との連携を行います。
HTTP POST で受け取った CLI コマンドは AMQP で network-poller-service へ渡され、network-poller-service がデバイスに SSH を行います。
network-poller-service の挙動については、ここでは説明しませんが、デバイスへの SSH を行うのが network-poller-service であることは conntrack コマンドなどでも確認できます。
[Sat Jul 25 22:39:21 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ sudo conntrack -E -d 192.168.12.11 [sudo] password for maglev: [NEW] tcp 6 120 SYN_SENT src=10.61.3.144 dst=192.168.12.11 sport=37854 dport=22 [UNREPLIED] src=192.168.12.11 dst=192.168.1.20 sport=22 dport=37854 [UPDATE] tcp 6 60 SYN_RECV src=10.61.3.144 dst=192.168.12.11 sport=37854 dport=22 src=192.168.12.11 dst=192.168.1.20 sport=22 dport=37854 [UPDATE] tcp 6 86400 ESTABLISHED src=10.61.3.144 dst=192.168.12.11 sport=37854 dport=22 src=192.168.12.11 dst=192.168.1.20 sport=22 dport=37854 [ASSURED] ^Cconntrack v1.4.3 (conntrack-tools): 3 flow events have been shown. [Sat Jul 25 22:39:52 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ sudo conntrack -L -n | grep 10.61.3.144 | grep 22 conntrack v1.4.3 (conntrack-tools): 60 flow entries have been shown. tcp 6 86379 ESTABLISHED src=10.61.3.144 dst=192.168.12.11 sport=37854 dport=22 src=192.168.12.11 dst=192.168.1.20 sport=22 dport=37854 [ASSURED] mark=0 use=1 [Sat Jul 25 22:42:07 UTC] maglev@10.70.70.231 (maglev-master-1) ~ $ kubectl get pods --all-namespaces -o wide | egrep "IP|10\.61\.3\.144" NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE fusion network-poller-service-5b648cd98b-tkvqn 1/1 Running 0 19d 10.61.3.144 192.0.2.2
HTTP POST に対してはレスポンスで taskId が通知されますので、ブラウザは task-service に API アクセスすることで task の進捗などを確認できます。
network-polller-service から AMQP で CLI コマンドの出力結果が command-runner-service に渡され、task-service に進捗がアップデートされます。
command-runner-service は AMQP で CLI コマンド出力結果のアップロード先を file-service に問い合わせて fileId を取得します。
command-runner-service はファイルをアップロードする前に maglevserver から file-service の各種プロパティを取得します。
maglevserver Pod は maglev-system namespace 内の各種 OSS を含むミドルウェア群を取りまとめる Pod で Kubernetes における kube-apiserver のような位置付けの Pod です。
command-runner-service はファイルを file-service へ POST し、task-service に endTime と fileId を通知します。当該 taskId へ API アクセスしているブラウザは fileId を知ることで CLI コマンドの結果へアクセス可能になります。
検索バーにキーワード、フレーズ、または質問を入力し、お探しのものを見つけましょう
シスコ コミュニティをいち早く使いこなしていただけるよう役立つリンクをまとめました。みなさんのジャーニーがより良いものとなるようお手伝いします
下記より関連するコンテンツにアクセスできます