Active IQ Unified Manager Discussions

Return parameters for operation results

MPARDINIRTP
14,796 Views

I'm working on a custom command in WFA for polling two controllers for option differences. I have the results in an array, but I'm having a hard time figuring out how to get that output to the workflow user. I see the return parameters tab in the workflow results window and I'm wondering how I can get the results into that tab so the user can see the differences. Any ideas?

24 REPLIES 24

paragp
13,534 Views

This is possible in WFA 2.2RC and onwards.  If you are using powershell commands you will need to add this line in your command

Add-WfaWorkflowParameter -Name volumeOptions -Value $volOptions -AddAsReturnParameter

and for perl it is

my $wfaUtil = WFAUtil->new();

$wfaUtil->addWfaWorkflowParameter(‘volumeOptions’, $volOptions, 1);

  The Add-WfaWorkflowParameter and Get-WfaWorkflowParameter are primarily used for transferring information between commands during execution. The last parameter -AddAsReturnParameter adds the value as a return parameter to the workflow

sinhaa
13,534 Views

Add-WfaWorkflowParameter -Name volumeOptions -Value $volOptions -AddAsReturnParameter

-Value takes only a string argument, and as you said you have an Array, you can't use the array object directly. I suggest you join the array object to a string and then use it in the above command.. Something like below.

# Joining on a colon :

$volOptions = $volArray -join ":"

Add-WfaWorkflowParameter -Name volumeOptions -Value $volOptions -AddAsReturnParameter



If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

francoisbnc
12,484 Views

Is there any improvements in wfa 4.0 version with Add-WfaWorkflowParameter.

 

Francois

 

abhit
12,420 Views

No.

Anything specific you are looking for?

 

Regards

Abhi

francoisbnc
12,341 Views

I think the main missing thing is add-wfaworkflowparameter accept only [string] type. Array of objects  will be great.

 

François

sinhaa
11,913 Views

@francoisbnc

 

It may not be possible for the WFA cmdlets to accomodate other data types like array or hash etc.

 

But there is a simple way to get this done. You can easily use join and spilt methods to join the elements of an array to form a string.

 

 $a= @("1","2","3")

 $value = [System.String]::Join("~",$a)   #Choose a separator which you are sure not an element in the array. Typically ~ or _ can work,

 

Add-WfaWorkflowParameter -Name "MyArray" -Value $Value

 

 

 

-----

 

In the next command where you want this array, get the value and call split on it.

 

 

$value = Get-WfaWorkflowParameter -Name "MyArray"

$a = $value.Split("~")

 

Now you got your Array as you wanted.

 

Similar methods are available in Perl for the WFA command in perl.

 

sinhaa

 

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

sinhaa
12,313 Views

@francoisbnc

 

 

I think you wanted array of Objects  and not just an array, did you?

 

This is not possible with cmdlet, the cmdlet writes this dynamic name-value pair into a file and then fetches this data back to you. 

 

The same join-split can help you in this case too.

 

sinhaa

 

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

francoisbnc
12,304 Views

Yes it is.

 

Many return values is difficult to read and maintain, a simple objet or array of objects would simple to manage.

 

sinhaa
11,202 Views

@francoisbnc

 

If you want to pass an object or even a array of objects between WFA commands, powershell itself provided you ways to do it. 

 

You can use Powershell cmdlet Export-Clixml to save the object into the file in comamnd-1

 

and Import-Clixml to retrieve that object from the above file in command-2

 

See how to use them here. If you need assistance in using this cmdlets, I can help as well.

 

sinhaa

 

 

 

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

francoisbnc
11,196 Views

I will have a try. Many thanks.

sinhaa
11,176 Views

@francoisbnc

 

 

It works like this:

 

In command-1

===

#Have an object parameter. Not string but a Powershell Object. You can have any data type, strings, arrays, array of objects.

 

$Date = Get-Date

Export-Clixml -Path ".\date.xml" -InputObject $Date

 

#It will create a file date.xml in the WFA temp directory. You can choose any other path as well

=====

 

In command-2

==

$Date = Import-Clixml -Path ".\date.xml"

 

#The date object is obtained successfully. Now you can call all methods on this obtained object as you could.

 

$Date.day

 

====

 

 

sinhaa

 

 

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

francoisbnc
11,100 Views

@sinhaa

Your workaround looks good thanks, but just in case of simultaneous run of same workflow, I have to manage to name of the export file that cannot be the same.

I can add a random number at the end, pass it  to the next command with add-wfaparameter,  not so simple to have a clear overview of the workflow at the first look.

 

 

 

 

 

 

mbeattie
11,084 Views

Hi @francoisbnc

 

Assuming you want to pass an array of variables between WFA commands within a workflow (without using the Add-WfaWorkflowParameter cmdlet) and you are exporting those variables to file locally on your WFA server, then the file name would need to be constant to ensure all the commands in your WFA workflow read the content from the same file name. To achieve that you might consider using the workflow ID as the file name as this is a unique identifier. EG

 

$workflowID = $(Get-WfaRestParameter "workflowId")

Also you might consider using a system environment variable or reading the registry to determine the file path (or the install location of WFA) as a path to save any temporary files.

Something like this:

 

#'------------------------------------------------------------------------------
#'Enumerate the WFA install path from the registry
#'------------------------------------------------------------------------------
[String]$registryPath = "hklm:\system\currentcontrolset\services\na_wfa_srv";
[Int]$workflowID      = $(Get-WfaRestParameter "workflowId")
Try{
   [System.Object]$result  = Get-ItemProperty -Path $registryPath -ErrorAction Stop
   [String]$wfaExeSpec     = $result.ImagePath.Split("/")[0].Trim() -Replace("""", "")
   [String]$wfaServicePath = $wfaExeSpec.SubString(0, $wfaExeSpec.LastIndexOf("\"))
   [String]$installPath    = $wfaServicePath.SubString(0, $wfaServicePath.LastIndexOf("\"))
   Get-WFALogger -Info -Message "Enumerated WFA installation Path from Registry key ""$registryPath"" as ""$installPath"""
}Catch{
   Get-WFALogger -Error -Message $("Failed Reading Registry Path ""$registryPath"". Error " + $_.Exception.Message)
   Throw "Failed Reading Registry Path ""$registryPath"""
}
#'------------------------------------------------------------------------------
#'Ensure the workflows XML file exists.
#'------------------------------------------------------------------------------
[String]$fileSpec = "$installPath\$workflowID.xml"
If(Test-Path -Path $fileSpec){
   Get-WFALogger -Info -Message "Reading WFA workflow XML file ""$fileSpec"""
}Else{
   Throw "The WFA workflow XML file ""$fileSpec"" does not exist"
}
#'------------------------------------------------------------------------------

Assuming you are creating custom WFA commands, each command can enumerate the current workflow ID and hence read from the correct configuration file which will ensure a uniquie file is created and read from for each workflow in the scenario where the workflow is being executed multiple times as the ID will always be unique.

 

Hope that helps

 

/Matt

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

francoisbnc
11,071 Views

Did you speak of workflow id or runtime id?

Because, as we gave the WFA console to multiple users, workflows can be launched at the same time with a potential of multiple RW access on file.

 

 

jauling_chou
9,704 Views

Hi,

 

Is this the best way to properly generate the output when listing snapshots for a specific volume?

 

Since OCUM6 and OCUM7 don't have volume snapshots anymore, I've resorted to querying volume snapshots via WFA4 workflows. We toyed with using a datasource job in WFA4 to do it, but it takes multiple hours for each job run, and puts a strain on our vservers.

 

My workflow command basically runs the following (I've purposefully omitted error-handling code, so keep this paste simple):

 

Connect-WfaCluster $Cluster

$vol = Get-NcVol -Name $VolumeName -Vserver $VserverName
$snaplist = $vol | Get-NcSnapshot

Get-WFALogger -Info -message $("Vserver: " + $VserverName + " Volume: " + $VolumeName + " Snapshots:" + $snaplist)

Add-WfaWorkflowParameter -Name "SnapList" -Value $snaplist -AddAsReturnParameter $true

 

 

The Get-WFALogger command only shows the snapshot name for some reason. I had assumed it would show all the default columns. Anyone know why this is?

 

The Add-WfaWorkflowParameter obviously fails, and gives me the following error:

ERROR  [get volume snapshot list] Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Value'. Specified method is not supported.
ERROR  [get volume snapshot list] Failed executing command. Exception: Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Value'. Specified method is not supported.

sinhaa
9,265 Views

 

Is this the best way to properly generate the output when listing snapshots for a specific volume?

 

-----

 

No.

 

 

But the error that is being thrown : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Value'. Specified method is not supported.

 

Is NOT generated at the Get-WfaLogger cmdlet. This error is being thrown by Add-WfaWorkflowParameter

 

 

Your code has problems at the 2 cmdlets. 

 

 

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

jauling_chou
9,259 Views

@sinhaa wrote:

 

Is this the best way to properly generate the output when listing snapshots for a specific volume?

 

-----

 

No.

 

 

But the error that is being thrown : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Value'. Specified method is not supported.

 

Is NOT generated at the Get-WfaLogger cmdlet. This error is being thrown by Add-WfaWorkflowParameter

 

 

Your code has problems at the 2 cmdlets. 

 

 


Hi @sinhaa

 

Apologies if I misstated myself. I absolutely understand that the two errors are regarding the workflow parameter code. It doesn't like the fact I'm passing an Object[] as a String. I need to somehow take the snapshot list output, and massage it into a String in order to successfully pass it as a WfaWorkflowParamter, correct?

 

A secondary issue is that the Get-WfaLogger, when I test the WFA cmd, only shows me the snapshot name, which to me seems a bit odd, since Get-NcSnapshot -Template shows 3 columns of data. I'm thinking that might just be how its outputted when it tries to print a table in WfaLogger, not exactly sure.

 

 

sinhaa
9,244 Views

@jauling_chou

 

Use the below code. Powershell is a great language, it can do virtually anything.

 

 

This will work.

 

 

Connect-WfaCluster $Cluster

$vol = Get-NcVol -Name $VolumeName -Vserver $VserverName
$snaplist = $vol | Get-NcSnapshot | Out-String

Get-WFALogger -Info -message $("Vserver: " + $VserverName + " Volume: " + $VolumeName + " Snapshots:" + $snaplist)

Add-WfaWorkflowParameter -Name "SnapList" -Value $snaplist -AddAsReturnParameter $true

sinhaa

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

mbeattie
9,915 Views

Hi,

 

There are a few approaches, you can return a comma delimted string however this can be limiting if you want additional snapshot properties or require a third party orchestrator to interperet the output as an object with multiple properties.

 

If the volume contains a high number of snapshots (255 maximum) it's possible that you might also run into issues with a maximum string length of the WFA return paramater. I can't find any documentation that defines the maximum string length of a WFA return paramater but i would assume there is a limit (perhaps 1024 characters???). It's possible you might reach that limit if returning comma delimited strings in a single WFA return paramater for 255 snapshot names

 

Also if you are selecting a snapshot name (for the basis of creating a clone) then you might want to return additional properties of each snapshot such as it's creation date (rather than relying on the snapshot name to be an accurate definition of the snapshots actual creation date). WFA limits the return paramater data types to strings, integers or booleans. There is a workaround to this limitiation and that is to return a URL (which links to an XML or JSON file that your WFA command code will dynamically create and save to the the 'log-download" directory in .XML file format). EG

 

C:\Program Files\NetApp\WFA\jboss\standalone\deployments\log-download.war\$WfaJobID.xml

 

Using this method WFA can set a single return paramater URL than can be interpreted by other applications or ochrestrators as an object (not a string).

You can then have any other process read the content of the URL return parameter via https from the WFA server.

 

This can be useful if you wanted a list of volume snapshots on an ad-hoc basis and don't requrie a schedule datasource that enumerates snapshots for all clusters and all volumes. If you only want to list a single volumes snapshots on an occassional basis then it's rather inefficent to configure a WFA datasource that is constantly requesting all volume snapshots unnecessarily from all clusters. Why not just request a list of the volume snapshots when and if you actually need them?

 

Here is some example code for you. It will

 

  • enumerate snapshots (matching a snapshot naming pattern) for a specified cluster, vserver and volume
  • create the results as an XML file
  • return the URL to the XML file to be read or downloaded.

 

#'------------------------------------------------------------------------------
Param(
   [Parameter(Mandatory=$True, HelpMessage="The cluster name or IP Address")]
   [String]$ClusterName,
   [Parameter(Mandatory=$True, HelpMessage="The Vserver name")]
   [String]$VserverName,
   [Parameter(Mandatory=$True, HelpMessage="The Volume name")]
   [String]$VolumeName,
   [Parameter(Mandatory=$False, HelpMessage="The snapshot pattern to match")]
   [String]$SnapShotPatternMatch,
   [Parameter(Mandatory=$True, HelpMessage="The hostname or IP Address of the WFA server")]
   [String]$HostName,
   [Parameter(Mandatory=$False, HelpMessage="The port number of the WFA server")]
   [String]$PortNumber,
   [Parameter(Mandatory=$True, HelpMessage="The site name hosted on the WFA server")]
   [String]$SiteName,
   [Parameter(Mandatory=$False, HelpMessage="The Protocol used by the WFA server")]
   [ValidateSet("http","https")]
   [String]$Protocol
)
#'------------------------------------------------------------------------------
#'Enumerate the WFA install path from the registry
#'------------------------------------------------------------------------------
[String]$registryPath = "hklm:\system\currentcontrolset\services\na_wfa_srv";
Try{
   [System.Object]$result  = Get-ItemProperty -Path $registryPath -ErrorAction Stop
   [String]$wfaExeSpec     = $result.ImagePath.Split("/")[0].Trim() -Replace("""", "")
   [String]$wfaServicePath = $wfaExeSpec.SubString(0, $wfaExeSpec.LastIndexOf("\"))
   [String]$installPath    = $wfaServicePath.SubString(0, $wfaServicePath.LastIndexOf("\"))
   Get-WFALogger -Info -Message "Enumerated WFA installation Path from Registry key ""$registryPath"" as ""$installPath"""
}Catch{
   Get-WFALogger -Error -Message $("Failed Reading Registry Path ""$registryPath"". Error " + $_.Exception.Message)
   Throw "Failed Reading Registry Path ""$registryPath"""
}
#'------------------------------------------------------------------------------
#'Connect to the cluster.
#'------------------------------------------------------------------------------
Connect-WFACluster $ClusterName
#'------------------------------------------------------------------------------
#'Enumerate WFA envrionment variables.
#'------------------------------------------------------------------------------
[Int]$jobId           = $(Get-WfaRestParameter "jobId")
[String]$userName     = $(Get-WfaRestParameter "userName")
[String]$workflowName = $(Get-WfaRestParameter "workflowName")
[Int]$workflowId      = $(Get-WfaRestParameter "workflowId")
#'------------------------------------------------------------------------------
#'Create the XML content in an array.
#'------------------------------------------------------------------------------
[Array]$lines  = @();
[Array]$lines += "<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>"
[Array]$lines += "<root revision=""0"" schemaVersion=""0"">"
[Array]$lines += "   <cluster>"
[Array]$lines += "      <cluster_name>$ClusterName</cluster_name>"
[Array]$lines += "      <vserver>"
[Array]$lines += "         <vserver_name>$VserverName</vserver_name>"
#'------------------------------------------------------------------------------
#'Enumerate the volume snapshots.
#'------------------------------------------------------------------------------
[String]$command = "Get-NcSnapshot -Volume $VolumeName -Vserver $vserverName -ErrorAction Stop"
Try{
   $snapShots = Invoke-Expression -Command $command -ErrorAction Stop
   Get-WFALogger -Info -Message "Executed Command`: $command"
}Catch{
   Get-WFALogger -Error -Message $("Failed Executing Command`: $command. Error " + $_.Exception.Message)
   %hrow "Failed enumerating snapshots for volume ""$VolumeName"" on vserver ""$VserverName"""
}
#'------------------------------------------------------------------------------
#'Add the snapshot property values to the XML content.
#'------------------------------------------------------------------------------
[Array]$lines += "         <volume>"
[Array]$lines += "            <volume_name>$VolumeName</volume_name>"
[Array]$lines += "            <snapshots>"
ForEach($snapShot In $snapShots){
   If($SnapshotPatternMatch){
      If($snapShot.Name -Match $SnapShotPatternMatch){
         [Array]$lines += "               <snapshot>"
         [Array]$lines += "                  <snapshot_name>" + $($snapShot.Name)    + "</snapshot_name>"
         [Array]$lines += "                  <created>"       + $($snapShot.Created) + "</created>"
         [Array]$lines += "               </snapshot>"
      }
   }Else{
      [Array]$lines += "               <snapshot>"
      [Array]$lines += "                  <snapshot_name>" + $($snapShot.Name)    + "</snapshot_name>"
      [Array]$lines += "                  <created>"       + $($snapShot.Created) + "</created>"
      [Array]$lines += "               </snapshot>"
   }
}
[Array]$lines += "            </snapshots>"
[Array]$lines += "         </volume>"
[Array]$lines += "      </vserver>"
[Array]$lines += "   </cluster>"
#'------------------------------------------------------------------------------
#'Add the WFA job variables to the XML.
#'------------------------------------------------------------------------------
[Array]$lines += "   <wfa>"
[Array]$lines += "      <workflow_name>" + $workflowName + "</workflow_name>"
[Array]$lines += "      <workflow_id>"   + $workflowId   + "</workflow_id>"
[Array]$lines += "      <job_id>"        + $jobId        + "</job_id>"
[Array]$lines += "      <username>"      + $username     + "</username>"
[Array]$lines += "   </wfa>"
[Array]$lines += "</root>"
#'------------------------------------------------------------------------------
#'Create the XML file.
#'------------------------------------------------------------------------------
[String]$fileSpec = "$installPath\jboss\standalone\deployments\$SiteName.war\$jobId.xml"
Try{
   Out-File -FilePath $FileSpec -Encoding ASCII -InputObject $lines -ErrorAction Stop
   Get-WFALogger -Info -Message "Created file ""$fileSpec"""
}Catch{
   Get-WFALogger -Error -Message $("Failed Creating file ""$fileSpec"". Error " + $_.Exception.Message)
   Throw "Failed Creating XML file ""$fileSpec"""
}
#'------------------------------------------------------------------------------
#'Add the volume snapshot names as a comma delimited string.
#'------------------------------------------------------------------------------
If($PortNumber){
   [String]$url = "$Protocol`://$HostName`:$PortNumber/$SiteName/$jobId.xml"
}Else{
   [String]$url = "$Protocol`://$HostName/$SiteName/$jobId.xml"
}
#'------------------------------------------------------------------------------
#'Add WFA return parameters
#'------------------------------------------------------------------------------
Get-WFALogger -Info -Message "Adding WFA retun paramater URL`: $url"
Add-WfaWorkflowParameter -Name "URL" -Value $url -AddAsReturnParameter $True
#'------------------------------------------------------------------------------

 

Note: the above code reads the WFA installation path from the registry so it's not hard coded to the default "C:\Program Files\NetApp\WFA" so it will work if a custom install location is used.

 

Example output (filtered to return all snapshots matching the pattern "daily")

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root revision="0" schemaVersion="0">
   <cluster>
      <cluster_name>cluster1</cluster_name>
      <vserver>
         <vserver_name>vserver1</vserver_name>
         <volume>
            <volume_name>volume_001</volume_name>
            <snapshots>
               <snapshot>
                  <snapshot_name>daily.2017-03-31_0010</snapshot_name>
                  <created>03/31/2017 11:10:00</created>
               </snapshot>
               <snapshot>
                  <snapshot_name>daily.2017-04-01_0010</snapshot_name>
                  <created>04/01/2017 11:10:00</created>
               </snapshot>
            </snapshots>
         </volume>
      </vserver>
   </cluster>
   <wfa>
      <workflow_name>get_volume_snapshots</workflow_name>
      <workflow_id>133</workflow_id>
      <job_id>44116</job_id>
      <username>admin</username>
   </wfa>
</root>

Return parameter:

 

https://XX.XX.XX.XX/log-download/$WfaJobID.xml

 

I can send you an example workflow if you like. Hope that gives you some ideas. Let me know if you have any questions.

 

/Matt

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

sinhaa
9,882 Views

@mbeattie @jauling_chou

 

The best approach in my opinion would be to use Export-Clixml and save the entire object into a temp timpe-stamped file. And pass this file-path Parameter to the next command.

 

On source command:

 

 

Connect-WfaCluster $Cluster

$vol = Get-NcVol -Name $VolumeName -Vserver $VserverName
$snaplist = $vol | Get-NcSnapshot 

Get-WFALogger -Info -message $("Vserver: " + $VserverName + " Volume: " + $VolumeName + " Snapshots:" + ( $snaplist | Out-String ))

$fileName = ".\myfile_" + (Get-Date -UFormat %s) + '_.xml'

Export-Clixml -Path $fileName -InputObject $snaplist

Add-WfaWorkflowParameter -Name ‘snapList’ -Value $fileName -AddAsReturnParameter $true

 

 

 

Now get the object in the destination command

 

 

 

$fileName = Get-WfaWorkflowParameter -Name ‘snapList’

$snaplist = Import-Clixml -Path $fileName

#Now do something with the object
foreach ($snap in $snaplist ) { $snap.Name , $snap.Created }


# Optionally delete this temporary file. WFA will anyway clear it off after some-time

Remove-Item $fileName

 

 

sinhaa

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.
Public