Any Powershell xml-rpc api examples


(Pete) #1

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.


(Paul Batchelor) #2

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


(Warren Frame) #3

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!


(Warren Frame) #4

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!

(Paul Batchelor) #5

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