| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | //go:build cgo && ibm_mq |
| 4 | |
| 5 | package pcf |
| 6 | |
| 7 | import ( |
| 8 | "fmt" |
| 9 | |
| 10 | "github.com/ibm-messaging/mq-golang/v5/ibmmq" |
| 11 | ) |
| 12 | |
| 13 | // GetQueueManagerStatus returns the status of the queue manager. |
| 14 | func (c *Client) GetQueueManagerStatus() (*QueueManagerMetrics, error) { |
| 15 | c.protocol.Debugf("QMGR getting status for queue manager '%s'", c.config.QueueManager) |
| 16 | |
| 17 | response, err := c.sendPCFCommand(ibmmq.MQCMD_INQUIRE_Q_MGR_STATUS, nil) |
| 18 | if err != nil { |
| 19 | // Fallback to basic inquiry |
| 20 | c.protocol.Debugf("QMGR status inquiry failed for queue manager '%s', trying basic inquiry: %v", c.config.QueueManager, err) |
| 21 | response, err = c.sendPCFCommand(ibmmq.MQCMD_INQUIRE_Q_MGR, nil) |
| 22 | if err != nil { |
| 23 | c.protocol.Errorf("QMGR failed to get status for queue manager '%s': %v", c.config.QueueManager, err) |
| 24 | return nil, err |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | attrs := make(map[int32]interface{}) |
| 29 | for _, param := range response { |
| 30 | switch param.Type { |
| 31 | case ibmmq.MQCFT_STRING: |
| 32 | if len(param.String) > 0 { |
| 33 | attrs[param.Parameter] = param.String[0] |
| 34 | } |
| 35 | case ibmmq.MQCFT_INTEGER: |
| 36 | if len(param.Int64Value) > 0 { |
| 37 | attrs[param.Parameter] = int32(param.Int64Value[0]) |
| 38 | } |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | // If we get a successful response, the queue manager is running |
| 43 | metrics := &QueueManagerMetrics{ |
| 44 | Status: 1, |
| 45 | ConnectionCount: NotCollected, // Default to not collected |
| 46 | StartDate: NotCollected, // Default to not collected |
| 47 | StartTime: NotCollected, // Default to not collected |
| 48 | Uptime: NotCollected, // Default to not collected |
| 49 | } |
| 50 | |
| 51 | // Extract connection count if available (only from MQCMD_INQUIRE_Q_MGR_STATUS) |
| 52 | if connectionCount, ok := attrs[ibmmq.MQIACF_CONNECTION_COUNT]; ok { |
| 53 | if count, ok := connectionCount.(int32); ok { |
| 54 | metrics.ConnectionCount = AttributeValue(count) |
| 55 | c.protocol.Debugf("QMGR '%s' connection count: %d", c.config.QueueManager, count) |
| 56 | } |
| 57 | } else { |
| 58 | c.protocol.Debugf("QMGR '%s' - MQIACF_CONNECTION_COUNT (value %d) not found in response", c.config.QueueManager, ibmmq.MQIACF_CONNECTION_COUNT) |
| 59 | } |
| 60 | |
| 61 | // Extract queue manager start date and time for uptime calculation |
| 62 | var startDate, startTime string |
| 63 | |
| 64 | // Try to get start date (constant 3175 = MQCACF_Q_MGR_START_DATE) |
| 65 | if date, ok := attrs[int32(3175)]; ok { |
| 66 | c.protocol.Debugf("Found attribute 3175, type: %T, value: %v", date, date) |
| 67 | if dateStr, ok := date.(string); ok { |
| 68 | startDate = dateStr |
| 69 | metrics.StartDate = AttributeValue(len(dateStr)) // Store length as indicator |
| 70 | c.protocol.Debugf("QMGR '%s' start date: %s", c.config.QueueManager, dateStr) |
| 71 | } else { |
| 72 | c.protocol.Warningf("QMGR: Attribute 3175 is not a string, type: %T", date) |
| 73 | } |
| 74 | } else { |
| 75 | c.protocol.Warningf("UPTIME DEBUG: QMGR '%s' - start date (attribute 3175) not found in response with %d attributes", c.config.QueueManager, len(attrs)) |
| 76 | } |
| 77 | |
| 78 | // Try to get start time (constant should be around 3176, but let's check for common values) |
| 79 | // Check for possible start time constants |
| 80 | timeConstants := []int32{3176, 3177, 3178} // Possible values near start date |
| 81 | for _, timeConstant := range timeConstants { |
| 82 | if time, ok := attrs[timeConstant]; ok { |
| 83 | if timeStr, ok := time.(string); ok { |
| 84 | startTime = timeStr |
| 85 | metrics.StartTime = AttributeValue(len(timeStr)) // Store length as indicator |
| 86 | c.protocol.Debugf("QMGR '%s' start time: %s (attribute %d)", c.config.QueueManager, timeStr, int32(timeConstant)) |
| 87 | break |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | if startTime == "" { |
| 92 | c.protocol.Debugf("QMGR '%s' - start time not found in response", c.config.QueueManager) |
| 93 | } |
| 94 | |
| 95 | // Calculate uptime if date is available (assume start time 00:00:00 if not provided) |
| 96 | if startDate != "" { |
| 97 | if startTime == "" { |
| 98 | // If no start time provided, assume start of day (00:00:00) |
| 99 | startTime = "00:00:00" |
| 100 | c.protocol.Debugf("QMGR '%s' - no start time found, assuming 00:00:00", c.config.QueueManager) |
| 101 | } |
| 102 | if uptime, err := CalculateUptimeSeconds(startDate, startTime); err == nil { |
| 103 | metrics.Uptime = AttributeValue(uptime) |
| 104 | c.protocol.Debugf("QMGR '%s' uptime: %d seconds", c.config.QueueManager, uptime) |
| 105 | } else { |
| 106 | c.protocol.Warningf("QMGR failed to calculate uptime for queue manager '%s': %v", c.config.QueueManager, err) |
| 107 | } |
| 108 | } else { |
| 109 | c.protocol.Warningf("QMGR '%s' - no start date available for uptime calculation", c.config.QueueManager) |
| 110 | } |
| 111 | |
| 112 | return metrics, nil |
| 113 | } |
| 114 | |
| 115 | // refreshStaticDataFromPCF refreshes cached static data using PCF commands (called from connection.go) |
| 116 | func (c *Client) refreshStaticDataFromPCF() error { |
| 117 | c.protocol.Debugf("Refreshing static data for queue manager '%s' after connection", c.config.QueueManager) |
| 118 | |
| 119 | // Get queue manager version and platform info |
| 120 | response, err := c.sendPCFCommand(ibmmq.MQCMD_INQUIRE_Q_MGR, nil) |
| 121 | if err != nil { |
| 122 | return fmt.Errorf("failed to get queue manager info for '%s': %w", c.config.QueueManager, err) |
| 123 | } |
| 124 | |
| 125 | attrs := make(map[int32]interface{}) |
| 126 | for _, param := range response { |
| 127 | switch param.Type { |
| 128 | case ibmmq.MQCFT_STRING: |
| 129 | if len(param.String) > 0 { |
| 130 | attrs[param.Parameter] = param.String[0] |
| 131 | } |
| 132 | case ibmmq.MQCFT_INTEGER: |
| 133 | if len(param.Int64Value) > 0 { |
| 134 | attrs[param.Parameter] = int32(param.Int64Value[0]) |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | // Extract and cache version info |
| 140 | if cmdLevel, ok := attrs[ibmmq.MQIA_COMMAND_LEVEL]; ok { |
| 141 | if level, ok := cmdLevel.(int32); ok { |
| 142 | c.cachedCommandLevel = level |
| 143 | // Convert command level to version string |
| 144 | major := level / 100 |
| 145 | minor := (level % 100) / 10 |
| 146 | c.cachedVersion = fmt.Sprintf("%d.%d", major, minor) |
| 147 | c.protocol.Debugf("QMGR '%s' version detected: %s (command level: %d)", |
| 148 | c.config.QueueManager, c.cachedVersion, level) |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | // Extract platform info |
| 153 | if platform, ok := attrs[ibmmq.MQIA_PLATFORM]; ok { |
| 154 | if plat, ok := platform.(int32); ok { |
| 155 | c.cachedPlatform = plat |
| 156 | // Use IBM library to get the correct platform name |
| 157 | platformName := ibmmq.MQItoString("PL", int(plat)) |
| 158 | c.cachedEdition = platformName |
| 159 | c.protocol.Debugf("QMGR '%s' platform detected: %s (platform code: %d)", |
| 160 | c.config.QueueManager, platformName, plat) |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | // Extract STATINT (Statistics Interval) using official IBM MQ constant |
| 165 | // MQIA_STATISTICS_INTERVAL = 131 (X'00000083') - Controls statistics publishing interval |
| 166 | if statInt, ok := attrs[ibmmq.MQIA_STATISTICS_INTERVAL]; ok { |
| 167 | if interval, ok := statInt.(int32); ok && interval > 0 { |
| 168 | c.cachedStatisticsInterval = interval |
| 169 | c.protocol.Debugf("QMGR '%s' STATINT detected: %d seconds (MQIA_STATISTICS_INTERVAL=%d)", |
| 170 | c.config.QueueManager, interval, ibmmq.MQIA_STATISTICS_INTERVAL) |
| 171 | } |
| 172 | } else { |
| 173 | c.protocol.Debugf("QMGR '%s' STATINT not available (MQIA_STATISTICS_INTERVAL not in response)", c.config.QueueManager) |
| 174 | } |
| 175 | |
| 176 | // Extract queue monitoring interval using MQIA_MONITORING_Q = 123 |
| 177 | if queueMonInt, ok := attrs[int32(123)]; ok { |
| 178 | if interval, ok := queueMonInt.(int32); ok && interval >= 0 { |
| 179 | c.cachedMonitoringQueue = interval |
| 180 | c.protocol.Debugf("QMGR '%s' queue monitoring interval detected: %d seconds (MQIA_MONITORING_Q=123)", |
| 181 | c.config.QueueManager, interval) |
| 182 | } |
| 183 | } else { |
| 184 | c.protocol.Debugf("QMGR '%s' queue monitoring interval not available (MQIA_MONITORING_Q not in response)", c.config.QueueManager) |
| 185 | } |
| 186 | |
| 187 | // Extract channel monitoring interval using MQIA_MONITORING_CHANNEL = 122 |
| 188 | if channelMonInt, ok := attrs[int32(122)]; ok { |
| 189 | if interval, ok := channelMonInt.(int32); ok && interval >= 0 { |
| 190 | c.cachedMonitoringChannel = interval |
| 191 | c.protocol.Debugf("QMGR '%s' channel monitoring interval detected: %d seconds (MQIA_MONITORING_CHANNEL=122)", |
| 192 | c.config.QueueManager, interval) |
| 193 | } |
| 194 | } else { |
| 195 | c.protocol.Debugf("QMGR '%s' channel monitoring interval not available (MQIA_MONITORING_CHANNEL not in response)", c.config.QueueManager) |
| 196 | } |
| 197 | |
| 198 | // Extract auto-defined cluster sender channel monitoring interval using MQIA_MONITORING_AUTO_CLUSSDR = 124 |
| 199 | if clussdrMonInt, ok := attrs[int32(124)]; ok { |
| 200 | if interval, ok := clussdrMonInt.(int32); ok && interval >= 0 { |
| 201 | c.cachedMonitoringAutoClussdr = interval |
| 202 | c.protocol.Debugf("QMGR '%s' auto cluster sender channel monitoring interval detected: %d seconds (MQIA_MONITORING_AUTO_CLUSSDR=124)", |
| 203 | c.config.QueueManager, interval) |
| 204 | } |
| 205 | } else { |
| 206 | c.protocol.Debugf("QMGR '%s' auto cluster sender channel monitoring interval not available (MQIA_MONITORING_AUTO_CLUSSDR not in response)", c.config.QueueManager) |
| 207 | } |
| 208 | |
| 209 | // For $SYS topic interval, we'll use a default of 10 seconds as per IBM documentation |
| 210 | // This interval is typically not exposed as a queue manager attribute but is configurable |
| 211 | // The default publication frequency to $SYS/ topics is approximately every 10 seconds |
| 212 | c.cachedSysTopicInterval = 10 |
| 213 | c.protocol.Debugf("QMGR '%s' $SYS topic interval set to default: %d seconds (configurable, see IBM MQ documentation)", |
| 214 | c.config.QueueManager, c.cachedSysTopicInterval) |
| 215 | |
| 216 | c.protocol.Debugf("QMGR static data refreshed for queue manager '%s' - version: %s, platform: %s, STATINT: %d, MONQ: %d, MONCH: %d, MONCLS: %d, SYSTOPIC: %d", |
| 217 | c.config.QueueManager, c.cachedVersion, c.cachedEdition, c.cachedStatisticsInterval, |
| 218 | c.cachedMonitoringQueue, c.cachedMonitoringChannel, c.cachedMonitoringAutoClussdr, c.cachedSysTopicInterval) |
| 219 | |
| 220 | return nil |
| 221 | } |