~ps10gel/ubuntu/xenial/trafficserver/6.2.0

« back to all changes in this revision

Viewing changes to proxy/mgmt2/web2/WebOverview.cc

  • Committer: Bazaar Package Importer
  • Author(s): Arno Toell
  • Date: 2011-01-13 11:49:18 UTC
  • Revision ID: james.westby@ubuntu.com-20110113114918-vu422h8dknrgkj15
Tags: upstream-2.1.5-unstable
ImportĀ upstreamĀ versionĀ 2.1.5-unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 
 
3
  A brief file description
 
4
 
 
5
  @section license License
 
6
 
 
7
  Licensed to the Apache Software Foundation (ASF) under one
 
8
  or more contributor license agreements.  See the NOTICE file
 
9
  distributed with this work for additional information
 
10
  regarding copyright ownership.  The ASF licenses this file
 
11
  to you under the Apache License, Version 2.0 (the
 
12
  "License"); you may not use this file except in compliance
 
13
  with the License.  You may obtain a copy of the License at
 
14
 
 
15
      http://www.apache.org/licenses/LICENSE-2.0
 
16
 
 
17
  Unless required by applicable law or agreed to in writing, software
 
18
  distributed under the License is distributed on an "AS IS" BASIS,
 
19
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
20
  See the License for the specific language governing permissions and
 
21
  limitations under the License.
 
22
 */
 
23
 
 
24
/****************************************************************************
 
25
 *
 
26
 *  WebOverview.cc - code to overview page
 
27
 *
 
28
 *
 
29
 ****************************************************************************/
 
30
 
 
31
#include "ink_platform.h"
 
32
#include "ink_unused.h"  /* MAGIC_EDITING_TAG */
 
33
 
 
34
#include "WebOverview.h"
 
35
#include "WebGlobals.h"
 
36
#include "WebHttpRender.h"
 
37
#include "WebHttpTree.h"
 
38
#include "WebMgmtUtils.h"
 
39
 
 
40
#include "Main.h"
 
41
#include "ClusterCom.h"
 
42
#include "MgmtDefs.h"
 
43
#include "CLI.h"
 
44
#include "CLIlineBuffer.h"
 
45
#include "Diags.h"
 
46
 
 
47
// Make this pointer to avoid nasty destruction
 
48
//   problems do to alarm
 
49
//   fork, execl, exit squences
 
50
overviewPage *overviewGenerator;
 
51
 
 
52
overviewRecord::overviewRecord(unsigned long inet_addr, bool local, ClusterPeerInfo * cpi)
 
53
{
 
54
 
 
55
  char *name_l;                 // hostname looked up from node record
 
56
  bool name_found;
 
57
  struct in_addr nameFailed;
 
58
 
 
59
  inetAddr = inet_addr;
 
60
 
 
61
  this->up = false;
 
62
  this->localNode = local;
 
63
 
 
64
  // If this is the local node, there is no cluster peer info
 
65
  //   record.  Remote nodes require a cluster peer info record
 
66
  ink_assert((local == false && cpi != NULL)
 
67
             || (local == true && cpi == NULL));
 
68
 
 
69
  // Set up the copy of the records array and initialize it
 
70
  if (local == true) {
 
71
    node_rec_data.num_recs = 0;
 
72
    node_rec_data.recs = NULL;
 
73
    recordArraySize = 0;
 
74
    node_rec_first_ix = 0;
 
75
  } else {
 
76
    node_rec_data.num_recs = cpi->node_rec_data.num_recs;
 
77
    recordArraySize = node_rec_data.num_recs * sizeof(RecRecord);
 
78
    node_rec_data.recs = new RecRecord[recordArraySize];
 
79
    memcpy(node_rec_data.recs, cpi->node_rec_data.recs, recordArraySize);
 
80
 
 
81
    // Recaculate the old relative index
 
82
    RecGetRecordOrderAndId(node_rec_data.recs[0].name, &node_rec_first_ix, NULL);
 
83
  }
 
84
 
 
85
 
 
86
  // Query for the name of the node.  If it is not there, some
 
87
  //   their cluster ip address
 
88
  name_l = this->readString("proxy.node.hostname_FQ", &name_found);
 
89
  if (name_found == false || name_l == NULL) {
 
90
    nameFailed.s_addr = inetAddr;
 
91
    mgmt_log("[overviewRecord::overviewRecord] Unable to find hostname for %s\n", inet_ntoa(nameFailed));
 
92
    xfree(name_l);              // about to overwrite name_l, so we need to free it first
 
93
    name_l = xstrdup(inet_ntoa(nameFailed));
 
94
  }
 
95
 
 
96
  const size_t hostNameLen = strlen(name_l) + 1;
 
97
  this->hostname = new char[hostNameLen];
 
98
  ink_strncpy(this->hostname, name_l, hostNameLen);
 
99
  xfree(name_l);
 
100
}
 
101
 
 
102
overviewRecord::~overviewRecord()
 
103
{
 
104
 
 
105
  AlarmListable *a;
 
106
 
 
107
  delete[]hostname;
 
108
 
 
109
  for (a = nodeAlarms.pop(); a != NULL; a = nodeAlarms.pop()) {
 
110
    delete a;
 
111
  }
 
112
 
 
113
  if (localNode == false) {
 
114
    delete[]node_rec_data.recs;
 
115
  }
 
116
}
 
117
 
 
118
// void overviewRecord::getStatus(char**, PowerLampState* , bool*, bool*)
 
119
// Retrieves information about the node
 
120
//
 
121
//  hostname - *hostname is set to point to a string containing the hostname
 
122
//             for the node represented by the record The storage for
 
123
//             this string belongs to this class instance and should
 
124
//             not be freed by the caller
 
125
//
 
126
//  *up - set to true if the node's manager is up
 
127
//            Set to false otherwise
 
128
//
 
129
//  *alarms -  set to true if there are any pending alarms for this
 
130
//            this node.   Set to false if there are no pending
 
131
//            alarms
 
132
//
 
133
//  *proxyUp - set to true is the proxy is up on the node and
 
134
//            false otherwise
 
135
//
 
136
void
 
137
overviewRecord::getStatus(char **hostnamePtr, bool * upPtr, bool * alarms, PowerLampState * proxyUpPtr)
 
138
{
 
139
  bool found;
 
140
  *hostnamePtr = this->hostname;
 
141
  *upPtr = this->up;
 
142
 
 
143
  if (this->up != true) {
 
144
    *proxyUpPtr = LAMP_OFF;
 
145
  } else {
 
146
    if (this->readInteger("proxy.node.proxy_running", &found) != 1) {
 
147
      *proxyUpPtr = LAMP_OFF;
 
148
    } else {
 
149
      if (this->localNode == true) {
 
150
        // For the local node, make sure all the cluster connections
 
151
        //   are up.  If not issue a warning lamp
 
152
        if (lmgmt->clusterOk() == false) {
 
153
          *proxyUpPtr = LAMP_WARNING;
 
154
        } else {
 
155
          *proxyUpPtr = LAMP_ON;
 
156
        }
 
157
      } else {
 
158
        // We can not currently check remote node
 
159
        //  cluster info
 
160
        *proxyUpPtr = LAMP_ON;
 
161
      }
 
162
    }
 
163
  }
 
164
 
 
165
  if (nodeAlarms.head == NULL) {
 
166
    *alarms = false;
 
167
  } else {
 
168
    *alarms = true;
 
169
  }
 
170
}
 
171
 
 
172
// void overviewRecord::updateStatus(time_t, ClusterPeerInfo*)
 
173
// updates up/down status based on the cluster peer info record
 
174
//
 
175
//   currentTime is the value of localtime(time()) - sent in as
 
176
//     a parameter so we do not have to make repetitive system calls.
 
177
//     overviewPage::checkForUpdates can just make one call
 
178
//
 
179
//   cpi - is a pointer to a structure we got from ClusterCom that represnets
 
180
//         information about this node
 
181
//
 
182
//   a machine is up if we have heard from it in the last 15 seconds
 
183
//
 
184
void
 
185
overviewRecord::updateStatus(time_t currentTime, ClusterPeerInfo * cpi)
 
186
{
 
187
 
 
188
  // Update if the node is up or down
 
189
  if (currentTime - cpi->idle_ticks > 15) {
 
190
    up = false;
 
191
  } else {
 
192
    up = true;
 
193
  }
 
194
 
 
195
  // Update the node records by copying them from cpi
 
196
  //  (remote nodes only)
 
197
  if (localNode == false) {
 
198
    memcpy(node_rec_data.recs, cpi->node_rec_data.recs, recordArraySize);
 
199
    RecGetRecordOrderAndId(node_rec_data.recs[0].name, &node_rec_first_ix, NULL);
 
200
  }
 
201
}
 
202
 
 
203
// adds a new alarm to the list of current alarms for the node
 
204
void
 
205
overviewRecord::addAlarm(alarm_t type, char *ip, char *desc)
 
206
{
 
207
 
 
208
  AlarmListable *alarm;
 
209
 
 
210
  alarm = new AlarmListable;
 
211
  alarm->ip = ip;
 
212
  alarm->type = type;
 
213
  alarm->desc = desc;
 
214
  nodeAlarms.push(alarm);
 
215
}
 
216
 
 
217
// adds a new alarm to the list of current alarms for the node
 
218
void
 
219
overviewRecord::addAlarm(AlarmListable * newAlarm)
 
220
{
 
221
  nodeAlarms.push(newAlarm);
 
222
}
 
223
 
 
224
// bool overviewRecord::ipMatch(char* ipStr)
 
225
//
 
226
//   Returns true if the passed in string matches
 
227
//     the ip address for this node
 
228
bool
 
229
overviewRecord::ipMatch(char *ipStr)
 
230
{
 
231
  if (inet_addr(ipStr) == inetAddr) {
 
232
    return true;
 
233
  } else {
 
234
    return false;
 
235
  }
 
236
}
 
237
 
 
238
// Runs throught the list of current alarms on the node
 
239
//  and asks the Alarms class if it is valid.  If the alarm
 
240
//  is expired it is removed from the alarm list
 
241
void
 
242
overviewRecord::checkAlarms()
 
243
{
 
244
 
 
245
  AlarmListable *current;
 
246
  AlarmListable *next;
 
247
 
 
248
  current = nodeAlarms.head;
 
249
  while (current != NULL) {
 
250
 
 
251
    next = current->link.next;
 
252
 
 
253
    if (!lmgmt->alarm_keeper->isCurrentAlarm(current->type, current->ip)) {
 
254
      // The alarm is no longer current.  Dispose of it
 
255
      nodeAlarms.remove(current);
 
256
      delete current;
 
257
    }
 
258
 
 
259
    current = next;
 
260
  }
 
261
}
 
262
 
 
263
//  overview::readCounter, overview::readInteger
 
264
//  overview::readFloat, overview::readString
 
265
//
 
266
//  Accessor functions for node records.  For remote node,
 
267
//    we get the value in the node_data array we maintain
 
268
//    in this object.  For the node, we do not maintain any data
 
269
//    and rely on lmgmt->record_data for both the retrieval
 
270
//    code and the records array
 
271
//
 
272
//  Locking should be done by overviewPage::accessLock.
 
273
//  CALLEE is responsible for obtaining and releasing the lock
 
274
//
 
275
RecCounter
 
276
overviewRecord::readCounter(const char *name, bool * found)
 
277
{
 
278
  RecCounter rec = 0;
 
279
  int rec_status = REC_ERR_OKAY;
 
280
  int order = -1;
 
281
  if (localNode == false) {
 
282
    rec_status = RecGetRecordOrderAndId(name, &order, NULL);
 
283
    if (rec_status == REC_ERR_OKAY) {
 
284
      order -= node_rec_first_ix; // Offset
 
285
      ink_release_assert(order < node_rec_data.num_recs);
 
286
      ink_debug_assert(order < node_rec_data.num_recs);
 
287
      rec = node_rec_data.recs[order].data.rec_counter;
 
288
    } else {
 
289
      mgmt_log(stderr, "node variables '%s' not found!\n");
 
290
    }
 
291
  }
 
292
 
 
293
  if (found) {
 
294
    *found = (rec_status == REC_ERR_OKAY);
 
295
  } else {
 
296
    mgmt_log(stderr, "node variables '%s' not found!\n");
 
297
  }
 
298
  return rec;
 
299
}
 
300
 
 
301
RecInt
 
302
overviewRecord::readInteger(const char *name, bool * found)
 
303
{
 
304
  RecInt rec = 0;
 
305
  int rec_status = REC_ERR_OKAY;
 
306
  int order = -1;
 
307
  if (localNode == false) {
 
308
    rec_status = RecGetRecordOrderAndId(name, &order, NULL);
 
309
    if (rec_status == REC_ERR_OKAY) {
 
310
      order -= node_rec_first_ix; // Offset
 
311
      ink_release_assert(order < node_rec_data.num_recs);
 
312
      ink_debug_assert(order < node_rec_data.num_recs);
 
313
      rec = node_rec_data.recs[order].data.rec_int;
 
314
    }
 
315
  } else {
 
316
    rec_status = RecGetRecordInt(name, &rec);
 
317
  }
 
318
 
 
319
  if (found) {
 
320
    *found = (rec_status == REC_ERR_OKAY);
 
321
  } else {
 
322
    mgmt_log(stderr, "node variables '%s' not found!\n");
 
323
  }
 
324
  return rec;
 
325
}
 
326
 
 
327
RecFloat
 
328
overviewRecord::readFloat(const char *name, bool * found)
 
329
{
 
330
  RecFloat rec = 0.0;
 
331
  int rec_status = REC_ERR_OKAY;
 
332
  int order = -1;
 
333
  if (localNode == false) {
 
334
    rec_status = RecGetRecordOrderAndId(name, &order, NULL);
 
335
    if (rec_status == REC_ERR_OKAY) {
 
336
      order -= node_rec_first_ix; // Offset
 
337
      ink_release_assert(order < node_rec_data.num_recs);
 
338
      ink_debug_assert(order < node_rec_data.num_recs);
 
339
      rec = node_rec_data.recs[order].data.rec_float;
 
340
    }
 
341
  } else {
 
342
    rec_status = RecGetRecordFloat(name, &rec);
 
343
  }
 
344
 
 
345
  if (found) {
 
346
    *found = (rec_status == REC_ERR_OKAY);
 
347
  } else {
 
348
    mgmt_log(stderr, "node variables '%s' not found!\n");
 
349
  }
 
350
  return rec;
 
351
}
 
352
 
 
353
RecString
 
354
overviewRecord::readString(const char *name, bool * found)
 
355
{
 
356
  RecString rec = NULL;
 
357
  int rec_status = REC_ERR_OKAY;
 
358
  int order = -1;
 
359
  if (localNode == false) {
 
360
    rec_status = RecGetRecordOrderAndId(name, &order, NULL);
 
361
    if (rec_status == REC_ERR_OKAY) {
 
362
      order -= node_rec_first_ix; // Offset
 
363
      ink_release_assert(order < node_rec_data.num_recs);
 
364
      ink_debug_assert(order < node_rec_data.num_recs);
 
365
      rec = xstrdup(node_rec_data.recs[order].data.rec_string);
 
366
    }
 
367
  } else {
 
368
    rec_status = RecGetRecordString_Xmalloc(name, &rec);
 
369
  }
 
370
 
 
371
  if (found) {
 
372
    *found = (rec_status == REC_ERR_OKAY);
 
373
  } else {
 
374
    mgmt_log(stderr, "node variables '%s' not found!\n");
 
375
  }
 
376
  return rec;
 
377
}
 
378
 
 
379
// bool overviewRecord::varStrFromName (char*, char*bufVal, char*, int)
 
380
//
 
381
//  Accessor function for node records.  Looks up varName for
 
382
//    this node and if found, turns it value into a string
 
383
//    and places it in bufVal
 
384
//
 
385
//  return true if bufVal was succefully set
 
386
//    and false otherwise
 
387
//
 
388
//  EVIL ALERT: varStrFromName in WebMgmtUtils.cc is extremely
 
389
//    similar to this function except in how it gets it's
 
390
//    data.  Changes to this fuction must be propogated
 
391
//    to its twin.  Cut and Paste sucks but there is not
 
392
//    an easy way to merge the functions
 
393
//
 
394
bool
 
395
overviewRecord::varStrFromName(const char *varNameConst, char *bufVal, int bufLen)
 
396
{
 
397
  char *varName;
 
398
  RecDataT varDataType;
 
399
  bool found = true;
 
400
  int varNameLen;
 
401
  char formatOption = '\0';
 
402
 
 
403
  union
 
404
  {
 
405
    MgmtIntCounter counter_data;        /* Data */
 
406
    MgmtInt int_data;
 
407
    MgmtFloat float_data;
 
408
    MgmtString string_data;
 
409
  } data;
 
410
 
 
411
  // Check to see if there is a \ option on the end of variable
 
412
  //   \ options indicate that we need special formatting
 
413
  //   of the results.  Supported \ options are
 
414
  //
 
415
  ///  b - bytes.  Ints and Counts only.  Amounts are
 
416
  //       transformed into one of GB, MB, KB, or B
 
417
  //
 
418
  varName = xstrdup(varNameConst);
 
419
  varNameLen = strlen(varName);
 
420
  if (varNameLen > 3 && varName[varNameLen - 2] == '\\') {
 
421
    formatOption = varName[varNameLen - 1];
 
422
 
 
423
    // Now that we know the format option, terminate the string
 
424
    //   to make the option disappear
 
425
    varName[varNameLen - 2] = '\0';
 
426
 
 
427
    // Return not found for unknown format options
 
428
    if (formatOption != 'b' && formatOption != 'm' && formatOption != 'c' && formatOption != 'p') {
 
429
      xfree(varName);
 
430
      return false;
 
431
    }
 
432
  }
 
433
  if (RecGetRecordDataType(varName, &varDataType) == REC_ERR_FAIL) {
 
434
    xfree(varName);
 
435
    return false;
 
436
  }
 
437
 
 
438
  switch (varDataType) {
 
439
  case RECD_INT:
 
440
    data.int_data = this->readInteger(varName, &found);
 
441
    if (formatOption == 'b') {
 
442
      bytesFromInt(data.int_data, bufVal);
 
443
    } else if (formatOption == 'm') {
 
444
      MbytesFromInt(data.int_data, bufVal);
 
445
    } else if (formatOption == 'c') {
 
446
      commaStrFromInt(data.int_data, bufVal);
 
447
    } else {
 
448
      sprintf(bufVal, "%" PRId64 "", data.int_data);
 
449
    }
 
450
    break;
 
451
  case RECD_COUNTER:
 
452
    data.counter_data = this->readCounter(varName, &found);
 
453
    if (formatOption == 'b') {
 
454
      bytesFromInt((MgmtInt) data.counter_data, bufVal);
 
455
    } else if (formatOption == 'm') {
 
456
      MbytesFromInt((MgmtInt) data.counter_data, bufVal);
 
457
    } else if (formatOption == 'c') {
 
458
      commaStrFromInt(data.counter_data, bufVal);
 
459
    } else {
 
460
      sprintf(bufVal, "%" PRId64 "", data.counter_data);
 
461
    }
 
462
    break;
 
463
  case RECD_FLOAT:
 
464
    data.float_data = this->readFloat(varName, &found);
 
465
    if (formatOption == 'p') {
 
466
      percentStrFromFloat(data.float_data, bufVal);
 
467
    } else {
 
468
      snprintf(bufVal, bufLen, "%.2f", data.float_data);
 
469
    }
 
470
    break;
 
471
  case RECD_STRING:
 
472
    data.string_data = this->readString(varName, &found);
 
473
    if (data.string_data == NULL) {
 
474
      bufVal[0] = '\0';
 
475
    } else if (strlen(data.string_data) < (size_t) (bufLen - 1)) {
 
476
      ink_strncpy(bufVal, data.string_data, bufLen);
 
477
    } else {
 
478
      ink_strncpy(bufVal, data.string_data, bufLen);
 
479
    }
 
480
    xfree(data.string_data);
 
481
    break;
 
482
  case RECD_NULL:
 
483
  default:
 
484
    found = false;
 
485
    break;
 
486
  }
 
487
 
 
488
  xfree(varName);
 
489
  return found;
 
490
}
 
491
 
 
492
bool
 
493
overviewRecord::varCounterFromName(const char *name, MgmtIntCounter * value)
 
494
{
 
495
  bool found = false;
 
496
 
 
497
  if (value)
 
498
    *value = readCounter((char *) name, &found);
 
499
  return found;
 
500
}
 
501
 
 
502
bool
 
503
overviewRecord::varIntFromName(const char *name, MgmtInt * value)
 
504
{
 
505
  bool found = false;
 
506
 
 
507
  if (value)
 
508
    *value = readInteger((char *) name, &found);
 
509
  return found;
 
510
}
 
511
 
 
512
bool
 
513
overviewRecord::varFloatFromName(const char *name, MgmtFloat * value)
 
514
{
 
515
  bool found = false;
 
516
 
 
517
  if (value)
 
518
    *value = readFloat((char *) name, &found);
 
519
 
 
520
  return found;
 
521
}
 
522
 
 
523
overviewPage::overviewPage():sortRecords(10, false)
 
524
{
 
525
 
 
526
  ink_mutex_init(&accessLock, "overviewRecord");
 
527
  nodeRecords = ink_hash_table_create(InkHashTableKeyType_Word);
 
528
  numHosts = 0;
 
529
  ourAddr = 0;                  // We will update this when we add the record for
 
530
  //  this machine
 
531
}
 
532
 
 
533
overviewPage::~overviewPage()
 
534
{
 
535
 
 
536
  // Since we only have one global object and we never destruct it
 
537
  //  do not actually free memeory since it causes problems the
 
538
  //  process is vforked, and the child execs something
 
539
  // The below code is DELIBERTLY commented out
 
540
  //
 
541
  // ink_mutex_destroy(&accessLock);
 
542
  // ink_hash_table_destroy(nodeRecords);
 
543
}
 
544
 
 
545
// overviewPage::checkForUpdates - updates node records as to whether peers
 
546
//    are up or down
 
547
void
 
548
overviewPage::checkForUpdates()
 
549
{
 
550
 
 
551
  ClusterPeerInfo *tmp;
 
552
  InkHashTableEntry *entry;
 
553
  InkHashTableIteratorState iterator_state;
 
554
  overviewRecord *current;
 
555
  time_t currentTime;
 
556
  bool newHostAdded = false;
 
557
 
 
558
  // grok through the cluster communication stuff and update information
 
559
  //  about hosts in the cluster
 
560
  //
 
561
  ink_mutex_acquire(&accessLock);
 
562
  ink_mutex_acquire(&(lmgmt->ccom->mutex));
 
563
  currentTime = time(NULL);
 
564
  for (entry = ink_hash_table_iterator_first(lmgmt->ccom->peers, &iterator_state);
 
565
       entry != NULL; entry = ink_hash_table_iterator_next(lmgmt->ccom->peers, &iterator_state)) {
 
566
 
 
567
    tmp = (ClusterPeerInfo *) ink_hash_table_entry_value(lmgmt->ccom->peers, entry);
 
568
 
 
569
    if (ink_hash_table_lookup(nodeRecords, (InkHashTableKey) tmp->inet_address, (InkHashTableValue *) & current) == 0) {
 
570
      this->addRecord(tmp);
 
571
      newHostAdded = true;
 
572
    } else {
 
573
      current->updateStatus(currentTime, tmp);
 
574
    }
 
575
  }
 
576
  ink_mutex_release(&lmgmt->ccom->mutex);
 
577
 
 
578
  // Now check to see if our alarms up to date
 
579
  for (int i = 0; i < numHosts; i++) {
 
580
    current = (overviewRecord *) sortRecords[i];
 
581
    current->checkAlarms();
 
582
  }
 
583
 
 
584
  // If we added a new host we must resort sortRecords
 
585
  if (newHostAdded) {
 
586
    this->sortHosts();
 
587
  }
 
588
 
 
589
  ink_mutex_release(&accessLock);
 
590
}
 
591
 
 
592
 
 
593
// overrviewPage::sortHosts()
 
594
//
 
595
// resorts sortRecords, but always leaves the local node
 
596
//   as the first record
 
597
//
 
598
// accessLock must be held by callee
 
599
void
 
600
overviewPage::sortHosts()
 
601
{
 
602
  void **array = sortRecords.getArray();
 
603
 
 
604
  qsort(array + 1, numHosts - 1, sizeof(void *), hostSortFunc);
 
605
}
 
606
 
 
607
// overviewPage::addRecord(ClusterPerrInfo* cpi)
 
608
//   Adds a new node record
 
609
//   Assuems that this->accessLock is already held
 
610
//
 
611
void
 
612
overviewPage::addRecord(ClusterPeerInfo * cpi)
 
613
{
 
614
 
 
615
  overviewRecord *newRec;
 
616
 
 
617
  AlarmListable *current;
 
618
  AlarmListable *next;
 
619
 
 
620
  ink_assert(cpi != NULL);
 
621
 
 
622
  newRec = new overviewRecord(cpi->inet_address, false, cpi);
 
623
  newRec->updateStatus(time(NULL), cpi);
 
624
 
 
625
  ink_hash_table_insert(nodeRecords, (InkHashTableKey) cpi->inet_address, (InkHashTableEntry *) newRec);
 
626
 
 
627
  // Check to see if we have alarms that need to be added
 
628
  //
 
629
  //  This an inefficient linear search, however there should
 
630
  //    never be a large number of alarms that do not
 
631
  //    nodes yet.  This should only happen at start up
 
632
  //
 
633
  current = notFoundAlarms.head;
 
634
  while (current != NULL) {
 
635
 
 
636
    next = current->link.next;
 
637
 
 
638
    if (newRec->ipMatch(current->ip) == true) {
 
639
      // The alarm belongs to this record, remove it and
 
640
      //    add it to the record
 
641
      notFoundAlarms.remove(current);
 
642
      newRec->addAlarm(current);
 
643
    }
 
644
 
 
645
    current = next;
 
646
  }
 
647
 
 
648
  sortRecords.addEntry(newRec);
 
649
  numHosts++;
 
650
}
 
651
 
 
652
// adds a record to nodeRecords for the local machine.
 
653
//   gets IP addr from lmgmt->ccom so cluster communtication
 
654
//   must be intialized before calling this function
 
655
//
 
656
//
 
657
void
 
658
overviewPage::addSelfRecord()
 
659
{
 
660
 
 
661
  overviewRecord *newRec;
 
662
  AlarmListable *current;
 
663
  AlarmListable *next;
 
664
 
 
665
  ink_mutex_acquire(&accessLock);
 
666
 
 
667
  // We should not have been called before
 
668
  ink_assert(ourAddr == 0);
 
669
 
 
670
  // Find out what our cluster addr is from
 
671
  //   from cluster com
 
672
  this->ourAddr = lmgmt->ccom->getIP();
 
673
 
 
674
  newRec = new overviewRecord(ourAddr, true);
 
675
  newRec->up = true;
 
676
 
 
677
  ink_hash_table_insert(nodeRecords, (InkHashTableKey) this->ourAddr, (InkHashTableEntry *) newRec);
 
678
 
 
679
  // Check to see if we have alarms that need to be added
 
680
  //   They would be listed for IP zero since the alarm
 
681
  //   manager knows ip address for the local node as NULL
 
682
  //
 
683
  current = notFoundAlarms.head;
 
684
  while (current != NULL) {
 
685
 
 
686
    next = current->link.next;
 
687
 
 
688
    if (current->ip == NULL) {
 
689
      // The alarm belongs to this record, remove it and
 
690
      //    add it to the record
 
691
      notFoundAlarms.remove(current);
 
692
      newRec->addAlarm(current);
 
693
    }
 
694
 
 
695
    current = next;
 
696
  }
 
697
 
 
698
  sortRecords.addEntry(newRec);
 
699
  numHosts++;
 
700
  ink_mutex_release(&accessLock);
 
701
}
 
702
 
 
703
// adds alarm to the node specified by the ip address
 
704
//   if ip is NULL, the node is local machine
 
705
void
 
706
overviewPage::addAlarm(alarm_t type, char *ip, char *desc)
 
707
{
 
708
 
 
709
  unsigned long inetAddr;
 
710
  InkHashTableValue lookup;
 
711
  overviewRecord *node;
 
712
  AlarmListable *alarm;
 
713
 
 
714
  ink_mutex_acquire(&accessLock);
 
715
 
 
716
  if (ip == NULL) {
 
717
    inetAddr = ourAddr;
 
718
  } else {
 
719
    inetAddr = inet_addr(ip);
 
720
  }
 
721
 
 
722
  if (ink_hash_table_lookup(nodeRecords, (InkHashTableKey) inetAddr, &lookup)) {
 
723
    // We found our entry
 
724
    node = (overviewRecord *) lookup;
 
725
    node->addAlarm(type, ip, desc);
 
726
  } else {
 
727
 
 
728
    Debug("dashboard", "[overviewRecord::addAlarm] Alarm for node that we have not seen %s\n", ip);
 
729
 
 
730
    // If we have not seen the node, queue the alarm.  The node
 
731
    //  should appear eventually
 
732
    alarm = new AlarmListable;
 
733
    alarm->ip = ip;
 
734
    alarm->type = type;
 
735
    alarm->desc = desc;
 
736
    notFoundAlarms.push(alarm);
 
737
  }
 
738
 
 
739
  ink_mutex_release(&accessLock);
 
740
}
 
741
 
 
742
// void overviewPage::generateAlarmsTable(textBuffer* output)
 
743
//
 
744
//  places an HTML table containing
 
745
//
 
746
//    resolve   hostname  alarm_description into output
 
747
//
 
748
void
 
749
overviewPage::generateAlarmsTable(WebHttpContext * whc)
 
750
{
 
751
 
 
752
  overviewRecord *current;
 
753
  AlarmListable *curAlarm;
 
754
  char numBuf[256];
 
755
  char name[32];
 
756
  //  char *alarm_query;
 
757
  int alarm_count;
 
758
 
 
759
  textBuffer *output = whc->response_bdy;
 
760
 
 
761
  ink_mutex_acquire(&accessLock);
 
762
 
 
763
  // Iterate through each host
 
764
  alarm_count = 0;
 
765
  for (int i = 0; i < numHosts; i++) {
 
766
    current = (overviewRecord *) sortRecords[i];
 
767
 
 
768
    // Iterate through the list of alarms
 
769
    curAlarm = current->nodeAlarms.head;
 
770
 
 
771
    while (curAlarm != NULL) {
 
772
      HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
773
 
 
774
      // Hostname is an entry
 
775
      HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_TOP, NULL, NULL, 0);
 
776
      output->copyFrom(current->hostname, strlen(current->hostname));
 
777
      HtmlRndrTdClose(output);
 
778
 
 
779
      // Alarm description is an entry
 
780
      HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_TOP, NULL, NULL, 0);
 
781
      if (curAlarm->desc != NULL) {
 
782
        output->copyFrom(curAlarm->desc, strlen(curAlarm->desc));
 
783
      } else {
 
784
        const char *alarmText = lmgmt->alarm_keeper->getAlarmText(curAlarm->type);
 
785
        output->copyFrom(alarmText, strlen(alarmText));
 
786
      }
 
787
      HtmlRndrTdClose(output);
 
788
 
 
789
      // the name of each checkbox is : "alarm:<alarm_count>"
 
790
      // the value of each checkbox is: "<alarmId>:<ip addr>"
 
791
      HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_CENTER, HTML_VALIGN_NONE, NULL, NULL, 0);
 
792
      if (curAlarm->ip == NULL)
 
793
        snprintf(numBuf, sizeof(numBuf), "%d:local", curAlarm->type);
 
794
      else
 
795
        snprintf(numBuf, sizeof(numBuf), "%d:%s", curAlarm->type, curAlarm->ip);
 
796
      snprintf(name, sizeof(name), "alarm:%d", alarm_count);
 
797
      HtmlRndrInput(output, HTML_CSS_NONE, HTML_TYPE_CHECKBOX, name, numBuf, NULL, NULL);
 
798
      HtmlRndrTdClose(output);
 
799
 
 
800
      HtmlRndrTrClose(output);
 
801
      curAlarm = curAlarm->link.next;
 
802
 
 
803
      alarm_count++;
 
804
 
 
805
    }
 
806
  }
 
807
 
 
808
  ink_mutex_release(&accessLock);
 
809
 
 
810
  // check if we didn't find any alarms
 
811
  if (alarm_count == 0) {
 
812
    HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_NONE);
 
813
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 3);
 
814
    HtmlRndrSpace(output, 2);
 
815
    HtmlRndrText(output, whc->lang_dict_ht, HTML_ID_NO_ACTIVE_ALARMS);
 
816
    HtmlRndrTdClose(output);
 
817
    HtmlRndrTrClose(output);
 
818
  }
 
819
 
 
820
}
 
821
 
 
822
void
 
823
overviewPage::generateAlarmsTableCLI(textBuffer * output)
 
824
{
 
825
  overviewRecord *current;
 
826
  AlarmListable *curAlarm;
 
827
  char ipBuf[64] = "";
 
828
  char alarmtypeBuf[32] = "";
 
829
  //  char            tmpBuf[256] = "";
 
830
  CLIlineBuffer *Obuf = NULL;
 
831
  char *out_buf = NULL;
 
832
  const char *field1 = " Alarm Id";
 
833
  const char *field2 = "Host";
 
834
  const char *field3 = "Alarm";
 
835
 
 
836
  ink_mutex_acquire(&accessLock);
 
837
 
 
838
  // Create Alarm header
 
839
  Obuf = new CLIlineBuffer(10);
 
840
  Obuf->addField("%-*s", field1, 21);
 
841
  Obuf->addField("%-*s", field2, 21);
 
842
  Obuf->addField("%-*s", field3, 30);
 
843
  out_buf = Obuf->getline();
 
844
  if (out_buf) {
 
845
    output->copyFrom(out_buf, strlen(out_buf));
 
846
    delete[]out_buf;
 
847
    out_buf = NULL;
 
848
  }
 
849
  Obuf->reset();
 
850
  output->copyFrom(CLI_globals::sep1, strlen(CLI_globals::sep1));
 
851
 
 
852
  // Iterate through each host
 
853
  for (int i = 0; i < numHosts; i++) {
 
854
    current = (overviewRecord *) sortRecords[i];
 
855
 
 
856
    // Iterate through the list of alarms
 
857
    curAlarm = current->nodeAlarms.head;
 
858
    while (curAlarm != NULL) {
 
859
      //
 
860
      //  The resolve input is <alarmId>:<ip addr>
 
861
      //
 
862
      if (curAlarm->ip == NULL)
 
863
        snprintf(ipBuf, sizeof(ipBuf), "%s", "local");
 
864
      else
 
865
        snprintf(ipBuf, sizeof(ipBuf), "%s", curAlarm->ip);
 
866
 
 
867
      snprintf(alarmtypeBuf, sizeof(alarmtypeBuf), "%d", curAlarm->type);
 
868
      // alarm id field is 21
 
869
      Obuf->addField("%*s", alarmtypeBuf, 3);
 
870
      Obuf->addField("%-*s", ":", 1);
 
871
      Obuf->addField("%-*s", ipBuf, 16);
 
872
      Obuf->addField("%*s", " ", 1);
 
873
      // host field is 21
 
874
      // Hostname is an entry
 
875
      Obuf->addField("%-*s", current->hostname, 20);
 
876
      Obuf->addField("%*s", " ", 1);
 
877
 
 
878
      // Alarm description is an entry
 
879
      if (curAlarm->desc != NULL) {     // Desc field is 30
 
880
        Obuf->addField("%-*s", curAlarm->desc, 30);
 
881
      } else {
 
882
        const char *alarmText = lmgmt->alarm_keeper->getAlarmText(curAlarm->type);
 
883
        Obuf->addField("%-*s", alarmText, 20);
 
884
      }
 
885
 
 
886
      out_buf = Obuf->getline();        // get formatted output
 
887
      if (out_buf) {            // copy to output buffer
 
888
        output->copyFrom(out_buf, strlen(out_buf));
 
889
        delete[]out_buf;        // need to free this every time
 
890
        out_buf = NULL;
 
891
      }
 
892
      Obuf->reset();            // reset every time i.e. reuse
 
893
 
 
894
      curAlarm = curAlarm->link.next;
 
895
    }                           // end while(.)
 
896
  }                             // end for(.)
 
897
 
 
898
  // cleanup
 
899
  delete Obuf;
 
900
 
 
901
  ink_mutex_release(&accessLock);
 
902
}                               // end generateAlarmsTableCLI()
 
903
 
 
904
#ifndef NO_WEBUI
 
905
// void overviewPage::generateAlarmsSummary(textBuffer* output)
 
906
//
 
907
//  alarm summary information (Alarm! [X pending])
 
908
//
 
909
void
 
910
overviewPage::generateAlarmsSummary(WebHttpContext * whc)
 
911
{
 
912
 
 
913
  overviewRecord *current;
 
914
  AlarmListable *curAlarm;
 
915
  char buf[256];
 
916
  int alarm_count;
 
917
  char *alarm_link;
 
918
 
 
919
  textBuffer *output = whc->response_bdy;
 
920
 
 
921
  ink_mutex_acquire(&accessLock);
 
922
 
 
923
  // Iterate through each host
 
924
  alarm_count = 0;
 
925
  for (int i = 0; i < numHosts; i++) {
 
926
    current = (overviewRecord *) sortRecords[i];
 
927
    // Iterate through the list of alarms
 
928
    curAlarm = current->nodeAlarms.head;
 
929
    while (curAlarm != NULL) {
 
930
      alarm_count++;
 
931
      curAlarm = curAlarm->link.next;
 
932
    }
 
933
  }
 
934
 
 
935
  ink_mutex_release(&accessLock);
 
936
 
 
937
  if (alarm_count > 0) {
 
938
    HtmlRndrTableOpen(output, "100%", 0, 0, 0);
 
939
 
 
940
    HtmlRndrTrOpen(output, HTML_CSS_ALARM_COLOR, HTML_ALIGN_NONE);
 
941
    HtmlRndrTdOpen(output, HTML_CSS_GREY_LINKS, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, "30", 0);
 
942
    alarm_link = WebHttpGetLink_Xmalloc(HTML_ALARM_FILE);
 
943
    HtmlRndrAOpen(output, HTML_CSS_NONE, alarm_link, NULL);
 
944
    xfree(alarm_link);
 
945
    HtmlRndrSpace(output, 2);
 
946
    HtmlRndrText(output, whc->lang_dict_ht, HTML_ID_ALARM);
 
947
    snprintf(buf, sizeof(buf), "! [%d ", alarm_count);
 
948
    output->copyFrom(buf, strlen(buf));
 
949
    HtmlRndrText(output, whc->lang_dict_ht, HTML_ID_PENDING);
 
950
    snprintf(buf, sizeof(buf), "]");
 
951
    output->copyFrom(buf, strlen(buf));
 
952
    HtmlRndrAClose(output);
 
953
    HtmlRndrTdClose(output);
 
954
    HtmlRndrTrClose(output);
 
955
 
 
956
    HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_NONE);
 
957
    HtmlRndrTdOpen(output, HTML_CSS_TERTIARY_COLOR, HTML_ALIGN_NONE, HTML_VALIGN_NONE, "1", "1", 0);
 
958
    HtmlRndrDotClear(output, 1, 1);
 
959
    HtmlRndrTdClose(output);
 
960
 
 
961
    HtmlRndrTrClose(output);
 
962
 
 
963
    HtmlRndrTableClose(output);
 
964
 
 
965
  }
 
966
}
 
967
 
 
968
// generates the table for the overview page
 
969
//
 
970
//  the entries are   hostname, on/off, alarm
 
971
//
 
972
void
 
973
overviewPage::generateTable(WebHttpContext * whc)
 
974
{
 
975
 
 
976
  // Varariables for finding out information about a specific node
 
977
  overviewRecord *current;
 
978
  char *hostName;
 
979
  bool alarm;
 
980
  bool up;
 
981
  bool found;
 
982
  PowerLampState proxyUp;
 
983
  bool sslEnabled = false;
 
984
  const char refFormat[] = "%s://%s:%d%s";
 
985
  char refBuf[256];             // Buffer for link to other nodes
 
986
  char *domainStart;            // pointer to domain name part of hostna,e
 
987
  int hostLen;                  // length of the host only portion of the hostname
 
988
  char outBuf[16 + 1];
 
989
  MgmtInt objs, t_hit, t_miss;
 
990
  MgmtFloat ops, hits, mbps;
 
991
  //  char *alarm_link;
 
992
 
 
993
  textBuffer *output = whc->response_bdy;
 
994
  MgmtHashTable *dict_ht = whc->lang_dict_ht;
 
995
 
 
996
  // Check to see if SSL is enabled.  Do one check, and record
 
997
  //  the info since it can change from under us in the pContext
 
998
  //  structure and we at least want to be able to give a
 
999
  //  consistent view
 
1000
  if (whc->server_state & WEB_HTTP_SERVER_STATE_SSL_ENABLED) {
 
1001
    sslEnabled = true;
 
1002
  }
 
1003
 
 
1004
  ink_mutex_acquire(&accessLock);
 
1005
  for (int i = 0; i < numHosts; i++) {
 
1006
    current = (overviewRecord *) sortRecords[i];
 
1007
    current->getStatus(&hostName, &up, &alarm, &proxyUp);
 
1008
 
 
1009
    // no longer need 'if (alarm)' since we have the alarm bar now
 
1010
    //if (alarm)
 
1011
    //HtmlRndrTrOpen(output, HTML_CSS_ALARM_COLOR, HTML_ALIGN_CENTER);
 
1012
    //else
 
1013
    HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_CENTER);
 
1014
 
 
1015
    // Make the hostname non-qualified if we have a host name and not
 
1016
    // an ip address
 
1017
    if (isdigit(*hostName))
 
1018
      domainStart = NULL;
 
1019
    else
 
1020
      domainStart = strchr(hostName, '.');
 
1021
 
 
1022
    if (domainStart == NULL)
 
1023
      hostLen = strlen(hostName);
 
1024
    else
 
1025
      hostLen = domainStart - hostName;
 
1026
 
 
1027
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1028
    // INKqa01119  - remove the up check since we are currently
 
1029
    // not sending heartbeat packets when the proxy is off
 
1030
    // if (up == true && current->localNode == false) {
 
1031
    if (current->localNode == false) {
 
1032
      char *link = WebHttpGetLink_Xmalloc(HTML_DEFAULT_MONITOR_FILE);
 
1033
      if (sslEnabled == false) {
 
1034
        // coverity[non_const_printf_format_string]
 
1035
        snprintf(refBuf, sizeof(refBuf), refFormat, "http", hostName, wGlobals.webPort, link);
 
1036
      } else {
 
1037
        // coverity[non_const_printf_format_string]
 
1038
        snprintf(refBuf, sizeof(refBuf), refFormat, "https", hostName, wGlobals.webPort, link);
 
1039
      }
 
1040
      HtmlRndrAOpen(output, HTML_CSS_GRAPH, refBuf, NULL);
 
1041
      output->copyFrom(hostName, hostLen);
 
1042
      HtmlRndrAClose(output);
 
1043
      xfree(link);
 
1044
    } else {
 
1045
      output->copyFrom(hostName, hostLen);
 
1046
    }
 
1047
    HtmlRndrTdClose(output);
 
1048
 
 
1049
    // Add On/Off light
 
1050
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1051
    switch (proxyUp) {
 
1052
    case LAMP_ON:
 
1053
      HtmlRndrText(output, dict_ht, HTML_ID_ON);
 
1054
      break;
 
1055
    case LAMP_OFF:
 
1056
      HtmlRndrText(output, dict_ht, HTML_ID_OFF);
 
1057
      break;
 
1058
    case LAMP_WARNING:
 
1059
      HtmlRndrText(output, dict_ht, HTML_ID_WARNING);
 
1060
      break;
 
1061
    }
 
1062
    HtmlRndrTdClose(output);
 
1063
 
 
1064
    // objects served
 
1065
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1066
    current->varIntFromName("proxy.node.user_agents_total_documents_served", &objs);
 
1067
    snprintf(outBuf, sizeof(outBuf), "%.10lld", objs);
 
1068
    output->copyFrom(outBuf, strlen(outBuf));
 
1069
    HtmlRndrTdClose(output);
 
1070
 
 
1071
    // ops/sec
 
1072
    if (up == true) {
 
1073
      ops = current->readFloat("proxy.node.user_agent_xacts_per_second", &found);
 
1074
    } else {
 
1075
      // Always report zero for down nodes
 
1076
      ops = 0;
 
1077
      found = true;
 
1078
    }
 
1079
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1080
    snprintf(outBuf, 16, "%.2f", ops);
 
1081
    output->copyFrom(outBuf, strlen(outBuf));
 
1082
    HtmlRndrTdClose(output);
 
1083
 
 
1084
    // hit rate
 
1085
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1086
    current->varFloatFromName("proxy.node.cache_hit_ratio_avg_10s", &hits);
 
1087
    snprintf(outBuf, sizeof(outBuf), "%.2f%% ", hits * 100.0);
 
1088
    output->copyFrom(outBuf, strlen(outBuf));
 
1089
    HtmlRndrTdClose(output);
 
1090
 
 
1091
    // throughput
 
1092
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1093
    current->varFloatFromName("proxy.node.client_throughput_out", &mbps);
 
1094
    snprintf(outBuf, sizeof(outBuf), "%.2f", mbps);
 
1095
    output->copyFrom(outBuf, strlen(outBuf));
 
1096
    HtmlRndrTdClose(output);
 
1097
 
 
1098
    // hit latency
 
1099
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1100
    current->varIntFromName("proxy.node.http.transaction_msec_avg_10s.hit_fresh", &t_hit);
 
1101
    snprintf(outBuf, sizeof(outBuf), "%" PRId64 "", t_hit);
 
1102
    output->copyFrom(outBuf, strlen(outBuf));
 
1103
    HtmlRndrTdClose(output);
 
1104
 
 
1105
    // miss latency
 
1106
    HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1107
    current->varIntFromName("proxy.node.http.transaction_msec_avg_10s.miss_cold", &t_miss);
 
1108
    snprintf(outBuf, sizeof(outBuf), "%" PRId64 "", t_miss);
 
1109
    output->copyFrom(outBuf, strlen(outBuf));
 
1110
    HtmlRndrTdClose(output);
 
1111
 
 
1112
    // row close
 
1113
    HtmlRndrTrClose(output);
 
1114
 
 
1115
    // show the 'details' section on the dashboard
 
1116
    if (whc->request_state & WEB_HTTP_STATE_MORE_DETAIL) {
 
1117
      this->addHostPanel(whc, current);
 
1118
    }
 
1119
 
 
1120
  }
 
1121
 
 
1122
  ink_mutex_release(&accessLock);
 
1123
}
 
1124
 
 
1125
//
 
1126
// generates the table for the dashboard page for CLI
 
1127
//
 
1128
void
 
1129
overviewPage::generateTableCLI(textBuffer * output)
 
1130
{
 
1131
  // Varariables for finding out information about a specific node
 
1132
  overviewRecord *current;
 
1133
  char *hostName;
 
1134
  bool alarm;
 
1135
  bool up;
 
1136
  bool found;
 
1137
  PowerLampState proxyUp;
 
1138
  MgmtInt docCount;
 
1139
  MgmtInt loadMetric;
 
1140
  const char sformat[] = "%-3d %-15s %-8s %-6s %12s %12s";
 
1141
  const char nameFormat[] = "%s";
 
1142
  const char nodeFormat[] = "%s";
 
1143
  const char alarmFormat[] = "%s";
 
1144
  char namebuf[64];
 
1145
  char nodebuf[32];
 
1146
  char alarmbuf[32];
 
1147
  char docCountBuf[32];
 
1148
  char loadMetricBuf[32];
 
1149
  char tmpbuf[256];
 
1150
  char *domainStart;            // pointer to domain name part of hostname
 
1151
  int hostLen;                  // length of the host only portion of the hostname
 
1152
 
 
1153
  // This determine is 'details' is displayed
 
1154
  //moreInfo = this->moreInfoButton(submission, output);
 
1155
 
 
1156
  // acquire overviewPage
 
1157
  ink_mutex_acquire(&accessLock);
 
1158
 
 
1159
  // for each host we know about
 
1160
  for (int i = 0; i < numHosts; i++) {  // get host status
 
1161
    current = (overviewRecord *) sortRecords[i];
 
1162
    current->getStatus(&hostName, &up, &alarm, &proxyUp);
 
1163
 
 
1164
    // Make the hostname non-qualified if we have
 
1165
    //   a host name and not an ip address
 
1166
    if (isdigit(*hostName))
 
1167
      domainStart = NULL;
 
1168
    else
 
1169
      domainStart = strchr(hostName, '.');
 
1170
 
 
1171
    if (domainStart == NULL)
 
1172
      hostLen = strlen(hostName);
 
1173
    else
 
1174
      hostLen = domainStart - hostName;
 
1175
 
 
1176
    snprintf(namebuf, hostLen + 1, nameFormat, hostName);
 
1177
    namebuf[hostLen] = '\0';
 
1178
 
 
1179
    // Add On/Off/Down status
 
1180
    switch (proxyUp) {
 
1181
    case LAMP_ON:
 
1182
      // coverity[non_const_printf_format_string]
 
1183
      snprintf(nodebuf, sizeof(nodebuf), nodeFormat, "ON");
 
1184
      break;
 
1185
    case LAMP_OFF:
 
1186
      // coverity[non_const_printf_format_string]
 
1187
      snprintf(nodebuf, sizeof(nodebuf), nodeFormat, "OFF");
 
1188
      break;
 
1189
    case LAMP_WARNING:         // means node is down and not clustering
 
1190
      // coverity[non_const_printf_format_string]
 
1191
      snprintf(nodebuf, sizeof(nodebuf), nodeFormat, "DOWN");
 
1192
      break;
 
1193
      // no default:
 
1194
    }
 
1195
 
 
1196
    // Add Alarm status
 
1197
    if (alarm == true) {
 
1198
      // coverity[non_const_printf_format_string]
 
1199
      snprintf(alarmbuf, sizeof(alarmbuf), alarmFormat, "ALARM");
 
1200
    } else {
 
1201
      // coverity[non_const_printf_format_string]
 
1202
      snprintf(alarmbuf, sizeof(alarmbuf), alarmFormat, "-");
 
1203
    }
 
1204
 
 
1205
    // Add document count
 
1206
    docCount = current->readInteger("proxy.node.user_agents_total_documents_served", &found);
 
1207
 
 
1208
    if (false == found) {
 
1209
      docCount = 0;
 
1210
    }
 
1211
    // Transactions per/sec
 
1212
    if (up == true) {
 
1213
      loadMetric = (MgmtInt)
 
1214
        current->readFloat("proxy.node.user_agent_xacts_per_second", &found);
 
1215
    } else {
 
1216
      // Always report zero for down nodes
 
1217
      loadMetric = 0;
 
1218
      found = true;
 
1219
    }
 
1220
 
 
1221
    snprintf(docCountBuf, sizeof(docCountBuf), "%" PRId64 "", docCount);
 
1222
    snprintf(loadMetricBuf, sizeof(loadMetricBuf), "%" PRId64 "", loadMetric);
 
1223
 
 
1224
    // create output status line
 
1225
    // coverity[non_const_printf_format_string]
 
1226
    snprintf(tmpbuf, sizeof(tmpbuf), sformat, i, namebuf, nodebuf, alarmbuf, docCountBuf, loadMetricBuf);
 
1227
    output->copyFrom(tmpbuf, strlen(tmpbuf));
 
1228
 
 
1229
    output->copyFrom("\n", strlen("\n"));
 
1230
  }                             // end for(..)
 
1231
 
 
1232
  // Add the Virtual IP Panel in the more detailed display
 
1233
 
 
1234
  ink_mutex_release(&accessLock);
 
1235
}                               // end generateTableCLI()
 
1236
 
 
1237
// overviewPage::addHostPanel
 
1238
//
 
1239
//  Inserts stats entries for the host referenced by parameter host
 
1240
//  Called by overviewPage::generateTable
 
1241
//
 
1242
void
 
1243
overviewPage::addHostPanel(WebHttpContext * whc, overviewRecord * host)
 
1244
{
 
1245
 
 
1246
  const char errorStr[] = "loading...";
 
1247
  char tmp[256];
 
1248
  in_addr ip;
 
1249
  char *ip_str;
 
1250
 
 
1251
  textBuffer *output = whc->response_bdy;
 
1252
  MgmtHashTable *dict_ht = whc->lang_dict_ht;
 
1253
 
 
1254
  //-----------------------------------------------------------------------
 
1255
  // SET 1: CACHE TRANSACTION SUMMARY
 
1256
  //-----------------------------------------------------------------------
 
1257
 
 
1258
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1259
  HtmlRndrTdOpen(output, HTML_CSS_NONE, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 8);
 
1260
 
 
1261
  MgmtFloat hits, hit_f, hit_r;
 
1262
  MgmtFloat errs, abts, f;
 
1263
 
 
1264
  // get aborts
 
1265
  abts = 0;
 
1266
  if (host->varFloatFromName("proxy.node.http.transaction_frac_avg_10s.errors.pre_accept_hangups", &f))
 
1267
    abts += f;
 
1268
  if (host->varFloatFromName("proxy.node.http.transaction_frac_avg_10s.errors.empty_hangups", &f))
 
1269
    abts += f;
 
1270
  if (host->varFloatFromName("proxy.node.http.transaction_frac_avg_10s.errors.early_hangups", &f))
 
1271
    abts += f;
 
1272
  if (host->varFloatFromName("proxy.node.http.transaction_frac_avg_10s.errors.aborts", &f))
 
1273
    abts += f;
 
1274
 
 
1275
  // get errors
 
1276
  errs = 0;
 
1277
  if (host->varFloatFromName("proxy.node.http.transaction_frac_avg_10s.errors.connect_failed", &f))
 
1278
    errs += f;
 
1279
  if (host->varFloatFromName("proxy.node.http.transaction_frac_avg_10s.errors.other", &f))
 
1280
    errs += f;
 
1281
 
 
1282
  // get hits
 
1283
  hits = hit_f = hit_r = 0;
 
1284
  if (host->varFloatFromName("proxy.node.http.transaction_frac_avg_10s.hit_fresh", &hit_f))
 
1285
    hits += hit_f;
 
1286
  if (host->varFloatFromName("proxy.node.http.transaction_frac_avg_10s.hit_revalidated", &hit_r))
 
1287
    hits += hit_r;
 
1288
#ifndef OLD_WAY
 
1289
  host->varFloatFromName("proxy.node.cache_hit_ratio_avg_10s", &hits);
 
1290
#endif /* !OLD_WAY */
 
1291
 
 
1292
#define SEPARATOR output->copyFrom("&nbsp;-&nbsp;", 13)
 
1293
 
 
1294
  HtmlRndrTableOpen(output, NULL, 0, 0, 0);
 
1295
 
 
1296
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1297
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1298
  HtmlRndrText(output, dict_ht, HTML_ID_CACHE_HIT_RATE);
 
1299
  HtmlRndrTdClose(output);
 
1300
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1301
  SEPARATOR;
 
1302
  snprintf(tmp, sizeof(tmp), "%.1f%% (%.1f%% ", hits * 100.0, hit_f * 100.0);
 
1303
  output->copyFrom(tmp, strlen(tmp));
 
1304
  HtmlRndrText(output, dict_ht, HTML_ID_FRESH);
 
1305
  snprintf(tmp, sizeof(tmp), ", %.1f%% ", hit_r * 100.0);
 
1306
  output->copyFrom(tmp, strlen(tmp));
 
1307
  HtmlRndrText(output, dict_ht, HTML_ID_REFRESH);
 
1308
  output->copyFrom(")", 1);
 
1309
  HtmlRndrTdClose(output);
 
1310
  HtmlRndrTrClose(output);
 
1311
 
 
1312
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1313
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1314
  HtmlRndrText(output, dict_ht, HTML_ID_ERRORS);
 
1315
  HtmlRndrTdClose(output);
 
1316
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1317
  SEPARATOR;
 
1318
  snprintf(tmp, sizeof(tmp), "%.1f%%", errs * 100.0);
 
1319
  output->copyFrom(tmp, strlen(tmp));
 
1320
  HtmlRndrTdClose(output);
 
1321
  HtmlRndrTrClose(output);
 
1322
 
 
1323
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1324
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1325
  HtmlRndrText(output, dict_ht, HTML_ID_ABORTS);
 
1326
  HtmlRndrTdClose(output);
 
1327
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1328
  SEPARATOR;
 
1329
  snprintf(tmp, sizeof(tmp), "%.1f%%", abts * 100.0);
 
1330
  output->copyFrom(tmp, strlen(tmp));
 
1331
  HtmlRndrTdClose(output);
 
1332
  HtmlRndrTrClose(output);
 
1333
 
 
1334
  //-----------------------------------------------------------------------
 
1335
  // SET 2: ACTIVE CONNECTIONS
 
1336
  //-----------------------------------------------------------------------
 
1337
 
 
1338
  MgmtInt clients, servers;
 
1339
 
 
1340
  clients = servers = 0;
 
1341
 
 
1342
  host->varIntFromName("proxy.node.current_client_connections", &clients);
 
1343
  host->varIntFromName("proxy.node.current_server_connections", &servers);
 
1344
 
 
1345
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1346
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1347
  HtmlRndrText(output, dict_ht, HTML_ID_ACTIVE_CLIENTS);
 
1348
  HtmlRndrTdClose(output);
 
1349
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1350
  SEPARATOR;
 
1351
  snprintf(tmp, sizeof(tmp), "%" PRId64 "", clients);
 
1352
  output->copyFrom(tmp, strlen(tmp));
 
1353
  HtmlRndrTdClose(output);
 
1354
  HtmlRndrTrClose(output);
 
1355
 
 
1356
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1357
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1358
  HtmlRndrText(output, dict_ht, HTML_ID_ACTIVE_SERVERS);
 
1359
  HtmlRndrTdClose(output);
 
1360
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1361
  SEPARATOR;
 
1362
  snprintf(tmp, sizeof(tmp), "%" PRId64 "", servers);
 
1363
  output->copyFrom(tmp, strlen(tmp));
 
1364
  HtmlRndrTdClose(output);
 
1365
  HtmlRndrTrClose(output);
 
1366
 
 
1367
  //-----------------------------------------------------------------------
 
1368
  // SET 3: CLUSTER ADDRESS
 
1369
  //-----------------------------------------------------------------------
 
1370
 
 
1371
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1372
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1373
  HtmlRndrText(output, dict_ht, HTML_ID_NODE_IP_ADDRESS);
 
1374
  HtmlRndrTdClose(output);
 
1375
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1376
  SEPARATOR;
 
1377
  ip.s_addr = host->inetAddr;
 
1378
  ip_str = inet_ntoa(ip);
 
1379
  output->copyFrom(ip_str, strlen(ip_str));
 
1380
  HtmlRndrTdClose(output);
 
1381
  HtmlRndrTrClose(output);
 
1382
 
 
1383
  //-----------------------------------------------------------------------
 
1384
  // SET 4: TS Lite
 
1385
  //-----------------------------------------------------------------------
 
1386
 
 
1387
  if (host->varStrFromName("proxy.node.cache.bytes_free\\b", tmp, 256) == false) {
 
1388
    ink_strncpy(tmp, errorStr, sizeof(tmp));
 
1389
  }
 
1390
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1391
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1392
  HtmlRndrText(output, dict_ht, HTML_ID_CACHE_FREE_SPACE);
 
1393
  HtmlRndrTdClose(output);
 
1394
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1395
  SEPARATOR;
 
1396
  output->copyFrom(tmp, strlen(tmp));
 
1397
  HtmlRndrTdClose(output);
 
1398
  HtmlRndrTrClose(output);
 
1399
 
 
1400
  if (host->varStrFromName("proxy.node.hostdb.hit_ratio_avg_10s\\p", tmp, 256) == false) {
 
1401
    ink_strncpy(tmp, errorStr, sizeof(tmp));
 
1402
  }
 
1403
  HtmlRndrTrOpen(output, HTML_CSS_NONE, HTML_ALIGN_LEFT);
 
1404
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1405
  HtmlRndrText(output, dict_ht, HTML_ID_HOSTDB_HIT_RATE);
 
1406
  HtmlRndrTdClose(output);
 
1407
  HtmlRndrTdOpen(output, HTML_CSS_BODY_TEXT, HTML_ALIGN_NONE, HTML_VALIGN_NONE, NULL, NULL, 0);
 
1408
  SEPARATOR;
 
1409
  output->copyFrom(tmp, strlen(tmp));
 
1410
  HtmlRndrTdClose(output);
 
1411
  HtmlRndrTrClose(output);
 
1412
 
 
1413
  HtmlRndrTableClose(output);
 
1414
 
 
1415
  HtmlRndrTdClose(output);
 
1416
  HtmlRndrTrClose(output);
 
1417
 
 
1418
#undef SEPARATOR
 
1419
}
 
1420
#endif
 
1421
 
 
1422
// int overviewPage::getClusterHosts(Expanding Array* hosts)
 
1423
//
 
1424
//   The names of all the cluster members are inserted
 
1425
//     into parameter hosts.  The callee is responsible
 
1426
//     for freeing the strings
 
1427
//
 
1428
int
 
1429
overviewPage::getClusterHosts(ExpandingArray * hosts)
 
1430
{
 
1431
  int number = 0;
 
1432
 
 
1433
  overviewRecord *current;
 
1434
 
 
1435
  ink_mutex_acquire(&accessLock);
 
1436
  number = sortRecords.getNumEntries();
 
1437
 
 
1438
  for (int i = 0; i < number; i++) {
 
1439
    current = (overviewRecord *) sortRecords[i];
 
1440
    hosts->addEntry(xstrdup(current->hostname));
 
1441
  }
 
1442
 
 
1443
  ink_mutex_release(&accessLock);
 
1444
  return number;
 
1445
}
 
1446
 
 
1447
// overviewRecord* overviewPage::findNodeByName(const char* nodeName)
 
1448
//
 
1449
//   Returns a pointer to node name nodeName
 
1450
//     If node name is not found, returns NULL
 
1451
//
 
1452
//   CALLEE MUST BE HOLDING this->accessLock
 
1453
//
 
1454
overviewRecord *
 
1455
overviewPage::findNodeByName(const char *nodeName)
 
1456
{
 
1457
  overviewRecord *current = NULL;
 
1458
  bool nodeFound = false;
 
1459
 
 
1460
  // Do a linear search of the nodes for this nodeName.
 
1461
  //   Yes, I know this is slow but the current word is ten
 
1462
  //   nodes would be a huge cluster so this should not
 
1463
  //   be a problem
 
1464
  //
 
1465
  for (int i = 0; i < numHosts; i++) {
 
1466
    current = (overviewRecord *) sortRecords[i];
 
1467
    if (strcmp(nodeName, current->hostname) == 0) {
 
1468
      nodeFound = true;
 
1469
      break;
 
1470
    }
 
1471
  }
 
1472
 
 
1473
  if (nodeFound == true) {
 
1474
    return current;
 
1475
  } else {
 
1476
    return NULL;
 
1477
  }
 
1478
}
 
1479
 
 
1480
// MgmtString overviewPage::readString(const char* nodeName, char* *name, bool *found = NULL)
 
1481
//
 
1482
//   Looks up a node record for a specific by nodeName
 
1483
//    CALLEE deallocates the string with free()
 
1484
//
 
1485
MgmtString
 
1486
overviewPage::readString(const char *nodeName, const char *name, bool * found)
 
1487
{
 
1488
  MgmtString r = NULL;
 
1489
  //  bool nodeFound = false;
 
1490
  bool valueFound = false;
 
1491
  overviewRecord *node;
 
1492
 
 
1493
  ink_mutex_acquire(&accessLock);
 
1494
 
 
1495
  node = this->findNodeByName(nodeName);
 
1496
 
 
1497
  if (node != NULL) {
 
1498
    r = node->readString(name, &valueFound);
 
1499
  }
 
1500
  ink_mutex_release(&accessLock);
 
1501
 
 
1502
  if (found != NULL) {
 
1503
    *found = valueFound;
 
1504
  }
 
1505
 
 
1506
  return r;
 
1507
}
 
1508
 
 
1509
// MgmtInt overviewPage::readInteger(const char* nodeName, char* *name, bool *found = NULL)
 
1510
//
 
1511
//   Looks up a node record for a specific by nodeName
 
1512
//
 
1513
MgmtInt
 
1514
overviewPage::readInteger(const char *nodeName, const char *name, bool * found)
 
1515
{
 
1516
  MgmtInt r = -1;
 
1517
  //  bool nodeFound = false;
 
1518
  bool valueFound = false;
 
1519
  overviewRecord *node;
 
1520
 
 
1521
  ink_mutex_acquire(&accessLock);
 
1522
 
 
1523
  node = this->findNodeByName(nodeName);
 
1524
 
 
1525
  if (node != NULL) {
 
1526
    r = node->readInteger(name, &valueFound);
 
1527
  }
 
1528
  ink_mutex_release(&accessLock);
 
1529
 
 
1530
  if (found != NULL) {
 
1531
    *found = valueFound;
 
1532
  }
 
1533
 
 
1534
  return r;
 
1535
}
 
1536
 
 
1537
// MgmtFloat overviewPage::readFloat(const char* nodeName, char* *name, bool *found = NULL)
 
1538
//
 
1539
//   Looks up a node record for a specific by nodeName
 
1540
//
 
1541
RecFloat
 
1542
overviewPage::readFloat(const char *nodeName, const char *name, bool * found)
 
1543
{
 
1544
  RecFloat r = -1.0;
 
1545
  //  bool nodeFound = false;
 
1546
  bool valueFound = false;
 
1547
  overviewRecord *node;
 
1548
 
 
1549
  ink_mutex_acquire(&accessLock);
 
1550
 
 
1551
  node = this->findNodeByName(nodeName);
 
1552
 
 
1553
  if (node != NULL) {
 
1554
    r = node->readFloat(name, &valueFound);
 
1555
  }
 
1556
  ink_mutex_release(&accessLock);
 
1557
 
 
1558
  if (found != NULL) {
 
1559
    *found = valueFound;
 
1560
  }
 
1561
 
 
1562
  return r;
 
1563
}
 
1564
 
 
1565
// void overviewPage::agCachePercentFree()
 
1566
//
 
1567
//  Updates proxy.cluster.cache.percent_free
 
1568
//
 
1569
void
 
1570
overviewPage::agCachePercentFree()
 
1571
{
 
1572
  MgmtInt bTotal;
 
1573
  MgmtInt bFree;
 
1574
  MgmtFloat pFree;
 
1575
 
 
1576
  clusterSumInt("proxy.node.cache.bytes_total", &bTotal);
 
1577
  clusterSumInt("proxy.node.cache.bytes_free", &bFree);
 
1578
 
 
1579
  if (bTotal <= 0) {
 
1580
    pFree = 0.0;
 
1581
  } else {
 
1582
    pFree = (MgmtFloat) ((double) bFree / (double) bTotal);
 
1583
  }
 
1584
 
 
1585
  ink_assert(varSetFloat("proxy.cluster.cache.percent_free", pFree));
 
1586
}
 
1587
 
 
1588
// void overviewPage::agCacheHitRate()
 
1589
//
 
1590
//   Updates OLD proxy.cluster.http.cache_hit_ratio
 
1591
//               proxy.cluster.http.cache_total_hits
 
1592
//
 
1593
//           NEW proxy.cluster.cache_hit_ratio
 
1594
//               proxy.cluster.cache_total_hits
 
1595
//
 
1596
void
 
1597
overviewPage::agCacheHitRate()
 
1598
{
 
1599
  static ink_hrtime last_set_time = 0;
 
1600
  const ink_hrtime window = 10 * HRTIME_SECOND; // update every 10 seconds
 
1601
  static StatTwoIntSamples cluster_hit_count = { "proxy.node.cache_total_hits", 0, 0, 0, 0 };
 
1602
  static StatTwoIntSamples cluster_miss_count = { "proxy.node.cache_total_misses", 0, 0, 0, 0 };
 
1603
  static const char *cluster_hit_count_name = "proxy.cluster.cache_total_hits_avg_10s";
 
1604
  static const char *cluster_miss_count_name = "proxy.cluster.cache_total_misses_avg_10s";
 
1605
 
 
1606
  MgmtIntCounter totalHits = 0;
 
1607
  MgmtIntCounter totalMisses = 0;
 
1608
  MgmtIntCounter totalAccess = 0;
 
1609
  MgmtFloat hitRate = 0.00;
 
1610
 
 
1611
  // get current time and delta to work with
 
1612
  ink_hrtime current_time = ink_get_hrtime();
 
1613
  //  ink_hrtime delta = current_time - last_set_time;
 
1614
 
 
1615
  ///////////////////////////////////////////////////////////////
 
1616
  // if enough time expired, or first time, or wrapped around: //
 
1617
  //  (1) scroll current value into previous value             //
 
1618
  //  (2) calculate new current values                         //
 
1619
  //  (3) only if proper time expired, set derived values      //
 
1620
  ///////////////////////////////////////////////////////////////
 
1621
  if (((current_time - last_set_time) > window) ||      // sufficient elapsed time
 
1622
      (last_set_time == 0) ||   // first time
 
1623
      (last_set_time > current_time))   // wrapped around
 
1624
  {
 
1625
    ////////////////////////////////////////
 
1626
    // scroll values for cluster Hit/Miss //
 
1627
    ///////////////////////////////////////
 
1628
    cluster_hit_count.previous_time = cluster_hit_count.current_time;
 
1629
    cluster_hit_count.previous_value = cluster_hit_count.current_value;
 
1630
 
 
1631
    cluster_miss_count.previous_time = cluster_miss_count.current_time;
 
1632
    cluster_miss_count.previous_value = cluster_miss_count.current_value;
 
1633
 
 
1634
    //////////////////////////
 
1635
    // calculate new values //
 
1636
    //////////////////////////
 
1637
    cluster_hit_count.current_value = -10000;
 
1638
    cluster_hit_count.current_time = ink_get_hrtime();
 
1639
    // TODO: Should we check return value?
 
1640
    clusterSumInt(cluster_hit_count.lm_record_name, &(cluster_hit_count.current_value));
 
1641
 
 
1642
    cluster_miss_count.current_value = -10000;
 
1643
    cluster_miss_count.current_time = ink_get_hrtime();
 
1644
    // TODO: Should we check return value?
 
1645
    clusterSumInt(cluster_miss_count.lm_record_name, &(cluster_miss_count.current_value));
 
1646
 
 
1647
    ////////////////////////////////////////////////
 
1648
    // if not initial or wrap, set derived values //
 
1649
    ////////////////////////////////////////////////
 
1650
    if ((current_time - last_set_time) > window) {
 
1651
      RecInt num_hits = 0;
 
1652
      RecInt num_misses = 0;
 
1653
      RecInt diff = 0;
 
1654
      RecInt total = 0;
 
1655
      // generate time window deltas and sum
 
1656
      diff = cluster_hit_count.diff_value();
 
1657
      varSetInt(cluster_hit_count_name, diff);
 
1658
      num_hits = diff;
 
1659
 
 
1660
      diff = cluster_miss_count.diff_value();
 
1661
      varSetInt(cluster_miss_count_name, diff);
 
1662
      num_misses = diff;
 
1663
 
 
1664
      total = num_hits + num_misses;
 
1665
      if (total == 0)
 
1666
        hitRate = 0.00;
 
1667
      else
 
1668
        hitRate = (MgmtFloat) ((double) num_hits / (double) total);
 
1669
 
 
1670
      // Check if more than one cluster node
 
1671
      MgmtInt num_nodes;
 
1672
      varIntFromName("proxy.process.cluster.nodes", &num_nodes);
 
1673
      if (1 == num_nodes) {
 
1674
        // Only one node , so grab local value
 
1675
        varFloatFromName("proxy.node.cache_hit_ratio_avg_10s", &hitRate);
 
1676
      }
 
1677
      // new stat
 
1678
      varSetFloat("proxy.cluster.cache_hit_ratio_avg_10s", hitRate);
 
1679
    }
 
1680
    /////////////////////////////////////////////////
 
1681
    // done with a cycle, update the last_set_time //
 
1682
    /////////////////////////////////////////////////
 
1683
    last_set_time = current_time;
 
1684
  }
 
1685
  // Deal with Lifetime stats
 
1686
  clusterSumInt("proxy.node.cache_total_hits", &totalHits);
 
1687
  clusterSumInt("proxy.node.cache_total_misses", &totalMisses);
 
1688
  totalAccess = totalHits + totalMisses;
 
1689
 
 
1690
  if (totalAccess != 0) {
 
1691
    hitRate = (MgmtFloat) ((double) totalHits / (double) totalAccess);
 
1692
  }
 
1693
  // old stats
 
1694
  ink_assert(varSetFloat("proxy.cluster.http.cache_hit_ratio", hitRate));
 
1695
  ink_assert(varSetInt("proxy.cluster.http.cache_total_hits", totalHits));
 
1696
  ink_assert(varSetInt("proxy.cluster.http.cache_total_misses", totalMisses));
 
1697
 
 
1698
  // new stats
 
1699
  ink_assert(varSetFloat("proxy.cluster.cache_hit_ratio", hitRate));
 
1700
  ink_assert(varSetInt("proxy.cluster.cache_total_hits", totalHits));
 
1701
  ink_assert(varSetInt("proxy.cluster.cache_total_misses", totalMisses));
 
1702
}
 
1703
 
 
1704
// void overviewPage::agHostDBHitRate()
 
1705
//
 
1706
//   Updates proxy.cluster.hostdb.hit_ratio
 
1707
//
 
1708
void
 
1709
overviewPage::agHostdbHitRate()
 
1710
{
 
1711
  static ink_hrtime last_set_time = 0;
 
1712
  const ink_hrtime window = 10 * HRTIME_SECOND; // update every 10 seconds
 
1713
  static StatTwoIntSamples cluster_hostdb_total_lookups = { "proxy.node.hostdb.total_lookups", 0, 0, 0, 0 };
 
1714
  static StatTwoIntSamples cluster_hostdb_hits = { "proxy.node.hostdb.total_hits", 0, 0, 0, 0 };
 
1715
  static const char *cluster_hostdb_total_lookups_name = "proxy.cluster.hostdb.total_lookups_avg_10s";
 
1716
  static const char *cluster_hostdb_hits_name = "proxy.cluster.hostdb.total_hits_avg_10s";
 
1717
 
 
1718
  RecInt hostDBtotal = 0;
 
1719
  RecInt hostDBhits = 0;
 
1720
  //  RecInt hostDBmisses = 0;
 
1721
  RecInt dnsTotal = 0;
 
1722
  RecFloat hitRate = 0.00;
 
1723
 
 
1724
  // get current time and delta to work with
 
1725
  ink_hrtime current_time = ink_get_hrtime();
 
1726
  //  ink_hrtime delta = current_time - last_set_time;
 
1727
 
 
1728
  ///////////////////////////////////////////////////////////////
 
1729
  // if enough time expired, or first time, or wrapped around: //
 
1730
  //  (1) scroll current value into previous value             //
 
1731
  //  (2) calculate new current values                         //
 
1732
  //  (3) only if proper time expired, set derived values      //
 
1733
  ///////////////////////////////////////////////////////////////
 
1734
  if (((current_time - last_set_time) > window) ||      // sufficient elapsed time
 
1735
      (last_set_time == 0) ||   // first time
 
1736
      (last_set_time > current_time))   // wrapped around
 
1737
  {
 
1738
    ////////////////////////////////////////
 
1739
    // scroll values for cluster DNS //
 
1740
    ///////////////////////////////////////
 
1741
    cluster_hostdb_total_lookups.previous_time = cluster_hostdb_total_lookups.current_time;
 
1742
    cluster_hostdb_total_lookups.previous_value = cluster_hostdb_total_lookups.current_value;
 
1743
 
 
1744
    cluster_hostdb_hits.previous_time = cluster_hostdb_hits.current_time;
 
1745
    cluster_hostdb_hits.previous_value = cluster_hostdb_hits.current_value;
 
1746
 
 
1747
    //////////////////////////
 
1748
    // calculate new values //
 
1749
    //////////////////////////
 
1750
    cluster_hostdb_total_lookups.current_value = -10000;
 
1751
    cluster_hostdb_total_lookups.current_time = ink_get_hrtime();
 
1752
    // TODO: Should we check return value?
 
1753
    clusterSumInt(cluster_hostdb_total_lookups.lm_record_name, &(cluster_hostdb_total_lookups.current_value));
 
1754
 
 
1755
    cluster_hostdb_hits.current_value = -10000;
 
1756
    cluster_hostdb_hits.current_time = ink_get_hrtime();
 
1757
    // TODO: Should we check return value?
 
1758
    clusterSumInt(cluster_hostdb_hits.lm_record_name, &(cluster_hostdb_hits.current_value));
 
1759
 
 
1760
    ////////////////////////////////////////////////
 
1761
    // if not initial or wrap, set derived values //
 
1762
    ////////////////////////////////////////////////
 
1763
    if ((current_time - last_set_time) > window) {
 
1764
      MgmtInt num_total_lookups = 0;
 
1765
      MgmtInt num_hits = 0;
 
1766
      MgmtInt diff = 0;
 
1767
 
 
1768
      // generate time window deltas and sum
 
1769
      diff = cluster_hostdb_total_lookups.diff_value();
 
1770
      varSetInt(cluster_hostdb_total_lookups_name, diff);
 
1771
      num_total_lookups = diff;
 
1772
 
 
1773
      diff = cluster_hostdb_hits.diff_value();
 
1774
      varSetInt(cluster_hostdb_hits_name, diff);
 
1775
      num_hits = diff;
 
1776
 
 
1777
      if (num_total_lookups == 0)
 
1778
        hitRate = 0.00;
 
1779
      else
 
1780
        hitRate = (MgmtFloat) ((double) num_hits / (double) num_total_lookups);
 
1781
 
 
1782
      // Check if more than one cluster node
 
1783
      MgmtInt num_nodes;
 
1784
      varIntFromName("proxy.process.cluster.nodes", &num_nodes);
 
1785
      if (1 == num_nodes) {
 
1786
        // Only one node , so grab local value
 
1787
        varFloatFromName("proxy.node.hostdb.hit_ratio_avg_10s", &hitRate);
 
1788
      }
 
1789
      // new stat
 
1790
      varSetFloat("proxy.cluster.hostdb.hit_ratio_avg_10s", hitRate);
 
1791
    }
 
1792
    /////////////////////////////////////////////////
 
1793
    // done with a cycle, update the last_set_time //
 
1794
    /////////////////////////////////////////////////
 
1795
    last_set_time = current_time;
 
1796
  }
 
1797
 
 
1798
  // Deal with Lifetime stats
 
1799
  clusterSumInt("proxy.node.hostdb.total_lookups", &hostDBtotal);
 
1800
  clusterSumInt("proxy.node.dns.total_dns_lookups", &dnsTotal);
 
1801
  clusterSumInt("proxy.node.hostdb.total_hits", &hostDBhits);
 
1802
 
 
1803
  if (hostDBtotal != 0) {
 
1804
    if (hostDBhits < 0) {
 
1805
      hostDBhits = 0;
 
1806
      mgmt_log(stderr, "truncating hit_ratio from %d to 0\n", hostDBhits);
 
1807
    }
 
1808
    hitRate = (MgmtFloat) ((double) hostDBhits / (double) hostDBtotal);
 
1809
  } else {
 
1810
    hitRate = 0.0;
 
1811
  }
 
1812
 
 
1813
  ink_assert(hitRate >= 0.0);
 
1814
  ink_assert(varSetFloat("proxy.cluster.hostdb.hit_ratio", hitRate));
 
1815
}
 
1816
 
 
1817
// void overviewPage::agBandwidthHitRate()
 
1818
//
 
1819
//   Updates proxy.cluster.http.bandwidth_hit_ratio
 
1820
//
 
1821
void
 
1822
overviewPage::agBandwidthHitRate()
 
1823
{
 
1824
  static ink_hrtime last_set_time = 0;
 
1825
  const ink_hrtime window = 10 * HRTIME_SECOND; // update every 10 seconds
 
1826
  static StatTwoIntSamples cluster_ua_total_bytes = { "proxy.node.user_agent_total_bytes", 0, 0, 0, 0 };
 
1827
  static StatTwoIntSamples cluster_os_total_bytes = { "proxy.node.origin_server_total_bytes", 0, 0, 0, 0 };
 
1828
  static const char *cluster_ua_total_bytes_name = "proxy.cluster.user_agent_total_bytes_avg_10s";
 
1829
  static const char *cluster_os_total_bytes_name = "proxy.cluster.origin_server_total_bytes_avg_10s";
 
1830
 
 
1831
  MgmtInt bytes;
 
1832
  MgmtInt UA_total = 0;         // User Agent total
 
1833
  MgmtInt OSPP_total = 0;       // Origin Server and Parent Proxy(?)
 
1834
  MgmtFloat hitRate;
 
1835
  MgmtInt totalHits = 0;
 
1836
  MgmtInt cacheOn = 1;          // on by default
 
1837
  MgmtInt httpCacheOn;
 
1838
 
 
1839
  // See if cache is on
 
1840
  ink_assert(varIntFromName("proxy.config.http.cache.http", &httpCacheOn));
 
1841
  cacheOn = httpCacheOn;
 
1842
 
 
1843
  // Get total cluster hits first, only calculate bandwith if > 0
 
1844
  varIntFromName("proxy.cluster.http.cache_total_hits", &totalHits);
 
1845
 
 
1846
  // User Agent
 
1847
 
 
1848
  // HTTP
 
1849
  varIntFromName("proxy.cluster.http.user_agent_total_request_bytes", &bytes);
 
1850
  UA_total += bytes;
 
1851
  varIntFromName("proxy.cluster.http.user_agent_total_response_bytes", &bytes);
 
1852
  UA_total += bytes;
 
1853
 
 
1854
  // HTTP
 
1855
  varIntFromName("proxy.cluster.http.origin_server_total_request_bytes", &bytes);
 
1856
  OSPP_total += bytes;
 
1857
  varIntFromName("proxy.cluster.http.origin_server_total_response_bytes", &bytes);
 
1858
  OSPP_total += bytes;
 
1859
  varIntFromName("proxy.cluster.http.parent_proxy_total_request_bytes", &bytes);
 
1860
  OSPP_total += bytes;
 
1861
  varIntFromName("proxy.cluster.http.parent_proxy_total_response_bytes", &bytes);
 
1862
  OSPP_total += bytes;
 
1863
 
 
1864
  // Special negative bandwidth scenario is treated here
 
1865
  // See (Bug INKqa03094) and Ag_Bytes() in 'StatAggregation.cc'
 
1866
  bool setBW = true;
 
1867
  if (UA_total != 0 && totalHits && cacheOn) {
 
1868
    hitRate = ((double) UA_total - (double) OSPP_total) / (double) UA_total;
 
1869
    if (hitRate < 0.0)
 
1870
      setBW = false;            // negative bandwidth scenario....
 
1871
  } else {
 
1872
    hitRate = 0.0;
 
1873
  }
 
1874
 
 
1875
  if (setBW) {
 
1876
    // old stat
 
1877
    ink_assert(varSetFloat("proxy.cluster.http.bandwidth_hit_ratio", hitRate));
 
1878
 
 
1879
    // new stat
 
1880
    ink_assert(varSetFloat("proxy.cluster.bandwidth_hit_ratio", hitRate));
 
1881
  }
 
1882
  // get current time and delta to work with
 
1883
  ink_hrtime current_time = ink_get_hrtime();
 
1884
  //  ink_hrtime delta = current_time - last_set_time;
 
1885
 
 
1886
  ///////////////////////////////////////////////////////////////
 
1887
  // if enough time expired, or first time, or wrapped around: //
 
1888
  //  (1) scroll current value into previous value             //
 
1889
  //  (2) calculate new current values                         //
 
1890
  //  (3) only if proper time expired, set derived values      //
 
1891
  ///////////////////////////////////////////////////////////////
 
1892
  if (((current_time - last_set_time) > window) ||      // sufficient elapsed time
 
1893
      (last_set_time == 0) ||   // first time
 
1894
      (last_set_time > current_time))   // wrapped around
 
1895
  {
 
1896
    ////////////////////////////////////////
 
1897
    // scroll values for node UA/OS bytes //
 
1898
    ///////////////////////////////////////
 
1899
    cluster_ua_total_bytes.previous_time = cluster_ua_total_bytes.current_time;
 
1900
    cluster_ua_total_bytes.previous_value = cluster_ua_total_bytes.current_value;
 
1901
 
 
1902
    cluster_os_total_bytes.previous_time = cluster_os_total_bytes.current_time;
 
1903
    cluster_os_total_bytes.previous_value = cluster_os_total_bytes.current_value;
 
1904
 
 
1905
    //////////////////////////
 
1906
    // calculate new values //
 
1907
    //////////////////////////
 
1908
    cluster_ua_total_bytes.current_value = -10000;
 
1909
    cluster_ua_total_bytes.current_time = ink_get_hrtime();
 
1910
    // TODO: Should we check return value?
 
1911
    clusterSumInt(cluster_ua_total_bytes.lm_record_name, &(cluster_ua_total_bytes.current_value));
 
1912
 
 
1913
    cluster_os_total_bytes.current_value = -10000;
 
1914
    cluster_os_total_bytes.current_time = ink_get_hrtime();
 
1915
    // TODO: Should we check return value?
 
1916
    clusterSumInt(cluster_os_total_bytes.lm_record_name, &(cluster_os_total_bytes.current_value));
 
1917
 
 
1918
    ////////////////////////////////////////////////
 
1919
    // if not initial or wrap, set derived values //
 
1920
    ////////////////////////////////////////////////
 
1921
    if ((current_time - last_set_time) > window) {
 
1922
      RecInt num_ua_total = 0;
 
1923
      RecInt num_os_total = 0;
 
1924
      RecInt diff = 0;
 
1925
 
 
1926
      // generate time window deltas and sum
 
1927
      diff = cluster_ua_total_bytes.diff_value();
 
1928
      varSetInt(cluster_ua_total_bytes_name, diff);
 
1929
      num_ua_total = diff;
 
1930
 
 
1931
      diff = cluster_os_total_bytes.diff_value();
 
1932
      varSetInt(cluster_os_total_bytes_name, diff);
 
1933
      num_os_total = diff;
 
1934
 
 
1935
      if (num_ua_total == 0 || (num_ua_total < num_os_total))
 
1936
        hitRate = 0.00;
 
1937
      else
 
1938
        hitRate = (MgmtFloat) (((double) num_ua_total - (double) num_os_total) / (double) num_ua_total);
 
1939
 
 
1940
      // Check if more than one cluster node
 
1941
      MgmtInt num_nodes;
 
1942
      varIntFromName("proxy.process.cluster.nodes", &num_nodes);
 
1943
      if (1 == num_nodes) {
 
1944
        // Only one node , so grab local value
 
1945
        varFloatFromName("proxy.node.bandwidth_hit_ratio_avg_10s", &hitRate);
 
1946
      }
 
1947
      // new stat
 
1948
      varSetFloat("proxy.cluster.bandwidth_hit_ratio_avg_10s", hitRate);
 
1949
    }
 
1950
    /////////////////////////////////////////////////
 
1951
    // done with a cycle, update the last_set_time //
 
1952
    /////////////////////////////////////////////////
 
1953
    last_set_time = current_time;
 
1954
  }
 
1955
 
 
1956
}                               // end overviewPage::agBandwidthHitRate()
 
1957
 
 
1958
// int overviewPage::clusterSumInt(char* nodeVar, MgmtInt* sum)
 
1959
//
 
1960
//   Sums nodeVar for every up node in the cluster and stores the
 
1961
//     sum in *sum.  Returns the number of nodes summed over
 
1962
//
 
1963
//   CALLEE MUST HOLD this->accessLock
 
1964
//
 
1965
int
 
1966
overviewPage::clusterSumInt(const char *nodeVar, RecInt * sum)
 
1967
{
 
1968
  int numUsed = 0;
 
1969
  int numHosts_local = sortRecords.getNumEntries();
 
1970
  overviewRecord *current;
 
1971
  bool found;
 
1972
 
 
1973
  ink_assert(sum != NULL);
 
1974
  *sum = 0;
 
1975
 
 
1976
  for (int i = 0; i < numHosts_local; i++) {
 
1977
    current = (overviewRecord *) sortRecords[i];
 
1978
    if (current->up == true) {
 
1979
      numUsed++;
 
1980
      *sum += current->readInteger(nodeVar, &found);
 
1981
      if (found == false) {
 
1982
      }
 
1983
    }
 
1984
  }
 
1985
 
 
1986
  return numUsed;
 
1987
}
 
1988
 
 
1989
//
 
1990
//   Updates proxy.cluster.current_client_connections
 
1991
//   Updates proxy.cluster.current_server_connections
 
1992
//   Updates proxy.cluster.current_cache_connections
 
1993
//
 
1994
void
 
1995
overviewPage::agConnections()
 
1996
{
 
1997
  MgmtInt client_conn = 0;
 
1998
  MgmtInt server_conn = 0;
 
1999
  MgmtInt cache_conn = 0;
 
2000
 
 
2001
  clusterSumInt("proxy.node.current_client_connections", &client_conn);
 
2002
  clusterSumInt("proxy.node.current_server_connections", &server_conn);
 
2003
  clusterSumInt("proxy.node.current_cache_connections", &cache_conn);
 
2004
 
 
2005
  ink_assert(varSetInt("proxy.cluster.current_client_connections", client_conn));
 
2006
  ink_assert(varSetInt("proxy.cluster.current_server_connections", server_conn));
 
2007
  ink_assert(varSetInt("proxy.cluster.current_cache_connections", cache_conn));
 
2008
}
 
2009
 
 
2010
// void overviewPage::clusterAgInt(const char* clusterVar, const char* nodeVar)
 
2011
//
 
2012
//   Updates clusterVar with the sum of nodeVar for every node in the
 
2013
//      cluster
 
2014
//   CALLEE MUST HOLD this->accessLock
 
2015
//
 
2016
void
 
2017
overviewPage::clusterAgInt(const char *clusterVar, const char *nodeVar)
 
2018
{
 
2019
  int numUsed = 0;
 
2020
  MgmtInt sum = 0;
 
2021
 
 
2022
  numUsed = clusterSumInt(nodeVar, &sum);
 
2023
  if (numUsed > 0) {
 
2024
    ink_assert(varSetInt(clusterVar, sum));
 
2025
  }
 
2026
}
 
2027
 
 
2028
void
 
2029
overviewPage::clusterAgIntScale(const char *clusterVar, const char *nodeVar, double factor)
 
2030
{
 
2031
  int numUsed = 0;
 
2032
  RecInt sum = 0;
 
2033
 
 
2034
  numUsed = clusterSumInt(nodeVar, &sum);
 
2035
  if (numUsed > 0) {
 
2036
    sum = (int) (sum * factor);
 
2037
    ink_assert(varSetInt(clusterVar, sum));
 
2038
  }
 
2039
}
 
2040
 
 
2041
// int overviewPage::clusterSumCounter(char* nodeVar, MgmtIntCounter* sum)
 
2042
//
 
2043
//   Sums nodeVar for every up node in the cluster and stores the
 
2044
//     sum in *sum.  Returns the number of nodes summed over
 
2045
//
 
2046
//   CALLEE MUST HOLD this->accessLock
 
2047
//
 
2048
int
 
2049
overviewPage::clusterSumCounter(char *nodeVar, RecInt * sum)
 
2050
{
 
2051
  int numUsed = 0;
 
2052
  int numHosts_local = sortRecords.getNumEntries();
 
2053
  overviewRecord *current;
 
2054
  bool found;
 
2055
 
 
2056
  ink_assert(sum != NULL);
 
2057
  *sum = 0;
 
2058
 
 
2059
  for (int i = 0; i < numHosts_local; i++) {
 
2060
    current = (overviewRecord *) sortRecords[i];
 
2061
    if (current->up == true) {
 
2062
      numUsed++;
 
2063
      *sum += current->readCounter(nodeVar, &found);
 
2064
      if (found == false) {
 
2065
      }
 
2066
    }
 
2067
  }
 
2068
 
 
2069
  return numUsed;
 
2070
}
 
2071
 
 
2072
// int overviewPage::clusterSumFloat(char* nodeVar, MgmtFloat* sum)
 
2073
//
 
2074
//   Sums nodeVar for every up node in the cluster and stores the
 
2075
//     sum in *sum.  Returns the number of nodes summed over
 
2076
//
 
2077
//   CALLEE MUST HOLD this->accessLock
 
2078
//
 
2079
int
 
2080
overviewPage::clusterSumFloat(const char *nodeVar, RecFloat * sum)
 
2081
{
 
2082
  int numUsed = 0;
 
2083
  int numHosts_local = sortRecords.getNumEntries();
 
2084
  overviewRecord *current;
 
2085
  bool found;
 
2086
 
 
2087
  ink_assert(sum != NULL);
 
2088
  *sum = 0.00;
 
2089
 
 
2090
  for (int i = 0; i < numHosts_local; i++) {
 
2091
    current = (overviewRecord *) sortRecords[i];
 
2092
    if (current->up == true) {
 
2093
      numUsed++;
 
2094
      *sum += current->readFloat(nodeVar, &found);
 
2095
      if (found == false) {
 
2096
      }
 
2097
    }
 
2098
  }
 
2099
  return numUsed;
 
2100
}
 
2101
 
 
2102
 
 
2103
// void overviewPage::clusterAgFloat(const char* clusterVar, const char* nodeVar)
 
2104
//
 
2105
//
 
2106
//   Sums nodeVar for every up node in the cluster and stores the
 
2107
//     sum in sumVar
 
2108
//
 
2109
//   CALLEE MUST HOLD this->accessLock
 
2110
//
 
2111
void
 
2112
overviewPage::clusterAgFloat(const char *clusterVar, const char *nodeVar)
 
2113
{
 
2114
  int numUsed = 0;
 
2115
  MgmtFloat sum = 0;
 
2116
 
 
2117
  numUsed = clusterSumFloat(nodeVar, &sum);
 
2118
 
 
2119
  if (numUsed > 0) {
 
2120
    ink_assert(varSetFloat(clusterVar, sum));
 
2121
  }
 
2122
}
 
2123
 
 
2124
// int varClusterIntFromName(char* nodeVar, MgmtInt* sum)
 
2125
//   External accessible wrapper for clusterIntFloat.
 
2126
//   It acquires the overveiw access lock, calls the
 
2127
//   clusterIntFloat function and then releases the lock.
 
2128
int
 
2129
overviewPage::varClusterIntFromName(char *nodeVar, RecInt * sum)
 
2130
{
 
2131
  ink_mutex_acquire(&accessLock);
 
2132
  int status = 0;
 
2133
  RecDataT varDataType;
 
2134
  RecInt tempInt = 0;
 
2135
  RecFloat tempFloat = 0.0;
 
2136
 
 
2137
  RecGetRecordDataType(nodeVar, &varDataType);
 
2138
 
 
2139
  if (varDataType == RECD_INT) {
 
2140
    status = clusterSumInt(nodeVar, &tempInt);
 
2141
    tempFloat = (RecFloat) tempInt;
 
2142
  } else if (varDataType == RECD_FLOAT) {
 
2143
    status = clusterSumFloat(nodeVar, &tempFloat);
 
2144
  }
 
2145
  *sum = (RecInt) tempFloat;
 
2146
  ink_mutex_release(&accessLock);
 
2147
  return (status);
 
2148
}
 
2149
 
 
2150
int
 
2151
overviewPage::varClusterFloatFromName(char *nodeVar, RecFloat * sum)
 
2152
{
 
2153
  ink_mutex_acquire(&accessLock);
 
2154
  int status = 0;
 
2155
  RecDataT varDataType;
 
2156
  RecInt tempInt = 0;
 
2157
  RecFloat tempFloat = 0.0;
 
2158
 
 
2159
  RecGetRecordDataType(nodeVar, &varDataType);
 
2160
 
 
2161
  if (varDataType == RECD_INT) {
 
2162
    status = clusterSumInt(nodeVar, &tempInt);
 
2163
    tempFloat = (RecFloat) tempInt;
 
2164
  } else if (varDataType == RECD_FLOAT) {
 
2165
    status = clusterSumFloat(nodeVar, &tempFloat);
 
2166
  }
 
2167
 
 
2168
  *sum = tempFloat;
 
2169
  ink_mutex_release(&accessLock);
 
2170
  return (status);
 
2171
}
 
2172
 
 
2173
int
 
2174
overviewPage::varClusterCounterFromName(char *nodeVar, RecInt * sum)
 
2175
{
 
2176
  int status;
 
2177
  ink_mutex_acquire(&accessLock);
 
2178
  status = clusterSumCounter(nodeVar, sum);
 
2179
  ink_mutex_release(&accessLock);
 
2180
  return (status);
 
2181
}
 
2182
 
 
2183
// void overviewPage::doClusterAg()
 
2184
//
 
2185
//   Aggregate data for cluster records
 
2186
//
 
2187
void
 
2188
overviewPage::doClusterAg()
 
2189
{
 
2190
 
 
2191
  ink_mutex_acquire(&accessLock);
 
2192
 
 
2193
 
 
2194
  // DNS
 
2195
  clusterAgFloat("proxy.cluster.dns.lookups_per_second", "proxy.node.dns.lookups_per_second");
 
2196
  clusterAgInt("proxy.cluster.dns.total_dns_lookups", "proxy.node.dns.total_dns_lookups");
 
2197
  // HTTP
 
2198
  clusterAgInt("proxy.cluster.http.throughput", "proxy.node.http.throughput");
 
2199
 
 
2200
  clusterAgFloat("proxy.cluster.http.user_agent_xacts_per_second", "proxy.node.http.user_agent_xacts_per_second");
 
2201
 
 
2202
  clusterAgInt("proxy.cluster.http.user_agent_current_connections_count",
 
2203
               "proxy.node.http.user_agent_current_connections_count");
 
2204
  clusterAgInt("proxy.cluster.http.origin_server_current_connections_count",
 
2205
               "proxy.node.http.origin_server_current_connections_count");
 
2206
  clusterAgInt("proxy.cluster.http.cache_current_connections_count", "proxy.node.http.cache_current_connections_count");
 
2207
 
 
2208
  clusterAgInt("proxy.cluster.http.current_parent_proxy_connections",
 
2209
               "proxy.node.http.current_parent_proxy_connections");
 
2210
 
 
2211
  clusterAgInt("proxy.cluster.http.user_agent_total_request_bytes", "proxy.node.http.user_agent_total_request_bytes");
 
2212
  clusterAgInt("proxy.cluster.http.user_agent_total_response_bytes", "proxy.node.http.user_agent_total_response_bytes");
 
2213
  clusterAgInt("proxy.cluster.http.origin_server_total_request_bytes",
 
2214
               "proxy.node.http.origin_server_total_request_bytes");
 
2215
  clusterAgInt("proxy.cluster.http.origin_server_total_response_bytes",
 
2216
               "proxy.node.http.origin_server_total_response_bytes");
 
2217
  clusterAgInt("proxy.cluster.http.parent_proxy_total_request_bytes",
 
2218
               "proxy.node.http.parent_proxy_total_request_bytes");
 
2219
  clusterAgInt("proxy.cluster.http.parent_proxy_total_response_bytes",
 
2220
               "proxy.node.http.parent_proxy_total_response_bytes");
 
2221
 
 
2222
  clusterAgInt("proxy.cluster.http.user_agents_total_transactions_count",
 
2223
               "proxy.node.http.user_agents_total_transactions_count");
 
2224
  clusterAgInt("proxy.cluster.http.user_agents_total_documents_served",
 
2225
               "proxy.node.http.user_agents_total_documents_served");
 
2226
  clusterAgInt("proxy.cluster.http.origin_server_total_transactions_count",
 
2227
               "proxy.node.http.origin_server_total_transactions_count");
 
2228
 
 
2229
  // Cache
 
2230
  clusterAgInt("proxy.cluster.cache.bytes_free", "proxy.node.cache.bytes_free");
 
2231
  clusterAgIntScale("proxy.cluster.cache.bytes_free_mb", "proxy.node.cache.bytes_free", MB_SCALE);
 
2232
  clusterAgInt("proxy.cluster.cache.contents.num_docs", "proxy.node.cache.contents.num_docs");
 
2233
 
 
2234
  this->agHostdbHitRate();
 
2235
  this->agCacheHitRate();
 
2236
  this->agCachePercentFree();
 
2237
  this->agBandwidthHitRate();
 
2238
  this->agConnections();
 
2239
 
 
2240
  // Overall
 
2241
  clusterAgFloat("proxy.cluster.client_throughput_out", "proxy.node.client_throughput_out");
 
2242
 
 
2243
  // FIXME
 
2244
  clusterAgFloat("proxy.cluster.user_agent_xacts_per_second", "proxy.node.user_agent_xacts_per_second");
 
2245
 
 
2246
  AgFloat_generic_scale_to_int("proxy.cluster.client_throughput_out",
 
2247
                               "proxy.cluster.client_throughput_out_kbit", MBIT_TO_KBIT_SCALE);
 
2248
  AgFloat_generic_scale_to_int("proxy.cluster.http.cache_hit_ratio",
 
2249
                               "proxy.cluster.http.cache_hit_ratio_int_pct", PCT_TO_INTPCT_SCALE);
 
2250
  AgFloat_generic_scale_to_int("proxy.cluster.cache_hit_ratio",
 
2251
                               "proxy.cluster.cache_hit_ratio_int_pct", PCT_TO_INTPCT_SCALE);
 
2252
  AgFloat_generic_scale_to_int("proxy.cluster.http.bandwidth_hit_ratio",
 
2253
                               "proxy.cluster.http.bandwidth_hit_ratio_int_pct", PCT_TO_INTPCT_SCALE);
 
2254
  AgFloat_generic_scale_to_int("proxy.cluster.bandwidth_hit_ratio",
 
2255
                               "proxy.cluster.bandwidth_hit_ratio_int_pct", PCT_TO_INTPCT_SCALE);
 
2256
  AgFloat_generic_scale_to_int("proxy.cluster.hostdb.hit_ratio",
 
2257
                               "proxy.cluster.hostdb.hit_ratio_int_pct", PCT_TO_INTPCT_SCALE);
 
2258
  AgFloat_generic_scale_to_int("proxy.cluster.cache.percent_free",
 
2259
                               "proxy.cluster.cache.percent_free_int_pct", PCT_TO_INTPCT_SCALE);
 
2260
  ink_mutex_release(&accessLock);
 
2261
}
 
2262
 
 
2263
// char* overviewPage::resolvePeerHostname(char* peerIP)
 
2264
//
 
2265
//   A locking interface to overviewPage::resolvePeerHostname_ml
 
2266
//
 
2267
char *
 
2268
overviewPage::resolvePeerHostname(const char *peerIP)
 
2269
{
 
2270
  char *r;
 
2271
 
 
2272
  ink_mutex_acquire(&accessLock);
 
2273
  r = this->resolvePeerHostname_ml(peerIP);
 
2274
  ink_mutex_release(&accessLock);
 
2275
 
 
2276
  return r;
 
2277
}
 
2278
 
 
2279
// char* overviewPage::resolvePeerHostname_ml(char* peerIP)
 
2280
//
 
2281
// Resolves the peer the hostname from its IP address
 
2282
//   The hostname is resolved by finding the overviewRecord
 
2283
//   Associated with the IP address and copying its hostname
 
2284
//
 
2285
// CALLEE frees storage
 
2286
// CALLEE is responsible for locking
 
2287
//
 
2288
char *
 
2289
overviewPage::resolvePeerHostname_ml(const char *peerIP)
 
2290
{
 
2291
  unsigned long int ipAddr;
 
2292
  InkHashTableValue lookup;
 
2293
  overviewRecord *peerRecord;
 
2294
  char *returnName = NULL;
 
2295
 
 
2296
  ipAddr = inet_addr(peerIP);
 
2297
 
 
2298
  // Check to see if our address is malformed
 
2299
  if ((long int) ipAddr == -1) {
 
2300
    return NULL;
 
2301
  }
 
2302
 
 
2303
  if (ink_hash_table_lookup(nodeRecords, (InkHashTableKey) ipAddr, &lookup)) {
 
2304
    peerRecord = (overviewRecord *) lookup;
 
2305
    returnName = xstrdup(peerRecord->hostname);
 
2306
  }
 
2307
 
 
2308
  return returnName;
 
2309
}
 
2310
 
 
2311
// resolveAlarm
 
2312
//
 
2313
//   Handles the form submission for alarm resolution
 
2314
//   uses the form arguments to call resolveAlarm.
 
2315
//
 
2316
//   Takes a hash-table returned by processFormSubmission
 
2317
//
 
2318
//   Note: resolving an alarm is asyncronous with the list of
 
2319
//      alarms maintained in overviewRecords.  That list
 
2320
//      is only updates when checkAlarms is called
 
2321
//
 
2322
void
 
2323
resolveAlarm(InkHashTable * post_data_ht)
 
2324
{
 
2325
 
 
2326
  InkHashTableIteratorState htis;
 
2327
  InkHashTableEntry *hte;
 
2328
  char *name;
 
2329
  char *value;
 
2330
  Tokenizer colonTok(":");
 
2331
  const char *ipAddr;
 
2332
  alarm_t alarmType;
 
2333
 
 
2334
  for (hte = ink_hash_table_iterator_first(post_data_ht, &htis);
 
2335
       hte != NULL; hte = ink_hash_table_iterator_next(post_data_ht, &htis)) {
 
2336
    name = (char *) ink_hash_table_entry_key(post_data_ht, hte);
 
2337
    value = (char *) ink_hash_table_entry_value(post_data_ht, hte);
 
2338
    if (strncmp(name, "alarm:", 6) != 0)
 
2339
      continue;
 
2340
    if (colonTok.Initialize(value) == 2) {
 
2341
      alarmType = atoi(colonTok[0]);
 
2342
      ipAddr = colonTok[1];
 
2343
      Debug("dashboard", "Resolving alarm %d for %s\n", alarmType, ipAddr);
 
2344
      if (strcmp("local", ipAddr) == 0)
 
2345
        ipAddr = NULL;
 
2346
      if (lmgmt->alarm_keeper->isCurrentAlarm(alarmType, (char *) ipAddr)) {
 
2347
        Debug("dashboard", "\t Before resolution the alarm is current\n");
 
2348
      } else {
 
2349
        Debug("dashboard", "\t Before resolution the alarm is NOT current\n");
 
2350
      }
 
2351
      lmgmt->alarm_keeper->resolveAlarm(alarmType, (char *) ipAddr);
 
2352
      if (lmgmt->alarm_keeper->isCurrentAlarm(alarmType, (char *) ipAddr)) {
 
2353
        Debug("dashboard", "\t After resolution the alarm is current\n");
 
2354
      } else {
 
2355
        Debug("dashboard", "\t After resolution the alarm is NOT current\n");
 
2356
      }
 
2357
    }
 
2358
  }
 
2359
  overviewGenerator->checkForUpdates();
 
2360
}
 
2361
 
 
2362
void
 
2363
resolveAlarmCLI(textBuffer * output, const char *request)
 
2364
{
 
2365
  NOWARN_UNUSED(output);
 
2366
  Tokenizer colonTok(":");
 
2367
  const char *ipAddr = NULL;
 
2368
  alarm_t alarmType;
 
2369
 
 
2370
  // Get ipAddr of host to resolve alarm for
 
2371
  // request is in form 'alarmType:ipAddr'
 
2372
  if (request && colonTok.Initialize(request) == 2) {
 
2373
    alarmType = atoi(colonTok[0]);
 
2374
    ipAddr = colonTok[1];
 
2375
 
 
2376
    Debug("cli", "resolveAlarmCLI: Resolving alarm %d for %s\n", alarmType, ipAddr);
 
2377
 
 
2378
    if (strcmp("local", ipAddr) == 0)
 
2379
      ipAddr = NULL;
 
2380
 
 
2381
    if (lmgmt->alarm_keeper->isCurrentAlarm(alarmType, (char *) ipAddr)) {
 
2382
      Debug("cli", "\t Before resolution the alarm is current\n");
 
2383
    } else {
 
2384
      Debug("cli", "\t Before resolution the alarm is NOT current\n");
 
2385
    }
 
2386
 
 
2387
    lmgmt->alarm_keeper->resolveAlarm(alarmType, (char *) ipAddr);
 
2388
 
 
2389
    if (lmgmt->alarm_keeper->isCurrentAlarm(alarmType, (char *) ipAddr)) {
 
2390
      Debug("cli", "\t After resolution the alarm is current\n");
 
2391
    } else {
 
2392
      Debug("cli", "\t After resolution the alarm is NOT current\n");
 
2393
    }
 
2394
  }
 
2395
 
 
2396
  overviewGenerator->checkForUpdates();
 
2397
}                               // end resolveAlarmCLI()
 
2398
 
 
2399
//   wrapper for the Alarm Callback
 
2400
void
 
2401
overviewAlarmCallback(alarm_t newAlarm, char *ip, char *desc)
 
2402
{
 
2403
  overviewGenerator->addAlarm(newAlarm, ip, desc);
 
2404
}
 
2405
 
 
2406
AlarmListable::~AlarmListable()
 
2407
{
 
2408
  if (ip != NULL) {
 
2409
    xfree(ip);
 
2410
  }
 
2411
 
 
2412
  if (desc != NULL) {
 
2413
    xfree(desc);
 
2414
  }
 
2415
}
 
2416
 
 
2417
// int hostSortFunc(const void* arg1, const void* arg2)
 
2418
//
 
2419
//   A compare function that we can to qsort that sorts
 
2420
//    overviewRecord*
 
2421
//
 
2422
int
 
2423
hostSortFunc(const void *arg1, const void *arg2)
 
2424
{
 
2425
  overviewRecord *rec1 = (overviewRecord *) * (void **) arg1;
 
2426
  overviewRecord *rec2 = (overviewRecord *) * (void **) arg2;
 
2427
 
 
2428
  return strcmp(rec1->hostname, rec2->hostname);
 
2429
}