Cisco Meraki モニタリング

最終更新日: 24 年 2024 月 XNUMX 日

免責事項: これらのモジュールは、Cisco Meraki デバイスを個別の LogicMonitor リソースとして監視し、Cisco Meraki (レガシー) モジュールへのインプレース アップグレードではありません。 詳細については、「」を参照してください。 Cisco Meraki (レガシー).

同じ Meraki ネットワーク内の同じ Meraki デバイスを監視するために、これらのモジュールを Cisco Meraki (レガシー) モジュール (または他のサードパーティ監視ソリューション) と並行して実稼働環境で使用することは推奨されません。 そうすると、Meraki ダッシュボード API 3 エラーが発生し、モジュールとサードパーティの監視ソリューションの両方でデータのギャップが発生する可能性があります。 リスクを軽減するには、組織の変更管理プロセスに従ってください。

LogicMonitor モジュールのこのパッケージは、ワイヤレス アクセス ポイント、スマート カメラ、セキュリティ アプライアンス、スイッチ、セルラー ゲートウェイ、環境センサーなどの Cisco Meraki デバイスを監視します。これらのモジュールは、Cisco Meraki ダッシュボード API と snmp.meraki.com の両方を使用してデータを収集します。詳細については、「 Meraki ダッシュボード API シスコのドキュメント。

要件

モニタリングにリソースを追加する

次の方法を使用して Cisco Meraki リソースを追加できます。

  • (推奨) Meraki ダッシュボード API を使用して、監視対象の各 Meraki デバイスのリソースを検出および作成する高度な NetScan。 NetScan は、各 Meraki ネットワークのリソース グループに各リソースを編成します。 詳細については、「」を参照してください。 ネットスキャンとは.
  • 手動で

警告: 超過を避けるために、NetScan を実行する前に、十分な LogicMonitor ライセンスがあることを確認してください。 さらに、NetScan フィルタをテストして、監視したくない Meraki デバイスを誤って追加しないようにしてください。

Advanced NetScan の使用の詳細については、次を参照してください。 強化されたスクリプト ネットスキャン.

  1. あなたの中で ロジックモニターポータル > モジュール > 応募者と、Cisco Meraki モジュールをインストールします。 モジュールのリストについては、を参照してください。 パッケージ内のLogicModules.
  2. MFAデバイスに移動する  「リソース」 > 「追加」 > 「複数のデバイス」 > 「高度な NetScan」.
      高度な NetScan を追加する ページが表示されます。
  3. この NetScan に関連付ける名前を入力します。 たとえば、「Cisco Meraki デバイス」です。 
  4. NetScan を実行するコレクタを選択します。
    詳細については、を参照してください。 コレクターの割り当て in 強化されたスクリプト ネットスキャン.
  5. 選択 強化されたスクリプト NetScan   方法 ドロップダウンリスト。
  6. 「拡張スクリプト」セクションから、次を選択します。 デバイスの資格情報 > このスキャンにはカスタム認証情報を使用します。
  7. NetScan に必要な Cisco Meraki 資格情報を提供する次のプロパティを追加し、NetScan がリソースを作成および編成する方法と場所を変更します。
プロパティ必須
meraki.api.orgMeraki 組織 ID有り
meraki.api.keyMeraki API キー
NetScan は、このプロパティの値をマスクします。
有り
meraki.snmp.community.pass
(SNMP v1 または v2c に適用)
コミュニティ文字列
注: これらの SNMP 設定は、Netscan 資格情報プロパティとして、または Meraki デバイスを含むリソース グループのカスタム プロパティとして構成します。 Meraki ダッシュボード -> 組織 -> 設定 -> SNMP から値を取得します。 これらの設定は、snmp.meraki.com との通信にのみ影響します。 SNMP_Network_Interfaces や SNMP_Host_Uptime などのローカル SNMP データソースも使用している場合、これらの設定によって設定や動作が変更されることはありません。
有り
(SNMP v2cを使用する場合)
meraki.snmp.セキュリティ
(SNMP v3に適用)

注: これらの SNMP 設定は、Netscan 資格情報プロパティとして、または Meraki デバイスを含むリソース グループのカスタム プロパティとして構成します。 Meraki ダッシュボード -> 組織 -> 設定 -> SNMP から値を取得します。 これらの設定は、snmp.meraki.com との通信にのみ影響します。 SNMP_Network_Interfaces や SNMP_Host_Uptime などのローカル SNMP データソースも使用している場合、これらの設定によって設定や動作が変更されることはありません。
有り
(SNMP v3を使用している場合)
meraki.snmp.auth
(SNMP v3に適用)
SHA または MD5
注: これらの SNMP 設定は、Netscan 資格情報プロパティとして、または Meraki デバイスを含むリソース グループのカスタム プロパティとして構成します。 Meraki ダッシュボード -> 組織 -> 設定 -> SNMP から値を取得します。 これらの設定は、snmp.meraki.com との通信にのみ影響します。 SNMP_Network_Interfaces や SNMP_Host_Uptime などのローカル SNMP データソースも使用している場合、これらの設定によって設定や動作が変更されることはありません。
有り
(SNMP v3を使用している場合)
meraki.snmp.authToken.pass
(SNMP v3に適用)
SHA または MD5 認証トークン (パスワード)

注: これらの SNMP 設定は、Netscan 資格情報プロパティとして、または Meraki デバイスを含むリソース グループのカスタム プロパティとして構成します。 Meraki ダッシュボード -> 組織 -> 設定 -> SNMP から値を取得します。 これらの設定は、snmp.meraki.com との通信にのみ影響します。 SNMP_Network_Interfaces や SNMP_Host_Uptime などのローカル SNMP データソースも使用している場合、これらの設定によって設定や動作が変更されることはありません。

有り
(SNMP v3を使用している場合)
meraki.snmp.priv
(SNMP v3に適用)
AES128 または DES
注: これらの SNMP 設定は、Netscan 資格情報プロパティとして、または Meraki デバイスを含むリソース グループのカスタム プロパティとして構成します。 Meraki ダッシュボード -> 組織 -> 設定 -> SNMP から値を取得します。 これらの設定は、snmp.meraki.com との通信にのみ影響します。 SNMP_Network_Interfaces や SNMP_Host_Uptime などのローカル SNMP データソースも使用している場合、これらの設定によって設定や動作が変更されることはありません。
有り
(SNMP v3を使用している場合)
meraki.snmp.privToken.pass
(SNMP v3に適用)
プライバシートークン(パスワード)
注: これらの SNMP 設定は、Netscan 資格情報プロパティとして、または Meraki デバイスを含むリソース グループのカスタム プロパティとして構成します。 Meraki ダッシュボード -> 組織 -> 設定 -> SNMP から値を取得します。 これらの設定は、snmp.meraki.com との通信にのみ影響します。 SNMP_Network_Interfaces や SNMP_Host_Uptime などのローカル SNMP データソースも使用している場合、これらの設定によって設定や動作が変更されることはありません。
有り
(SNMP v3を使用している場合)
meraki.api.org.フォルダーこの NetScan が作成するか、すでに存在する場合は使用する LogicMonitor リソース グループの名前
値には、ネストされた子フォルダーを指定できます。 たとえば、フォルダー/フォルダー/フォルダーなどです。
いいえ
meraki.api.org.nameMeraki 組織名
これは Cisco Meraki のルート リソース グループの名前になります。また、meraki.api.org.name プロパティを使用して代替ルート フォルダを指定した場合は、最上位の子リソース グループの名前になります。
いいえ
meraki.api.org.networks含めるカンマ区切りのネットワーク ID (その他は除外されます)
いいえ
meraki.api.org.collector.networks.csvデフォルトでは、NetScan によって作成された新しいリソースは、NetScan を実行するコレクタに割り当てられます。 このプロパティは、CSV ファイルを使用してデフォルトの動作をオーバーライドし、Meraki ネットワーク ID と LogicMonitor コレクタ名に基づいて Meraki デバイスを目的のコレクタに割り当てます。 CSV ファイルは、NetScan を実行するコレクタに保存する必要があります。 Linux: /usr/local/logicmonitor/agent/bin Windows: C:\Program Files\LogicMonitor\Agent\bin 詳細については、次を参照してください。 Cisco Meraki デバイスの LM Envision Collector へのマッピングいいえ
meraki.service.url代替リージョン URL には、https://dashboard.meraki.cn/v1 が含まれます。いいえ
lmaccess.id
OR
ロジックモニター.アクセス.id
ポータルにリソースを追加する前に NetScan で重複リソースを検索するための LogicMonitor API アクセス ID。 ポータル監視の詳細については、次を参照してください。 LogicMonitorポータルモニタリング.有り
lmaccess.key
OR
ロジックモニター.アクセス.キー
ポータルにリソースを追加する前に NetScan で重複リソースを検索するための LogicMonitor API キー。 ポータル監視の詳細については、次を参照してください。 LogicMonitorポータルモニタリング.有り
ホスト名.ソースNetScan で使用されるホスト名のソースを選択できます。 これは、既存のリソースに競合が見つかった場合に、重複したデバイスの作成を防ぐのに役立ちます。 詳細については、「」を参照してください。 NetScan のトラブルシューティング.いいえ
スキップ.device.dedupeデバイスの重複排除チェックをスキップできるため、LogicMonitor API 認証情報が不要になります。いいえ
meraki.disableswitchesスイッチごとの API 呼び出しとデータ収集を無効にする

値を「true」に設定する場合は、URL から標準の SNMP プロパティを設定し、Meraki ダッシュボードを使用してローカル デバイスの SNMP データ収集を設定します。 さらに、SNMP_Network_Interfaces を使用してローカル SNMP データ収集を確認する場合は、Cisco_Meraki_SwitchInterfaces モジュールを無効にする、適用解除する、またはアンインストールします。

注: DataSource を無効にすると履歴データが保持されますが、DataSource を適用解除またはアンインストールしても履歴データは保持されません。

詳細については、以下を参照してください SNMP の概要と構成 Cisco Meraki のドキュメント。
いいえ
  1. [フィルター] セクションで、次のフィルター プロパティを使用して、特定のリソース (アプライアンス、スイッチ、ワイヤレス アクセス ポイント、カメラ、セルラー ゲートウェイなど) を検出から除外します。
    これらのフィルターは、Active Discovery フィルターと同じように動作します。 次のデバイス レベルのプロパティが自動的に検出され、フィルタリングできます。 利用可能なフィルター操作は次のとおりです。 Equal、NotEqual、GreaterThan、GreaterEqual、LessThan、LessEqual、Contain、NotContain、Exist、NotExist、RegexMatch、RegexNotMatch.

推奨事項: 必要な機能を確認するには、監視する一部のデバイスの特定のフィルターから始めて、フィルター操作または値を段階的にオンボード監視対象リソースに段階的に変更します。 たとえば、プロパティとフィルタが適切に定義されて目的の結果が得られると確信するまで、一度に XNUMX つの Meraki ネットワークのみを検出またはインポートするようにフィルタを設定できます。

警告: フィルタを設定しない場合、NetScan は検出されたすべての Meraki デバイスを個別の LogicMonitor リソースとして追加しようとします。

プロパティ操作必須
meraki.productType等しいこのタイプの Meraki デバイスをインポートするアプライアンスいいえ
等しいこのタイプの Meraki デバイスをインポートするスイッチいいえ
等しいこのタイプの Meraki デバイスをインポートする無線いいえ
等しくないこのタイプの Meraki デバイスを除外するカメラいいえ
等しいこのタイプの Meraki デバイスをインポートするセルラーゲートウェイいいえ
等しいこのタイプの Meraki デバイスをインポートするセンサーいいえ
メラキ.タグ等しいこの Meraki ダッシュボード タグを使用してデバイスをインポートします製造いいえ
等しいこの Meraki ダッシュボード タグを使用してデバイスをインポートしますモニターいいえ
等しくないこの Meraki ダッシュボード タグを持つデバイスを除外しますステージングいいえ
メラキシリアル等しいこの Meraki シリアル番号を持つデバイスをインポートしますQ1AB-2CD3-EFGHいいえ
等しいこの Meraki シリアル番号を持つデバイスをインポートしますQ2AB-3CD4-EFGHいいえ
等しくないこの Meraki シリアル番号を持つデバイスを除外しますQ3AB-4CD5-EFGHいいえ
meraki.ファームウェア等しいこのファームウェア バージョンを持つ Meraki デバイスをインポートしますスイッチ-16-4いいえ
等しくないこのファームウェア バージョンを持つ Meraki デバイスを除外しますワイヤレス-29-5-1いいえ
等しくないこのファームウェア バージョンを持つ Meraki デバイスを除外しますファームウェアがロックされました。 サポート問い合わせ先。いいえ
meraki.api.network.name等しいこの Meraki ネットワークにデバイスをインポートするストア105いいえ
等しくないこの Meraki ネットワーク内のデバイスを除外しますストア106いいえ
  1. 選択 Groovyスクリプトを埋め込む 次のスクリプトを埋め込みます。

警告: スクリプトを編集しないでください。 編集された拡張スクリプト NetScan はサポートされていません。 LogicMonitor が提供するスクリプトを編集した場合、問題が発生した場合、LogicMonitor サポートはサポートされているスクリプトで編集内容を上書きするよう要求する場合があります。 拡張スクリプト NetScan は、LM Envision リソースの作成を 600 時間あたり 600 以下に制限します。 XNUMX を超えるリソースを作成するには、すべてのリソースが追加されるまで NetScan を XNUMX 時間ごとに繰り返すようにスケジュールします。

/*******************************************************************************
 * © 2007-2024 - LogicMonitor, Inc. All rights reserved.
 ******************************************************************************/

import com.logicmonitor.common.sse.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
// To enable logging, set to true
Boolean log = 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
}

String org = props.get("meraki.api.org")
String key = props.get("meraki.api.key")
if (!org) {
    throw new Exception("Must provide meraki.api.org to run this script.  Verify necessary credentials have been provided in Netscan properties.")
}
if (!key) {
    throw new Exception("Must provide meraki.api.key credentials to run this script.  Verify necessary credentials have been provided in Netscan properties.")
}

def logCacheContext = "${org}::cisco-meraki-cloud"
Boolean skipDeviceDedupe = props.get("skip.device.dedupe", "false").toBoolean()
String hostnameSource    = props.get("hostname.source", "")?.toLowerCase()?.trim()

Integer collectorVersion = AgentVersion.AGENT_VERSION.toInteger()
 
// Bail out early if we don't have the correct minimum collector version to ensure netscan runs properly
if (collectorVersion < 32400) {
    def formattedVer = new DecimalFormat("00.000").format(collectorVersion / 1000)
    throw new Exception("Upgrade collector running netscan to 32.400 or higher to run full featured enhanced netscan. Currently running version ${formattedVer}.")
}

def modLoader = GSH.getInstance()._getScript("Snippets", Snippets.getLoader()).withBinding(getBinding())
def emit        = modLoader.load("lm.emit", "1.1")
def lmDebugSnip = modLoader.load("lm.debug", "1")
def lmDebug     = lmDebugSnip.debugSnippetFactory(out, debug, log, logCacheContext)
def httpSnip    = modLoader.load("proto.http", "0")
def http        = httpSnip.httpSnippetFactory(props)
def cacheSnip   = modLoader.load("lm.cache", "0")
def cache       = cacheSnip.cacheSnippetFactory(lmDebug, logCacheContext)
// Only initialize lmApi snippet class if customer has not opted out
def lmApi
if (!skipDeviceDedupe) {
    def lmApiSnippet = modLoader.load("lm.api", "0")
    lmApi = lmApiSnippet.lmApiSnippetFactory(props, http, lmDebug)
}
def ciscoMerakiCloudSnip = modLoader.load("cisco.meraki.cloud", "0")
def ciscoMerakiCloud     = ciscoMerakiCloudSnip.ciscoMerakiCloudSnippetFactory(props, lmDebug, cache, http)

String orgDisplayname      = props.get("meraki.api.org.name") ?: "MerakiOrganization"
String rootFolder          = props.get("meraki.api.org.folder") ? props.get("meraki.api.org.folder") + "/" : ""
String serviceUrl          = props.get("meraki.service.url")
def disableSwitches        = props.get("meraki.disableswitches")?.toBoolean() ?: false
def networksWhitelist      = props.get("meraki.api.org.networks")?.tokenize(",")?.collect{ it.trim() }
def collectorNetworksCSV   = props.get("meraki.api.org.collector.networks.csv")
def collectorNetworkInfo
if (collectorNetworksCSV) collectorNetworkInfo = processCollectorNetworkInfoCSV(collectorNetworksCSV)

String merakiSnmpCommunity = props.get("meraki.snmp.community.pass") 
String merakiSnmpSecurity  = props.get("meraki.snmp.security") 
String merakiSnmpAuth      = props.get("meraki.snmp.auth") 
String merakiSnmpAuthToken = props.get("meraki.snmp.authToken.pass") 
String merakiSnmpPriv      = props.get("meraki.snmp.priv") 
String merakiSnmpPrivToken = props.get("meraki.snmp.privToken.pass") 

// 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)
    }
}

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 device names and display names, keyed by display name that would be assigned by the netscan, found within LogicMonitor portal.  Refer to documentation for how to resolve name collisions using 'hostname.source' netscan property.",
    "total" : 0,
    "resources" : []
]

// Gather data from cache if running in debug otherwise make API requests
def orgDevicesStatuses
def orgDevices
if (debug) {
    orgDevicesStatuses = cache.cacheGet("${org}::organizationDeviceStatuses")
    if (orgDevicesStatuses) orgDevicesStatuses = ciscoMerakiCloud.slurper.parseText(orgDevicesStatuses).values()
} else {
    orgDevicesStatuses = ciscoMerakiCloud.httpGetWithPaging("/organizations/${org}/devices/statuses")
    if (orgDevicesStatuses) orgDevicesStatuses = ciscoMerakiCloud.slurper.parseText(orgDevicesStatuses)
}

// Organization device status data is required; we cannot proceed without it
if (!orgDevicesStatuses) {
    throw new Exception("Error occurred during organizations/${org}/devices/statuses HTTP GET: ${orgDevicesStatuses}.")
}

if (debug) {
    orgDevices = cache.cacheGet("${org}::organizationDevices")
    if (orgDevices) orgDevices = ciscoMerakiCloud.slurper.parseText(orgDevices).values()
} else {
    orgDevices = ciscoMerakiCloud.httpGetWithPaging("/organizations/${org}/devices")
    if (orgDevices) orgDevices = ciscoMerakiCloud.slurper.parseText(orgDevices)
}

def orgNetworkNames = [:]
def orgNetworks
if (debug) {
    orgNetworks = cache.cacheGet("${org}::organizationNetworks")
    if (orgNetworks) orgNetworks = ciscoMerakiCloud.slurper.parseText(orgNetworks).values()
} else {
    orgNetworks = ciscoMerakiCloud.httpGetWithPaging("/organizations/${org}/networks")
    if (orgNetworks) orgNetworks = ciscoMerakiCloud.slurper.parseText(orgNetworks)
}

// Network data is required; we cannot proceed without it
if (!orgNetworks) {
    throw new Exception("Error occurred during organizations/${org}/networks HTTP GET: ${orgNetworks}.")
}

orgNetworks.each { orgNetwork ->
    def networkId = orgNetwork.get("id")
    def networkName = orgNetwork.get("name")
    orgNetworkNames.put(networkId, networkName)
}

orgDevicesStatuses.each { orgDevice ->
    def networkId   = orgDevice.get("networkId")
    def networkName = orgNetworkNames.get(networkId)
    // Verify this network should be included based on customer whitelist configuration
    if (networksWhitelist != null && !networksWhitelist.contains(networkId)) return

    def ip = orgDevice.get("lanIp", orgDevice.get("publicIp"))
    if (ip == null) ip = orgDevice.get("wan1Ip", orgDevice.get("wan2Ip"))
    def name = orgDevice.get("name")
    def productType = orgDevice.get("productType")
    def serial = orgDevice.get("serial")
    def model = orgDevice.get("model")

    String displayName = name

    // 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)
            if (!deviceMatch) deviceMatch = lmApi.findPortalDeviceByName(ip, args)
        }
        else if (pathFlag == "all") {
            deviceMatch = lmApi.checkExistingDevices(displayName, lmDevices)
            if (!deviceMatch) deviceMatch = lmApi.checkExistingDevicesByName(ip, 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 collisionInfo = [
            (displayName) : [
                "Netscan" : [
                    "hostname"    : ip,
                    "displayName" : displayName
                ],
                "LM" : [
                    "hostname"    : deviceMatch.name,
                    "collectorId" : deviceMatch.currentCollectorId,
                    "displayName" : deviceMatch.displayName
                ],
                "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
            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)
    }

    // Verify we have minimum requirements for device creation
    if ((ip || productType == "sensor") && networkId && productType && serial) {
        if (ip == "127.0.0.1") ip = name
        if (!name) name = ip
        def deviceProps = [
            "meraki.api.key"          : key,
            "meraki.api.org"          : emit.sanitizePropertyValue(org),
            "meraki.api.network"      : emit.sanitizePropertyValue(networkId),
            "meraki.api.network.name" : emit.sanitizePropertyValue(networkName),
            "meraki.serial"           : emit.sanitizePropertyValue(serial)
        ]

        if (networksWhitelist != null) deviceProps.put("meraki.api.org.networks", emit.sanitizePropertyValue(networksWhitelist))
        if (disableSwitches) deviceProps.put("meraki.disableswitches", "true")

        if (merakiSnmpCommunity) deviceProps.put("meraki.snmp.community.pass", merakiSnmpCommunity)
        if (merakiSnmpSecurity) deviceProps.put("meraki.snmp.security", merakiSnmpSecurity)
        if (merakiSnmpAuth) deviceProps.put("meraki.snmp.auth", merakiSnmpAuth)
        if (merakiSnmpAuthToken) deviceProps.put("meraki.snmp.authToken.pass", merakiSnmpAuthToken)
        if (merakiSnmpPriv) deviceProps.put("meraki.snmp.priv", merakiSnmpPriv)
        if (merakiSnmpPrivToken) deviceProps.put("meraki.snmp.privToken.pass", merakiSnmpPrivToken)

        if (serviceUrl) deviceProps.put("meraki.service.url", serviceUrl)

        def tags = orgDevices?.find { it.serial == serial}?.tags
        if (tags && tags != "[]") deviceProps.put("meraki.tags", tags.join(","))
    
        def firmware = orgDevices.find { it.serial == serial}?.firmware
        if (firmware) deviceProps.put("meraki.firmware", firmware)

        def address = orgDevices.find { it.serial == serial}?.address
        if (address) deviceProps.put("location", address)

        if (productType == "camera") {
            deviceProps.put("system.categories", "CiscoMerakiCamera")
        } else if (productType == "switch") {
            deviceProps.put("system.categories", "CiscoMerakiSwitch")
        } else if (productType == "wireless") {
            deviceProps.put("system.categories", "CiscoMerakiWireless")
        } else if (productType == "appliance") {
            deviceProps.put("system.categories", "CiscoMerakiAppliance")
        } else if (productType == "cellularGateway") {
            deviceProps.put("system.categories", "CiscoMerakiCellularGateway")
        } else if (productType == "sensor") {
            ip = "${name}.invalid"
            if (model == "MT10") deviceProps.put("system.categories", "CiscoMerakiTemperatureSensor,NoPing")
            if (model == "MT12") deviceProps.put("system.categories", "CiscoMerakiWaterSensor,NoPing")
            if (model == "MT20") deviceProps.put("system.categories", "CiscoMerakiDoorSensor,NoPing")
            if (model == "MT40") deviceProps.put("system.categories", "CiscoMerakiPowerSensor,NoPing")        
        }

        deviceProps.put("meraki.productType", productType)

        // Set group and collector ID based on user CSV inputs if provided
        def collectorId = null
        Map resource = [:]
        if (collectorNetworkInfo) {
            collectorId = collectorNetworkInfo[networkId]["collectorId"]
            def folder      = collectorNetworkInfo[networkId]["folder"]
            resource = [
                "hostname"    : ip,
                "displayname" : name,
                "hostProps"   : deviceProps,
                "groupName"   : ["${rootFolder}${folder}/${networkName}"],
                "collectorId" : collectorId
            ]
        } else {
            resource = [
                "hostname"    : ip,
                "displayname" : name,
                "hostProps"   : deviceProps,
                "groupName"   : ["${rootFolder}${orgDisplayname}/${networkName}"]
            ]
        }

        // 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)
        }
    }
}

lmDebug.LMDebugPrint("Duplicate Resources:")
duplicateResources.resources.each {
    lmDebug.LMDebugPrint("\t${it}")
}

emit.resource(resources, debug)

return 0

/**
 * Processes a CSV with headers collector id, network organization device name, folder, and network
 * @param filename String
 * @return collectorInfo Map with network id as key and Map of additional attributes as value
*/
Map processCollectorNetworkInfoCSV(String filename) {
    // Read file into memory and split into list of lists
    def csv = newFile(filename, "csv")
    def rows = csv.readLines()*.split(",")
    def collectorInfo = [:]

    // Verify whether headers are present and expected values
    // Sanitize for casing and extra whitespaces while gathering headers
    def maybeHeaders = rows[0]*.toLowerCase()*.trim()
    if (maybeHeaders.contains("collector id") && maybeHeaders.contains("folder") && maybeHeaders.contains("network")) {
        Map headerIndices = [:]
        maybeHeaders.eachWithIndex{ val, i ->
            headerIndices[val] = i
        }
        // Index values for headers to ensure we key the correct index regardless of order
        def ni = headerIndices["network"]
        def ci = headerIndices["collector id"]
        def fi = headerIndices["folder"]

        // Remove headers from dataset
        def data = rows[1..-1]
        // Build a map indexed by network for easy lookups later
        data.each{ entry ->
            collectorInfo[entry[ni]] = [
                    "collectorId" : entry[ci],
                    "folder"      : entry[fi]
                ]
        }
    }
    // Bail out early if we don't have the expected headers in the provided CSV
    else {
        throw new Exception(" Required headers not provided in CSV.  Please provide \"Collector ID\", \"Network Organization Device Name\", \"Folder Name, \"and Network (case insensitive).  Headers provided: \"${rows[0]}\"")
    }

    return collectorInfo
}

/**
 * Sanitizes filepath and instantiates File object
 * @param filename String
 * @param fileExtension String
 * @return File object using sanitized relative filepath
*/
File newFile(String filename, String fileExtension) {
    // Ensure relative filepath is complete with extension type
    def filepath
    if (!filename.startsWith("./")) {
        filepath = "./${filename}"
    }
    if (!filepath.endsWith(".${fileExtension}")) {
        filepath = "${filepath}.${fileExtension}"
    }

    return new File(filepath)
}
  1.  スケジュール セクション、選択 このNetScanをスケジュールに従って実行する。 動的環境の場合、NetScan を XNUMX 時間ごとに実行するようにスケジュールできます。

注: 後続の NetScan の実行では、Cisco Meraki ダッシュボードの変更に基づいてリソースまたはリソース グループが追加または移動されます。 ただし、NetScan はリソースを削除できません。

  1. 選択 Save or 保存して実行.

NetScan を実行した後、追加されたリソースの数、または NetScan がリソースを作成しない場合はエラー メッセージの履歴を確認します。

Cisco Meraki リソースの手動追加

  1. Cisco Meraki 組織リソース グループを作成するか、次のプロパティを既存のリソース グループに適用します。
    • プロパティ: meraki.api.org
    • 値: Meraki 組織 ID
    • プロパティ: meraki.api.key
    • 値: API キー
    • プロパティ: meraki.snmp.community.pass (SNMP v1 または v2c に適用)
    • 値: コミュニティ文字列
    • プロパティ: meraki.snmp.security (SNMP v3 に適用)
    • 値: ユーザー名
    • プロパティ: meraki.snmp.auth (SNMP v3 に適用)
    • 値: SHA または MD5
    • プロパティ: meraki.snmp.authToken.pass (SNMP v3 に適用)
    • 値: SHA または MD5 認証トークン (パスワード)
    • プロパティ: meraki.snmp.priv (SNMP v3 に適用)
    • 値: AES128 または DES
    • プロパティ: meraki.snmp.privToken.pass (SNMP v3 に適用)
    • 値: プライバシー トークン (パスワード)

注: SNMP 設定を Netscan 資格情報プロパティとして、または Meraki デバイスを含むリソース グループのカスタム プロパティとして構成します。 Meraki ダッシュボード -> 組織 -> 設定 -> SNMP から値を取得します。

これらの設定は、snmp.meraki.com との通信にのみ影響します。 SNMP_Network_Interfaces や SNMP_Host_Uptime などのローカル SNMP データソースも使用している場合、これらの設定によって設定や動作が変更されることはありません。

  1. 次のプロパティを追加して、ネットワーク リソース グループの下の Meraki 組織リソース グループにデバイスを追加します。
    • プロパティ: meraki.api.network
    • 値: Meraki ネットワーク ID
    • プロパティ: meraki.serial
    • 値: Meraki デバイスのシリアル
  2. 詳細については、以下を参照してください 自分の組織 ID とネットワーク ID を見つけるにはどうすればよいですか? Cisco Meraki のドキュメント。

LogicModulesをインポートする

LogicMonitor Exchange から、パッケージからすべての Cisco Meraki LogicModules をインポートします。 詳細については、を参照してください。 パッケージ内のLogicModules。 これらの LogicModule がすでにインストールされている場合は、最新バージョンであることを確認してください。

LogicModule がインポートされると、 addCategory_Cisco_Meraki_Device 監視を開始するには、PropertySource を実行して必要なプロパティを設定する必要があります。 選択されたデバイスがデータの収集を正常に開始した後、すべてのデバイスのデータ収集が自動的に開始されます。 Cisco_Meraki_API 情報元。 モジュールのインポートの詳細については、を参照してください。 LMエクスチェンジ.

Cisco Meraki デバイスの LM Envision Collector へのマッピング

コレクターIDフォルダネットワーク
1南北アメリカMerakiネットワークID01
1南北アメリカMerakiネットワークID02
1南北アメリカMerakiネットワークID03
1南北アメリカMerakiネットワークID04
1南北アメリカMerakiネットワークID05
1南北アメリカMerakiネットワークID06
4EMEAMerakiネットワークID07
4EMEAMerakiネットワークID08
4EMEAMerakiネットワークID09
4EMEAMerakiネットワークID10
4EMEAMerakiネットワークID11
4EMEAMerakiネットワークID12
7APACMerakiネットワークID13
7APACMerakiネットワークID14
7APACMerakiネットワークID15
7APACMerakiネットワークID16

以下は、コレクタ ID、フォルダ、およびネットワークのカンマ区切り値です。

collector id,folder,network
1,Americas,MerakiNetworkID01
1,Americas,MerakiNetworkID02
1,Americas,MerakiNetworkID03
1,Americas,MerakiNetworkID04
1,Americas,MerakiNetworkID05
1,Americas,MerakiNetworkID06
4,EMEA,MerakiNetworkID07
4,EMEA,MerakiNetworkID08
4,EMEA,MerakiNetworkID09
4,EMEA,MerakiNetworkID10
4,EMEA,MerakiNetworkID11
4,EMEA,MerakiNetworkID12
7,APAC,MerakiNetworkID13
7,APAC,MerakiNetworkID14
7,APAC,MerakiNetworkID15
7,APAC,MerakiNetworkID16

トラブルシューティング

  • このパッケージは、コレクタ スクリプト キャッシュに依存して、Cisco Meraki API からデータを継続的に取得して保存し、レート制限の制約を最小限に抑えます。 詳細については、「」を参照してください。 コレクタースクリプトのキャッシュ.
    継続的なデータ収集は、選択された Meraki デバイス上で維持されます。 Cisco_Meraki_API DataSource: API 応答をコレクター スクリプト キャッシュに書き込みます。 addCategory_Cisco_Meraki_Device PropertySource を最初に実行して、このデバイスに適切なカテゴリを設定する必要があります。 Cisco_Meraki_API このパッケージ内の他のすべてのモジュールが正常に動作するには、DataSource が正常に実行されている必要があります。
  • オンボーディング中に、その後、このパッケージ内の追加の PropertySource に対して Active Discovery を手動で実行できます。 Cisco_Meraki_API データの収集を開始して、監視とトポロジ マッピングを促進します。
  • データのギャップが見られる場合は、検証します Cisco_Meraki_API が正常に機能していることを確認し、LogicMonitor_Collector_ScriptCache データソースでスクリプト キャッシュの状態を確認します。

注: データの取得に使用される API にはレート制限があります。 Meraki デバイスの Cisco_Meraki_API をチェックして、API が到達不能かどうか、またはモニタリングが API レート制限に達していないかどうかを確認します。

パッケージ内のLogicModules

Cisco Meraki 用の LogicMonitor パッケージは、次の LogicModules で構成されています。 完全にカバーするには、次のすべての LogicModule を LogicMonitor プラットフォームにインポートします。

表示名タイプ説明
Cisco_Meraki_TopologyトポロジーソースCisco Meraki Cloud トポロジをマップします。
Meraki APIデータソースMeraki API の使用状況を監視します。
Meraki アクセス ポイントの状態データソースMeraki が管理するアクセス ポイントの健全性。
Meraki アクセス ポイントのパフォーマンスデータソースMeraki が管理するアクセス ポイントのパフォーマンス。
Meraki アクセス ポイント無線データソースMeraki が管理するアクセス ポイントの無線チャネルの使用率。
Meraki アクセス ポイントの SSIDデータソースSSID クライアントと Meraki が管理するアクセス ポイントのトラフィック。
Meraki カメラの健康状態データソースMeraki が管理するカメラの健全性。
Meraki セルラー ゲートウェイデータソースMeraki が管理するセルラー ゲートウェイの健全性
Meraki セルラー ゲートウェイのパフォーマンスデータソースMeraki が管理するセルラー ゲートウェイのパフォーマンス
Meraki インターフェイスデータソースMeraki が管理するアクセス ポイントとセキュリティ アプライアンスのインターフェイス スループットとパケット送信。
Meraki セキュリティ アプライアンスの健全性データソースMeraki が管理するセキュリティ アプライアンスの健全性。
Meraki セキュリティ アプライアンスのパフォーマンスデータソースMeraki が管理するセキュリティ アプライアンスのパフォーマンス。
Meraki セキュリティ アプライアンス トンネルデータソースMeraki が管理するセキュリティ アプライアンスのトンネル メトリック。
Meraki セキュリティ アプライアンス アンダーレイデータソースMeraki が管理するセキュリティ アプライアンスの WAN 接続メトリック。
Meraki スイッチの健全性データソースMeraki が管理するスイッチの健全性。
Meraki スイッチ インターフェイスデータソースMeraki が管理するスイッチのインターフェイス データ スループット。
Meraki スイッチのパフォーマンスデータソースMeraki が管理するスイッチのパフォーマンス。
Meraki ネットワーク イベントログソースCisco Meraki デバイスのネットワーク イベント ログを提供します。
Meraki セキュリティ アプライアンスのイベントログソースCisco Meraki MX セキュリティ アプライアンスのセキュリティ イベント ログを提供します。
Meraki 水センサーデータソースMeraki が管理する水センサーのステータスと正常性。
Meraki 温度センサーデータソースMeraki が管理する温度センサーのステータスと正常性。
Meraki ドア センサーデータソースMeraki が管理するドア センサーのステータスと正常性。
Meraki パワー センサーデータソースMeraki が管理するパワー センサーのステータスと正常性。
addCategory_Cisco_Meraki_DeviceプロパティソースCiscoMeraki システム カテゴリを、Cisco Meraki によって管理されるデバイスおよび他の自動プロパティに追加します。 データを収集するには Cisco_Meraki_Org DataSource (オブザーバ) が必要です。
addERI_Cisco_Meraki_DeviceプロパティソースMeraki リソース用の Meraki 固有の ERI を検出して追加します。

このパッケージの DataSource によって追跡されるさまざまなメトリクスに静的なデータポイントのしきい値を設定する場合、LogicMonitor はテクノロジー所有者のベスト プラクティス KPI 推奨事項に従います。

推奨事項: 必要に応じて、環境固有のニーズを満たすように事前定義されたしきい値を調整します。 データポイントのしきい値の調整の詳細については、を参照してください。 データポイントの静的しきい値の調整.

記事上で