class ACIrb::RestClient

REST client end point implementation

Attributes

baseurl[RW]
debug[RW]
format[RW]
password[RW]
refresh_time[R]
user[RW]
verify[RW]

Public Class Methods

new(options = {}) click to toggle source

Public: Initializes and establishes an authenticated session with APIC

REST endpoint

options - Hash options used to specify connectivity

attributes (default: {}):

:url - string URL of APIC, e.g., https://apic (required)
:user - string containing User ID for authentication (required)
:password - string containing Password for
            authentication (required)
:debug - boolean true or false for including verbose REST output
         (default: false)
:format - string 'xml' or 'json' specifying the format to use
          for messaging to APIC. (default: xml)
:verify - boolean true or false for verifying the SSL
          certificate. (default: false)

Examples:

rest = ACIrb::RestClient.new(url: 'https://apic', user: 'admin',
                             password: 'password', format: 'json',
                             debug: false)
# File lib/acirb/restclient.rb, line 42
def initialize(options = {})
  uri = URI.parse(options[:url])
  @baseurl = '%s://%s:%s' % [uri.scheme, uri.host, uri.port]
  @format = options[:format] ? options[:format] : 'xml'

  @user = options[:user]
  @password = options[:password]

  @verify = options[:verify]

  @client = HTTPClient.new

  @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE \
    unless options[:verify] && uri.scheme == 'https'

  @debug = options[:debug]

  @auth_cookie = ''

  authenticate if @user && @password
end

Public Instance Methods

authenticate() click to toggle source

Public: Authenticates the REST session with APIC Sends a aaaLogin message to APIC and updates the following instance variables:

@auth_cookie - session cookie
@refresh_time - session refresh timeout in seconds

Returns nothing.

# File lib/acirb/restclient.rb, line 71
def authenticate
  builder = Nokogiri::XML::Builder.new do |xml|
    xml.aaaUser(name: @user, pwd: @password)
  end
  post_url = URI.encode(@baseurl.to_s + '/api/mo/aaaLogin.xml')
  puts 'POST REQUEST', post_url if @debug
  puts 'POST BODY', builder.to_xml if @debug
  response = @client.post(post_url, body: builder.to_xml)
  puts 'POST RESPONSE: ', response.body if @debug
  doc = Nokogiri::XML(response.body)
  fail ApicAuthenticationError, 'Authentication error(%s): %s' % [doc.at_css('error')['code'], doc.at_css('error')['text']] \
    if doc.at_css('error')
  fail ApicErrorResponse, 'Unexpected HTTP Error response code(%s): %s' % [response.code, response.body] if response.code != 200
  @auth_cookie = doc.at_css('aaaLogin')['token']
  @refresh_time = doc.at_css('aaaLogin')['refreshTimeoutSeconds']
end
escape(uri) click to toggle source

Internal: Escape URI before using with APIC REST interface

uri - URI to escape

Returns escaped URI as string

# File lib/acirb/restclient.rb, line 113
def escape(uri)
  return URI.encode(uri, /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/)
end
get(options) click to toggle source

Internal: Queries the APIC REST API for data

options - Hash options for defining get parameters (default: {})

:url - relative URL for request (required)

Returns results of parse_response, which will be the parsed results of the XML or JSON payload represented as ACIrb::MO objects

# File lib/acirb/restclient.rb, line 150
def get(options)
  get_url = self.escape(@baseurl.to_s + options[:url].to_s)

  puts 'GET REQUEST', get_url if @debug
  response = @client.get(get_url)
  puts 'GET RESPONSE: ', response.body if @debug

  parse_response(response)
end
lookupByClass(cls, options = {}) click to toggle source

Public: Helper function that performs a simple lookup on a Class

cls - string containing the class name to query (required) options - Hash options for defining query options (default: {})

:subtree - specifies the subtree query options, which can be
           children, full or self

Examples

# return all L1 physical interfaces on the fabric with complete subtree
mo = rest.lookupByClass('l1PhysIf', subtree: 'full')

Returns an array of ACIrb::MO objects for the query

# File lib/acirb/restclient.rb, line 327
def lookupByClass(cls, options = {})
  subtree = options[:subtree]
  cls_query = ACIrb::ClassQuery.new(cls)
  cls_query.subtree = subtree
  query(cls_query)
end
lookupByDn(dn, options = {}) click to toggle source

Public: Helper function that performs a simple lookup on a Dn

dn - string containing distinguished name for the object to query

(required)

options - Hash options for defining query options (default: {})

:subtree - specifies the subtree query options, which can be
           children, full or self

Examples

mo = rest.lookupByDn('uni/tn-common', subtree: 'full')

Returns a single ACIrb::MO object or nil if no response for the query is received

# File lib/acirb/restclient.rb, line 303
def lookupByDn(dn, options = {})
  subtree = options[:subtree]
  dn_query = ACIrb::DnQuery.new(dn)
  dn_query.subtree = subtree

  mos = query(dn_query)
  if mos.length == 1
    return mos[0]
  else
    return nil
  end
end
parse_error(doc) click to toggle source

Internal: Parses for error responses in APIC response payload

doc - Nokigiri XML document or Hash array containing well formed

APIC response payload (required)
# File lib/acirb/restclient.rb, line 164
def parse_error(doc)
  if format == 'xml'
    fail ApicErrorResponse, 'Error response from APIC (%s): "%s"' % \
      [doc.at_css('error')['code'], doc.at_css('error')['text']] \
      if doc.at_css('error')
  elsif format == 'json'
    fail ApicErrorResponse, 'Error response from APIC (%s): "%s"' % \
      [doc['imdata'][0]['error']['attributes']['code'].to_s, \
       doc['imdata'][0]['error']['attributes']['text'].to_s] \
       if doc['imdata'].length > 0 && doc['imdata'][0].include?('error')
  end
end
parse_response(response) click to toggle source

Internal: Parses APIC response payload into ACIrb::MO objects

response - string containing the XML or JSON payload that will be

parsed according to the format defined at instance creation
(required)
# File lib/acirb/restclient.rb, line 182
def parse_response(response)
  if format == 'xml'
    xml_data = response.body
    doc = Nokogiri::XML(xml_data)

    parse_error(doc)

    mos = []
    doc.root.elements.each do |xml_obj|
      mo = ACIrb::Loader.load_xml(xml_obj)
      mos.push(mo)
    end

    return mos

  elsif format == 'json'
    json_data = response.body
    doc = JSON.parse(json_data)

    parse_error(doc)

    mos = []
    doc['imdata'].each do |json_obj|
      mo = ACIrb::Loader.load_json(json_obj)
      mos.push(mo)
    end

    return mos
  end
end
post(options) click to toggle source

Internal: Posts data to the APIC REST interface

options - Hash options for defining post parameters (default: {})

:url - relative URL for request (required)
:data - post payload to be included in the request (required)

Returns results of parse_response, which will be the parsed results of the XML or JSON payload represented as ACIrb::MO objects

# File lib/acirb/restclient.rb, line 125
def post(options)
  post_url = self.escape(@baseurl.to_s + options[:url].to_s)

  data = options[:data]
  if @format == 'xml'
    data = data.to_xml
  elsif @format == 'json'
    data = data.to_json
  end

  puts 'POST REQUEST', post_url if @debug
  puts 'POST BODY', data if @debug
  response = @client.post(post_url, body: data)
  puts 'POST RESPONSE: ', response.body if @debug

  parse_response(response)
end
query(query_obj) click to toggle source

Public: Sends a query to APIC and returns the matching MO objects

query_obj - ACIrb::Query object, typically either ACIrb::DnQuery or

ACIrb::ClassQuery which contains the query that will be issued
(required)

Examples

dn_query = ACIrb::DnQuery.new('uni/tn-common')
dn_query.subtree = 'full'
mos = rest.query(dn_query)

Returns array of ACIrb::MO objects for the query

# File lib/acirb/restclient.rb, line 225
def query(query_obj)
  query_uri = query_obj.uri(@format)
  get(url: query_uri)
end
refresh_session() click to toggle source

Public: Refreshes an existing RestClient object session Sends a aaaRefresh message to APIC and updates the following instance variables:

@auth_cookie - session cookie
@refresh_time - session refresh timeout in seconds

Returns nothing.

# File lib/acirb/restclient.rb, line 95
def refresh_session
  get_url = URI.encode(@baseurl.to_s + '/api/mo/aaaRefresh.xml')
  puts 'GET REQUEST', get_url if @debug
  response = @client.get(get_url)
  puts 'GET RESPONSE: ', response.body if @debug
  doc = Nokogiri::XML(response.body)
  fail ApicAuthenticationError, 'Authentication error(%s): %s' % [doc.at_css('error')['code'], doc.at_css('error')['text']] \
    if doc.at_css('error')
  @auth_cookie = doc.at_css('aaaLogin')['token']
  @refresh_time = doc.at_css('aaaLogin')['refreshTimeoutSeconds']
end
refresh_subscription(subscription_id) click to toggle source

Public: Refreshes an existing subscription query

subscription_id - string containing the subscription ID for a previously

subscribed to query

Examples

class_query = ACIrb::ClassQuery.new('fvCEp')
class_query.page_size = '1'
class_query.page = '0'
subscription_id = rest.subscribe(class_query)
sleep(50)
rest.refresh_subscription(subcription_id)

Returns nothing.

# File lib/acirb/restclient.rb, line 286
def refresh_subscription(subscription_id)
  query_uri = '/api/subscriptionRefresh.%s?id=%s' % [@format, subscription_id]
  get(url: query_uri)
end
subscribe(query_obj) click to toggle source

Public: Sends an event subscription query to APIC

query_obj - ACIrb::Query object, typically either ACIrb::DnQuery or

ACIrb::ClassQuery which contains the query that will be
issued. This query will have the .subscribe property set
to "yes" as part of the subscription process (required)

Examples

# subscribe to all changes on fvCEp end points on fabric
# but restrict the results of the query to only include 1
# as to reduce the initial subscription time
class_query = ACIrb::ClassQuery.new('fvCEp')
class_query.page_size = '1'
class_query.page = '0'
subscription_id = rest.subscribe(class_query)

Returns the subscription ID for the newly registered subscription

# File lib/acirb/restclient.rb, line 247
def subscribe(query_obj)
  query_obj.subscribe = 'yes'
  query_uri = query_obj.uri(@format)

  get_url = self.escape(@baseurl.to_s + query_uri.to_s)

  puts 'GET REQUEST', get_url if @debug
  response = @client.get(get_url)
  puts 'GET RESPONSE: ', response.body if @debug

  if format == 'xml'
    xml_data = response.body
    doc = Nokogiri::XML(xml_data)
    parse_error(doc)
    subscriptionId = doc.at_css('imdata')['subscriptionId']
  elsif format == 'json'
    json_data = response.body
    doc = JSON.parse(json_data)
    parse_error(doc)
    subscriptionId = doc['subscriptionId']
  end

  subscriptionId
end