neue Session – Powershell Toolbelt für DBAs – PASS UserGroup Hamburg

Am 13.04.2017 habe ich das Vergnügen erneut vor der Regionalgruppe Hamburg der PASS Deutschland e.V. sprechen zu dürfen. Sascha Lorenz und Cornelia Matthesius veranstalten – wie jeden Monat – wieder ein Treffen in der Hamburger Geschäftsstelle von Microsoft, dieses Mal ist es Mittwoch, der 13. April. Anmeldungen am Besten über die Meetup-Plattform =>  SQL Server Hamburg (by PASS Deutschland e.V.)

Automation makes things easier

Wer kennt es nicht? Immer heißt es “du musst effizienter werden”, aber wie… also noch schneller die Arbeit erledigen, aber wo kann man noch Zeit sparen? Natürlich bei immer wieder kehrenden Aufgaben, hier gibt es zweierlei Ansätze…

  • Tasks automatisieren
  • länger dauernde, komplizierte Aufgaben einfacher machen

Wie man das in Bezug auf die DBA-Tätigkeiten umsetzen kann und welche Tools/Produkte/Skripte dabei zum Einsatz kommen (können), darüber werde ich an diesem Abend erzählen.

Beispiele ?!

  • Wie lange braucht man um einen SQL Server von einer älteren Version auf eine neuere Version anzuheben?
    Natürlich inklusiv der Übernahme aller Logins, aller Jobs, aller Linked Server, aller Alerts und aller Datenbanken etc – 1-2 Tage je nach Umfang der einzelnen Unterpunkte.Was würdet ihr aber sagen, wenn man das auch in ~5 Minuten schaffen kann (abhängig von der Datenbank-Größe) und nur einer (!!) Kommandozeile ???
  • Datenbanken von Server zu Server kopieren, Datenfiles und Datenbank umbenennen und Orphaned-Users bereinigen in drei Zeilen Powershell ???

Nicht möglich ???

Lasst euch überraschen, ich habe einige Demos vorbereitet anhand derer ich euch diese Tools vorstellen werde.

Veranstaltungsort

PASS Deutschland e.V. ist die deutsche Microsoft SQL Server Community und ein offizielles Chapter der PASS International. Die Mitgliedschaft bei der PASS ist kostenfrei.

Das Treffen findet am 13. April 2017 um 18:30 in der Microsoft Niederlassung Hamburg in der Gasstraße 6a statt und wird von PASS Deutschland e.V.. ausgerichtet.

Björn arbeitet in Hamburg als Datenbank-Administrator und Head of Competence für MS SQL und mySQL. Er nimmt regelmäßig an den PASS Regionalgruppen Treffen in Hamburg, den Veranstaltungen der PASS wie SQLSaturday und SQLGrillen teil und er organisiert in Hamburg das Azure Meetup. Er interessiert sich neben den Themen rund um den SQL Server, Powershell und Azure für Science-Fiction, Snowboarden, Backen 😉 und Radfahren.

#3.1 Update zu meinem Powershell Skript “Öffnen von Firewall Port”

Nachdem ich meinen Beitrag zum Thema “Öffnen der Firewall Ports mit Powershell” Ende Oktober veröffentlicht hatte, habe ich mein Skript nochmals überarbeitet. Der SQL Server und seine Features wie z.B. die Analysis Services haben eigene Ports, welche auch nur geöffnet werden müssen, wenn die jeweiligen Features installiert sind.

Grundlage für meine weiteren Versuche ist der MSDN-Beitrag zu diesem Thema und der eigene Wunsch nach mehr Flexibilität beim Erstellen von Regeln.

Flexibilität durch eigene Powershell-Funktion “GET SQLServices for Firewall”

Ich habe also meine bisherige Funktion aufgesplittet, so dass ich nicht mehr wie bisher stur (per Copy&Paste) die Regel erstelle. Jetzt habe ich eine Funktion gebaut, welche alle relevanten SQL Server Dienste ermittelt, um diese dann individuell freizuschalten. Also mein erster Schritt ist die Analyse der SQL Server Installation:

function GET_SQLServices_for_Firewall($SQLServerTCPPort) {
    # TCP = 6, UDP = 17

    Write-Host "Opening Firewall ports for this Instance"
    # General Ports
    OpenFirewallPorts 4022 6 "SQL Service Broker"
    OpenFirewallPorts 1434 17 "SQL Browser"

    $Services=get-wmiobject -class win32_service | where {$_.DisplayName -like '*SQL*'} | select-object DisplayName 
    foreach ( $service in $Services ) {
        
        # DB-Engine Ports
        if ($Service.DisplayName -like '*SQL Server (*') { 
            OpenFirewallPorts $SQLServerTCPPort 6 "SQL Server"
        }

        # SSAS Ports
        if ($Service.DisplayName -like '*Analysis Services (*') {
            OpenFirewallPorts 2383 6 "SQL - Analysis Services"
            OpenFirewallPorts 2382 6 "SQL - Analysis Services Browserservice"
        }
    }
}

Wie man nun erkennen kann, rufe ich nun eine weitere Funktion auf mit der ich die Verwaltung der Windows Firewall übernehme. Die eigentlichen Teile meines bisherigen Skriptes habe ich somit relativ unverändert gelassen, nur “eingedampft” und variabler gestaltet.
Ich rufe also die eigentliche Arbeitsfunktion mit entsprechenden Parametern auf, um den jeweiligen Port, das Protokoll und eine sprechende Beschreibung zu übermitteln.

function OpenFirewallPorts ([int]$Port, [int]$Protocol, [string]$FirewallRuleDescsription) {
    
    if ($Protocol -eq 6) {$Protocol_String = "TCP"}
    if ($Protocol -eq 17) {$Protocol_String = "UDP"}

    Try {
	Write-Host "Opening Firewall on $Protocol_String Port $Port" 
	$port1 = New-Object -ComObject HNetCfg.FWOpenPort
	$port1.Port = $SQLServerTCPPort
	$port1.Name = $FirewallRuleDescsription + "(" + $Protocol_String + " " + $Port + ") " + $InstanceName 
	$port1.Enabled = $true
	$port1.Protocol = $Protocol
	$fwMgr = New-Object -ComObject HNetCfg.FwMgr
	$profiledomain=$fwMgr.LocalPolicy.GetProfileByType(0)
	$profiledomain.GloballyOpenPorts.Add($port1)
        Write-Host "[INFO] Successfully opened Firewall on $Protocol_String Port $Port." -ForegroundColor Green
	} 
    Catch { 
        Write-Host "[ERROR] Opening Firewall on $Protocol_String Port $Port failed." -ForegroundColor Red 
    }
}

Für mich und meine Zwecke funktioniert das soweit ganz gut und ist natürlich bei Bedarf individuell anpassbar. Wenn man als Beispiel einen weiteren Listener im SQL Server konfiguriert, dann könnte man dies ebenfalls ermitteln und in der Windows Firewall freischalten. Grob => Invoke-SQLcmd “Get Listener Port” => OpenFirewallPorts newListenerPortNumber 6 “Additional SQL Server Listener”

Für mich bedeutet das Niederschreiben und Erläutern der einzelnen Schritte und Veränderungen auch einen Lernprozess. Wenn jemand Anmerkungen oder Verbesserungen für mich hat, freue ich mich sehr darüber. “Please share your knowledge” 😉

Björn arbeitet in Hamburg als Datenbank-Administrator und Head of Competence für MS SQL und mySQL. Er nimmt regelmäßig an den PASS Regionalgruppen Treffen in Hamburg, den Veranstaltungen der PASS wie SQLSaturday und SQLGrillen teil und er organisiert in Hamburg das Azure Meetup. Er interessiert sich neben den Themen rund um den SQL Server, Powershell und Azure für Science-Fiction, Snowboarden, Backen 😉 und Radfahren.

#3 Firewall Ports für den SQL Server öffnen mit Powershell

Nach ein paar Tagen Ruhe kommt nun mein dritter Teil der Powershell Serie zum Thema “Firewall Ports öffnen“.
Sicherlich wird der ein oder andere sagen, “Firewall? die interne? die haben wir per Gruppenrichtlinie immer ausgeschaltet!”…was ist vielleicht in einem halben Jahr, jemand aktiviert die Windows Firewall auf dem Server manuell? Kann der SQL Server (also die Datenbank-Enigne) dann immer noch mit der Welt da draußen kommunizieren?

Sicherheit geht vor – daher nur definiert und bewußt die Firewall Ports öffnen

Windows Firewall Ports öffnen für den SQL Server

Ich empfehle nur die notwendigen Ports für die Datenbank-Enigne zu öffnen, im Beispielbild wären das die folgenden Ports

  • SQL Server DB Engine => TCP 10001
  • der SQL Browser => UDP 1434
  • der SQL Service => Broker 4022

Dies reicht völlig aus, um dem SQL Server und den nutzenden Applikationen im Notfall eine Kommunikation zu ermöglichen. Nun gibt es mehrere Möglichkeiten dies zu realisieren, die Konfiguration der Firewall über die grafische Oberfläche, was doch umständlich und “kompliziert” sein kann, aber ebenso der Weg über die Kommandozeile entweder als DOS-Befehl bzw mit dem netsh-Befehl oder mit einem Powershell-Skript.

Wer noch andere Features des SQL Servers auf dem selben Server einsetzt, muss natürlich noch weitere Ports öffnen und auch die entsprechenden Skripte erweitern, aber in der Regel reichen diese Firewall Ports für einen Datenbank Server aus.

weitere Hinweise zu den Firewall Ports des SQL Server findet man hier : https://msdn.microsoft.com/de-de/library/cc646023.aspx

Umsetzung über die Kommandozeile

netsh advfirewall firewall add rule name="SQL Server (TCP 10001) TEST_Instanz" dir=in action=allow protocol=TCP localport=10001 profile=domain
netsh advfirewall firewall add rule name="SQL Service Broker (TCP 4022)" dir=in action=allow protocol=TCP localport=4022 profile=domain
netsh advfirewall firewall add rule name="SQL Browser (UDP 1434)" dir=in action=allow protocol=UDP localport=1434 profile=domain

Der netsh-Befehl lässt sich sehr gut parametrisieren und skripten, ist daher sehr individuell einsetzbar, also recht umgänglich. Hier kommt es auf die Unternehmensstrategie oder die der Systemadministratoren an, welche Skriptsprache bevorzugt wird. Ich bevorzuge (mittlerweile) Powershell, was auch Hauptthema dieser Serie ist, daher gehe ich nun etwas tiefer in die Umsetzung “Firewall Ports öffnen” mit Powershell ein.

Umsetzung mittels Powershell

Auch die Windows-Firewall hat ein Powershell Commandlet, so dass wir die Firewall Ports recht einfach und komfortabel öffnen können. Mein Lösungsansatz ist vielleicht etwas länger und umfangreicher, der erfahrene Powershell Programmierer bekommt das bestimmt auch in eine Schleife gepackt oder gar in eine Zeile 😉
Meine Zeilen stammen aus einem Skript, welches ich für die Konfiguration nach der Installation verwende, daher sind hier zahlreiche Variablen genutzt worden, um die Flexibilität zu erhalten.

  • $SQLServerTCPPort = 10001
  • $InstanceName = TEST_Instanz

Auch ein wenig “Monitoring” habe ich implementiert, so dass man gleich das Ergebnis, den Fehlschlag bzw den Fortschritt anhand der Ausgabe erkennen kann. Für meine Zwecke im Rahmen der SQL Server Installation reichen diese Zeilen um die Firewall Ports zu öffnen, zudem erklären sich die einzelnen Parameter nahezu von selbst.

Write-Host "Opening Firewall ports for this Instance"
    Try {
		Write-Host "Opening Firewall on Port $SQLServerTCPPort" 
		$port1 = New-Object -ComObject HNetCfg.FWOpenPort
		$port1.Port = $SQLServerTCPPort
		$port1.Name = "SQL Server (TCP " + $SQLServerTCPPort + ") " + $InstanceName 
		$port1.Enabled = $true
		$port1.Protocol = 6
		$fwMgr = New-Object -ComObject HNetCfg.FwMgr
		$profiledomain=$fwMgr.LocalPolicy.GetProfileByType(0)
		$profiledomain.GloballyOpenPorts.Add($port1)
        Write-Host "[INFO] Successfully opened Firewall on Port $SQLServerTCPPort." -ForegroundColor Green
     } 
    Catch { 
        Write-Host "[ERROR] Opening Firewall on Port $SQLServerTCPPort failed." -ForegroundColor Red 
    }
   
   Try {
		Write-Host "Opening Firewall on Port 4022" 
		$port1 = New-Object -ComObject HNetCfg.FWOpenPort
		$port1.Port = 4022
		$port1.Name = "SQL Service Broker (TCP 4022)"
		$port1.Enabled = $true
		$port1.Protocol = 6
		$fwMgr = New-Object -ComObject HNetCfg.FwMgr
		$profiledomain=$fwMgr.LocalPolicy.GetProfileByType(0)
		$profiledomain.GloballyOpenPorts.Add($port1) 
        Write-Host "[INFO] Successfully opened Firewall on Port 4022." -ForegroundColor Green
    } 
    Catch { 
        Write-Host "[ERROR] Opening Firewall on Port 4022 failed." -ForegroundColor Red 
    }
   
    Try {
		Write-Host "Opening Firewall on Port UDP 1434" 
		$port1 = New-Object -ComObject HNetCfg.FWOpenPort
		$port1.Port = 1434
		$port1.Name = "SQL Browser (UDP 1434)"
		$port1.Enabled = $true
		$port1.Protocol = 17
		$fwMgr = New-Object -ComObject HNetCfg.FwMgr
		$profiledomain=$fwMgr.LocalPolicy.GetProfileByType(0)
		$profiledomain.GloballyOpenPorts.Add($port1) 
        Write-Host "[INFO] Successfully opened Firewall on Port UDP 1434." -ForegroundColor Green
    } 
    Catch { 
        Write-Host "[ERROR] Opening Firewall on Port 1434 failed." -ForegroundColor Red 
    }
Björn arbeitet in Hamburg als Datenbank-Administrator und Head of Competence für MS SQL und mySQL. Er nimmt regelmäßig an den PASS Regionalgruppen Treffen in Hamburg, den Veranstaltungen der PASS wie SQLSaturday und SQLGrillen teil und er organisiert in Hamburg das Azure Meetup. Er interessiert sich neben den Themen rund um den SQL Server, Powershell und Azure für Science-Fiction, Snowboarden, Backen 😉 und Radfahren.

#2 Disable NIC Power Save Mode mit Powershell

Weiter geht es mit dem zweiten Teil meiner Powershell-Reihe:
Diesmal möchte ich den Power Save Mode der Netzwerkkarten abschalten, damit diese Funktion nicht “plötzlich” zum Verhängnis wird. In Verbindung mit dem “Balanced”-Powerplan kann Windows der Meinung sein, dass die Netzwerkkarte bei Inaktivität in den “Sleep”-Mode versetzt wird. Somit verliert der SQL Server plötzlich seine (inaktiven) Connections, zahlreiche Applikationen werden diese Trennungen nicht mögen. Noch viel schlimmer in einem Cluster oder mit AlwaysOn Availability-Groups, plötzlich ist der Heartbeat weg und das Cluster schwenkt.

Widersprüchliches in High Performance

Auch wenn man meinen würde, dass dieser Power Save Mode der Netzwerkkarte abgeschaltet wird, wenn man den Powerplan auf “High Performance” setzt. Nein, laut Aussage eines Microsoft PFE Team-Members (Oktober 2016) hilft auch dies nicht:

• If the server is set to High Performance – Windows places the system in the highest performance state and disables the dynamic scaling of performance in response to varying workload levels. Therefore, special care should be taken before setting the power plan to High Performance as this can increase power consumption unnecessarily when the system is underutilized.
• We will have to disable “Allow the computer to turn off this device to save power” option of Power management from NIC. Setting the server in High Performance would not stop the NIC to go in sleep mode whenever there is no activity, as that is a setting at an OS level. The setting on NIC will take preference in this situation.

Somit hilft nur das Abschalten dieses Power Save Modes an allen Netzwerkkarten!

Disable NIC Power Save Mode

Diese Powershell-Lösung habe ich mir nicht selber ausgedacht, mit dieser hatte ich die wenigsten Probleme, sie funktionierte auf zahlreichen Maschinen.
Die Lösung von Ingmar Verheij habe ich angepasst von WiFi auf LAN-Adapter gemäß iana.org, lässt sich sicherlich auch auf andere Adapter anpassen.

Was macht das Powershell Snippet?

Das Skript sucht sich alle installierten Netzwerkkarten und überprüft, ob diese vom Typ “6” (ethernetCsmacd(6), for all ethernet-like interfaces, regardless of speed, as per RFC3635) sind. Wurde eine entsprechende Ethernet-Netzwerkkarte gefunden wird für diese in der Registry der Wert entsprechend geändert, so dass die Netzwerkkarte nicht mehr in den Power Save Mode wechseln kann.

$intNICid=0; do {
     #Read network adapter properties
     $objNICproperties = (Get-ItemProperty -Path ("HKLM:\SYSTEM\CurrentControlSet\Control\Class\{0}\{1}" -f "{4D36E972-E325-11CE-BFC1-08002BE10318}", ( "{0:D4}" -f $intNICid)) -ErrorAction SilentlyContinue)
 
     #Determine if the Network adapter index exists 
     If ($objNICproperties) {
          #Filter network adapters
          # * only Ethernet adapters (ifType = ieee80211(6) - http://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib)
          # * root devices are exclude (for instance "WAN Miniport*")
          # * software defined network adapters are excluded (for instance "RAS Async Adapter")
          If (($objNICproperties."*ifType" -eq 6) -and ($objNICproperties.DeviceInstanceID -notlike "ROOT\*") -and ($objNICproperties.DeviceInstanceID -notlike "SW\*")) {
               #Read hardware properties
               $objHardwareProperties = (Get-ItemProperty -Path ("HKLM:\SYSTEM\CurrentControlSet\Enum\{0}" -f $objNICproperties.DeviceInstanceID) -ErrorAction SilentlyContinue)
               If ($objHardwareProperties.FriendlyName) {
                    $strNICDisplayName = $objHardwareProperties.FriendlyName
               } else { 
                    $strNICDisplayName = $objNICproperties.DriverDesc
               }
               #Read Network properties
               $objNetworkProperties = (Get-ItemProperty -Path ("HKLM:\SYSTEM\CurrentControlSet\Control\Network\{0}\{1}\Connection" -f "{4D36E972-E325-11CE-BFC1-08002BE10318}", $objNICproperties.NetCfgInstanceId) -ErrorAction SilentlyContinue)

               #Inform user
               Write-Host -NoNewline -ForegroundColor White " ID : "; Write-Host -ForegroundColor Yellow ( "{0:D4}" -f $intNICid)
               Write-Host -NoNewline -ForegroundColor White " Network: "; Write-Host $objNetworkProperties.Name
               Write-Host -NoNewline -ForegroundColor White " NIC : "; Write-Host $strNICDisplayName
               Write-Host -ForegroundColor White " Actions:"

               #Disable power saving
               Set-ItemProperty -Path ("HKLM:\SYSTEM\CurrentControlSet\Control\Class\{0}\{1}" -f "{4D36E972-E325-11CE-BFC1-08002BE10318}", ( "{0:D4}" -f $intNICid)) -Name "PnPCapabilities" -Value "24" -Type DWord
                Write-Host -ForegroundColor Green (" - Power saving disabled")
                Write-Host ""
           }
      } 
      #Next NIC ID
      $intNICid+=1
} while ($intNICid -lt 255)
Björn arbeitet in Hamburg als Datenbank-Administrator und Head of Competence für MS SQL und mySQL. Er nimmt regelmäßig an den PASS Regionalgruppen Treffen in Hamburg, den Veranstaltungen der PASS wie SQLSaturday und SQLGrillen teil und er organisiert in Hamburg das Azure Meetup. Er interessiert sich neben den Themen rund um den SQL Server, Powershell und Azure für Science-Fiction, Snowboarden, Backen 😉 und Radfahren.

#1 Powerplan – SQL Server konfigurieren mit Powershell

Ich möchte in den folgenden Beiträgen auf meine Erfahrung bzw meinen Umstieg auf die Konfiguration des SQL Servers mittels Powershell eingehen und einige Beispiele (hier zum Beispiel den Powerplan) präsentieren. Es kommt immer wieder vor, dass wir verschiedenste Ausprägungen des SQL Server installieren sollen, eines haben aber alle im Grunde genommen gemeinsam… eine auf den Best-Practices beruhende Grundkonfiguration. Diese muss auf jedem SQL Server ausgerollt werden, damit dieser performant läuft. Bisher haben wir das immer manuell oder mit T-SQL gemacht, nun wird es aber Zeit diese Methode umzustellen auf eine etwas einfachere bzw zentralere Variante. Die Konfiguration des SQL Servers mit Powershell sowie ich es hier vorstelle, muss nicht für jede Umgebung passen, seht es bitte nur als Leitfaden an. Des Weiteren werde ich hier nur auf meine “Snippets” eingehen und nicht mein ganzes Skript vorstellen, es gehört natürlich ein gewisser Powershell Rahmen (Synopsis, Hilfe etc) um das Snippet, damit es überall von jedem ausgeführt werden kann.

Powershell against “Balanced” Powerplan

Seit dem Windows 2008R2 Betriebssystem werden die Power Pläne immer mit dem Default-Powerplan “Balanced” ausgerollt, dies kann aber unter Umständen zu starken Performance-Einbussen führen. Bei der Ausführung von einfachen Skripten oder Programmen ist die Wahrscheinlichkeit der Performance-Reduzierung eher sehr gering, allerdings je komplexer die auszuführenden Anwendungen sind, desto mehr die Server-Ressourcen genutzt werden müssen, desto mehr wird man die Nachteile des “Balanced”-Modes spüren. Im Balance-Powerplan wird zum Beispiel die Taktung des Prozessors und die Energieaufnahme der einzelnen Kerne reduziert, dadurch kann die Leistung der CPU nicht voll genutzt werden. Erst wenn man den Powerplan umkonfiguriert auf “High Performance” kommt man in den Genuss der vollen CPU-Leistung.

Windows Server Powerplan für mehr Performance anpassen

Nun kann man sich lange durch die Einstellungen der Power Optionen des Windows Betriebssystemes klicken:

1. Start => Control Panel
2. Control Panel => Power Options (notfalls das Wort “Power” in das Suchfeld eingeben)
3. Per default ist die Auswahl der einzelnen Powerpläne disabled, man muss also erst auf den Link “Change settings that are currently unavailable” klicken.
4. Nun kann man den Powerplan “High Performance” auswählen.
5. Power Option Fenster schliessen

Man könnte dies aber – wie der Beitragstitel andeutet – auch mit Powershell ändern, dies vereinfacht bei Wiederholungen die Änderung am jeweiligen Server.

function SetPowerPlan([string]$PreferredPlan) 
{ 
    Try
    {
        Write-Host "Setting Powerplan to $PreferredPlan" 
        $HighPerf = powercfg -l | %{if($_.contains("High performance")) {$_.split()[3]}}
        $CurrPlan = $(powercfg -getactivescheme).split()[3]
        if ($CurrPlan -ne $HighPerf) {powercfg -setactive $HighPerf}
    } 
    Catch
    {
        Write-Host "Setting the value of powerplan properties failed." -ForegroundColor Red
    }
}

Man muss zwar den leichten Umweg über die “Kommandozeile” nehmen, diese lässt sich mit Powershell aber recht einfach “auslesen” und weiter verarbeiten bzw manipulieren.
Also erst die ID des “High Performance” Powerplan ermitteln, dann auch die ID des aktuellen Powerplans, beide IDs miteinander vergleichen, bei Anweichungen wird über die powercfg.exe der neue Powerplan aktiviert.

Wie ich mittlerweile gelernt habe… typisch Powershell… kurz, einfach und wirksam 😉
Ich kann nur jedem empfehlen sich mit Powershell für den SQL Server zu beschäftigen, in den nächsten Beiträgen werde ich noch einige Snippets veröffentlichen.

Morgen finden erst einmal die Pre-Cons im Rahmen des SQLSaturday München #555 statt, auch hier werde ich wieder viel zum Thema Automatisierung auf SQL Servern mittels Powershell erfahren. Garantiert wieder mit vielen neuen Ideen für weitere Anwendungen von Powershell im täglichen Einsatz.

Björn arbeitet in Hamburg als Datenbank-Administrator und Head of Competence für MS SQL und mySQL. Er nimmt regelmäßig an den PASS Regionalgruppen Treffen in Hamburg, den Veranstaltungen der PASS wie SQLSaturday und SQLGrillen teil und er organisiert in Hamburg das Azure Meetup. Er interessiert sich neben den Themen rund um den SQL Server, Powershell und Azure für Science-Fiction, Snowboarden, Backen 😉 und Radfahren.