Используйте PowerShell для анализа отчета IBM MQ для форматирования, который может использовать Прометей - PullRequest
0 голосов
/ 28 февраля 2020

Я делаю PO C для Prometheus, и одна из вещей, от которой мы хотели бы получить метрики, - это сервер IBM MQ, работающий на W2012R2. Для IBM MQ существует экспортер Prometheus, но он предназначен для Linux и не работает на Windows.

У нас есть возможность запланировать отчет для записи данных в файл, однако вывод этот файл бесполезен для Прометея, поэтому документ нужно переформатировать.

Я ломаю голову над тем, как этого добиться с помощью PowerShell, но, поскольку у меня нет опыта написания сценариев с PowerShell, я решил спросить здесь help.

Вы, ребята, знаете, как изменить отчет в этом макете:

5724-H72 (C) Copyright IBM Corp. 1994, 2015.
Starting MQSC for queue manager QMGR.


     1 : DISPLAY CHSTATUS(*) 

AMQ8450: Display Channel Status details.
   CHANNEL(QMGR.HOSTNAME.R1)               CHLTYPE(RCVR)
   CONNAME(10.10.10.10)                    CURRENT
   RQMNAME(QMGR)                           STATUS(RUNNING)
   SUBSTATE(RECEIVE)                    
AMQ8450: Display Channel Status details.
   CHANNEL(QMGR.HOSTNAME.R3)               CHLTYPE(RCVR)
   CONNAME(10.10.10.10)                    CURRENT
   STATUS(RUNNING)                         SUBSTATE(RECEIVE)

На что-то похожее на это:

status{channel="QMGR.HOSTNAME.R1", chltype="rcvr", conname="10.10.10.10", rqmname="QMGR"} running
substate{channel="QMGR.HOSTNAME.R1", chltype="rcvr", conname="10.10.10.10", rqmname="QMGR"} receive
status{channel="QMGR.HOSTNAME.R3", chltype="rcvr", conname="10.10.10.10"} running
substate{channel="QMGR.HOSTNAME.R3", chltype="rcvr", conname="10.10.10.10"} receive

Это то, что я до сих пор придумал, не совсем красиво, я думаю, но это, кажется, шаг в правильном направлении ...

$Contents = Get-Content ".\CHSTATUS.txt" | select -Skip 5 
foreach($Line in $Contents) {
    foreach($channel in [Regex]::Matches($Line, '(?<=CHANNEL\()(.*?)(?=\))')) {}
    foreach($chltype in [Regex]::Matches($Line, '(?<=CHLTYPE\()(.*?)(?=\))')) {}
    foreach($conname in [Regex]::Matches($Line, '(?<=CONNAME\()(.*?)(?=\))')) {}
    foreach($status in [Regex]::Matches($Line, '(?<=STATUS\()(.*?)(?=\))')) {}
    foreach($substate in [Regex]::Matches($Line, '(?<=SUBSTATE\()(.*?)(?=\))')) {
    Write-Host "status{channel=""$channel"", chltype=""$chltype"", conname=""$conname""}" $status 
    Write-Host "substate{channel=""$channel"", chltype=""$chltype"", conname=""$conname""}" $substate
    }
}

Надеюсь, кто-то здесь может дать мне pu sh в правильном направлении на как справиться с этим.

Ответы [ 3 ]

1 голос
/ 28 февраля 2020

Чтобы преобразовать этот вход и разобрать поля, вы можете сделать это:

$inputFile = 'D:\Report.txt'

# read the file, split into usable blocks and collect in variable $report
$report = ((Get-Content -Path $inputFile -Raw) -split '.*Display Channel Status details\.' | Select-Object -Skip 1 | ForEach-Object {
    $channel  = ([regex] 'CHANNEL\(([^)]+)\)').Match($_).Groups[1].Value
    $chltype  = ([regex] 'CHLTYPE\(([^)]+)\)').Match($_).Groups[1].Value.ToLower()
    $conname  = ([regex] 'CONNAME\(([^)]+)\)').Match($_).Groups[1].Value
    $rqmname  = ([regex] 'RQMNAME\(([^)]+)\)').Match($_).Groups[1].Value
    $status   = ([regex] 'STATUS\(([^)]+)\)').Match($_).Groups[1].Value.ToLower()
    $substate = ([regex] 'SUBSTATE\(([^)]+)\)').Match($_).Groups[1].Value.ToLower()

    if ($rqmname) {
        $out = 'status{{channel="{0}", chltype="{1}", conname="{2}", rqmname="{3}"}} {4}' + "`r`n" +
               'substate{{channel="{0}", chltype="{1}", conname="{2}", rqmname="{3}"}} {5}'
        # output the two lines including rqmname
        $out -f $channel, $chltype, $conname, $rqmname, $status, $substate

    }
    else {
        $out = 'status{{channel="{0}", chltype="{1}", conname="{2}"}} {3}' + "`r`n" +
               'substate{{channel="{0}", chltype="{1}", conname="{2}"}} {4}'
        # output the two lines excluding rqmname
        $out -f $channel, $chltype, $conname, $status, $substate
    }
}) -join "`r`n"

# output on screen
$report

# or save to new file
$report | Set-Content -Path 'D:\ConvertedReport.txt'

Результат:

status{channel="QMGR.HOSTNAME.R1", chltype="rcvr", conname="10.10.10.10", rqmname="QMGR"} running
substate{channel="QMGR.HOSTNAME.R1", chltype="rcvr", conname="10.10.10.10", rqmname="QMGR"} receive
status{channel="QMGR.HOSTNAME.R3", chltype="rcvr", conname="10.10.10.10"} running
substate{channel="QMGR.HOSTNAME.R3", chltype="rcvr", conname="10.10.10.10"} receive
1 голос
/ 29 февраля 2020

В прошлом мне приходилось делать нечто подобное для предупреждения Nag ios о статусе канала MQ. Вот функция общего назначения для разбора выходных данных в структурированные хеш-таблицы:

function Invoke-ParseMqscOutput
{
    param( $output )

    # strip off the main header and split the AMQ#### records into a $parts array
    $recordHeaderPattern = "^(?<AmqCode>AMQ\d+): (?<AmqDescription>.*)$";
    $parts = [regex]::Split($stdout, $recordHeaderPattern, "Multiline");

    # patterns for splitting attribute strings into AttributeName and AttributeValues
    #     + e.g. CHANNEL(QMGR.HOSTNAME.R1)
    # as well as some pathological cases like:
    #     + special characters        - e.g. ATTRXNAME(attrx.ATTRX_12345[W X-Y\Z])
    #     + comma-separated lists     - e.g. ATTRYNAME(ATTRY1, ATTRY2)
    #     + "flags" with no "( ... )" - e.g. CURRENT
    $attributeNamePattern = "(?<AttributeName>[a-z|A-Z]+)";
    $attributeValuesPattern = "(?<AttributeValues>[a-z|A-Z|0-9|,|_|\-|\.|\[|\]|\\|\s]*)";
    $attributePattern = $attributeNamePattern + "(\(" + $attributeValuesPattern + "\))?";

    # convert the parts array into an array of hashtables
    # (one hashtable per record in the output)
    $records = @();
    $index = 1;
    while( $index -lt $parts.Length )
    {

        # split the attributes block into individual attributes and parse them
        $attributes = [ordered] @{};
        $attributeMatches = [regex]::Matches($parts[$index + 2], $attributePattern, "Multiline");
        $attributeMatches | % {
            $attributeName = $_.Groups["AttributeName"].Value;
            $attributeValues = $_.Groups["AttributeValues"].Value;
            $attributeValues = $attributeValues.Split(",") | % { $_.Trim(); };
            $attributes.Add($attributeName, $attributeValues);
        }

        # collect the next 3 "$parts" values into a single record
        $records += @{
            "AmqCode" = $parts[$index]
            "Message" = $parts[$index + 1]
            "Attributes" = $attributes
        };

        # move on to the start of the next record
        $index = $index + 3;

    }

    return @(, $records );

}

пример использования:

$output = @"
5724-H72 (C) Copyright IBM Corp. 1994, 2015.
Starting MQSC for queue manager QMGR.


     1 : DISPLAY CHSTATUS(*)

AMQ8450: Display Channel Status details.
   CHANNEL(QMGR.HOSTNAME.R1)               CHLTYPE(RCVR)
   CONNAME(10.10.10.10)                    CURRENT
   RQMNAME(QMGR)                           STATUS(RUNNING)
   SUBSTATE(RECEIVE)
   ATTRXNAME(attrx.ATTRX_12345[W X-Y\Z])
   ATTRYNAME(ATTRY1, ATTRY2)
AMQ8450: Display Channel Status details.
   CHANNEL(QMGR.HOSTNAME.R3)               CHLTYPE(RCVR)
   CONNAME(10.10.10.10)                    CURRENT
   STATUS(RUNNING)                         SUBSTATE(RECEIVE)
"@

$records = Invoke-ParseMqscOutput $output;

# examples of how to traverse the $records:
write-host $records[0].Attributes.CHANNEL      # QMGR.HOSTNAME.R1
write-host $records[0].Attributes.ATTRXNAME    # attrx.ATTRX_12345[W X-Y\Z]
write-host $records[0].Attributes.ATTRYNAME[0] # ATTRY1

А затем для генерации выходных данных вы можете сделать это:

$metrics = @();
foreach( $record in $records )
{
    switch( $record.AmqCode )
    {
        "AMQ8450" {
            $attributes = $record.Attributes;
            $metrics += "status{channel=`"$($attributes.CHANNEL)`", chltype=`"$($attributes.CHLTYPE)`", conname=`"$($attributes.CONNAME)`", rqmname=`"$($attributes.RQMNAME)`"} $($attributes.STATE)";
            $metrics += "substate{channel=`"$($attributes.CHANNEL)`", chltype=`"$($attributes.CHLTYPE)`", conname=`"$($attributes.CONNAME)`", rqmname=`"$($attributes.RQMNAME)`"} $($attributes.SUBSTATE)";
        }
    }
}

write-host ($metrics | fl * | out-string);

который выводит:

status{channel="QMGR.HOSTNAME.R1", chltype="RCVR", conname="10.10.10.10", rqmname="QMGR"}
substate{channel="QMGR.HOSTNAME.R1", chltype="RCVR", conname="10.10.10.10", rqmname="QMGR"} RECEIVE
status{channel="QMGR.HOSTNAME.R3", chltype="RCVR", conname="10.10.10.10", rqmname=""}
substate{channel="QMGR.HOSTNAME.R3", chltype="RCVR", conname="10.10.10.10", rqmname=""} RECEIVE

Обратите внимание, что если вы способны использовать API Websphere для вызова команд, вам будет намного проще обрабатывать результаты, но если у вас абсолютно есть для анализа выходных данных runmqsc.exe, то вышеприведенное может вам помочь ...

1 голос
/ 28 февраля 2020

Я не думаю, что это хороший подход. Было бы гораздо проще использовать Java / PCF и выводить данные в нужном вам формате.

Я опубликовал простую (полностью работающую) программу MQ / PCF / Java под названием: MQListChannelStatus01 .

Просто обновите параметры следующим образом:

request.addParameter(CMQCFC.MQIACH_CHANNEL_INSTANCE_ATTRS,
                     new int []
                     {
                        CMQCFC.MQCACH_CHANNEL_NAME,
                        CMQCFC.MQCACH_CONNECTION_NAME,
                        CMQCFC.MQIACH_CHANNEL_STATUS,
                        CMQCFC.MQIACH_CHANNEL_SUBSTATE,
                        CMQC.MQCA_REMOTE_Q_MGR_NAME
                     } );

Вам нужно знать, что вы получите информацию обо ВСЕХ каналах, которые работают, пытаются запустить, et c .. Следовательно, «MQCA_REMOTE_Q_MGR_NAME» недопустимо для всех типов каналов.

Вам нужно будет сделать следующее в «for l oop»:

int chlType = responses[i].getIntParameterValue(CMQCFC.MQIACH_CHANNEL_TYPE);

String remoteQMName ="";
if (chlType == CMQXC.MQCHT_RECEIVER)
{
   remoteQMName = responses[i].getStringParameterValue(CMQC.MQCA_REMOTE_Q_MGR_NAME);
   if (remoteQMName != null)
      remoteQMName = remoteQMName.trim();
}
...