Freitag, 23. Oktober 2015

Timestamped Logfiles in Powershell (even for long running commands)

Anyone who writes some PS Scripts which should run automated want to know what happens when, especial when Shit Happens. Logfiles save your ass - because when you know what went wrong it is easier to find who caused it.

I used several aproaches in the past, Using Out-File -Append is working fine too for a "quick info" but here my favorite using the .Net Streamwriter which is the fastest and most reliable way.

1. Define your Logfile, here i stamp it with DAY only, be aware this approach will overwrite your file with every run if has the same name - if you want to run it more than once per day just put a more detailed timestamp ("yyyyMMdd-hhmm") in it.

$LogfileName = $(-join('c:\_Script\',$(Get-Date -Format "yyyyMMdd"),'_MyScript.log'))
$global:Logfile = [System.IO.StreamWriter] $LogfileName
$Logfile.AutoFlush = $true

2. Define a function for "quick" entrys.

function LogEntry ($Log) {@($Log)|%{
   if ($_) {
      $Logfile.WriteLine((Get-Date -Format 'yyyy-MM-dd HH:mm:ss:fff ') + $_.ToString())
         }
      }
   }

3. How to use it ..

LogEntry("My Script was started")

Variables can put in ..

$user=&'whoami'
LogEntry("I run under the Account $user")

Be aware that when you want to log a property etc you have to enclose the variable with $() ...

$vm=get-view -type VirtualMachine Filter @{"Name"=$vmname}
LogEntry "MyScript is messing up the VM $($vm.Name) which has Status $($vm.Runtime.PowerState) ."

If your output has several lines ...

Set-NaCifsShareAcl -User $user -Share $share -AccessRights 'Full Control' -OutVariable Log -ErrorAction Stop
LogEntry($Log|Out-String -Stream)

If you dont trust your Command use it in try catch and get the Errormessage in your Logfile.

try {
   $data = Import-CSV '\\fileserver\mydata.csv' -ErrorAction Stop
   LogEntry("Importing Data from Share went fine, got $($data.Count) entries.")
   } catch {
   LogEntry("Importing Data from Share went wrong with $($_.Exception.Message) .")
   }

This do his job in most cases, and you get usefull informations in your log. 

4. But how about when you start some long running command like a backuptask from command line. You can capture the output in a variable and enter it in the log - but the caveeat is that the entries will only show up AFTER the command is done and all lines will share the timestamp when it was finished. In worst case you have a hanging command/process and you have no glue which state it has.

So how to fill up the logfile while the command is still running in realtime, so we can watch it with baretail or similar ?

We need another function, which does only the timestamping and is able to pipe and stream.

function TimeStampIt {process{"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss:fff ') $_"}}

Now we pipe the console output of our command through this function line by line to the WriteLine method of the Logfile Object. The "%" is a alias for "foreach".

My Labsample is a simple cmd File which ping local host.

&'C:\testping.cmd'|TimeStampIt|%{$Logfile.WriteLine($_)}

This is the result in your Logfile, you see every line has it own Timestamp, while the command is running you can watch the logfile per Baretail or similar.

2015-10-23 10:35:11:574  
2015-10-23 10:35:11:574  C:\Users\blablabla>ping 127.0.0.1 -n 5 
2015-10-23 10:35:11:590  
2015-10-23 10:35:11:590  Pinging 127.0.0.1 with 32 bytes of data:
2015-10-23 10:35:11:590  Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
2015-10-23 10:35:12:604  Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
2015-10-23 10:35:13:618  Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
2015-10-23 10:35:14:632  Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
2015-10-23 10:35:15:646  Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
2015-10-23 10:35:15:646  
2015-10-23 10:35:15:646  Ping statistics for 127.0.0.1:
2015-10-23 10:35:15:646      Packets: Sent = 5, Received = 5, Lost = 0 (0% loss),
2015-10-23 10:35:15:646  Approximate round trip times in milli-seconds:
2015-10-23 10:35:15:646      Minimum = 0ms, Maximum = 0ms, Average = 0ms

5. Dont forget the close the Logfile in the end of the Script.

LogEntry("Done my Job.")
$Logfile.Close()

I hope this will help you to make "good" and easy to troubleshoot scripts.

Freitag, 9. Oktober 2015

Notes about Netapp Ontap 8.3 cDot Simulator

Notes about Netapp Ontap 8.3 cDot Simulator

Preq:

- The Backendnetwork consist of 2 IPs per Clusternode, plan 1 IPs per Clusternode, 1 for cluster management, and 2 for SVMs - so 9 completly

- it create 4 interfaces, 2 cluster ones 1 you assign the "e0M" equivalent Managementport the 4th is the Service network
- Cluster network use on default EPIPA addresses 168.254.0.0/16

1. After deploying the OVA use vmkfstools -X 550g /vmfs/volumes/[DS]/[SIMVM]/[SIMVM]_1.vmdk to grow the harddrives which store the simulated harddrives. It works directly on ESX 5.1U2 - could be that it is nessesary to change the type to lsilogic (replace ide) by editing the [SIMVM]_1.vmdk file, and change it back after grow.

2. The Console of the filer VM switch on [Scroll lock] ("Rollen dear Germans) all the time, which blocks the command line.

3. The nodes need to "swipe" his config, press CTRL-C during boot when asked, use '4' to wipe the config  - a loooong reboot will be initiated which prepare the data disk for the virtual images.

4. We dont want to use this images, thatswhy we quit the wizzard. We have to set a admin password, unlock the diag user, and set a password for diag - use something easy here, like passwd12 because it is hard to enter it on the sloppy VM console, later you can change a secure one.

security login password
Please enter password: XXXXXXXX
Please enter a new password: XXXXXXXX
Please enter it again: XXXXXXXX
security login unlock -username diag
security login password -username diag
Please enter a new password: XXXXXXXX
Please enter it again: XXXXXXXX

Now we have to exit the shell and logon as diag user again, many other guides point to "systemshell local" or "system node run-console -node local" which does not work.

exit
login: diag
Password: XXXXXXXX

cd /sim/dev/,disks
sudo rm *
cd ..
sudo vsim_makedisks -n 14 -t 36 -a 0
sudo vsim_makedisks -n 14 -t 36 -a 1
sudo vsim_makedisks -n 14 -t 36 -a 2
sudo vsim_makedisks -n 14 -t 36 -a 3
ls ,disks/
sudo halt

By the way, if you wanna play arround with "hybrid" aggregates here is the point where you could choose a SATA drive type by using -t 37 (all other simulated disktypes can be checked by vsim_makedisk -h).

Reset the SIM, press again CTRL-C and choose 5 for maintenance when prompted. You can now check the created disks with "storage show disk" as we are still on the VM Console you need to enable "scroll lock" to check the whole output with the "up" key. We assign the first 3 disks.

disk assign v0.16 v0.17 v0.18
disk show
halt

It will message you some "unable to obtain owner" warning, just ignore it.

Reboot the SIM press again CTRL-C and wait until prompt. Enter selection 4 ‘Clean configuration and initialize all disks’ and answer ‘y’ to the two prompts.

5. Now as you have a nice Simulator a wizzard ask you to configure the management interface, your third vNIC on the VM.

Your "admin" user has no right to use SSH, the confusing thing it just close the ssh session after authentification. You have to assign the right for SSH by :

'security login create -user-or-group-name admin -role admin -application ssh  -authmethod password -comment "SSH Access for Admin"'

4Console ninjas it is 'sec[TAB]c[TAB]m[TAB][TAB]admin [TAB]ss[TAB][TAB]pa[TAB][TAB] [TAB] [TAB]c[TAB]"SSH Access for Admin"'

Now you can use your favorite ssh client (mine is portableKiTTY) to access the management IP, and get rid of the sloppy VM console.

Connect to your Management IP and run "cluster setup" to finish. The wizzard is quite straightforward.


6. A cluster with one node is quite boring. After deploying the 2. node and blow up the Sim-Disk vmdk, you have to change the SYS ID of the SIM to fit to the licences. When you boot the SIM the first time stop the Bootloader by pressing "some key - not Enter", in the Bootloader menu type this :

setenv SYS_SERIAL_NUM 4034389062
setenv bootarg.nvram.sysid 4034389062
boot

If you forget this step, and have allready booted the SIM once you can enter the "diag" mode (step 4) and delete the ",reservation" file in the ",disk" folder before you change the SYSID.

cd /sim/dev/,disks
sudo rm ,reserv*

7. For someone who is used 7-mode vFiler it is confusing that a SVM can get a Active Directory account without running a CIFS server, while the ADMIN SVM (your Cluster Instance) can run a NIS/LDAP client - but can not join a Domain neither sees their users, but the Admin-SVM can tunnel his requests to a SVM serving CIFS and beeing member or just beeing Member.

This joins a SVM to a Domain without running a CIFS Server :

vserver active-directory create -vserver [SVM] -domain [Domain FQDN] -account-name [CN of the Account] -ou ["OU=folder-3,OU=folder-2,OU=folder-1"|"CN=Computer"]

security login domain-tunnel create -vserver [SVM]

Now you can use the security login create command with -authmethod domain to provide Domainusers (really only users, not groups) access to the Cluster by SSH and HTTP (ontapi).

Crazy Powershell Oneliners

Here i present some 'useful' Oneliners - no need to understand them, will be extendet over the time

#Binding Order


Get IP Binding Order, it reads a registry key and print out the IPs

([WMIClass]"Root\Default:StdRegProv").GetMultiStringValue(2147483650,"SYSTEM\CurrentControlSet\services\Tcpip\Linkage","Bind").sValue|%{$(Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "SettingID = '$($_.Substring(8))'").IPAddress}

For a lesser known enviroment you properly want to know if your Binding serves the NIC first which has the default Gateway.

([WMIClass]"Root\Default:StdRegProv").GetMultiStringValue(2147483650,"SYSTEM\CurrentControlSet\services\Tcpip\Linkage","Bind").sValue|%{$(Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "SettingID = '$($_.Substring(8))'")|Select IPAddress, Description,DefaultIPGateway}

You should see the gateway on the first line, if not something is wrong.

Over Remote - this is surely usefull for healthchecks

$(Get-WmiObject -computer [COMPUTERNAME] -Namespace "Root\Default" -List | ?{$_.Name -eq "StdRegProv"}).GetMultiStringValue(2147483650,"SYSTEM\CurrentControlSet\services\Tcpip\Linkage","Bind").sValue|%{$(Get-WmiObject -computer [COMPUTERNAME] -Class Win32_NetworkAdapterConfiguration -Filter "SettingID = '$($_.Substring(8))'")|Select IPAddress, Description,DefaultIPGateway}

#Get all RSAT Tools

dism /Online /Get-Capabilities|Select-String Rsat|%{dism /Online /Add-Capability /CapabilityName:$($($_|Out-String).Split(':')[1].Trim())}