Let's face it, we like to believe that disaster will never happen, but how many times have you accidentally deleted a document you have been working on for a presentation without a working backup?  Odds are more than once.  If it can happen on your desktop, then it can happen on the network and imagine the situation where your network configuration is running the bread and butter for your company.  Wouldn't it be nice to have the knowledge that your network configuration is securely archived for easy recovery?  Well, this article will illustrate an application that performs configuration archiving for a BIG-IP device by allowing for the creation of configuration restore points as well as remote file upload/download capabilities.  Read this article, and fear no more!

Usage

The arguments for this application are the address, username, and password of the BIG-IP along with optional argument for the operation to be performed along with an optional configuration file name.  This is declared in the top of the script with the following param statement.  There is also a Write-Usage function to display the arguments to the user.

param (
  $g_bigip = $null,
  $g_uid = $null,
  $g_pwd = $null,
  $g_cmd = $null,
  $g_name = $null
);

$DEFAULT_CHUNK_SIZE = (64*1024);

Set-PSDebug -strict;

#-------------------------------------------------------------------------
# function Write-Usage
#-------------------------------------------------------------------------
function Write-Usage()
{
  Write-Host "Usage: ConfigArchive.ps1 host uid pwd [list|save|delete|download|install|rollback|upload [name]]";
  exit;
}

Initialization

As is with all of my PowerShell scripts, the initialization component will look to see if the iControlSnapIn is loaded into the current PowerShell session.  If not, the Add-PSSnapIn Cmdlet is called to add the snapin into the runtime.  Then a call to the Initialize-F5.iControl cmdlet to setup the connection to the BIG-IP.  If this succeeds, then the rest of the optional arguments are processed.

The application will then look for the "list", "save", "delete", "download", "install", "rollback", or "upload" commands. For those the associated functions will be called, otherwise the usage message will be presented to the user.

function Do-Initialize()
{
  if ( (Get-PSSnapin | Where-Object { $_.Name -eq "iControlSnapIn"}) -eq $null )
  {
    Add-PSSnapIn iControlSnapIn
  }
  $success = Initialize-F5.iControl -HostName $g_bigip -Username $g_uid -Password $g_pwd;
 
  return $success;
}

if ( ($g_bigip -eq $null) -or ($g_uid -eq $null) -or ($g_pwd -eq $null) -or ($g_cmd -eq $null) )
{
  Write-Usage;
}

if ( Do-Initialize )
{
  switch ($g_cmd.ToLower())
  {
    "list" {
      Get-ConfigList;
    }
    "save" {
      Save-Configuration $g_name;
    }
    "delete" {
      Delete-Configuration $g_name;
    }
    "download" {
      Download-Configuration $g_name;
    }
    "install" {
      Install-Configuration $g_name;
    }
    "rollback" {
      Rollback-Configuration;
    }
    "upload" {
      Upload-Configuration $g_name;
    }
    default {
      Write-Usage;
    }
  }
}
else
{
  Write-Error "ERROR: iControl subsystem not initialized"
}

Querying the list of configuration files

In case you didn't know what the existing configurations are on your BIG-IP, this function will query that list and write the configuration files along with their associated timestamp.  The System.ConfigSync.get_configuration_list() iControl method returns a list of ConfigFileEntry structures containing these values and they are then output to the console with the Write-Host cmdlet.

function Get-ConfigList()
{
  $ConfigFileEntryList = (Get-F5.iControl).SystemConfigSync.get_configuration_list();
  Write-Host "Available Configuration Files";
  Write-Host "-----------------------------";
  foreach ($ConfigFileEntry in $ConfigFileEntryList)
  {
    $file_name = $ConfigFileEntry.file_name;
    $file_datetime = $ConfigFileEntry.file_datetime;
   
    Write-Host "-> $file_name ($file_datetime)";
  }
}

Saving the Configuration

Ok, it's time to archive.  At this point, the first step you'll want to take is to save the current configuration.  the Save-Configuration function will use the iControl System.ConfigSync.save_configuration() method to save the entire configuration to a configuration archive (.ucs file) on the BIG-IP with the specified name.  If a name is not given, then the included Generate-ConfigName function will do it based on the current BIG-IP hostname appended with the date and time.

function Save-Configuration()
{
  param($config_name = $null);
  if ( $config_name -eq $null )
  {
    $config_name = Generate-ConfigName;
  }
  Write-Host "Saving Configuration to file $config_name"
  (Get-F5.iControl).SystemConfigSync.save_configuration($config_name, "SAVE_FULL");
 
  Get-ConfigList;
}

function Generate-ConfigName()
{
  $now = [DateTime]::Now;
  $year = $now.year;
  $month = $now.month; if ($month -lt 10) { $month = "0${month}" }
  $day = $now.day; if ( $day -lt 10 ) { $day = "0{$day}" }
  $hour = $now.hour; if ( $hour -lt 10 ) { $hour = "0{$hour}" }
  $minute = $now.minute; if ( $minute -lt 10 ) { $minute = "0{$minute}" }
  $second = $now.second; if ( $second -lt 10 ) { $second = "0{$second}" }
  $config_name = "${g_bigip}-${year}${month}${day}-${hour}${minute}${second}.ucs"
  return $config_name;
}

Deleting a configuration

Now, you may not wish to keep all the configurations on your BIG-IP since the whole idea of archiving is getting them off the device and onto a secured resiliant storage of some sort.  Thus the Delete-Configuration function will call the iControl System.ConfigSync.delete_configuration() method with the supplied configuration name and will delete that file from the filesystem on the BIG-IP.

function Delete-Configuration()
{
  param($config_name);
  if ( $config_name -eq $null )
  {
    Write-Usage;
  }
  else
  {
    Write-Host "Deleting configuration $config_name";
    (Get-F5.iControl).SystemConfigSync.delete_configuration($config_name);
   
    Get-ConfigList;
  }
}

Downloading a Configuration

Since archiving is the main goal of this application, it wouldn't be much use without the ability to download a configuration after you've saved it on the BIG-IP.  The Download-Configuration function takes as input a configuration name, and uses the iControl System.ConfigSync.download_configuration() method to download the file.  Now, configurations can be quite large at times so that is why we've implemented the download_configuration, as well as the more generic download_file, methods to operate in a chunking mode.  The use is illustrated in the Download-Configuration function.  I'm using the native .Net FileStream and BinaryWriter classes to handle output to local disk.  I tried a pure native Set-Content and Add-Content implementation but the performance was much slower than accessing the .Net File IO classed directly.

Essentially this function sits in a loop requesting $chunk_size (64k) bytes of data from the file at the given file_offset.  The file_offset is incremented on the server for the next request.  Then the downloaded data is written to the BinaryWriter class to append the data to the local file in the current working directory with the same name as the downloaded configuration.

When the chain_type of FILE_LAST, or FILE_FIRST_AND_LAST is returned, signifying the end of the data, the loop is exited and the local files are closed.

function Download-Configuration()
{
  param($config_name);
 
  Write-Host "Downloading Configuration file $config_name"
 
  $loc = Get-Location
  $local_file = "$loc\$config_name";
 
  $ctx = New-Object -TypeName iControl.SystemConfigSyncFileTransferContext;
  $chunk_size = $DEFAULT_CHUNK_SIZE;
  $file_offset = 0;
  $bContinue = 1;
  $mode = [System.IO.FileMode]::CreateNew;
  if ( Test-Path $local_file )
  {
    $mode = [System.IO.FileMode]::Truncate;
  }
 
  $fs = New-Object -TypeName System.IO.FileStream -argumentList ($local_file, $mode);
  $w = New-Object -TypeName System.IO.BinaryWriter -argumentList ($fs);
 
  while($bContinue -eq 1)
  {
    $ctx = (Get-F5.iControl).SystemConfigSync.download_configuration($config_name, $chunk_size, [ref]$file_offset);
    $w.Write($ctx.file_data, 0, $ctx.file_data.Length);

    Write-Host "Bytes Transferred: $file_offset";
    if ( ($ctx.chain_type -eq "FILE_LAST") -or ($ctx.chain_type -eq "FILE_FIRST_AND_LAST") )
    {
      $bContinue = 0;
    }
  }
  $w.Close()
  $fs.Close()
}

Uploading an Archived Configuration

Now that you find yourself in the situation of wanting to restore a configuration from your local archive store, you'll first need to upload the configuration to the BIG-IP.  The Upload-Configuration function basically does the opposite as the Download-Configuration function.  It sits in a loop reading $chunk_size bytes from a BinaryReader object based on the locale file requested.  As long as there are more bytes to read, they are passed up to the BIG-IP to be appended to the specified configuration file with the iControl System.ConfigSync.upload_configuration() method.  Once all of the bytes have been uploaded, the local file handles are closed and the function is exited.

function Upload-Configuration()
{
  param($config_name);

  $loc = Get-Location
  $local_file = "$loc\$config_name";
 
  Write-Host "Uploading file $local_file";
 
  $ctx = New-Object -TypeName iControl.SystemConfigSyncFileTransferContext;
  $bContinue = 1;
  $ctx.chain_type = "FILE_FIRST";
  $chunk_size = $DEFAULT_CHUNK_SIZE;
  $total_bytes = 0;
  $fs = New-Object -TypeName System.IO.FileStream -argumentList ($local_file, [System.IO.FileMode]::Open);
  $r = New-Object -TypeName System.IO.BinaryReader -argumentList ($fs)

  while ($bContinue -eq 1)
  {
    $ctx.file_data = $r.ReadBytes($chunk_size);
    if ( $ctx.file_data.Length -ne $chunk_size )
    {
      # At the end, check to see if it is the first request
      if ( $total_bytes -eq 0 )
      {
        $ctx.chain_type = "FILE_FIRST_AND_LAST";
      }
      else
      {
        $ctx.chain_type = "FILE_LAST";
      }
      $bContinue = 0;
    }
    $total_bytes = $total_bytes + $ctx.file_data.Length;
   
    # Upload bytes
    (Get-F5.iControl).SystemConfigSync.upload_configuration($config_name, $ctx);
   
    # move to middle
    $ctx.chain_type = "FILE_MIDDLE";
   
    Write-Host "Uploaded $total_bytes bytes";
  }
 
  $r.Close();
  $fs.Close();
}

Installing a Configuration

Once you have a configuration uploaded, you'll want to install that configuration into the running configuration system.  This is done with the Install-Configuration function that in turn calls the iControl System.ConfigSync.install_configuration() method with the specified configuration name.  When the installation of the configuration is complete, a message will be written to the console indicating a success.

function Install-Configuration()
{
  param($config_name);
  if ( $config_name -eq $null )
  {
    Write-Usage;
  }
  else
  {
    Write-Host "Installing Configuration $config_name";
    (Get-F5.iControl).SystemConfigSync.install_configuration($config_name);
    Write-Host "Configuration successfully installed.";
  }
}

Rolling Back to the Last Configuration

Now, let's say you accidentally installed the wrong configuration and want to undo your operation?  Never fear, the Rollback-Configuration function calls the iControl System.ConfigSync.rollback_configuration() method that will roll back the running configuration to the previous settings before the last "load" operation.

function Rollback-Configuration()
{
  Write-Host "Rolling back configuration"
  (Get-F5.iControl).SystemConfigSync.rollback_configuration();
  Write-Host "Configuration Rollback successful"
}

Conclusion

So, next time you find yourself in the situation where you have a hard drive malfunction, or even something as rare as an operator accidentally deleting configuration objects (which never happens - right???), if you were smart enough to implement a solution illustrated by this application, you'll just be a few clicks away from a full restore of your configuration and your job will be secure for a little while longer.

The entire script can be found in the iControl CodeShare under PsConfigArchiving.

 

Get the Flash Player to see this player.