Showing posts with label distributed file system. Show all posts
Showing posts with label distributed file system. Show all posts

14 May, 2016

Find target of DFS links - OS

There are many times when people need to know the target folder of a DFS link. However, when you have multiple levels of DFS namespaces, for example, you have a Domain Based root called \\tatooine.com\root and under it there are DFS folders and links, but one link points to a standalone DFS root, e.g.: \\tatooine\root\cities\moseisley and this pointer points to another DFS root: \\c3podc1\root and then there are folders and pointers under this namespace as well.

It takes many clicks on DFS Management console to open each DFS namespace and root and walk trough them. Alternatively, you can use the DFSN module on Windows Server 2012 but the command Get-DFSNFolderTarget doesn't understand the multi level structure, it can only wok in one given DFSN root.

Let's take an example, we want to know which file server and which folder the following path points to : \\tatooine.com\root\cities\moseisley\cantina\stock
In the DFS management console, you have to
  1. add \\tatooine.com\root namespace for display
  2. drill down to the MosEisley folder to find that it actually points to \\c3podc1\moseisley
  3. so then you add \\c3podc1\moseisley DFS root to the console
  4. drill down to the Stock folder under Cantina and find that it points to \\fileserver\share1\documents folder

multi-level dfs roots
Find a target in a multi-level DFS structure on DFS Management Console



















It's not very rewarding after you get the 5th of these requests from users...

Here is a quick script which can walk through the folder structure, just specify the full path of a folder and it will tell you for each level where it points to, the script requires the DFSN PS module installed on the box - included in the DFS Management Tools Feature.
If the given folder level is just a DFS folder without target, obviously it will show and empty target, e.g.:

DFS lookup with powershell


On the above picture:

  • \\tatooine.com\root\cities is just a folder because it doesn't point anywhere
  • \\tatooine.com\root\cities\moseisley points to another DFS root
  • \\c3podc1\moseisley\Cantina is again just a folder without DFS target
  • \\c3podc1\moseisley\Cantina\stock is the pointer we wanted to look for and it points to \\fileserver\share1\documents


Here is the code:
 param([string]$folderpath = "")  
   
 $erroractionpreference = "silentlycontinue"  
 Import-module DFSN
 $arr = $folderpath.split("\\")  
 $basepath = ("\\" + $arr[2] + "\" + $arr[3])  
 $testpath = $basepath  
   
 # go from the 4th element of the path and try to get the target of each
 for($i = 4; $i -lt $arr.count; $i++){  
    $testpath = $testpath + "\" + $arr[$i]  
    $result = get-dfsnfoldertarget $testpath  
    $testpath + "->" + $result.targetpath  
   
    if($result.targetpath){  
       $testpath = $result.targetpath  
    }  
 }  



12 October, 2013

DFS link count check - DFS

There have been cases when people started screaming that their folders disappeared or they are there but are not accessible. OK... nicely defined problem, this is the sort of issue you want on a Monday morning, just to get the adrenalin flowing a bit. Let's see then, after checking a couple of these folders, it turns out that all of  them are DFS links. If I check those, they work for me... so this issue is even better that I thought 5 mins ago.
Let's check all DFS servers then. All of them seem to be healthy, but hold on... there's one which shows half of the number of pointers than it supposed to.
If you check locally, the DFS folder structure is there, but if you check it via the DFS root share, half the pointers are gone... weird, quick reboot helps, MSFT wants to reproduce the issue to get to the bottom of it... well, I don't want to reproduce it to be honest, but what I can do is run a quick check in the morning which would make sure I catch this before the users do.

If you look at the issue again, it's easy to pinpoint what to check: the count of directories on the DFS root on the DFS server's local drive is OK, however, the count of DFS links are not the same. Ok, so I need a script which counts both, compares them and if they are different, sends an alert.

Count number of folders which point to another folder (ReparsePoint):
dir $dfsdir -rec | ? {$_.Attributes -imatch 'ReparsePoint'} | measure | %{$_.count}


Count number of DFS links:
dfscmd /view $dfsroot /batch | ?{($_ -imatch "\\\\$dfsserver\\$dfsRootName") -and ($_ -inotmatch "^rem| /add")} | measure | %{$_.count}


Dfscmd lists all pointers and actions how to recreate them (map or add), we filter out all lines apart from the ones with /map and we also filter out every line which does not have the DFS root in them.


We just need to compare these numbers for each server, evaluate the result, put them in a nice output and e.g. send them via email.

Output of a GREEN checkout on two DFS roots


In my final script, I used Win32_Process to run the dir and dfscmd commands on the remote DFS servers and also used PowerShell jobs to run them in parallel therefore it's a lot quicker, however for the purpose of this article, I simplified the code to show the essence of the logic.


 $ErrorActionPreference = "SilentlyContinue"  
   
 #### Function for checking if a host is alive on the network  
 function PingServer ([string]$srv){  
      $ping = new-object System.Net.Networkinformation.Ping  
      Trap {Continue} $pingresult = $ping.send($srv, 3000, [byte[]][char[]]"z"*16)  
      if($pingresult.Status -ieq "Success"){$true} else {$false}  
 }  
                  
 $objColl = @()  
 $dfsrootlist = @($Input)  
   
 foreach($dfsroot in $dfsrootlist){  
      $obj = "" | Select DFS_Root,Access,Dir_Count,Link_Count,Result  
      $obj.DFS_Root = $dfsroot  
      $obj.Access="N/A"  
      $obj.Dir_Count="N/A"  
      $obj.Link_Count="N/A"  
      $obj.Result="N/A"  
        
      $dfsserver = $dfsroot.split("\")[2]  
      $dfsRootName = $dfsroot.split("\")[3]  
      $dfsdir = "\\" + $dfsserver + "\d$\DFSRoots\" + $dfsRootName  
        
      if(PingServer $dfsserver){  
           $obj.Access = "OK"  
   
           $obj.Dir_count = dir $dfsdir -rec | ? {$_.Attributes -imatch 'ReparsePoint'} | measure | %{$_.count}  
           $obj.Link_count = dfscmd /view $dfsroot /batch | ?{($_ -imatch "\\\\$dfsserver\\$dfsRootName") -and ($_ -inotmatch "^rem| /add")} | measure | %{$_.count}  
   
           # checking if link or directory count is 0  
           if(!$obj.Link_Count -or ($obj.Link_Count -eq 0)){  
                $obj.Result = "Error: Link count is 0"  
           }  
             
           if($obj.Link_Count -eq "N/A"){  
                $obj.Result += "Error-Could not collect dfs link count data"  
           }  
             
           if($obj.Dir_Count -eq "N/A"){  
                $obj.Result += "Error-Could not collect directory count data"  
           }  
             
           if(!$obj.Dir_Count -or ($obj.Dir_Count -eq 0)){  
                $obj.Result = "Error: Directory count is 0 <br>"  
           }  
             
           if($obj.Dir_Count -ne $obj.Link_Count){  
                if($obj.Result -eq "N/A"){$obj.Result = "Link/Dir count mismatch"}  
           }  
           else{  
                if($obj.Result -eq "N/A"){$obj.Result = "OK"}  
           }  
      }  
      else{  
           $obj.Access = "Error"  
      }  
      $objColl += $obj  
 }  
   
 $objColl  
   
 $rfbcount = ($objColl | ?{$_.result -ine "ok"} | measure).count  
 $Subject = "DFS Link Count"  
 if($rfbcount -ge 1){  
      if($rfbcount -ge 2)     {$subject += " - RED"}  
      else                    {$subject += " - AMBER"}  
 }  
 else{  
      $subject += " - GREEN"  
 }  
   
 $smtpServer = "smtpdroid.tatooine.com"  
 $smtpFrom = "reports@tatooine.com"  
 $smtpTo = "tompa@tatooine.com"  
 $message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto  
 $message.Subject = $Subject  
 $message.IsBodyHTML = $true  
 $message.Body = $objColl | ConvertTo-Html  
 $smtp = New-Object Net.Mail.SmtpClient($smtpServer)  
 $smtp.Send($message)  
   


t

06 July, 2013

Compare two roots - DFS

Imagine you have many DFS (Distributed File System) servers across the globe and they are in pairs, so each location has 2 DFS servers which hold the same set of 1000+ DFS pointers in a load balanced fashion. In this case you want to make sure both these servers have exactly the same DFS pointers, otherwise when a user hits the first box will see different folder structure when hitting the second box. Of course you get a call from a user saying sometimes folders disappear and then when opening up the namespace again they are there. Hm... very suspicious. First thing that comes to mind is that the 2 DFS servers have a difference in the number of DFS pointers. How could you figure out from the 1000+ pointer which are the ones that don't exist on both servers. Bingo... a bit of PowerShelling will help (You can do it with dfsutil /compare as well but that wouldn't make me write an article on a PS solution and also it doesn't have the flexibility to pipe the output to  another command ;) ).

The easiest way of doing this is using dfsutil.exe to export the namespace to an xml file on both servers, read them, compare and then make a readable output of the differences.

Export namespace to an xml:
$command = "dfsutil /root:$dfsrt /export:c:\temp\$filename /api"
writelog 0 "running command: $command" 
$err = invoke-expression $command

Pick up the content with PowerShell but only the part we need and store it in our custom object for later use (pointer name and target):
$tmpXMLcontent = ([xml] [string]::join("`n",(get-content c:\temp\$filename))).root.link | %{$_.name}
$script:sObject.DFS_Links = $tmpXMLcontent

When we have this from both DFS servers, we can compare them and store differences in separate variables:
$notOnFirstHost = compare $objcoll[0].DFS_links $objcoll[1].DFS_links -SyncWindow 100000 | ?{$_.SideIndicator -ieq "=>;"} | %{$_.InputObject}
$notOnSecondHost = compare $objcoll[0].DFS_links $objcoll[1].DFS_links -SyncWindow 100000 | ?{$_.SideIndicator -ieq "<="} | %{$_.InputObject}

Now go through the content of the 2 variables and create our custom object collection with the results, e.g.:
$notOnFirstHost | %
$obj = "" | select DFS_Link,$objColl[0].ComputerName,$objColl[1].ComputerName
$obj.DFS_Link = $_ 
$obj."$($objColl[0].ComputerName)" = "Doesn't exist" 
$obj."$($objColl[1].ComputerName)" = "Exists" 
$resColl += $obj 
}


If we want to make the data even more detailed, we can look up the target folder of the particular DFS link from the xml dump to see where it is supposed to point to:
function lookupDFSTarget($fdfslink, $fdfsdumpfile, $fxmlcontent){
$retvalArr = @() 
$targets = $fxmlcontent | ?{$_.name -ieq $fdfslink} | %{$_.target} 
$targets | %
   $tmpstr = ("\\" + $_.server + "\" + $_.folder) 
   $retvalArr += $tmpstr 
}
$retval = [string]::Join(";", $retvalArr
return $retval 
}
 



The output is something like this:



The full script (usage compare-dfslinks.ps1 -dfsroots \\c3podfs1\root,c3podfs2\root -lookup)

   
param (     [string] $dfsroots = "",  
           [string] $log = "",  
           [switch] $lookupTarget = $false)  
 
 $logfile = new-item -type file "C:\temp\compare-dfslinks.log" -force
 
 #### Function for creating log entries in a logfile and on standard output  
 function writeLog ([int]$type, [string]$message, [string]$modifier) { #usage: writeLog 0 "info or error message"  
      # $modifier: <nonew | extend>  
      # Value nonew: writes the output the the console and the logfile without carriage return  
      # Value raw: writes the message to the output without date and   
      # both values can be used e.g. for writting to the console and logfile and put a status message at the end of the line as a second step  
      $date = get-date -uformat "%Y/%m/%d %H:%M:%S"  
      if($modifier -eq "extend"){  
           switch ($type) {  
                "0"     {$color = "Green"}  
                "1"     {$color = "Yellow"}  
                "2"     {$color = "Red"}  
           }  
      }  
      else{  
           switch ($type) {  
                "0"     {$message = $date + ", INF, " + $message; $color = "Green"}  
                "1"     {$message = $date + ", WAR, " + $message; $color = "Yellow"}  
                "2"     {$message = $date + ", ERR, " + $message; $color = "Red"}  
           }  
      }  
        
      if($modifier -eq "nonew"){  
           write-host $message -ForegroundColor $color -NoNewLine  
           $bytes = [text.encoding]::ascii.GetBytes($message)  
           $bytes | add-content $logfile -enc byte  
      }  
      else{  
           write-host $message -ForegroundColor $color  
           Add-Content $logfile $message  
      }  
 }  
   
   
   
 #### Function to lookup DFS target of a DFS link from dump xml  
 function lookupDFSTarget($fdfslink, $fdfsdumpfile, $fxmlcontent){  
      $retvalArr = @()  
      $targets = $fxmlcontent | ?{$_.name -ieq $fdfslink} | %{$_.target}  
      $targets | %{  
           $tmpstr = ("\\" + $_.server + "\" + $_.folder)  
           $retvalArr += $tmpstr  
      }  
      $retval = [string]::Join(";", $retvalArr)  
      return $retval  
 }  
   
   
 $objColl = $dfscontent = $resColl = @()  
 $k = 1  
 $dfsrootlist = $null
   
 $dfsrootsArr = @($dfsroots.split(" "))  
 $dfsrootlist += $dfsrootsArr  
   
 $dfsrootlistlength = $dfsrootlist.length  
   
   
 if($dfsrootlistlength -gt 0){  
      if($dfsrootlistlength -eq 2){  
           foreach($dfsrt in $dfsrootlist){  
             
                Write-Progress -id 1 -activity "Performing DFS checks" -Status "Processing DFS Master $k of $dfsrootlistlength : $dfsrt " -PercentComplete ($k/$dfsrootlistlength * 100) -currentoperation "checking DFS..."  
                $filename = $dfsrt.replace("\\","")  
                $filename = $filename.replace("\","_") + ".xml"  
                $srvname = $dfsrt.split("\")[2]  
                  
                $script:sObject = new-Object -typename System.Object  
                $script:sObject | add-Member -memberType noteProperty -name ComputerName -Value $srvname  
                $script:sObject | add-Member -memberType noteProperty -name DFS_Links -Value ""  
                $script:sObject | add-Member -memberType noteProperty -name DFS_dump_file -Value ""  
                  
                if($filename -and (Test-Path "c:\temp\$filename")){  
                     Remove-Item c:\temp\$filename -Force  
                }  
                  
                $script:sObject.DFS_dump_file = "c:\temp\$filename"  
                  
                $command = "dfsutil /root:$dfsrt /export:c:\temp\$filename"  
                writelog 0 "running command: $command"  
                $err = invoke-expression $command  
                 
                $tmpXMLcontent = ([xml] [string]::join("`n",(get-content c:\temp\$filename))).root.link | %{$_.name}  
                $script:sObject.DFS_Links = $tmpXMLcontent  
                $objColl += $script:sObject  
                $k++  
           }  
      }  
 }  
 else{  
      writeLog 2 "No DFS server is specified."  
 }  
   
   
 writelog 0 "Running compare between the 2 xmls"  
 $notOnFirstHost = compare $objcoll[0].DFS_links $objcoll[1].DFS_links -SyncWindow 100000 | ?{$_.SideIndicator -ieq "=>"} | %{$_.InputObject}  
 $notOnSecondHost = compare $objcoll[0].DFS_links $objcoll[1].DFS_links -SyncWindow 100000 | ?{$_.SideIndicator -ieq "<="} | %{$_.InputObject}  
   
   
 if($notOnFirstHost -or $notOnSecondHost){  
      if($notOnFirstHost){  
           $xmlcontent = $null  
           if($lookupTarget)     {$xmlcontent = ([xml] [string]::join("`n",(get-content $($objColl[1].DFS_dump_file)))).root.link}  
           $notOnFirstHost | %{  
                  
                if($lookupTarget)     {$obj = "" | select DFS_Link,DFS_Target,$objColl[0].ComputerName,$objColl[1].ComputerName}  
                else                    {$obj = "" | select DFS_Link,$objColl[0].ComputerName,$objColl[1].ComputerName}  
                  
                $obj.DFS_Link = $_  
                $obj."$($objColl[0].ComputerName)" = "Doesn't exist"  
                $obj."$($objColl[1].ComputerName)" = "Exists"  
   
                if($lookupTarget){$obj.DFS_Target = lookupDFSTarget $_ ($($objColl[1].DFS_dump_file)) $xmlcontent }  
                $resColl += $obj  
           }  
        
      }  
        
      if($notOnSecondHost){  
           $xmlcontent = $null  
           if($lookupTarget)     {$xmlcontent = ([xml] [string]::join("`n",(get-content $($objColl[0].DFS_dump_file)))).root.link}  
           $notOnSecondHost | %{  
                if($lookupTarget)     {$obj = "" | select DFS_Link,DFS_Target,$objColl[0].ComputerName,$objColl[1].ComputerName}  
                else                    {$obj = "" | select DFS_Link,$objColl[0].ComputerName,$objColl[1].ComputerName}  
                  
                  
                $obj.DFS_Link = $_  
                $obj."$($objColl[1].ComputerName)" = "Doesn't exist"  
                $obj."$($objColl[0].ComputerName)" = "Exists"  
                  
                if($lookupTarget){$obj.DFS_Target = lookupDFSTarget $_ ($($objColl[0].DFS_dump_file)) $xmlcontent }  
                $resColl += $obj  
           }  
      }       
 }  
 else{  
      writelog 1 ("No differences between links on " + $srvArr[0] + " and " + $srvArr[1] )  
 }  
   
 $resColl  
 
   


sdfsdf