Ruckus One AP モニタリング
最終更新日 - 20年2026月XNUMX日
LogicMonitorのRUCKUS One AP監視パッケージは、CommScopeのRUCKUS Oneプラットフォームを通じて導入されたクラウド管理型ワイヤレスアクセスポイント(AP)の包括的な可視性を提供します。このパッケージは、RUCKUS OneパブリックREST APIを使用して、複数の拠点に分散されたAPからパフォーマンス、インベントリ、クライアントテレメトリデータを収集し、ネットワークチームにワイヤレスインフラストラクチャの健全性と使用状況に関する詳細な情報を提供します。
RUCKUS One APIの詳細については、以下を参照してください。 概要(API) の三脚と クライアント管理 (0.0.1) RUCKUSより。
RUCKUS One AP モニタリングの要件
RUCKUS One AP モニタリングを使用するには、次のものが必要です。
- テナントに対してRUCKUS One APIアクセスが有効
- API アクセス用に構成された OAuth 2.0 クライアント資格情報
詳細については、を参照してください。 アプリケーショントークンの生成 RUCKUSより。 - RUCKUS One に登録され、会場に割り当てられたリソース
- 適切な RUCKUS One API リージョンへのコレクターのアウトバウンド HTTPS アクセス (TCP 443)
RUCKUS One AP リソースを監視に追加する
RUCKUS One AP リソースは、次のいずれかの方法で監視対象にオンボードできます。
- 拡張スクリプト NetScan (推奨)
- 手動でリソースを追加する
NetScan の詳細については、次を参照してください。 NetScan の概要。
拡張スクリプトNetScanを使用してリソースを追加する
- LogicMonitorで、次の場所に移動します モジュール > 応募者と.
- RUCKUS One AP モジュール パッケージを見つけてインストールします。
- MFAデバイスに移動する リソース > 追加 > 高度なネットスキャン.
- NetScan の名前を入力します (例: RUCKUS One AP)。
- NetScan を実行するコレクタを選択します。
- 「拡張スクリプトNetScan」を選択します。 方法 ドロップダウンメニュー。
- 拡張スクリプトセクションで、 デバイスの資格情報 > このスキャンにはカスタム資格情報を使用します。
- NetScan に必要な RUCKUS One 資格情報を提供し、リソースの編成方法を制御するには、次のプロパティを追加します。
| プロパティ | 詳細説明 | 必須? |
ruckus.one.tenant.id | AP を照会する Ruckus One テナント ID。 | あり |
ruckus.one.client.id | Ruckus One API クライアント ID。 | あり |
ruckus.one.client.key | Ruckus One API クライアント シークレット/キー。 | あり |
ruckus.one.region | Ruckus One APIのホスト名/リージョン。デフォルトは api.ruckus.cloud 設定されていない場合。 | オプション |
root.folder.name | デバイスが作成される LM 内のルートフォルダパス。デフォルトは Ruckus One です。 | オプション |
skip.device.dedupe | に設定され true LM API 経由の重複デバイスチェックをスキップします。デフォルトは false です。 | オプション |
hostname.source | ホスト名の衝突を解決する方法を制御します: lm、logicmonitor、または netscan。 | オプション |
lmapi.timelimit.sec | LM APIデバイス検索の時間制限(30~120秒)。デフォルトは60秒です。 | オプション |
ruckus.one.ap.venues.csv | カスタム会場名を含む CSV ファイルの名前 | オプション |
注意: ruckus.one.tenant.idのいずれかの場合、 ruckus.one.client.id、または ruckus.one.client.key がない場合、スクリプトはエラーをスローし、NetScan は失敗します。
- 選択する Groovyスクリプトを埋め込む 次のスクリプトを埋め込みます。
警告: スクリプトを編集しないでください。 編集された拡張スクリプト NetScan はサポートされていません。 LogicMonitor が提供するスクリプトを編集した場合、問題が発生した場合、LogicMonitor サポートはサポートされているスクリプトで編集内容を上書きするよう要求する場合があります。 拡張スクリプト NetScan は、LM Envision リソースの作成を 600 時間あたり 600 以下に制限します。 XNUMX を超えるリソースを作成するには、すべてのリソースが追加されるまで NetScan を XNUMX 時間ごとに繰り返すようにスケジュールします。
/*******************************************************************************
* © 2007-2026 - LogicMonitor, Inc. All rights reserved.
******************************************************************************/
import com.santaba.agent.groovy.utils.GroovyScriptHelper as GSH
import com.logicmonitor.mod.Snippets
import com.santaba.agent.AgentVersion
import java.text.DecimalFormat
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
// To run in debug mode, set to true
Boolean debug = false
// Set props object based on whether or not we are running inside a netscan or debug console
def props
try {
hostProps.get("system.hostname")
props = hostProps
debug = true // set debug to true so that we can ensure we do not print sensitive properties
}
catch (MissingPropertyException) {
props = netscanProps
}
// Credentials Check
if (!props.get("ruckus.one.tenant.id") || !props.get("ruckus.one.client.id") || !props.get("ruckus.one.client.key")) {
throw new Exception("Must provide 'ruckus.one.tenant.id', 'ruckus.one.client.id', and 'ruckus.one.client.key' to run this script. Verify necessary credentials have been provided.")
}
// Optional properties
def rootFolder = props.get("root.folder.name", "Ruckus One") // root folder can be nested, i.e. 'site1/subfolder1'
Boolean skipDeviceDedupe = props.get("skip.device.dedupe", "false").toBoolean()
String hostnameSource = props.get("hostname.source", "")?.toLowerCase()?.trim()
// Get region & tenant ID
def region = props.get("ruckus.one.region") ?: "api.ruckus.cloud"
def tenantId = props.get("ruckus.one.tenant.id")
// Cache context
def logCacheContext = "${tenantId}::ruckus-one-ap"
def modLoader = GSH.getInstance(GroovySystem.version).getScript("Snippets", Snippets.getLoader()).withBinding(getBinding())
def lmEmit = modLoader.load("lm.emit", "1.1")
def lmDebug = modLoader.load("lm.debug", "1.0").debugSnippetFactory(out, debug)
def http = modLoader.load("proto.http", "0").httpSnippetFactory(props)
def cache = modLoader.load("lm.cache", "0").cacheSnippetFactory(lmDebug, logCacheContext)
def ruckusOneAp = modLoader.load("ruckus.one.ap", "0").create(props, lmDebug, cache, http)
// Only initialize lmApi snippet class if customer has not opted out
def lmApi
if (!skipDeviceDedupe) {
lmApi = modLoader.load("lm.api", "0").lmApiSnippetFactory(props, http, lmDebug)
}
// Get information about devices that already exist in LM portal
List fields = ["name", "currentCollectorId", "displayName"]
Map args = ["size": 1000, "fields": fields.join(",")]
def lmDevices
// But first determine if the portal size is within a range that allows us to get all devices at once
def pathFlag, portalInfo, timeLimitSec, timeLimitMs
if (!skipDeviceDedupe) {
portalInfo = lmApi.apiCallInfo("Devices", args)
timeLimitSec = props.get("lmapi.timelimit.sec", "60").toInteger()
timeLimitMs = (timeLimitSec) ? Math.min(Math.max(timeLimitSec, 30), 120) * 1000 : 60000 // Allow range 30-120 sec if configured; default to 60 sec
if (portalInfo.timeEstimateMs > timeLimitMs) {
lmDebug.LMDebugPrint("Estimate indicates LM API calls would take longer than time limit configured. Proceeding with individual queries by display name for each device to add.")
lmDebug.LMDebugPrint("\t${portalInfo}\n\tNOTE: Time limit is set to ${timeLimitSec} seconds. Adjust this limit by setting the property lmapi.timelimit.sec. Max 120 seconds, min 30 seconds.")
pathFlag = "ind"
}
else {
lmDebug.LMDebugPrint("Response time indicates LM API calls will complete in a reasonable time range. Proceeding to collect info on all devices to cross reference and prevent duplicate device creation.\n\t${portalInfo}")
pathFlag = "all"
lmDevices = lmApi.getPortalDevices(args)
}
}
// Get all AP information for tenant
def apsEndpoint = "venues/aps/query"
def apsEndpointBody = '{"pageSize": 10000 , "fields": ["serialNumber","venueId","model","macAddress","firmwareVersion", "name", "networkStatus.ipAddress" ] }'
def apsResponseData = null
if(debug){
apsResponseData = cache.cacheGet(apsEndpoint)
} else {
apsResponseData = ruckusOneAp.requestDataFromAPI("POST", apsEndpoint, apsEndpointBody)
}
lmDebug.LMDebugPrint("\nResponse Data from ${apsEndpoint}:\n${apsResponseData}\n")
def apData = ruckusOneAp.slurper.parseText(apsResponseData)
lmDebug.LMDebugPrint("AP Data: ${apData}")
// Get venue ID to name mappings
def venueEndpoint = "venues/query"
def venueEndpointBody = '{"pageSize": 10000, "fields": ["id","name"] }'
def venueResponseData = null
if(debug){
venueResponseData = cache.cacheGet(venueEndpoint)
} else {
venueResponseData = ruckusOneAp.requestDataFromAPI("POST", venueEndpoint, venueEndpointBody)
}
lmDebug.LMDebugPrint("\nResponse Data from ${apsEndpoint}:\n${venueResponseData}\n")
venueResponseData = ruckusOneAp.slurper.parseText(venueResponseData)
Map<String,String> venueIdToNameMap = venueResponseData?.data?.collectEntries { venue ->
[(venue.id): venue.name]
}
lmDebug.LMDebugPrint("venueIdToNameMap: ${venueIdToNameMap}")
// Get tenant ID and name
def tenantEndpoint = "tenants/self"
def tenantResponseData = ruckusOneAp.requestDataFromAPI("GET", tenantEndpoint)
lmDebug.LMDebugPrint("\nResponse Data from ${tenantEndpoint}:\n${tenantResponseData}\n")
tenantResponseData = ruckusOneAp.slurper.parseText(tenantResponseData)
def responseTenantId = tenantResponseData?.id
def responseTenantName = tenantResponseData?.name
lmDebug.LMDebugPrint("Tenant Info: 'id'=${responseTenantId}, 'name'=${responseTenantName}")
// Fail safe
if(responseTenantId != tenantId) {
throw new Exception("Tenant ID provided in credentials (${tenantId}) does not match tenant ID retrieved from API (${responseTenantId}). Verify credentials and try again.")
}
List<Map> resources = []
def now = new Date()
def dateFormat = "yyyy-MM-dd'T'HH:mm:ss.s z"
TimeZone tz = TimeZone.getDefault()
Map duplicateResources = [
"date" : now.format(dateFormat, tz),
"message" : "Duplicate display names found within LogicMonitor portal wherein hostname in LM does not match hostname in Netscan output. Refer to documentation for how to resolve name collisions using 'hostname.source' netscan property.",
"total" : 0,
"resources" : []
]
// Loop through data to build device map with proper keys
apData?.data?.each{ device ->
String displayName = device.name
Integer collectorId
// Check for existing device in LM portal with this displayName; set to false initially and update to true when dupe found
def deviceMatch = false
// If customer has opted out of device deduplication checks, we skip the lookups where we determine if a match exists and proceed as false
if (!skipDeviceDedupe) {
if (pathFlag == "ind") {
deviceMatch = lmApi.findPortalDevice(displayName, args)
}
else if (pathFlag == "all") {
deviceMatch = lmApi.checkExistingDevices(displayName, lmDevices)
}
}
if (deviceMatch) {
// Log duplicates that would cause additional devices to be created; unless these entries are resolved, they will not be added to resources for netscan output
def ip = device?.networkStatus?.ipAddress // ip from the endpoint data
if (ip != deviceMatch.name) {
def collisionInfo = [
(displayName) : [
"Netscan" : [
"hostname" : ip
],
"LM" : [
"hostname" : deviceMatch.name,
"collectorId" : deviceMatch.currentCollectorId
],
"Resolved" : false
]
]
// If user specified to use LM hostname on display name match, update hostname variable accordingly
// and flag it as no longer a match since we have resolved the collision with user's input
if (hostnameSource == "lm" || hostnameSource == "logicmonitor") {
ip = deviceMatch.name
collectorId = deviceMatch.currentCollectorId
deviceMatch = false
collisionInfo[displayName]["Resolved"] = true
}
// If user specified to use netscan data for hostname, update the display name to make it unique
// and flag it as no longer a match since we have resolved the collision with user's input
else if (hostnameSource == "netscan") {
// Update the resolved status before we change the displayName
collisionInfo[displayName]["Resolved"] = true
displayName = "${displayName} - ${ip}"
deviceMatch = false
}
duplicateResources["resources"].add(collisionInfo)
}
}
// Initialize groupName and assign value based on whether a proper venue name has been provided via CSV
List<String> groupName = ["${rootFolder}/${responseTenantName}/${venueIdToNameMap[device.venueId]}"]
def hostPropsToEmit = [
"ruckus.one.region" : region, // region, defaults to "api.ruckus.cloud" if not set
"ruckus.one.tenant.name": responseTenantName, // tenant name
"ruckus.one.tenant.id" : responseTenantId, // tenant
"ruckus.one.client.id" : props.get("ruckus.one.client.id"), // credentials
"ruckus.one.client.key": props.get("ruckus.one.client.key"), // credentials
"ruckus.one.venue.id" : device.venueId, // used to filter data by venue
"ruckus.one.venue.name": venueIdToNameMap[device.venueId], // venue name
"ruckus.one.serial" : device.serialNumber.toString(), // used to filter data by AP serial number
]
// Build resource map
Map resource = [
"hostname" : "${device.networkStatus.ipAddress}", // String
"displayname" : device.name, // String
"hostProps" : hostPropsToEmit, // Map<String, String>
"groupName" : groupName, // List<String>
"collectorId" : collectorId // Integer
]
// Only add the collectorId field to resource map if we found a collector ID above
if (collectorId) {
resource["collectorId"] = collectorId
duplicateResources["resources"][displayName]["Netscan"][0]["collectorId"] = collectorId
}
if (!deviceMatch) {
resources.add(resource)
}
}
// Output validated data in JSON format
lmEmit.resource(resources, debug)
// Report devices that already exist in LM via log file named after root folder
if (duplicateResources["resources"].size() > 0) {
def netscanDupLog = new File("../logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json")
new File(netscanDupLog.getParent()).mkdirs()
duplicateResources["total"] = duplicateResources["resources"].size()
def json = JsonOutput.prettyPrint(JsonOutput.toJson(duplicateResources))
netscanDupLog.write(json)
if (hostnameSource) {
lmDebug.LMDebug("${duplicateResources["resources"].size()} devices found that were resolved with hostname.source=${hostnameSource} in netscan output. See LogicMonitor/Agent/logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json for details.")
}
else {
lmDebug.LMDebug("${duplicateResources["resources"].size()} devices found that were not reported in netscan output. See LogicMonitor/Agent/logs/NetscanDuplicates/${rootFolder.replaceAll(" ", "_")}.json for details.")
}
}
return 0- 「スケジュール」セクションで、「この NetScan をスケジュールに従って実行する」を選択します。動的環境の場合、NetScan を 1 時間ごとに実行するようにスケジュールできます。
- 選択する Save or 保存して実行.
NetScan を実行した後、追加されたリソースの数、または NetScan がリソースを作成しない場合はエラー メッセージの履歴を確認します。
RUCKUS One AP リソースの手動追加
- RUCKUS One AP リソース グループを作成します。
詳細については、を参照してください。 リソースグループの追加. - AP を RUCKUS One AP リソース グループにリソースとして追加します。
- リソースまたはリソース グループに次のプロパティを追加するか、設定されていることを確認します。
| プロパティ | 詳細説明 |
system.hostname | AP管理IPまたは解決可能なホスト名 |
ruckus.one.tenant.id | テナントID |
ruckus.one.client.id | OAuthクライアントID |
ruckus.one.client.key | OAuthクライアントシークレット |
ruckus.one.serial | APシリアル番号 |
ruckus.one.venue.id | 会場識別子 |
ruckus.one.region | API リージョン(該当する場合) |
プロパティの設定の詳細については、を参照してください。 リソースとインスタンスのプロパティ.
LogicModulesをインポートする
に含まれるすべてのLogicModuleをインポートします RUCKUS One AP モニタリング パッケージ LM Exchange から。
これらのLogicModuleが既に存在する場合は、最新バージョンに更新されていることを確認してください。インポート後、検出とデータ収集が自動的に開始されます。
トラブルシューティング
RUCKUS One AP モニタリング パッケージの導入または操作時に発生する一般的な問題を診断および解決するには、次の表を参照してください。
| 問題 | 解像度 |
| データは収集されません | 必要なすべてのプロパティが構成されていることを確認し、コレクターからの送信 HTTPS 接続を確認します。 |
| モジュールが適用されない | 確認する addCategory_Ruckus_One_AP PropertySource は正しいシステム カテゴリを割り当てています。 |
| 認証エラー | API 資格情報とテナント ID 構成を確認します。 |
パッケージ内のLogicModules
LogicMonitorのRUCKUS One APモニタリングパッケージは、以下のLogicModuleで構成されています。完全なカバレッジを得るには、以下のLogicModuleがすべてLogicMonitorプラットフォームにインポートされていることを確認してください。
| 表示名 | タイプ | 詳細説明 |
Ruckus_One_AP_API | データソース | RUCKUS One クラウド API に接続して、他の RUCKUS One AP モジュールで使用するためにアクセス ポイント、会場のインベントリ、およびヘルス メトリックを収集してキャッシュします。 |
Ruckus_One_AP_Performance | データソース | RUCKUS One の会場とシリアル番号に応じてクライアントの数を処理します。 |
Ruckus_One_AP_SSIDs | データソース | RUCKUS One アクセス ポイント上の各 SSID を検出し、SSID ごとのクライアント負荷、トラフィック、RF 品質 (RSSI、SNR、ノイズ フロア) を監視します。 |
Ruckus_One_AP_Radios | データソース | RUCKUS One アクセス ポイント上の各無線帯域を検出し、無線ごとのクライアント負荷、トラフィック、RF 品質 (RSSI、SNR、ノイズ フロア) を監視します。 |
addCategory_Ruckus_One_AP | プロパティソース | 割り当て system.categories の値 RuckusOneAP RUCKUS One アクセス ポイント デバイスにその他の必要なプロパティを追加します。 |
Ruckus One Webhooks | ログソース | RUCKUS One からの Webhook イベントを関連する LogicMonitor リソースにマッピングします。 |
このパッケージの DataSource によって追跡されるさまざまなメトリクスに静的なデータポイントのしきい値を設定する場合、LogicMonitor はテクノロジー所有者のベスト プラクティス KPI 推奨事項に従います。
推奨事項: 必要に応じて、環境固有のニーズに合わせてこれらの事前定義されたしきい値を調整します。 データポイントのしきい値の調整の詳細については、を参照してください。 データポイントの静的しきい値.