How to create a API poller proxy in Python using Orion API

Hi,

The native SAM api poller handling  is very static requiring manual handling in SW for changes on the target side.

With a proxy Python script we could handle changes as well as just installing the script for other sites.

Further, the api poller is a SAM function whereas the api data is related to different network elements.

The proxy could then also resolve this mismatch.

 I have envisaged that we should be able to generate a python script which will:

  • Initialize - identify the components from the api response to be monitored and configure SW variables accordingly
  • For each polling it will detect if any configuration changes have been made on the target side and adapt the SW config accordingly
  • For each polling it will update the SW variables and SW actions should be taken if status is down etc as natively handled by SW

My starting point for changing values in SW is to use the Orion api via Python as documented at https://github.com/solarwinds/orionsdk-python

One should then be able to add nodes and set custom properties for those.

However, I do not understand how one should be able to apply a “push model” (from the proxy into SW) yielding a SW handling with alarms etc for these variables / custom properties.

The documentation is focused on poller types which is the opposite way of data handling (fetch model).

I would then be pleased to get some guidance on how use the orion api with python for our purposes.

Thanks!
Goran

Parents
  • This all depends on what you are looking to change.  If you want to assign or unassign specific Application Templates (you mentioned Server & Application Monitor (SAM)  near the beginning), that can be done with the API.  If you want to assign custom properties to either the assigned application or nodes, this can also be done.

    But most people "shortcut" this by using Groups.  Here's a (simplified) scenario:

    • You have an application template (we'll call it "Custom Application")
    • This application needs to be assigned to a set of nodes.
    • You create a custom property (for Nodes) called "RunsCustomApp" (True/False) and check the box when the server runs the application.
    • You create a Group called "Custom App Servers" and have a Dynamic Query defining membership where "RunsCustomApp" is True.
    • You can then assign the "Custom Application" template to the group.
    • When nodes move in and out of the group, the application is either assigned or unassigned from the device.
  • Thanks for the feedback!
    I am sorry, but I am not able to follow some details in what you are suggesting (as I am not an experienced user), and would be pleased if you could direct me to the associated documentation and to add some details 

    • From your reply my understanding is that my (Python) proxy would be the Custom application to be created. (I have no experience in applying custom applications)
    • I will then have to create a Group of nodes ("Custom App Servers") there the nodes have a specific custom property defined and set. I would name the new property myself e.g. "RunsCustomApp".
    • There should then be a dynamic membership as to add/remove nodes from the Group which should be controlled by the proxy/application

    Thereby the node mapping is achieved and one could then e.g. use maps to present the topology.
    However, it is not clear how the status information shall be updated / pushed into SW by the proxy/application.

  •    Kevin, can you refer to someone who has created API pollers via the SDK or some documentation with examples.

    We thought about opening a support ticket, but they push back on helping with custom scripts!

  • We don't have any documentation for it.  All the API's docs are up in the GitHub Wiki (and some samples).  I don't think we have any specific for API pollers.

  • I don't see a section for the API poller.  Can you help point to that specific section of the Github Wiki?

  • Here is an example adapted from some internal design documents.  I haven't tested this, so take it with a big grain of salt.  If this doesn't work, I'll ask the team that worked on this feature to comment here.

    CreateApiPollerFromTemplate allows you to create an API Poller based on a template exported either from an existing API Poller or from the out-of-the-box template library.  But how do you get that template file to begin with?  If you're exporting an existing API Poller to a file (template.xml in the example below), you could use the ExportTemplate verb:

    Import-Module SwisPowerShell
     
    $hostname = "IP"
    $username = "Username"
    $plainTextPassword = "***"
    $password = $plainTextPassword | ConvertTo-SecureString -AsPlainText -Force
     
    $cred = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
    $swis = Connect-Swis -host $hostname -cred $cred
    
    $templateId = 1
    $result = Invoke-SwisVerb $swis Orion.APIPoller.Templates ExportTemplate @($templateId)
    $exportedTemplate = $result.InnerText
    $exportedTemplate | Out-File ".\template.xml"

    Once you have a template file to work with, you can import it via the CreateApiPollerFromTemplate verb:

    Import-Module SwisPowerShell
     
    $hostname = "IP"
    $username = "Username"
    $plainTextPassword = "***"
    $password = $plainTextPassword | ConvertTo-SecureString -AsPlainText -Force
     
    $cred = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $password
    $swis = Connect-Swis -host $hostname -cred $cred
     
    $apiPollerTemplate = (Get-Content -Path ".\template.xml" | Out-String)
     
    $configuration= @{
        "CredentialType" = "BasicAuth"
        "CredentialID" = "1"
      };
    $parameters= @{ };
     
    $result = Invoke-SwisVerb $swis "Orion.ApiPoller.ApiPoller" "CreateApiPollerFromTemplate" @("Orion.Nodes", 1, $apiPollerTemplate, $configuration, $parameters)
    $apiPollerId = $result.InnerText

  •     Thank you for the response.  This documentation helps a lot.

    I was able to get it to work manually through the SWQL studio, but when trying via the PowerShell script I keep getting this message.  I can't figure out what is does not like.

    Invoke-SwisVerb : Verb Orion.APIPoller.ApiPoller.CreateApiPollerFromTemplate cannot unpackage parameter 3 of type System.Collections.Generic.Dictionary`2[System.String,System.String]

    Initially I was trying to pass in the XML string as it was passed in via SWQL studio and then I changed the $configuration and $parameters to the hash table format you suggested, but it returns the same error.

    Here is my code snippet:

    $entityType = "Orion.Nodes"
    $entityID = 3045
    $template = '<?xml version="1.0" encoding="utf-8"?><Template xmlns:xsi="">www.w3.org/.../XMLSchema-instance" xmlns:xsd="">www.w3.org/.../DisplayName><Description /><Created>2024-03-05T04:38:10.3644824Z</Created><Updated>2024-03-05T04:38:10.3644824Z</Updated><Version>1</Version><RequestDetailsCollection><RequestDetails><Url>blah.com</Url><Body /><HttpVerb>Get</HttpVerb><ValueToMonitorCollection><ValueToMonitor><DisplayName>status code</DisplayName><Path>$</Path><ThresholdRule>NotEqualTo</ThresholdRule><WarningThresholdValue xsi:nil="true" /><CriticalThresholdValue>200</CriticalThresholdValue><Type>StatusCode</Type><StringToNumberTransformationRules /><StringToNumberTransformationOtherValues>0</StringToNumberTransformationOtherValues></ValueToMonitor></ValueToMonitorCollection><RequestVariables /><RequestDetailsOrder>0</RequestDetailsOrder></RequestDetails></RequestDetailsCollection><PollingInterval>60</PollingInterval></Template>'
    
    ## Variable format option 1
    #$configuration = '<ArrayOfKeyValueOfstringstring xmlns:i="">www.w3.org/.../XMLSchema-instance" xmlns="'">schemas.microsoft.com/.../ArrayOfKeyValueOfstringstring>' | ConvertFrom-StringData
    #$parameters = '<ArrayOfKeyValueOfstringstring xmlns:i="">www.w3.org/.../XMLSchema-instance" xmlns="'">schemas.microsoft.com/.../ArrayOfKeyValueOfstringstring>' | ConvertFrom-StringData
    
    ## Variable format option 2
    $configuration = @{
    "Name" = $pollerName
    "DisplayName" = $pollerName
    "Url" = $pollerURL
    "VerifySslCertificate" = $true
    "UseProxy" = $false
    "PollingInterval" = 60
    "CredentialType" = "OAuth2Shared"
    "CredentialID" = "173"
    };
    
    $parameters = @{ };
    
    Invoke-SwisVerb $swis Orion.APIPoller.ApiPoller CreateApiPollerFromTemplate @($entityType, $entityID, $template, $configuration, $parameters)

  • Internally, the last two parameters for this SWIS verb (configuration and parameters) are of type Dictionary<string, string> in C#.  That means it's a dictionary data structure with a string key and a string value.  When serialized as XML, Microsoft uses ArrayOfKeyValueOfstringstring (see https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-sadmws/5a8ba9b2-6287-44b4-a8fc-ab88b7d7c3c4).  There's a lot of useful information about working with that kind of data and some examples of what the XML looks like at https://stackoverflow.com/questions/49519253/deserializing-xml-from-soap-call-into-array-of-objects.  See Where are Orion.Credential.CreateCredentials & Orion.Credential.UpdateCredentials for an example of another place this data format is used with the Orion SDK for a different verb.  The XML there should give you a good idea of what this should look like. 

  • Did you ever get this working?

    I'm stuck on same issue.

  • Hi Daryl. We got it working with Powershell and I later converted it to Python. I'm sure you can finagle this to suit your needs. Note, that it reads a list of url's and uses regex to create the Poller Name based on the text in the URL so that you can mass create API pollers. We had some weirdness where I had to go in and re-select the 'No Auth' for each one (or select the auth, you can also set the auth in the script, but we did not) otherwise they were stuck in 'unknown' status. You can modify the regex as needed. Here is an example of the format of the API list .txt file: 'https://api.corporation.corp/sapi-thing1-events-storage/v1/healthcheckendpoint' and here is the script. Cheers!

    from orionsdk import SwisClient
    import requests
    import re
    requests.packages.urllib3.disable_warnings()




    configuration = {"VerifySslCertificate" : "False", "UseProxy" : "False"}
    parameters = {}
    nodeid = 1234
    entity = 'orion.nodes'


    def create_template_from_txt():
        file_path = 'C:\\path\\apiURLs.txt
        with open(file_path, 'r') as file:
            urls = file.readlines()

    # Extracting the API names and entire URLs
        api_info = [(re.search(r'.corp/(.*?)/v1', url).group(1), url.strip()) for url in urls]

    # Printing the results
        for api_name, full_url in api_info:
            main(api_name, full_url)
       

    def main(api_name, full_url):
        template_xml = (f'<?xml version="1.0" encoding="utf-8"?><Template xmlns:xsi="">www.w3.org/.../XMLSchema-instance" xmlns:xsd="">www.w3.org/.../Name><DisplayName>PRODUCTION{api_name}</DisplayName><Description /><Version>1</Version><RequestDetailsCollection><RequestDetails><Url>{full_url}</Url><Body /><HttpVerb>Get</HttpVerb><RequestHeaders><RequestHeader><Name>x-api-key</Name><Value>123lj21o3i3o42i5o4h5oij34ghi5ygu</Value></RequestHeader></RequestHeaders><ValueToMonitorCollection><ValueToMonitor><DisplayName>status code</DisplayName><Path>$</Path><ThresholdRule>NotEqualTo</ThresholdRule><WarningThresholdValue xsi:nil="true" /><CriticalThresholdValue>200</CriticalThresholdValue><Type>StatusCode</Type><StringToNumberTransformationRules /><StringToNumberTransformationOtherValues>0</StringToNumberTransformationOtherValues></ValueToMonitor></ValueToMonitorCollection><RequestVariables /><RequestDetailsOrder>0</RequestDetailsOrder></RequestDetails></RequestDetailsCollection></Template>')
        npm_server = 'orionserver01'
        ORION_USERNAME = '<username>'
        ORION_PASSWORD = '<password>'
        swis = SwisClient(npm_server, ORION_USERNAME, ORION_PASSWORD)
        invoke_create_poller = swis.invoke ('Orion.ApiPoller.ApiPoller', 'CreateApiPollerFromTemplate', entity , nodeid,  template_xml, configuration, parameters)
        if invoke_create_poller:
            print('Success')

        else:
            print('Failed')
       

    if __name__ == "__main__":
        create_template_from_txt()
     
  • Im actually doing this in powershell, I should be able to convert it back.

    Thanks

  • you may want to remove your KEY in the xml

Reply Children
No Data