Log MSSQL Server Instance CPU usage

Here is a simple PowerShell script for logging CPU usage of each SQL instance on a server to InfluxDB. Server hostname and SQL Instance name are tags, the rest are fields

# Build Uri 
$uri = "http://10.10.10.1:8086/write?db=telegraf" 

[array]$SQLData = Get-WmiObject Win32_Process -Filter "Name = 'sqlservr.exe'" | select ProcessId, commandline

$processor_time = (((Get-Counter '\Process(sqlservr*)\% processor time' -ErrorAction SilentlyContinue -MaxSamples 1 -Sampleinterval 1).Countersamples)) | select -Property Path,InstanceName,CookedValue,Status
$regex = "^[^:]+?_\s*"
$regex2 = "_([^/]+)$"
$hostname = hostname
[array]$SQL_Name = ""
[array]$SQL_PID = ""

#loop through all SQL instances
for ($i=0; $i -lt $SQLData.Length; $i++) {
#get the SQL Instance Name from the PID
$SQL_Name = ($SQLData[$i].commandline -split'-s')[1]                                
[int]$SQL_PID = $SQLData[$i].ProcessId

#extract the process name from the perf counter path (CPU)
$proc_time = $processor_time | Where-Object {(($_.InstanceName) -replace($regex,"") -eq $SQL_PID)}  | Select-Object -Property InstanceName, CookedValue
#if there is no process name next to a PID, just write dummy process name
if(($SQL_PID | measure-object -character | select -expandproperty characters) -lt 3) { $SQL_PID = 0 }

#DEBUG: 
($env:COMPUTERNAME +": "+  $SQL_Name + " PID: " + $SQL_PID + " Processor Time: "  + ([math]::Round($proc_time.CookedValue/$env:NUMBER_OF_PROCESSORS,2)) ) 
#generate the Influx logging calls
$measurement = "sql_cpu"
$tags = "host="+$hostname.ToString() + ",Instance="+$SQL_Name
$values = "PID="+$SQL_PID + ",CPU="+(([math]::Round($proc_time.CookedValue,2))/$env:NUMBER_OF_PROCESSORS)


# Write data to Influx DB 
Invoke-RestMethod -Uri $uri -Method Post -Body "$measurement,$tags $values" -TimeoutSec 10
}

Grafana Panel JSON for visualising the data:

{
  "type": "graph",
  "title": "Panel Title",
  "gridPos": {
    "x": 0,
    "y": 0,
    "w": 12,
    "h": 9
  },
  "id": 2,
  "targets": [
    {
      "refId": "A",
      "policy": "default",
      "resultFormat": "time_series",
      "orderByTime": "ASC",
      "tags": [],
      "groupBy": [
        {
          "type": "time",
          "params": [
            "$__interval"
          ]
        },
        {
          "type": "tag",
          "params": [
            "host"
          ]
        },
        {
          "type": "tag",
          "params": [
            "Instance"
          ]
        },
        {
          "type": "fill",
          "params": [
            "previous"
          ]
        }
      ],
      "select": [
        [
          {
            "type": "field",
            "params": [
              "CPU"
            ]
          },
          {
            "type": "mean",
            "params": []
          }
        ]
      ],
      "measurement": "sql_cpu",
      "alias": "$tag_host\\$tag_Instance"
    }
  ],
  "options": {
    "alertThreshold": true
  },
  "fieldConfig": {
    "defaults": {},
    "overrides": []
  },
  "pluginVersion": "7.5.10",
  "renderer": "flot",
  "yaxes": [
    {
      "label": null,
      "show": true,
      "logBase": 1,
      "min": null,
      "max": null,
      "format": "percent",
      "$$hashKey": "object:140"
    },
    {
      "label": null,
      "show": true,
      "logBase": 1,
      "min": null,
      "max": null,
      "format": "short",
      "$$hashKey": "object:141"
    }
  ],
  "xaxis": {
    "show": true,
    "mode": "time",
    "name": null,
    "values": [],
    "buckets": null
  },
  "yaxis": {
    "align": false,
    "alignLevel": null
  },
  "lines": true,
  "fill": 1,
  "linewidth": 1,
  "dashLength": 10,
  "spaceLength": 10,
  "pointradius": 2,
  "legend": {
    "show": true,
    "values": false,
    "min": false,
    "max": false,
    "current": false,
    "total": false,
    "avg": false
  },
  "nullPointMode": "null",
  "tooltip": {
    "value_type": "individual",
    "shared": true,
    "sort": 0
  },
  "aliasColors": {},
  "seriesOverrides": [],
  "thresholds": [],
  "timeRegions": [],
  "fillGradient": 0,
  "dashes": false,
  "hiddenSeries": false,
  "points": false,
  "bars": false,
  "stack": false,
  "percentage": false,
  "steppedLine": false,
  "timeFrom": null,
  "timeShift": null,
  "datasource": null
}

war3z.org вече и по SSL

С напредването на годините все по-често започвам да разбирам един забавен виц:

Съдия към подсъдим:
-Защо убихте жена си след 20 години брак?
Подсъдим:
-От мързел.
Садия:
-Е как от мързел?
Подсъдим:
-Еми, а днеска, а утре… и годините минаха..

Та и при мен се получава нещо подобно – доста на пръв поглед очевидни неща се проточват с години 🙂 По принцип SSL не е необходим за целите ми, но понеже вече идва момент, в който някои браузъри спират връзката до сайтове без SSL, активирах сертификат и за war3z.org 🙂

12 години war3z.org

idea

Image by ClipFest

Преди 12 години се роди една идея, един експеримент с неясно бъдеще – war3z.org

През годините war3z.org беше дом на гейм сървъри, аудио библиотеки, хостинг решения, блогове, хардуерни проекти и още много, много идеи и решения на проблеми.

В момента бъдещето е все така неясно както беше и през далечната 2005-та. За сега хостинг услугите продължават, когато е необходимо се публикуват блог постове по проблемите на деня. Остава активен и хардуерния експеримент – http://weather.war3z.org – метео станция базирана на Ардуино

За всички, които имат нужда от помощ, хостинг решения, технически съвети (оптимизации на бази данни, виртуализация, механизми за бекъп и възстановяване след бедствия, мониторинг на всякакви софтуерни услуги, мрежови решения и др.), можете да се свържете с мен на sawo@war3z.org

Как да си направим собствена метео станция – част 3

В част 3 от проекта домашна метео станция вече стигаме до най-интересната (за мен) част – свързването на всички сензори и извличане на данните от тях.

Първата стъпка ще е добавянето на ethernet shield-a към основната платка – просто го щракваме върху нея – нищо особено 🙂

*Тук е момента де се отбележи - ако станцията ви ще е в някаква малка кутия или метео клетка, доста желателно е да изведете датчиците с кабел максимално далече от платката. 
Основния чип на ethernet shield-a загрява съвсем спокойно до 42-43 градуса при 22 градуса околна температура, което може да повлияе сериозно на получените данни.

След като имаме основните модули събрани, може да добавим и сензорите:

Това което направих аз е:

  • Свързване на BMP180 сензора за атм. налягане и температура: SCL към аналогов пин 5, SDA към аналогов пин 4, VDD към пина с 5 волта, GND към маса;
  • Свързване на SHT75 сензора за темп. и влажност: Clock(Pin1) на цифров пин 2, Data(Pin4) на цифров пин 3, Vcc (Pin2) към пина с 5 волта, GND (Pin3) към маса. За DHT11 свързването става по сходен начин;
  • Свързване на сензора за дъжд: А1 на аналогов пин 1, Vcc към пина с 5 волта, GND към маса.

Ето как изглежда тестовия вариант на платката със закачени сензори BMP180 и DS18B20, който ползвах първоначално за температурата:

IMAG0511

След като сме свързали сензорите, остава да напишем кода, който да извлича данните. Някои сензори изискват библиотека за използването им, например BMP180 изисква библотека, която може да бъде свалена от ТУК

По-долу ще пусна кода, който написах аз със коментари кое какво прави по-точно. Като цяло резултата от кода е принтирането на резултатите от сензорите в серийния монитор (Tools > Serial Monitor), както и Web сървър на определен IP адрес, който вади данните в минималистичен вид, така че после да може да се свалят и обработят лесно:

#include <Wire.h>
#include <SPI.h>
#include <Ethernet.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>
#include <Sensirion.h>
     
/*    
   Connections [BMP180]
   ===========
   Connect SCL to analog 5
   Connect SDA to analog 4
   Connect VDD to 5V DC
   Connect GROUND to common ground

   Connections [SHT75]
   ===========
   Connect Clock(Pin1 - White) to 2 (digital)
   Connect Data(Pin4 - Yellow) to 3 (digital)
   Connect Vcc (Pin2 - Red) to 5V DC
   Connect GND (Pin3 - Black) to common ground
*/
/*Sensirion SHT75 Constants*/
const uint8_t dataPin =  3;              // SHT serial data
const uint8_t sclkPin =  2;              // SHT serial clock
const uint32_t TRHSTEP   = 5000UL;       // Sensor query period
const int rainMin = 0;     // rain sensor minimum
const int rainMax = 1024;  // rain sensor maximum

Sensirion sht = Sensirion(dataPin, sclkPin);
 
uint16_t rawData;
float temperature;
float humidity;
float humidex;
float dewpoint;
 
byte measActive = false;
byte measType = TEMP;
 
unsigned long trhMillis = 0;             // Time interval tracking

/*Sensirion SHT75 Constants END*/

Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10085); //BMP180 code
/*OneWire  ds(7);  // on pin 2 (a 4.7K resistor is necessary)*/
float celsius = 0; // global temperature variable
float pressurekPa = 0; //global pressure variable

//Ethernet setup >
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 200);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
EthernetServer server(80);

void setup(void) 
{
  Serial.begin(9600);
  /*SHT75 SETUP:*/
  delay(15);                             // Wait >= 11 ms before first cmd
// Demonstrate blocking calls
  sht.measTemp(&rawData);                // sht.meas(TEMP, &rawData, BLOCK)
  temperature = sht.calcTemp(rawData);
  sht.measHumi(&rawData);                // sht.meas(HUMI, &rawData, BLOCK)
  humidity = sht.calcHumi(rawData, temperature);
  dewpoint = sht.calcDewpoint(humidity, temperature);
  logData();
  /*SHT75 SETUP END*/
  
  /* Ethernet config */
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
  
  /* Initialise the BMP180 sensor */
  if(!bmp.begin())
  {
    /* There was a problem detecting the BMP180 ... check your connections */
    Serial.print("Ooops, no BMP180 detected ... Check your wiring or I2C ADDR!");
    while(1);
  }

}

void loop(void) 
{
    /* Temp readings: */
    /*SHT75 loop: */
      unsigned long curMillis = millis();          // Get current time
     
      // Demonstrate non-blocking calls
      if (curMillis - trhMillis >= TRHSTEP) {      // Time for new measurements?
        measActive = true;
        measType = TEMP;
        sht.meas(TEMP, &rawData, NONBLOCK);        // Start temp measurement
       
        BMP180();                                  // Pressure measurement
        
        trhMillis = curMillis;
      }
      if (measActive && sht.measRdy()) {           // Note: no error checking
        if (measType == TEMP) {                    // Process temp or humi?
          measType = HUMI;
          temperature = sht.calcTemp(rawData);     // Convert raw sensor data
          sht.meas(HUMI, &rawData, NONBLOCK);      // Start humidity measurement
        } else {
          measActive = false;
          humidity = sht.calcHumi(rawData, temperature); // Convert raw sensor data
          dewpoint = sht.calcDewpoint(humidity, temperature);
          
          //humidex code:
          float h,e; //humidex and other
          e = 6.11 * exp(5417.7530 * ((1/273.16) - (1/(dewpoint + 273.15)))); 
          h = (0.5555)*(e - 10.0);
          humidex = temperature + h;
          logData();
        }
      }
    /*SHT75 loop end*/
    
    
  /* Web server: */
    EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html"); // text/html
          client.println("Connection: close");  // the connection will be closed after completion of the response
          //client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          //client.println("<!DOCTYPE HTML>");
          //client.println("<html>");
          // output the data
            client.print(temperature);
            client.print(" ");
            client.print(humidity);
            client.print(" ");
            client.print(pressurekPa);
            client.print(" ");
            client.print(dewpoint);
            client.print(" ");
            client.print(humidex);
            client.print(" ");
            client.print(analogRead(A1));
            //client.println("<br />");
          
          //client.println("</html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
}



void BMP180() {
  delay(1500);
  /* Get a new sensor event */ 
  sensors_event_t event;
  bmp.getEvent(&event);
 
  /* Display the results (barometric pressure is measure in kPa) */
  if (event.pressure)
  {
    /* Display atmospheric pressue in kPa */
    Serial.print("[BMP180]Pressure:    ");
    Serial.print(event.pressure / 10);
    pressurekPa = event.pressure / 10;
    Serial.println(" kPa");
         
    /* First we get the current temperature from the BMP085 */
    float temperature;
    bmp.getTemperature(&temperature);
    Serial.print("[BMP180]Temperature: ");
    Serial.print(temperature);
    Serial.println(" C");

    /* Then convert the atmospheric pressure, and SLP to altitude         */
    /* Update this next line with the current SLP for better results      */
    float seaLevelPressure = 1022 /*SENSORS_PRESSURE_SEALEVELHPA*/;
    Serial.print("[BMP180]Altitude:    "); 
    Serial.print(bmp.pressureToAltitude(seaLevelPressure,
                                        event.pressure)); 
    Serial.println(" m");
    Serial.println("");
  }
  else
  {
    Serial.println("Sensor error");
  }
}

    void logData() {
      Serial.print("[SHT75]Temperature = ");   Serial.print(temperature);
      Serial.print(" C, Humidity = ");  Serial.print(humidity);
      Serial.print(" %, Dewpoint = ");  Serial.print(dewpoint);
      Serial.println(" C");
      Serial.print("Humidex: ");
      Serial.println(humidex);
      Serial.print("Water Level: ");
      Serial.println(analogRead(A1));
    }

По-горния код ще направи уеб сървър на IP адрес 192.168.1.200, където на един ред ще се извеждат данните от всички сензори. Тези данни вече може да взимате, за да си генерирате графики, да си ги съхранявате във файл или каквото намерите за най-удобно във вашия случай. Аз лично използвам линукс машина, която чертае графики посредством munin софтуер за графики (базиран на rrdtool). Има хиляди решения за чертане на графики, просто трябва да разгледате опциите.

В най-общи линии това е техническата част на станцията, ако искате точни данни обаче, съветвам ви да си направите дървена метео клетка.

Как да си направим собствена метео станция – част 2

След като вече си набавихме материалите за метео станцията в част първа , време е да пристъпим към подготовка за програмирането им 🙂

Преди това обаче трябва да си подготвим софтуера, с който ще програмираме Arduino-то. Последната версия на софтуера може да се изтегли от ТУК. Понеже обаче нашето Arduino не е оригинално, трябва да си изтеглим драйвери за него допълнително 🙂 Драйвери за Arduino с чипсет CH340/CH341 може да се свалят от ТУК или ТУК. Ако не знаете как се инсталират драйверите, може да намерите инструкции на официалния им сайт. Единствено на стъпката където посочвате .inf файла естествено посочвате файла, който предварително свалихме и разархивирахме от линковете по-горе 🙂

След като имаме всичко необходимо, стартираме IDE-то, което изглежда така:

В менюто Tools отивате на подменюто Board и избирате „Arduino/Genuino Uno“ или каквато платка притежавате съответно.

В подменюто Port избирате порта, на който се вижда платката. Тук е момента да уточня, че за да изпълните тази стъпка, платката трябва да е свързана посредством USB кабела към компютъра. Ако това подменю е сиво, това означава, че драйверите ви не са инсталирани коректно. НЕ инсталирайте драйверите, които идват с IDE-то от официалния сайт на Arduino, освен ако не сте си купили оригинална платка – платките, които се продават по 10-20лв в интернет не са оригинални и съответно ви трябват драйверите, които споменахме по-горе.

Нека разгледаме и структурата на кода, който ще пишем:

Блок setup (void setup() {  }):

Тук въвеждаме кода, който ще се изпълнява еднократно при включване на платката или след рестартирането й от reset бутона.

Блок setup (void loop() {  }):

Тук въвеждаме кода, който ще се повтаря постоянно, докато платката работи.

* Ако искаме да ползваме глобални променливи, да инициализираме библиотеки за определени сензори и т.н., попълват се извън тези два блока.

В менюто File > Examples > 01.Basics има елементарни примери, които може да се тестват за първи стъпки с платката (например Read Analog Voltage)

В част трета ще преминем към свързване на сензорите към основната платка, както и към извличане на данните от сензорите 🙂

Как да си направим собствена метео станция – част 1

Предполагам на доста хора им е хрумвало по едно или друго време да си направят метео станция и най-вече на тези, които имат нужда от нещо специфично или просто обичат сами да си правят нещата. С такава станция може да следите времето на село, над жилището си или вътре в него 🙂 В моя случай нуждата беше породена от факта, че в града ни няма метео станция и данните, които всички сайтове дават са реално от други градове.

Преди да продължим, да уточним какво точно ще прави станцията, която аз правя:

  1. Ще измерва температура, влажност, атмосферно налягане и интензивност на валежите посредством датчици, свързани към arduino (eдноплатков микроконтролер – звучи страшно, но не е);
  2. Ще измерва dew point и humidex (температура както се усеща от човешкото тяло) на база формули с данните от другите датчици;
  3. Ще прави данните достъпни в  мрежа/интернет, като за визуализирането им ще се използват графики, които се чертаят от сървър, който ще събира данните;

Какви знания са нужни ?

  1.  Минимален опит с поялник;
  2.  Минимални познания по програмиране – контролера се програмира с IDE на C, всички датчици, които ще използваме имат готови библиотеки, кода просто трябва да се нагласи според нуждите ви;

Ето и какви материали ще са ни необходими:

  • Базова платка Arduino Uno, версията със запоен чип е достъпна за около 12лв из olx и други родни места:
  • Датчик за температура и влажност – аз ще използвам SHT75, защото имам случайно под ръка, но препоръчвам използването на DHT11/DHT22 (на снимката по-долу) – цената му е 6-7лв отново в olx и подобни сайтове:
  • Датчик за атмосферно налягане BMP180, произведен от Bosch – цената отново е 5-6лв в olx, доста прецизен датчик. Има вграден датчик за температура, но не е съвсем точен – ако не ви пречат отклонения от 2-3 градуса, може съвсем спокойно да се използва:
  • Arduino Ethernet Shield – намира се за около 15лв в olx и подобните сайтове у нас:
  • Rain sensor – намира се за около 5-6лв в olx и подобните:Eдно уточнение – всички датчици и самата основна платка може да се намерят по-евтино по китайските сайтове, ако ви се чака доставката 🙂

В част втора ще преминем към конфигуриране на IDE-то, с което ще програмираме основната платка 🙂