15 Replies Latest reply on Jul 29, 2017 9:03 AM by robert.booth

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

    echo

      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: Add SWIS features to IPAM
      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

        • Re: Verbs and their parameters for IPAM.IPNode in addition to 'UpdateManyStatus'??
          cignul9

          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.

            • Re: Verbs and their parameters for IPAM.IPNode in addition to 'UpdateManyStatus'??
              cignul9

              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

                • Re: Verbs and their parameters for IPAM.IPNode in addition to 'UpdateManyStatus'??
                  andrew.grech@tattsgroup.com

                  Hey Cignul9,

                   

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

                  • Re: Verbs and their parameters for IPAM.IPNode in addition to 'UpdateManyStatus'??
                    amosc

                    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

                    • Re: Verbs and their parameters for IPAM.IPNode in addition to 'UpdateManyStatus'??
                      robert.booth

                      Cignul9,

                       

                      I'm trying to automate a last step in a complete AWS automated deployment.  The step is to get two ip addresses from one of 100+  /30 subnets we have in IPAM for gre tunnels.  All the /30 subnets have the identical comment "AWS-Prod_GRE_Tunnels" which is how I select one of the subnets which works fine; however, I'm running into a problem if someone makes a mistake and marks one of the two ip addresses to "used".  My script then returns one available ip from that subnet and one from another which shouldnt be occuring.  I was looking at your partial script and thought perhaps your python code could assist.  You mentioned on your post that you would share it for those interested.  If that still applies, I'm very interested and could use the help.

                       

                      Thanks,

                       

                      Robert Booth

                      Robert.l.booth@hpe.com

                      Robert.booth@ventech.hcqis.org