Wi-Fi Arubaのshow clientsを取得してDBに入れて見える化するまで
無線アクセスポイントが場所により遅いなどトラブルがあり、手作業でログを取得して解析しているチームがあったので、以下の点で手伝ってみました。
- 自動取得からDB取り込み
- 見える化(Power BI)
目次
目的
アクセスポイントへのアクセスを見える化して、障害時や問合せ対応のときに即座に参照できるようにする。
やったこと
自動取得からDB取り込み
データの取得
arubaにtelnetで接続し、 show clients というコマンドを実行することでデータが取れるもよう。
結果はこうなる
データの出力
どうにかCSVにしたかったけれど方法が見つからず、しょうがないのでtelnetのログファイル出力をし、それをどうにかして読み込むことにしました。
無事、ログファイルはできましたが、コマンドプロンプト上の改行も加味されて出力されてしまうので、コマンドプロンプトの設定を以下のように設定
- フォント:8
- ウインドウのサイズ: 幅250 ※環境依存かと思います。
出力データの整形
PowerShellで無理やり組みました。以上。
SignalやSpeedについては 速度と評価が併記されているので、分解して数値として取り込みやすくしました。 #現在では活用できず。。。
<# Aruba のログ解析(クライアント) telnetから標準出力をテキストとして出力したものを元にCSVとして吐き出す。 #> # 設定値 $csvPath = "show_clients.csv" $csvText = "" $hasHeader = $true $headers = "Name","IP Address","MAC Address","OS","ESSID","Access Point","Channel","Type","Role","IPv6 Address","Signal","Speed (mbps)" $TableTitle = "Client List" $TableTail = "Number of Clients" # 事前処理 Remove-Item $csvPath -Force -ErrorAction Ignore $now = (Get-Date) $text = Get-Content .\show clients.log $columnIndex = @() $isSkip = $true for($i=0; $i -lt $text.Count;$i++){ $line = $text[$i] # ヘッダーの取得 if($line -eq $TableTitle){ $i += 2; $headerText = $text[$i] # ヘッダーテーブルのフォーマット取得 for($j = 0; $j -lt $headers.Count;$j++){ $columnIndex += $headerText.IndexOf($headers[$j]) } if($hasHeader){ $csvText += (("DateTime,"+($headers -join ",")) + "`r`n") } $i++ $isSkip = $false continue; } if($text[$i].StartsWith($TableTail)){ $isSkip = $true } # スキップ if($isSkip){ continue; } $csvLine = $line for($ci = $columnIndex.Length -1; $ci -gt 0; $ci--){ $csvLine = $csvLine.Insert($columnIndex[$ci],",") } $csvText += (("$($now.ToString('yyyy-MM-dd HH:mm:ss.fff')),"+$csvLine) + "`r`n") } # Signal とSpeed を分解してわかりやすくする ConvertFrom-Csv $csvText | select DateTime, "Name","IP Address","MAC Address","OS","ESSID","Access Point","Channel","Type","Role","IPv6 Address", @{Name="Signal Value";Expression={$_.Signal -replace "\(.*","" }}, @{Name="Signal Grade";Expression={$_.Signal -replace "[\d\(\)]",""}}, @{Name="Speed (mbps) Value";Expression={$_."Speed (mbps)" -replace "\(.*","" }}, @{Name="Speed (mbps) Grade";Expression={$_."Speed (mbps)" -replace "[\d\(\)]",""}} | ConvertTo-Csv -NoTypeInformation | %{ $_.Replace('"','') -replace " +,",',' } | Set-Content -Path $csvPath
データ取得処理の自動化
単純なのでVBScriptでオートメーション化しました。
command="show clients" path="C:\Aruba\"& command &".log" ip="" userId="" password ="" set oShell = CreateObject("WScript.Shell") oShell.run "Telnet -f " & path WScript.Sleep 1000 oShell.SendKeys("Open " & ip & "{Enter}") WScript.Sleep 5000 oShell.SendKeys(userId & "{ENTER}") WScript.Sleep 1000 oShell.SendKeys( password & "{ENTER}") WScript.Sleep 1000 oShell.SendKeys(command & "{ENTER}") WScript.Sleep 10000 oShell.SendKeys("exit{ENTER}") WScript.Sleep 500 oShell.SendKeys("{Enter}") WScript.Sleep 1000 oShell.SendKeys("quit{Enter}") WScript.Sleep 1000
データの取り込み
bcp コマンドで取り込み
見える化(Power BI)
アクセス数
情報取得した時間のアクセス数はMACアドレスのユニークカウントとしました。 また、下記の観点でアクセス数を絞り込みます。 - アクセスポイント名 - SSID名
フロアマップで見たくない?
BI製品の宣伝によくある「地図上に円がプロットされて、円のサイズでその数字を表す!」なグラフを作りたい、と思ったので散布図を活用して試してみました。
散布図に必要なデータは以下のようにしています。
- 日時:取得日時
- X座標:フロアマップのX座標
- Y座標:フロアマップのY座標
- 値:アクセスしているクライアント数
座標のとり方
Excelにフロアマップ画像を貼り付けて、背景色を透過することで力技で座標を設定。 アクセスポイント名をリレーションのキーとして紐付けます。
アクセスポイント名 | X座標 | Y座標 |
---|---|---|
AP_FUGA | 2 | 9 |
AP_HOGE | 8 | 7 |
AP_FOO | 7 | 2 |
出来上がり
中心はずれているけれど大体あえばいい。 とりあえず、なんとなく形になったので完成。
大体合計1.5日くらいかかりましたが、満足の出来です。 取り込み処理も定期実行に載せたのでこれからデータがじわじわ貯まるのが楽しみです。 これで集中しているAPなどが一目で分かり、次のアクションにつながるとよいなと思っています。
弊社現場レベルで使えるであろう簡単な社内FAQページを作った
経緯
新しい施策を社内で行うと問い合わせが増える。 その問い合わせがある程度蓄積してきたのでFAQを立てようと思い作りました。 あくまで弊社現場レベルなのでレベルは高くありません。
環境
- Windows Server + IIS
- 社内イントラのサーバーに間借りして設置
- 社内イントラ関連ファイル(html/css/jsなど)のフォルダは共有されており、許可された者のみ編集可能
- なんでもExcelで管理しているからメンテはExcelでできるとよい。
- 弊社現場レベルで使えるのはExcelくらい
作ったもの
まずはFAQページを作り、Vue.jsでうまいことデータを表示し、
メンテに気を使いつつ、Vue.jsにわたすjsonを作れるExcelマクロとPowerShellを組みました。
全体像
こんな感じのExcelにFAQを追加して更新すると。。。
FAQページがこうなります。
こんな環境を作りました。
ExcelのFAQ台帳
- FAQの源流
- 保存前タイミングでCSVを出力、そのCSVをjsonに変換するPowerShell起動
- マクロボタンとかを用意するとメンテする人が押し忘れる可能性があるので保存前
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean) Dim sheet As Worksheet Dim name As String Dim csv As String: csv = ThisWorkbook.Path & "\qna.csv" Dim ps As String: ps = ThisWorkbook.Path & "\convertQnACSV2JSON.ps1" Set sheet = Worksheets("Q&A") ' ファイル名 name = ThisWorkbook.Path & "\qna.csv" ' シートを別のワークブックにコピーする sheet.Copy ' コピーしたワークブックを上書き保存 Application.DisplayAlerts = False ActiveWorkbook.SaveAs Filename:=name, FileFormat:=xlCSV Application.DisplayAlerts = True ' コピーしたワークブックを閉じる ActiveWorkbook.Close SaveChanges:=False strCommand = "Powershell -File """ & ps & """" Set WshShell = CreateObject("WScript.Shell") WshShell.Exec (strCommand) End Sub
PowerShellスクリプト
Excelブックマクロから出力されたCSVをjson形式に変換します。
FAQデータは以下の構造として捉えています(わかりにくい)
- ジャンル別Q&Aのリスト
- 単一Q&A
- Question
- Answer
- 参考リンクのリスト
- タイトル
- URL
- 単一Q&A
<# # Q&AコンテンツのCSVをJson形式に変換する CSVレイアウトは以下 | genre | id | q | a | title1 | url1 | title2 | url2 | ~ | url5 | #> Push-Location -Path (Split-Path $MyInvocation.MyCommand.Path) $csv = Import-Csv ".\qna.csv" -Encoding Default # スコープはjson全体 $json = @{content=(New-Object System.Collections.ArrayList)} # スコープはジャンル別のグループ foreach($gen in ($csv | group "genre")){ # スコープは各Q&A $jgen=@{genre=$gen.Name;qnas=(New-Object System.Collections.ArrayList)} foreach($qna in $gen.Group){ # Q&A参考リンクは列でタイトルとURLを表現している。 # 5つまでのタイトルとURLの組み合わせを配列に変換する。 $tmpLinks = @( @{title=$qna.title1; url=$qna.url1}, @{title=$qna.title2; url=$qna.url2}, @{title=$qna.title3; url=$qna.url3}, @{title=$qna.title4; url=$qna.url4}, @{title=$qna.title5; url=$qna.url5} ) # リンクは自由入力欄のため、ある場合のみリストに加える $jqna = @{id=$qna.id;q=$qna.q;a=$qna.a;keywords=$qna.keywords;links=(New-Object System.Collections.ArrayList)} foreach($link in $tmpLinks){ if($link.url -ne ""){ # タイトルなしの場合URLにする if($link.title -eq ""){ $link.title = $link.url } $jqna.links.Add($link) } } $jgen.qnas.Add($jqna) } $json.content.Add($jgen) } # 出力する Set-Content qna.json ( ConvertTo-Json $json -Depth 6 ) -Encoding UTF8
FAQページ
見た目は全体像の通り、FAQはアコーディオンで開きます。
なお、こちらはBootstrapのフリーのFAQテンプレートを流用して作っており、参考URLと検索欄の他はほぼそのまま。 www.prepbootstrap.com
このページに変換機能もあったので気まぐれでBootstrap4にアップデートしています。
テンプレからの変更点1:検索機能
検索欄に入力すると、FAQが絞れます。検索というよりは絞り込みですね。 既存の環境はいじれないのでバックエンドは考えずフロントでどうにかしています。
function search() { // 検索条件を取得、スペースで区切られた各条件で検索する。 var input = document.querySelector("#search-criteria"); var conditions = input.value.split(/ | /).filter(function(elem) { return elem != ""; }); // 正規表現の作成 var tmpExp = ""; conditions.forEach(function(c, i, a) { tmpExp += ("(?=.*" + c + ")"); }); var exp = "^" + tmpExp; var regexp = new RegExp(exp, "i"); // Q&Aの各要素を取得。条件にヒットしないものを非表示にする。 var divTmp = document.querySelectorAll(".qna") divs = Array.prototype.slice.call(divTmp, 0) divs.forEach(function(div, index, ar) { div.style.display = regexp.test(div.innerHTML) ? "" : "none"; }); }
テンプレからの変更点2:Vue.js
以下の理由からVue.jsに触れることにしました。
- 最近流行っている技術に触れたい
- メンテナンスを楽にしたい
- jsonを更新するだけで反映される構造にしたい
- とはいえシンプルなものでさっさと作りたい
- Reactも気になっていたんですが、環境作るのが難しそうで今回は断念
Web開発の業務経験がないので、こんな理由で使っていいのかアレですが、いろいろ興味のあるものは触れてみると楽しいので。
活用したのは「FAQのjsonデータを元にDOMを作る」目的です(言葉の使い方が正しいか不安) 以前にも riotjsを使ったことがあったので、スムーズに取り入れることができました。
具体的には以下のようなコードを書きました。
<div class="container"> <br /> <div class="form-group"> <label for="usr">検索</label> <input type="text" class="form-control" id="search-criteria" placeholder="検索" onkeyup="search()"> </div> <div class="" id="accordion"> <div id="qnaAll"> <div v-for="qnaByGenre in content"> <div class="faqHeader">{{ qnaByGenre.genre }}</div> <div class="card qna" v-for="qna in qnaByGenre.qnas"> <div class="card-header"> <h4 class=""><a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#accordion" v-bind:href="'#' + qna.id">{{ qna.q }}</a></h4> </div> <div v-bind:id="qna.id" class="panel-collapse collapse"> <div class="card-block"> <div class="answer">{{ qna.a }}</div> <div style="display:none">検索用キーワード {{ qna.keywords }}</div> <div class="links" v-if="qna.links.length > 0"> <hr /> <h5>参考リンク</h5> <ul v-for="link in qna.links"> <li><a v-bind:href="link.url" target="_blank"> {{ link.title }}</a></li> </ul> </div> </div> </div> </div> </div> </div> </div> </div>
SQL Serverで全DB全テーブル全カラムのメタデータを取得する
背景
テーブルレイアウトも仕様書も、弊社内製既存のアプリがことごとく情報がなく、とりあえずDBのメタデータを出力することに。とりあえずメモ。
これらを使って開発側で仕様や意味合いなどまとめていきたい。。。
環境
- SQL Server 2014
コード
DECLARE @DBName NVARCHAR(256) DECLARE @varSQL NVARCHAR(512) DECLARE @getDBName CURSOR SET @getDBName = CURSOR FOR SELECT name FROM sys.databases CREATE TABLE #TmpTable ( DBName NVARCHAR(256),SchemaName NVARCHAR(256),TableName NVARCHAR(256),ColumnName NVARCHAR(256), ValueType NVARCHAR(256),max_length NVARCHAR(256),precision NVARCHAR(256),scale NVARCHAR(256),is_nullable NVARCHAR(256) ) OPEN @getDBName FETCH NEXT FROM @getDBName INTO @DBName WHILE @@FETCH_STATUS = 0 BEGIN print @DBName; SET @varSQL = 'USE ' + @DBName + '; INSERT INTO #TmpTable SELECT ''' + @DBName + ''' AS DatabaseName , SCHEMA_NAME(obj.schema_id) AS SchemaName , obj.name AS TableName , col.name AS ColumnName , typ.name AS ValueType , col.max_length , col.precision , col.scale , col.is_nullable FROM sys.objects obj INNER JOIN sys.columns col ON obj.object_id = col.object_id INNER JOIN sys.types typ ON col.user_type_id = typ.user_type_id WHERE obj.type = ''U'' ' EXEC (@varSQL) FETCH NEXT FROM @getDBName INTO @DBName END CLOSE @getDBName DEALLOCATE @getDBName SELECT * FROM #TmpTable ORDER BY DBName,TableName,ColumnName DROP TABLE #TmpTable
参考
各データベースへのループ処理
https://blog.sqlauthority.com/2008/04/29/sql-server-find-table-in-every-database-of-sql-server/
メタデータ関連
新春MacBook(Unibody 2010)をSSDに、RAM8GBに換装した
家族が端末ほしいと言うので新しく古いMacBook Unibody 2010mid を使えるようにHDDをSSD(500GB)に変え、RAMを8GB(4GBx2)にしてみました。
用途は簡単なプログラミングやネットサーフィンくらいかな。 前提として、元あったデータも何も捨てるのでバックアップなどは一切しません。
やったこと
- 下調べ
- パーツ購入
- 換装作業
- OS再インストール
下調べ
古いMacBookのSSD化、またRAMの換装はしたことがなかったので下調べ。
分解・換装方法
写真はifixit、動画はGeekanoids氏で確認しました。
HDD
RAM
使用パーツ
新年にコンビニ受け取りし、かつ全部まとめてもらいたかったのでAmazonで注文。
HDD
秋葉館のページを参照し対応商品を確認。 www.akibakan.com
近いものをAmazonでレビューを確認しつつ購入。 www.amazon.co.jp
RAM
Apple の公式より、RAMを確認。 MacBook (13-inch, Mid 2010) - 技術仕様
Amazonからこれを購入。 www.amazon.co.jp
精密ドライバー
レビューもまずまずで、かっこよかった(キッズ並観点)のでこれを購入。
換装作業
動画そのまま、問題なく実行できました。 特にメモリは Geekanoidsさんの動画で挿すときに角度をつけるなど、気をつけるべきところがわかったので助かりました。 今気づいたんですが、ifixitにあるバッテリー外し忘れてましたね。
OS再インストール
OS再インストールは
付属DVDからSnowLeopardをインストール
個人情報やAppleIDについては入力しませんでした。
ソフトウェアアップデート(トラブルあり)
SnowLeopardをインストール完了し、ソフトウェアアップデートをかけたら デフォルトの壁紙だけ表示されており、マウスポインタは動かせるが、何もすすまない状態になりました。
調べたところ、以下のQ&Aで解決しました。
SnowLeopard10.6.3でソ… - Apple コミュニティ
そうしたらしばらくアップデートと再起動の繰り返しです。
El Capitanへのアップデート
App Storeからダウンロードするはずなんですが、検索しても見つからないので AppleのサイトからApp Storeを開くリンクを使って移動し、インストールしました。
以上! いい感じに使えるといいなぁ。
源泉徴収票には賞与が含まれる。
久々の記事でまったくコンテンツ性のないタイトル落ちの記事です。 本文を見ている方には申し訳がありません。 ふと思い立って検索するとタイトルで人を寄せて内容を読ませるタイプの記事が多かったのでタイトルだけで終わる記事もいいのでは?と書きました。
以上!
「画像を表示」を復活させるブックマークレットを作ってみたらもっといいものが既にあった
あらすじ
- web系開発に触れていないがjavascriptで色々するのが楽しい。
- 流れで最近話題のGoogle画像検索から消えた「画像を表示」を復活(&改善)させようとブックマークレットを作った。
- ググったら既に拡張機能が作られていたよ。
- 勿体無いのでブックマークレットを記事にメモした。
該当記事
私の作ったもの
2018/02/20 版
javascript:(function(){ function convertHref2Url(href){ var startTag = "/imgres?imgurl="; var endTag = "&imgrefurl="; return decodeURIComponent(href.substring(href.indexOf(startTag) + startTag.length, href.indexOf(endTag))); }; Array.from(document.querySelectorAll("a[jsname='hSRGPd']"), e => { var imageLink = document.createElement("a"); imageLink.setAttribute("target","_blank"); imageLink.style ="height:30px;width:80px;" + "background-color:rgba(130, 130, 255, 0.8);" + "color:rgba(255, 255, 255, 1.0);" + "z-index:1;" + "border-radius: 10px;" + "display: flex;" + "flex-direction: column;" + "justify-content: center;" + "align-items: center;" + "position: absolute;" + "text-decoration: none;"; imageLink.href = convertHref2Url(e.href); var linkTitle = document.createElement("div"); linkTitle.style = "color:black;font-size:5px;"; linkTitle.innerText = "View image"; imageLink.appendChild(linkTitle); e.parentNode.appendChild(imageLink); }); })();
2018/02/21 版
面白そうだったので色々試した
- スクロールした後、リンクのついてない画像が出て来る。 何度も実行することを考慮して重複しないようにした。
- すべての追加される要素にstyle属性を直書きすると文字数がすごいことになる(影響不明)のでstyle要素を追加した。 style要素も重複対応済。
javascript: (function () { function convertHref2Url(href) { var startTag = "/imgres?imgurl="; var endTag = "&imgrefurl="; return decodeURIComponent(href.substring(href.indexOf(startTag) + startTag.length, href.indexOf(endTag))); }; var styleId = "gas_view-image_style"; var labelClass = "gas_view-image_label"; var linkClass = "gas_view-image_link"; var s = document.querySelector("#" + styleId); if (s === null) { var style = document.createElement("style"); style.setAttribute("id", styleId); style.nodeType = "text/css"; style.appendChild(document.createTextNode(` .${labelClass} { height:30px; width:80px; background-color:rgba(130, 130, 255, 0.9); color:rgba(255, 255, 255, 1.0); z-index:1; border-radius: 10px; display: flex; flex-direction: column; justify-content: center; align-items: center; position: absolute; text-decoration: none; } .${linkClass}{ color:black; font-size:8px; } `)); document.head.appendChild(style); } Array.from(document.querySelectorAll("a[jsname='hSRGPd']"), e => { if (e.parentNode.querySelector("." + labelClass) === null) { var imageLink = document.createElement("a"); imageLink.setAttribute("target", "_blank"); imageLink.setAttribute("class", labelClass); imageLink.href = convertHref2Url(e.href); var linkTitle = document.createElement("div"); linkTitle.innerText = "View image"; linkTitle.setAttribute("class", linkClass); imageLink.appendChild(linkTitle); e.parentNode.appendChild(imageLink); } }); })();
あと記事の問題ですが、コードハイライトが有効になるように修正。 (javascript を js にしていた。)
動作
こんな感じに「View image」のリンクが作られます。
javascriptどころかweb系全然触れてない(言い訳)のでコードの適当さがあります。
以上!