Does anyone have any examples of using Powershell with the xml-rpc api? Trying to do something simple, like query the config of a vm.
Hi Pete,
I’ve been doing some work around offline patching for non-persistent windows images
This is what I’ve been using -
Firstly, download the PS RPC-XML module from here - https://github.com/mosserlee/PSClient-for-XML-RPC
I had to make some tweaks to this module to get it to work with OpenNebula, PM me and I can give you a copy of the updated module. (sorry I haven’t yet submitted it back to the original author)
After that it’s simply a matter of -
import-module RPC-Client
function getImageInfo( $id )
{
$uri = “http://your-frontend.domain:2633/RPC2”
$method = “one.image.info”
$creds = “userid:password”
$params = @( $creds, $id )
$body = New-RPCMethod -MethodName $method -Params $params
$image = Invoke-RPCMethod -RpcServerUri $uri -RequestBody $body
if ($image[0])
{
return [xml]$image[1]
}
else
{
return $image.faultCode
}
}
$imageinfo = getImageInfo( Image ID here )
return $imageinfo
This automatically casts the XML into PS objects, so you can just reference the properties with something like:
$vmname = $imageinfo.IMAGE.NAME
I’ve only tried this on Powershell 4 but it should work with other versions
Hiyo! This is quite old, but did you ever end up publishing the changes you made Paul? Any chance you still have them and would be up for sharing?
Thanks!
Alrighty!
In case anyone somehow ends up (1) with OpenNebula, and (2) using the xmlrpc API rather than CLI…
- Used https://www.powershellgallery.com/packages/XmlRpc
- Used -Debug output to see the methodCall it was creating
- Used the screenshots from this, to figure out the correct schema for the methodCall (i.e. the first parameter (“username:password”) should simple be enclosed in
<value>username:password</value>
, not wrapped in a type - Constructed the methodCall manually (todo: create function), everything worked!
Hey Warren
I didn’t submit the updated module back to the original author as I only cared about making it work with OpenNebula, and didn’t want to bother testing the changes with any other XMLRPC APIs.
I see you’ve pretty much made the same changes I did (the original version of the module didn’t appear to set the property types (string, integer, etc) correctly.
Here’s a copy the updated module:
<#
.Synopsis
New XML_RPC method string.
.DESCRIPTION
New XML_RPC method string with RPC method and parameters.
.EXAMPLE
New-RPCMethod -MethodName 'new.post' -Params @("1",2,'string')
.INPUTS
Object.
.OUTPUTS
Xml format string.
#>
function New-RPCMethod
{
param(
[string]$MethodName,
[Array]$Params
)
$xmlMethod = "<?xml version='1.0' encoding='ISO-8859-1' ?>
<methodCall>
<methodName>{0}</methodName>
<params>{1}</params>
</methodCall>"
[string]$paramsValue=""
foreach($param in $Params)
{
$paramsValue += '<param>{0}</param>' -f (ConvertTo-RPCXmlObject -Object $param)
}
return ([xml]($xmlMethod -f $MethodName,$paramsValue)).OuterXml
}
<#
.Synopsis
Invoke XML_RPC method request.
.DESCRIPTION
Invoke XML_RPC request to RPC server.
.EXAMPLE
$blogUrl = 'http://www.pstips.net/myrpc.php'
$method = New-RPCMethod -MethodName 'wp.getPostTypes' -Params @(1,'userName','password')
.OUTPUTS
The response result from RPC server.
#>
function Invoke-RPCMethod
{
param(
[uri]$RpcServerUri,
[string]$RequestBody
)
$xmlResponse = Invoke-RestMethod -Uri $RpcServerUri -Method Post -Body $RequestBody
if($xmlResponse)
{
# Normal response
$paramNodes = $xmlResponse.SelectNodes('methodResponse/params/param/value')
if($paramNodes)
{
$paramNodes | foreach {
$value = $_.ChildNodes |
Where-Object { $_.NodeType -eq 'Element' } |
Select-Object -First 1
ConvertFrom-RPCXmlObject -XmlObject $value
}
}
# Fault response
$faultNode = $xmlResponse.SelectSingleNode('methodResponse/fault')
if ($faultNode)
{
$fault = ConvertFrom-RPCXmlObject -XmlObject $faultNode.value.struct
return $fault
}
}
}
<#
.Synopsis
Convert object to XML-RPC object string.
.DESCRIPTION
Convert object to XML-RPC object string.
.EXAMPLE
ConvertTo-RPCXmlObject 3
<int>3</int>
ConvertTo-RPCXmlObject '3'
<string>3</string>
ConvertTo-RPCXmlObject 3.5
<double>3.5</double>
.OUTPUTS
The XML-RPC object string.
#>
function ConvertTo-RPCXmlObject
{
param(
$Object
)
if($Object -ne $null)
{
# integer type
if( ($Object -is [int]) -or ($Object -is [int64]))
{
return "<value><int>$Object</int></value>"
}
# double type
elseif( ($Object -is [float]) -or ($Object -is [double]) -or ($Object -is [decimal]))
{
return "<value><double>$Object</double></value>"
}
# string type
elseif( $Object -is [string])
{
return "<value><string>$Object</string></value>"
}
# date/time type
elseif($Object -is [datetime])
{
$dateStr = $Object.ToString('yyyyMMddTHH:mm:ss')
return "<value><dateTime.iso8601>$dateStr</dateTime.iso8601></value>"
}
# boolean type
elseif($Object -is [bool])
{
$bool = [int]$Object
return "<value><boolean>$bool</boolean></value>"
}
# base64 type
elseif( ($Object -is [array]) -and ($Object.GetType().GetElementType() -eq [byte]))
{
$base64Str = [Convert]::ToBase64String($Object)
return "<value><base64>$base64Str</base64></value>"
}
# array type
elseif( $Object -is [array])
{
$result = '<value><array>
<data>'
foreach($element in $Object)
{
$value = ConvertTo-RPCXmlObject -Object $element
$result += "<value>{0}</value>" -f $value
}
$result += '</data>
</array></value>'
return $result
}
# struct type
elseif($Object -is [Hashtable])
{
$result = '<value><struct>'
foreach ($key in $Object.Keys)
{
$member = "<member>
<name>{0}</name>
<value>{1}</value>
</member>"
$member = $member -f $key, (ConvertTo-RPCXmlObject -Object $Object[$key])
$result = $result + $member
}
$result = $result + '</struct></value>'
return $result
}
elseif($Object -is [PSCustomObject])
{
$result = '<value><struct>'
$Object |
Get-Member -MemberType NoteProperty |
ForEach-Object{
$member = "<member>
<name>{0}</name>
<value>{1}</value>
</member>"
$member = $member -f $_.Name, (ConvertTo-RPCXmlObject -Object $Object.($_.Name))
$result = $result + $member
}
$result = $result + '</struct></value>'
return $result
}
else{
throw "[$Object] type is not supported."
}
}
}
<#
.Synopsis
Convert to object from XML-RPC object string.
.DESCRIPTION
Convert to object from XML-RPC object string.
.EXAMPLE
$s1= '<i4>1919</i4>'
ConvertFrom-RPCXmlObject -XmlObject $s1
.OUTPUTS
The XML-RPC object string.
#>
function ConvertFrom-RPCXmlObject
{
param($XmlObject)
if($XmlObject -is [string])
{
$XmlObject= ([xml]$XmlObject).DocumentElement
}
elseif( $XmlObject -is [xml] ){
$XmlObject = $XmlObject.DocumentElement
}
elseif( $XmlObject -isnot [Xml.XmlElement])
{
throw 'Only types [string](xml format), [xml], [System.Xml.XmlElement] are supported'
}
$node = $XmlObject
if($node)
{
$typeName = $node.Name
switch($typeName)
{
# Bool
('boolean') {
if($node.InnerText -eq '1'){
return $true
}
return $false
}
# Number
('i4') {[int64]::Parse($node.InnerText) }
('int') {[int64]::Parse($node.InnerText) }
('double'){ [double]::Parse($node.InnerText) }
# String
('string'){ $node.InnerText }
# Base64
('base64') {
[Text.UTF8Encoding]::UTF8.GetBytes($node.InnerText)
}
# Date Time
('dateTime.iso8601'){
$format = 'yyyyMMddTHH:mm:ss'
$formatProvider = [Globalization.CultureInfo]::InvariantCulture
[datetime]::ParseExact($node.InnerText, $format, $formatProvider)
}
# Array
('array'){
$node.SelectNodes('data/value') | foreach{
ConvertFrom-RPCXmlObject -XmlObject $_.FirstChild
}
}
# Struct
('struct'){
$hashTable = @{}
$node.SelectNodes('member') | foreach {
$hashTable.Add($_.name,(ConvertFrom-RPCXmlObject -XmlObject $_.value.FirstChild))
}
[PSCustomObject]$hashTable
}
}
}
}
Cheers