functions/ConvertFrom-JobSchedulerXml.ps1
function ConvertFrom-JobSchedulerXml { <# .SYNOPSIS Converts JobScheduler 1.x objects to a JSON format for migration to JS7 releases (JobScheduler 2.x) .DESCRIPTION JS7 (JobScheduler 2.0) is the successor product to JobScheduler 1.x. As JS7 makes use of a JSON format for workflows and related objects a conversion is performed by this cmdlet. The cmdlet converts and exports JobScheduler 1.x objects to an OS directory and stores them in a JSON format. In addition, the converted objects can be added to a .zip archive for direct import into JS7. PREREQUISITES The cmdlet makes use of API version 1.13.5 or later, i.e. it can be used with a JobScheduler release 1.13.5 or later. Use with earlier JobScheduler releases is possible within limits of converted object types, however, this is not in scope of testing of the cmdlet by SOS. The cmdlet can be operated from any machine with a PowerShell version 5.1, 6.x and 7.x. The cmdlet will access JOC Cockpit and JobScheduler Master running on the same or on a remote machine. For operation of the cmdlet both ports for JOC Cockpit and for JobScheduler Master have to be accessible. OBJECT SELECTION The cmdlet supports a number of selections of objects based on folders and recursion, by direct specification of object paths and by pipelining from other cmdlets, e.g. using ConvertFrom-JobSchedulerXml -ArchivePath /tmp/export.zip -JobChain /product_demo/shell_chain ConvertFrom-JobSchedulerXml -ArchivePath /tmp/export.zip -Directory /product_demo -Recursive Get-JobSchedulerJob -IsStandaloneJob -Directory /product_demo | ConvertFrom-JobSchedulerXml -ArchivePath /tmp/export.zip STANDALONE JOBS JS7 considers standalone jobs being workflows with a single job node. Therefore, migrating standalone jobs results in one workflow per job. For each workflow created a schedule is added with the path and name of the job. JOB CHAINS JobScheduler 1.x job chains are migrated to JS7 workflows. A JS7 workflow includes both the job nodes and the job configurations. Therefore, migrating job chains includes that the jobs that are referenced by the respective job chain nodes are migrated to the JS7 workflow. ORDERS Consider the change in wording: JobScheduler 1.x orders are migrated to JS7 schedules. Therefore an order for a job chain maps to a schedule for a workflow in JS7. Such schedules are used by the Daily Plan service to generate individual orders for the respective dates and times of the Daily Plan. AGENT CLUSTERS Consider that converted JobScheduler 1.x Agent Clusters cannot be directly imported with the import functionality of the JS7 GUI or with the respective Import-JS7InventoryItem cmdlet. Instead, the export archive created by this cmdlet can be used with the Set-JS7Agent cmdlet to populate the Agent inventory. For migrating Agent Cluster configuratoins consider two approaches: * Manually map Agent Cluster to JS7 Agents: ** $map = @{} ** $map.Add( '/product_demo/Agent_Linux', @{ 'AgentId' = 'primaryAgent'; 'AgentName' = 'Agent_Linux' } ) ** $map.Add( '/global/Agent_Cluster_Linux', @{ 'AgentId' = 'primaryAgent'; 'AgentName' = 'Agent_Cluster_Linux_Active_Passive' } ) * Map Agent Clusters from existing configuration: ** $agentMapping = @{} ** $agentClusters = Get-JobSchedulerAgentCluster ** foreach( $agentCluster in $agentClusters ) ** { *** if ( $agentCluster.volatile.agents[0].os.name -eq 'Windows' ) *** { **** $agentMapping.Add( $agentCluster.agentCluster, @{ 'AgentId' = 'wintestPrimaryAgent'; 'AgentName' = ([System.IO.Path]::GetFileName( $agentCluster.AgentCluster )) } ) *** } else { **** $agentMapping.Add( $agentCluster.agentCluster, @{ 'AgentId' = 'primaryAgent'; 'AgentName' = ([System.IO.Path]::GetFileName( $agentCluster.AgentCluster )) } ) *** } ** } CONVERSION OUTPUT The cmdlet can be used to store converted objects in a local directory and to create a .zip archive with converted objects. The .zip archive can be used for import with JS7. .PARAMETER ArchivePath Specifies the name and path of an archive file in .zip format to which converted JobScheduler objects are added. Independent from the archive file the directory specified with the -OutputDirectory parameter contains converted objects. The output directory is automatically removed if an archive file is specified with the -ArchivePath parameter. .PARAMETER Directory Optionally specifies the folder for which JobScheduler objects should be converted. The directory is determined from the root folder, i.e. the "live" directory of a JobScheduler Master. .PARAMETER Recursive Specifies that any sub-folders should be looked up when used with the -Directory parameter. By default no sub-folders will be looked up for jobs. .PARAMETER Job Optionally specifies the path of a job that should be converted. Jobs can be retrieved with the Get-JobSchedulerJob cmdlet and can be pipelined to this cmdlet. .PARAMETER JobChain Optionally specifies the path of a job chain that should be converted. Job Chains can be retrieved with the Get-JobSchedulerJobChain cmdlet and can be pipelined to this cmdlet. .PARAMETER JobStream Optionally specifies the path of a job stream that should be converted. Job Streams can be retrieved with the Get-JobSchedulerJobStream cmdlet and can be pipelined to this cmdlet. .PARAMETER OrderId Optionally specifies the order ID of of an order that should be converted. This parameter requires use of the -JobChain parameter to specify the order's job chain. Orders can be retrieved with the Get-JobSchedulerOrder cmdlet and can be pipelined to this cmdlet. .PARAMETER Calendar Optionally specifies a calendar that should be converted. Calendars can be retrieved with the Get-JobSchedulerCalendar cmdlet and can be pipelined to this cmdlet. .PARAMETER Lock Optionally specifies a lock that should be converted. Locks can be retrieved with the Get-JobSchedulerLock cmdlet and can be pipelined to this cmdlet. .PARAMETER AgentCluster Optionally specifies an Agent cluster that should be converted. Agent Clusters can be retrieved with the Get-JobSchedulerAgentCluster cmdlet and can be pipelined to this cmdlet. .PARAMETER OutputDirectory Specifies the OS directory to which converted JobScheduler objects files (.json) are stored. By default the directory for temporary files and a unique sub-directory is used. The output directory can be removed after conversion of objects by use of the -RemoveOutputDirectory parameter. .PARAMETER BaseFolder Optionally specifies a base folder that preceeds the folders created for files with converted objects and for references within the objects. This allows to export converted objects to a new folder structure that includes the base folder. .PARAMETER DefaultAgentName JS7 requires any jobs to be executed with Agents. Therefore jobs that are executed with a Master from a JobScheduler 1.x release will use the Agent Name that is specified with this parameter. Jobs or job chains that are assigned an Agent are not affected by this parameter. .PARAMETER ForcedAgentName Specifies an Agent Name that overwrites any Agent assignments in jobs and job chains. .PARAMETER MappedAgentNames This parameter performs a mapping of JobScheduler 1.x Agent Clusters to JS7 Agent identifiers. * In JobScheduler 1.x a number of Agent Clusters can be created on top of a single Agent installation * In JS7 Agents and their URLs are unique Therefore a number of JobScheduler 1.x Agent Clusters have to be mapped to a single JS7 Agent. A JS7 Agent is identified by its Agent ID (that is unchangeable after Agent installation) and is assigned an Agent Name. Additional Agent Names can be assigned an Agent to specify alias names. The value of this parameter accepts a hashmap that is e.g. created like this: $map = @{} $map.Add( '/product_demo/Agent_Linux_Active_Active', @{ 'AgentId' = 'AgentLinux_0023'; 'AgentName' = 'Agent_Linux_Active_Active' } ) $map.Add( '/product_demo/Agent_Linux_Active_Passive', @{ 'AgentId' = 'AgentLinux_0023'; 'AgentName' = 'Agent_Linux_Active_Passive' } ) The key of the hashmap is the JobScheduler 1.x Agent Cluster, e.g. '/product_demo/Agent_Linux_Active_Active'. The value of the hashmap is a nested hashmap that is expected to contain the "AgentId" and "AgentName" entries. In the above example multiple Agent Clusters map to the same Agent ID but use different Agent Names that can be used as alias names for the same Agent ID in the JS7 GUI. .PARAMETER PrefixOrders Order IDs are unique for a given job chain only. Therefore, when converting a number of orders then duplicate converted objects could result. For later import of converted objects to JS7 uniqueness of Order IDs (Schedules) is required. This switch specifies to prefix the order ID with the name of the job chain when an order is converted to a schedule. .PARAMETER PlanOrders When converting order objects then this switch specifies if the orders should be planned automatically by the Daily Plan service or if the planning of orders is performed by users with the Daily Plan GUI. .PARAMETER SubmitOrders When converting order objects then this switch specifies if the orders should be submitted automatically to a Controller by the Daily Plan service or if the submission of orders is performed by users with the Daily Plan GUI. .PARAMETER UseJobs When used with the -Directory parameter then this switch specifies to convert job objects. If none of the -Use* switches is used then all object types are exported. .PARAMETER UseJobChains When used with the -Directory parameter then this switch specifies to convert job chain objects. If none of the -Use* switches is used then all object types are exported. .PARAMETER UseJobStreams When used with the -Directory parameter then this switch specifies to convert job stream objects. If none of the -Use* switches is used then all object types are exported. .PARAMETER UseOrders When used with the -Directory parameter then this switch specifies to convert order objects. If none of the -Use* switches is used then all object types are exported. .PARAMETER UseCalendars When used with the -Directory parameter then this switch specifies to convert calendar objects. If none of the -Use* switches is used then all object types are exported. .PARAMETER UseLocks When used with the -Directory parameter then this switch specifies to convert lock objects. If none of the -Use* switches is used then all object types are exported. .PARAMETER UseAgentClusters When used with the -Directory parameter then this switch specifies to convert Agent Cluster objects. If none of the -Use* switches is used then all object types are exported. .PARAMETER UpdateArchive Specifies that any converted objects will be added to an existing archive file that is specified with the -ArchivePath parameter. Without this parameter an existing archive file will be overwritten. .PARAMETER RemoveOutputDirectory Specifies that the output directory that is indicated with the -OutputDirectory parameter will be removed after JobScheduler objects have been converted and the archive file that is specified with the -ArchivePath parameter has been created. .INPUTS This cmdlet accepts pipelined objects. .OUTPUTS This cmdlet does not return any output .EXAMPLE ConvertFrom-JobSchedulerXml -ArchivePath /tmp/export.zip Converts any JobScheduler objects to a JSON format and stores them in an archive file for import with JS7. .EXAMPLE ConvertFrom-JobSchedulerXml -ArchivePath /tmp/export.zip -RemoveOutputDirectory Converts any JobScheduler objects to a JSON format and stores them in an archive file for import with JS7. The temporary directory used to store converted objects is removed after adding converted files to the archive file. .EXAMPLE ConvertFrom-JobSchedulerXml -Job /some_path/myJob -ArchivePath /tmp/export.zip Converts the indicated standalone job and stores the converted JSON file to an archive. .EXAMPLE ConvertFrom-JobSchedulerXml -JobChain /product_demo/shell_chain -ArchivePath /tmp/export.zip Converts the indicated job chain and stores the converted JSON file to an archive. .EXAMPLE ConvertFrom-JobSchedulerXml -Directory /product_demo -Recursive -OutputDirectory /tmp/js7/jobchain2js7/js7 Selects any JobScheduler objects from the indicated directory and sub-directories and stores converted objects with the indicated OS directory. .EXAMPLE ConvertFrom-JobSchedulerXml -Directory /product_demo -Recursive -UseJobChains -UseJobs -OutputDirectory /tmp/js7/jobchain2js7/js7 Selects standalone job objects and job chains objects from the indicated directory and sub-directories and stores converted objects with the indicated OS directory. .EXAMPLE ConvertFrom-JobSchedulerXml -Directory /product_demo -OutputDirectory /tmp/js7/jobchain2js7/js7 .EXAMPLE Get-JobSchedulerJob -IsStandaloneJob -Directory /product_demo | ConvertFrom-JobSchedulerXml -ArchivePath /tmp/export.zip Reads standalone jobs from the specified directory and pipes the result to the converter cmdlet. .EXAMPLE Get-JobSchedulerJobChain -Directory /product_demo -Recursive | ConvertFrom-JobSchedulerXml -ArchivePath /tmp/export.zip Reads job chains from the specified directory and any sub-directories and pipes the result to the converter cmdlet. #> [cmdletbinding()] param ( [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $ArchivePath, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $Directory = '/', [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $Recursive, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $Job, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $JobChain, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $JobStream, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $OrderId, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $Calendar, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $Lock, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $AgentCluster, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $OutputDirectory = [System.IO.Path]::GetTempPath() + (New-Guid).guid, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $BaseFolder, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $DefaultAgentName = 'primaryAgent', [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [string] $ForcedAgentName, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [hashtable] $MappedAgentNames, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $PrefixOrders, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $SubmitOrders, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $PlanOrders, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $UseJobs, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $UseJobChains, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $UseJobStreams, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $UseOrders, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $UseCalendars, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $UseLocks, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $UseAgentClusters, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $UpdateArchive, [Parameter(Mandatory=$False,ValueFromPipeline=$False,ValueFromPipelinebyPropertyName=$True)] [switch] $RemoveOutputDirectory ) Begin { Approve-JobSchedulerCommand $MyInvocation.MyCommand $stopWatch = Start-JobSchedulerStopWatch $directories = @() $jobs = @() $jobChains = @() $jobStreams = @() $orders = @() $calendars = @() $agentClusters = @() $locks = @() } Process { Write-Verbose ".. $($MyInvocation.MyCommand.Name): parameter FilePath=$FilePath, Directory=$Directory, Job=$Job, JobChain=$JobChain, JobStream=$JobStream, Lock=$Lock, OutputDirectory=$OutputDirectory" if ( $Directory ) { if ( !$Directory.endsWith( '/' ) ) { $Directory += '/' } } if ( $BaseFolder.endsWith( '/' ) ) { $BaseFolder = $BaseFolder.Substring( 0, $BaseFolder.Length-1 ) } if ( $OutputDirectory.endsWith( '/' ) ) { $OutputDirectory = $OutputDirectory.Substring( 0, $OutputDirectory.Length-1 ) } if ( !(Test-Path $OutputDirectory -PathType container -ErrorAction continue) ) { New-Item -Path $OutputDirectory -ItemType Directory | Out-Null } if ( !$UseJobs -and !$UseJobChains -and !$UseJobStreams -and !$UseOrders -and !$UseCalendars -and !$UseLocks -and !$UseAgentClusters ) { $UseJobs = $True $UseJobChains = $True $UseJobStreams = $True $UseOrders = $True $UseCalendars = $True $UseLocks = $True $UseAgentClusters = $True } if ( $Directory ) { $directories += $Directory } if ( $Job ) { $objPath = New-Object PSObject if ( $Job.startsWith( '/' ) ) { Add-Member -Membertype NoteProperty -Name 'Path' -value $Job -InputObject $objPath } else { Add-Member -Membertype NoteProperty -Name 'Path' -value "$($Directory)$($Job)" -InputObject $objPath } $jobs += $objPath } if ( $JobChain ) { $objPath = New-Object PSObject Add-Member -Membertype NoteProperty -Name 'Path' -value $JobChain -InputObject $objPath $jobChains += $objPath } if ( $JobStream ) { $objPath = New-Object PSObject Add-Member -Membertype NoteProperty -Name 'Path' -value $JobStream -InputObject $objPath $jobStreams += $objPath } if ( $OrderId ) { if ( !$JobChain ) { throw "use of -OrderId parameter requires to specify the -JobChain parameter" } $objPath = New-Object PSObject Add-Member -Membertype NoteProperty -Name 'orderId' -value $OrderId -InputObject $objPath Add-Member -Membertype NoteProperty -Name 'jobChain' -value $JobChain -InputObject $objPath $orders += $objPath } if ( $Calendar ) { $objPath = New-Object PSObject Add-Member -Membertype NoteProperty -Name 'Path' -value $Calendar -InputObject $objPath $calendars += $objPath } if ( $Lock ) { $objPath = New-Object PSObject if ( $Lock.startsWith( '/' ) ) { Add-Member -Membertype NoteProperty -Name 'Path' -value $Lock -InputObject $objPath } else { Add-Member -Membertype NoteProperty -Name 'Path' -value "$($Directory)$($Lock)" -InputObject $objPath } $locks += $objPath } if ( $AgentCluster ) { $objPath = New-Object PSObject Add-Member -Membertype NoteProperty -Name 'Path' -value $AgentCluster -InputObject $objPath $agentClusters += $objPath } } End { Write-Verbose ".. exporting objects to output directory: $OutputDirectory" $arguments = @{} $arguments.Add( 'OutputDirectory', $OutputDirectory ) $arguments.Add( 'BaseFolder', $BaseFolder ) if ( $Directory -and !$jobs -and !$jobChains -and !$jobStreams -and !$calendars -and !$locks -and !$agentClusters ) { if ( $UseJobs ) { Get-JobSchedulerJob -Directory $Directory -Recursive:$Recursive -IsStandaloneJob | ConvertFrom-JobSchedulerXmlJob -DefaultAgentName $DefaultAgentName -ForcedAgentName $ForcedAgentName -MappedAgentNames $MappedAgentNames -PrefixOrders:$PrefixOrders -SubmitOrders:$SubmitOrders -PlanOrders:$PlanOrders @arguments } if ( $UseJobChains ) { Get-JobSchedulerJobChain -Directory $Directory -Recursive:$Recursive | ConvertFrom-JobSchedulerXmlJobChain -DefaultAgentName $DefaultAgentName -ForcedAgentName $ForcedAgentName -MappedAgentNames $MappedAgentNames @arguments } if ( $UseJobStreams ) { # Get-JobSchedulerJobStream -Directory $Directory -Recursive:$Recursive | ConvertFrom-JobSchedulerXmlJobStream -DefaultAgentName $DefaultAgentName -ForcedAgentName $ForcedAgentName -MappedAgentNames $MappedAgentNames @arguments } if ( $UseOrders ) { Get-JobSchedulerOrder -Directory $Directory -Recursive:$Recursive -Permanent | ConvertFrom-JobSchedulerXmlOrder -PrefixOrders:$PrefixOrders -SubmitOrders:$SubmitOrders -PlanOrders:$PlanOrders @arguments } if ( $UseCalendars ) { Get-JobSchedulerCalendar -Directory $Directory -Recursive:$Recursive | ConvertFrom-JobSchedulerXmlCalendar @arguments } if ( $UseLocks ) { Get-JobSchedulerLock -Directory $Directory -Recursive:$Recursive | ConvertFrom-JobSchedulerXmlLock @arguments } if ( $UseAgentClusters ) { Get-JobSchedulerAgentCluster -Directory $Directory -Recursive:$Recursive | ConvertFrom-JobSchedulerXmlAgentCluster -MappedAgentNames $MappedAgentNames @arguments } } if ( $jobs ) { $jobs | ConvertFrom-JobSchedulerXmlJob -DefaultAgentName $DefaultAgentName -ForcedAgentName $ForcedAgentName -MappedAgentNames $MappedAgentNames -PrefixOrders:$PrefixOrders -SubmitOrders:$SubmitOrders -PlanOrders:$PlanOrders @arguments } if ( $jobChains ) { $jobChains | ConvertFrom-JobSchedulerXmlJobChain -DefaultAgentName $DefaultAgentName -ForcedAgentName $ForcedAgentName -MappedAgentNames $MappedAgentNames @arguments } if ( $jobStreams ) { # $jobStreams | ConvertFrom-JobSchedulerXmlJobStream -DefaultAgentName $DefaultAgentName -ForcedAgentName $ForcedAgentName -MappedAgentNames $MappedAgentNames @arguments } if ( $orders ) { $orders | ConvertFrom-JobSchedulerXmlOrder -SubmitOrders:$SubmitOrders -PlanOrders:$PlanOrders @arguments } if ( $calendars ) { $calendars | ConvertFrom-JobSchedulerXmlCalendar @arguments } if ( $locks ) { $locks | ConvertFrom-JobSchedulerXmlLock @arguments } if ( $agentClusters ) { $agentClusters | ConvertFrom-JobSchedulerXmlAgentCluster -MappedAgentNames $MappedAgentNames @arguments } if ( $ArchivePath ) { if ( $UpdateArchive ) { Write-Verbose ".. updating archive file: $ArchivePath" Compress-Archive -DestinationPath $ArchivePath -Path "$($OutputDirectory)/*" -Update } else { Write-Verbose ".. creating archive file: $ArchivePath" Compress-Archive -DestinationPath $ArchivePath -Path "$($OutputDirectory)/*" -Force } if ( $RemoveOutputDirectory ) { Write-Verbose ".. removing output directory: $OutputDirectory" Remove-Item -Path $OutputDirectory -Recurse -Force } } Trace-JobSchedulerStopWatch $MyInvocation.MyCommand.Name $stopWatch } } |