This discussion has been locked. The information referenced herein may be inaccurate due to age, software updates, or external references.
You can no longer post new replies to this discussion. If you have a similar question you can start a new discussion in this forum.

Verbs and their parameters for IPAM.IPNode in addition to 'UpdateManyStatus'??

PREFACE:

  1. I'll be referring to the blog: How to use the IPAM API and get "Free IP Address" specifically a post by mtokarz‌ on June 12, 2014 2:35PM in response to jgovednik
  2. I am also aware of and have voted for the feature request:
  3. I have also referred to this question post: Re: Full documentation of Orion SDK verbs? to see if any verbs exist for IPAM.IPNode, but none that I can find.

Following the post mentioned in #1, I am able to successfully find the next free ip address and update the status of an IP address in Solarwinds using Powershell:

Pre-reqs: Download the Orion SDK: solarwinds/OrionSDK · GitHub

Getting a Free IP Address:

# START Connect to SWIS

Add-PSSnapin SwisSnapin

$hostname = "mysolarwinds"

$username = "myuser"

$password = ConvertTo-SecureString -String "mypassword" -asplaintext -force

$creds = New-Object -TypeName System.Management.Automation.PSCredential -Argumentlist $username, $password

$swis = Connect-Swis -host $hostname -Credential $creds

# END Connect to SWIS


Function SWOIPAM-GetFreeIpAddress($subnetAddress)

{

  $ipaddress = Get-SwisData $swis 'SELECT TOP 1 I.DisplayName FROM IPAM.IPNode I WHERE Status=2 AND I.Subnet.Address=@subnetAddress AND I.DnsBackward IS NULL' @{subnetAddress = $subnetAddress}

  return $ipaddress

}


$subnetProvision = 'X.X.X.X'

SWOIPAM-GetFreeIpAddress($subnetProvision)

Where:

  • $subnetProvision = Desired subnet to get next available IP Address from

Updating the Free IP Address' status from 'Available' to 'Used':

$postParams = @{__EVENTTARGET='ctl00$BodyContent$ctl05';__EVENTARGUMENT='';'ctl00$BodyContent$Username'='admin';'ctl00$BodyContent$Password'='password'}

Invoke-WebRequest -Uri http://mysolarwinds:8787/Orion/Login.aspx?autologin=no -Method POST -SessionVariable s -Body $postParams

$postParams =  @{'entity'='IPAM.IPNode';'verb'='UpdateManyStatus';'ObjectId'='129';'ipstatus'='1';'ids'='4'}

Invoke-WebRequest -Uri http://mysolarwinds:8787/Orion/IPAM/ExtCmdProvider.ashx -Method POST -WebSession $s -Body $postParams

Where:

  • ObjectID : type = number => SubnetId from IPAM.IPNode
    • One can obtain the SubnetId with the query: SELECT SubnetId, Address, FriendlyName, VLAN FROM IPAM.Subnet
  • ipstatus : type = number => where 1 (Used), 2 (Available), 4 (Reserved)
  • ids : type = number => the 4th octet of the IP Address you want to update in the specified subnet

THE QUESTION:

If the verb 'UpdateManyStatus' (with the params of ObjectID, ipstatus and ids) exist for IPAM.IPNode to update a particular IP address' status,

(@{'entity'='IPAM.IPNode';'verb'='UpdateManyStatus';'ObjectId'='129';'ipstatus'='1';'ids'='4'}),

are there any other verbs or documentation of verbs with necessary parameters for IPAM.IPNode that will update the DnsBackward (hostname), Comments and SkipScan fields for a particular IP Address??


If I can find out the verbs and their necessary parameters, then I would be able to update via the same method used for updating an IP Address' status in Powershell.

Thank you

  • I wish to second this well-written request.  We're trying to automate IP address reservations.  We bought Solarwinds because we were promised it had an API and afterwards discovered that it only has the easy-to-write read-only half of an API.  This and other problems with SAM and NPM have given us a case of buyers' remorse.

    We're willing to work around what I consider to be a pretty serious problem with this product's feature set limits, but we need documentation that doesn't exist yet.  UpdateManyStatus only changes the status, and appears to do so for a list of addresses in a subnet.  We want the verb that changes all writeable properties of a single IPAM.IPNode.  I queried Metadata.Verb but there are none listed for IPAM at all.  Either the Metadata.Verb table is incomplete or those verbs don't cover whatever is available from ExtCmdProvider.ashx.

  • I've been able to emulate clicking around the web UI to make write operations to IPAM.  I wrote this in Python, but if you look at it you can easily make a similar actions in Powershell with Invoke-WebRequest or whatever you prefer.  For the bit that does a write look at the setIpamAddress method.  The high-level description is as follows:

    • Login and get a session
    • Get the subnet Id and the ordinal (last octet) of IP address using the standard query API.
    • Using these bits get ip.edit.aspx
    • Parse its HTML and get the __VIEWSTATE and __VIEWSTATEGENERATOR values.  These are needed in the form
    • Post the form (which also has some URL parameters) and check for success

    The form I've got in the class is not the complete set of things you can change; just the stuff I care about.  If anyone's interested I'll put the rest of it on here.

    from bs4 import BeautifulSoup

    from collections import OrderedDict

    import ipaddress

    import json

    import re

    import requests

    import sys

    class SwisConnection():

      def __init__(self,username,password,host,**kwargs):

        self.username = username

        self.password = password

        self.host = host

        try:

          self.no_verify = kwargs['no_verify']

        except:

          self.no_verify = False

        try:

          self.verbose = kwargs['verbose']

        except:

          self.verbose = False

        self.loginUrl = \

          'https://{0}/Orion/Login.aspx?autologin=no'.\

          format(self.host)

        self.queryUrl = \

          'https://{0}:17778/SolarWinds/InformationService/v3/Json/Query'.\

          format(self.host)

        self.ipamBulkStatusEditUrl = \

          'https://{0}/Orion/IPAM/ExtCmdProvider.ashx'.\

          format(self.host)

        self.subnetsUrl = \

          'https://{0}/Orion/IPAM/subnets.aspx'.\

          format(self.host)

        self.ipamAddressEditUrl = \

          'https://{0}/Orion/IPAM/ip.edit.aspx'.\

          format(self.host)

        self.session = requests.Session()

        loginData = {

           '__EVENTTARGET':'ctl00$BodyContent$ctl05'

          ,'__EVENTARGUMENT=':''

          ,'ctl00$BodyContent$Username':self.username

          ,'ctl00$BodyContent$Password':self.password

        }

        try:

          response = self.session.post(

             self.loginUrl

            ,data=loginData

          )

          if response.status_code != 200:

            sys.stderr.write('Error connecting to {0}\n'.\

            format(self.loginUrl))

          if re.search('Login Failure', response.text):

            sys.stderr.write(

              'Unable to authenticate to {0} with username {2}\n'.\

              format(self.loginUrl,self.username)

            )

        except:

          sys.stderr.write('Unable to connect to {0}\n'.\

            format(self.loginUrl))

     

      def query(self,queryString,queryParameters):

        queryData = json.dumps({

           'query': queryString

          ,'parameters': queryParameters

        })

        header = {'Content-Type': 'application/json'}

        requestParameters = {

           'data': queryData

          ,'headers': header

          ,'auth':(self.username, self.password)

        }

        if self.no_verify:

          requestParameters['verify'] = False

        try:

          response = requests.post(self.queryUrl, **requestParameters)

        except:

          sys.stderr.write('Unable to query to {0}\n'.format(self.queryUrl))

          return False

        return response.json()

     

      def getIpamAddress(self,address):

        addr = str(address)

        queryParameters = {'address': addr}

        queryString = '''

          SELECT IPAddress, Alias, DnsBackward, Status, SkipScan

          FROM IPAM.IPNode

          WHERE

            IPAddress = @address

        '''

        response = self.query(queryString,queryParameters)

        try:

          response['results']

        except:

          return False

        return response['results']

     

      def getIpamAvailable(self,network):

        net = str(network.network_address)

        cidr = network.prefixlen

        queryParameters = {'network': net, 'cidr': cidr}

        queryString = '''

          SELECT IPAddress

          FROM IPAM.IPNode

          WHERE

            Status = 2

            AND SubnetId IN(

              SELECT TOP 1 SubnetId

              FROM IPAM.Subnet

              WHERE

                GroupTypeText = 'Subnet'

                AND Address = @network

                AND CIDR = @cidr

            )

        '''

        response = self.query(queryString, queryParameters)

        try:

          response['results']

        except:

          return False

        return response['results']

     

      def getIpamUsed(self,network):

        net = str(network.network_address)

        cidr = network.prefixlen

        queryParameters = {'network': net, 'cidr': cidr}

        queryString = '''

          SELECT

             IPAddress

            ,Alias

            ,DnsBackward

            ,SkipScan

          FROM IPAM.IPNode

          WHERE

            Status = 1

            AND SubnetId IN(

              SELECT TOP 1 SubnetId

              FROM IPAM.Subnet

              WHERE

                GroupTypeText = 'Subnet'

                AND Address = @network

                AND CIDR = @cidr

            )

        '''

        response = self.query(queryString,queryParameters)

        try:

          response['results']

        except:

          return False

        return response['results']

     

      def setIpamAddress(self,address,status,**kwargs):

        address = str(address)

        queryString = '''

          SELECT SubnetId, IPOrdinal

          FROM IPAM.IPNode

          WHERE

            IPAddress = @address

        '''

        queryParameters = {'address': address}

        try:

          response = self.query(queryString, queryParameters)

          subnetId = response['results'][0]['SubnetId']

          iPOrdinal = response['results'][0]['IPOrdinal']

        except:

          sys.stderr.write(

            'Invalid response from server for query {0}, having parameters {1}\n'.\

            format(queryString,queryParameters)

          )

          if self.verbose:

            sys.stderr.write('{0}\n'.format(sys.last_traceback))

          return False

        if status == 'Available':

          editData = {

             'entity': 'IPAM.IPNode'

            ,'verb': 'UpdateManyStatus'

            ,'ObjectId': subnetId

            ,'ipstatus': 2

            ,'ids': iPOrdinal

          }

          response = self.session.post(self.ipamBulkStatusEditUrl,data=editData)

        elif status == 'Used':

          ipamParameters = {

             'NoChrome': True

            ,'msgq': 'mainWindow'

            ,'SubnetId': subnetId

            ,'ipOrdinal': iPOrdinal

          }

          try:

            name = kwargs['name']

          except:

            sys.stderr.write(

              'name argument required to set IPAM address status to used\n'

            )

            return False

          fqdn = ''

          try:

            fqdn = kwargs['fqdn']

          except:

            pass

          scan = 1

          try:

            if kwargs['no_scan']:

              scan = 0

          except:

            pass

          try:

            self.session.headers.update({'referer':self.subnetsUrl})

            response = self.session.get(

              self.ipamAddressEditUrl,params=ipamParameters

            )

            content = BeautifulSoup(response.content, 'html.parser')

            viewState = content.find_all(

              'input', id='__VIEWSTATE')[0]['value']

            viewStateGenerator = content.find_all(

              'input', id='__VIEWSTATEGENERATOR')[0]['value']

          except:

            sys.stderr.write('Unable to fetch edit ID at {0}\n'.\

              format(self.ipamAddressEditUrl)

            )

            return False

          ipamData = {

             '__VIEWSTATE': viewState

            ,'__VIEWSTATEGENERATOR': viewStateGenerator

            ,'ctl00$ctl00$BodyContent$main$ddlStatus': 'Used'

            ,'ctl00$ctl00$BodyContent$main$txtAlias': name

            ,'ctl00$ctl00$BodyContent$main$txtDNS': fqdn

            ,'ctl00$ctl00$BodyContent$main$ddlScanning': scan

            ,'__EVENTTARGET':'ctl00$ctl00$BodyContent$main$MsgListener'

            ,'__EVENTARGUMENT':'save'

          }

          try:

            response = self.session.post(

               self.ipamAddressEditUrl

              ,data=ipamData

              ,params=ipamParameters

            )

          except:

            sys.stderr.write('Unable to post status change to {0}\n'.\

              format(self.ipamAddressEditUrl))

            return False

        if response.status_code != 200:

          sys.stderr.write('Attempt to update IP address {0} returned error\n'.\

            format(address)

          )

          if self.verbose:

            sys.stderr.write('{0}\n'.format(sys.last_traceback))

          return False

        return response

  • Hey Cignul9,

    I'd be interested in seeing a full write up. Have you used your solution between IPAM versions?

  • Based on cignul9's python code above, I converted it into PowerShell Script which someone might need to work on same thing in PS.

    $username = "admin"

    $password = "************"

    $ipamhost="xxxxxx.nonprod.com"

    $loginUrl = "https://{0}/Orion/Login.aspx";

    $ipEditURL = "https://{0}/Orion/IPAM/ip.edit.aspx?NoChrome=False&msgq=mainWindow&SubnetId={1}&ipOrdinal={2}"

    #Custom field defined for find out the ips within that pool

    $pool="testpool"

    #Login and get a session

    #Get the subnet Id and the ordinal (last octet) of IP address using the standard query API.

    #Using these bits get ip.edit.aspx

    #Parse its HTML and get the __VIEWSTATE and __VIEWSTATEGENERATOR values.  These are needed in the form

    #Post the form (which also has some URL parameters) and check for success

    Add-PSSnapin "SwisSnapin"

    $password1 = ConvertTo-SecureString -String "$password" -asplaintext -Force

    $cred = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $password1;

    $swis = Connect-Swis -host $ipamhost -cred $cred;

    $a = $null

    function ExecuteQuery ($query) {

         $tries = 0

         While (!$a -and $tries -lt 3) {

             try { return (Get-SwisData $swis "$query" -Timeout 100) }

             catch { $ErrorMessage = $_.Exception.Message

                     if ($tries -eq 2) { return $ErrorMessage }

             }

             $tries++

         }

    }

    #Get the subnet Id and the ordinal (last octet) of IP address using the standard query API.

    $query = "SELECT TOP 1 IPNodeId, Pool_Name,s.Location, n.DnsBackward, n.IPAddress,  n.Status, n.IPOrdinal, n.SubnetId, s.FriendlyName, s.Address, s.AddressMask, s.CIDR, s.VLAN FROM IPAM.IPNodeAttr as na inner join IPAM.IPNode as n on na.IPNodeId = n.IPNodeId inner join IPAM.Subnet as s on n.SubnetId = s.SubnetId  and Pool_Name = '$pool'"

    $a = ExecuteQuery($query)

    $loginUrl=[string]::Format($loginUrl,$ipamhost)

    $ipEditURL=[string]::Format($ipEditURL, $ipamhost, $a.SubnetId,$a.ipOrdinal)

    #This cert policy trust might not necessary if it is not self-signed cert, have to use this on test server box which is self signed cert installed to ignore any cert issues

    add-type @"

    using System.Net;

    using System.Security.Cryptography.X509Certificates;

    public class TrustAllCertsPolicy : ICertificatePolicy {

        public bool CheckValidationResult(

            ServicePoint srvPoint, X509Certificate certificate,

            WebRequest request, int certificateProblem) {

                return true;

            }

    }

    "@

    [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

    #Login and get a session

    $postParams = @{__EVENTTARGET='ctl00$BodyContent$ctl04';__EVENTARGUMENT='';'ctl00$BodyContent$Username'="$username";'ctl00$BodyContent$Password'="$password"}

    Invoke-WebRequest -Uri $loginUrl -Method POST -SessionVariable s -Body $postParams -UseBasicParsing >$null

    $response=Invoke-WebRequest -Uri $ipEditURL -Method GET -WebSession $s

    #Parse its HTML and get the __VIEWSTATE and __VIEWSTATEGENERATOR values.  These are needed in the form

    $viewState=$response.InputFields.Find("__VIEWSTATE").value

    $viewStateGenerator=$response.InputFields.Find("__VIEWSTATEGENERATOR").value

    #Post the form (which also has some URL parameters) and check for success

    $ipamData = @{

             '__VIEWSTATE'= $viewState;

             '__VIEWSTATEGENERATOR'= $viewStateGenerator;

             'ctl00$ctl00$ctl00$BodyContent$ContentPlaceHolder1$main$txtNetworkAddress'= $a.IPAddress;

             'ctl00$ctl00$ctl00$BodyContent$ContentPlaceHolder1$main$ddlStatus'='Reserved';

             'ctl00$ctl00$ctl00$BodyContent$ContentPlaceHolder1$main$txtDNS'= 'XXXXXX';

             'ctl00$ctl00$ctl00$BodyContent$ContentPlaceHolder1$main$AttributesRepeater$ctl01$AttributeText'= $pool;

             '__EVENTTARGET'='ctl00$ctl00$ctl00$BodyContent$ContentPlaceHolder1$main$MsgListener';

             '__EVENTARGUMENT'='save'

          }

    $r=Invoke-WebRequest -Uri $ipEditURL -Body $ipamData -WebSession $s -Method POST

  • FYI - IPAM 4.5 just hit Release Candidate. This version adds an API for many IP management tasks. See docs at https://github.com/solarwinds/OrionSDK/wiki/IPAM-API

    If you are an IPAM customer with active maintenance you can try it out.

  • I was really looking forward to this release mainly because it was supposed to add "extended API capabilities" such as adding subnets. I installed the Beta and apparently you can add subnets using just an IP and CIDR. The bad news is that those are the only parameters that can be used.

    So basically you can add subnets with CIDRs only in the root group, without description or any other custom fields..which is pretty much useless.

    We were hoping to replicate our subnet structure (multiple group and subnet levels) using the API. Currently I have a PowerShell script that inserts directly in the DB and creates the whole ancestor relations.

    So yeah...you can add un-named subnets to the root group using the API..and that's about it.

  • The CreateSubnet verb is very basic, but you can use a Create operation for IPAM.Subnet to specify more properties, like the ParentId. See https://github.com/solarwinds/OrionSDK/wiki/IPAM-API#crud-operations-for-subnets

  • Ok. Tried it and it kind of helps. But there are a few properties that are not accessible (like GroupTypeText).

    So you can create just subnets (no supernets or groups); I tried doing a create for IPAM.GroupNode but that is not possible.

    Just to give you a bit of a context: we currently have another IPAM tool that we are decommissioning and  are trying to migrate the data into SolarWinds. The data is structured on multiple levels to account for different groups, world regions and sites and there is no easy way to replicate the same structure in SolarWinds. That is why I wrote the PowerShell script and I was hoping to use the API now that CRUD is (kind of) possible on IPAM objects.

  • Right - the IPAM 4.5 API does not yet cover supernets and groups.

  • But future version of the API might make those properties editable?