CloudForms API extension: fast way
Sometimes it can be quite difficult to work with CloudForms API. Obviously, there is nothing complicated using it, but if you need to extend API to provide some additional service, you may spend some serious time to understand how it works.There are several ways to extend existing API, today I will describe the fastest, which takes just a little time to realize.
This method is suitable only if you don't need to add new instances to CloudForms, but just want to collect some existing info in one place and pass it through API.
CloudForms API provides limited information about services, which wasn't enough for our case. We wanted to add some extra info, especially about Virtual Machines.
Firs of all describe new API instance in
${VMDB}/config/api.yml
---
:extended_services:
:description: Extended Service API
:options:
- :collection
:methods: *70174834086080
:extended_services: name of our new API
- :collection
means that it will be available directly, so our new API URL is {hostname}/api/extended_services:methods: *70174834086080 is a hash of HTTP Methods available for collection. You can find all values in ${VMDB}/config/api.yml
---
:method:
:names:
- :get
- :put
- :post
- :patch
- :delete
:sets:
:g: &70174834086080
- :get
:gp: &70174834085860
- :get
- :post
:gpd: &70174834085620
- :get
- :post
- :delete
:gpppd: &70174834084700
- :get
- :put
- :post
- :patch
- :delete
In our case we need only GET method. Though POST method realization is in some way similar to GET, there are still some serious differences, which we will describe in future articles.
Then we need to write our new controller, which will actually do all the work
${VMDB}/app/controllers/api_controller/extended_services.rb
class ApiController
module ExtendedServices
def show_extended_services
#@req[:c_id] is global variable, id of collection instance from url.
#check if this param exists
#and if not, render whole collection instead of one element
if !@req[:c_id]
render_extended_services_collection()
else
render_extended_services(@req[:c_id])
end
end
def render_extended_services_collection()
resp = Jbuilder.new
my_services = find_filtered_api(Service, :all)
resp.resources my_services do |my_service|
resp.id my_service.id
resp.name my_service.name
resp.created_at my_service.created_at
resp.template my_service.service_template_id
resp.cpus my_service.aggregate_direct_vm_cpus
resp.vms my_service.vms.size
resp.memory my_service.aggregate_all_vm_memory
end
render :json => resp.target!
end
def render_extended_services(id)
my_service = find_by_id_filtered_api(Service, id)
resp = Jbuilder.new
resp.resources do
resp.id id
resp.name my_service.name
resp.created_at my_service.created_at
resp.vms my_service.vms do |vm|
resp.id vm.id
resp.name vm.name
resp.state vm.normalized_state
resp.cpu vm.num_cpu
resp.used_storage vm.used_storage
resp.snapshots vm.snapshots
end
end
render :json => resp.target!
end
end
end
Thit is important, that one method called "show_"+{api name}
because after /api/extended_services called,
CloudForms looks exactly for method with this name and invokes it.
Here all information that we need from service is collected and rendered.All CloudForms api responses should be in JSON, we use Jbuilder to make JSON from our data.
We use custom methods
find_filtered_api and find_by_id_filtered_api to find instances, user have access to. This methods are just slightly corrected methods find_filtered and find_by_id_filtered from app/controllers/application_controller.rb. We use basic ideas of this methods, but we apply all filters to @user global variable which stores API user.
We save this methods in our custom api helper, so we can use them in future.${VMDB}app/helpers/api_helper/custom_helper.rb
---
def find_filtered_api(db, count, options={})
user = User.find_by_userid(@auth_user)
mfilters = user ? user.get_managed_filters : []
bfilters = user ? user.get_belongsto_filters : []
$api_log.info(bfilters)
if db.respond_to?(:find_filtered) && !mfilters.empty?
result = db.find_tags_by_grouping(mfilters, :conditions => options[:conditions], :ns=>"*")
else
result = Rbac.search(:class => db, :conditions => options[:conditions], :userid => @auth_user, :results_format => :objects).first
end
result = MiqFilter.apply_belongsto_filters(result, bfilters) if db.respond_to?(:find_filtered) && result
result
end
def find_by_id_filtered_api(db, id)
raise "Invalid input" unless is_integer?(id)
userid = @auth_user
unless db.where(:id => from_cid(id)).exists?
msg = I18n.t("flash.record.selected_item_no_longer_exists", :model => ui_lookup(:model => db.to_s))
raise msg
end
msg = "User '#{userid}' is not authorized to access '#{ui_lookup(:model=>db.to_s)}' record id '#{id}'"
conditions = ["#{db.table_name}.id = ?", id]
result = Rbac.search(:class => db, :conditions => conditions, :userid => userid, :results_format => :objects).first.first
raise msg if result.nil?
result
end
---
Include new module in ${VMDB}/app/controllers/api_controller.rb
--- include_concern "ExtendedServices"Restart CFME:
$> cd /var/www/miq/vmdb/ $> rake evm:restartThat's it!
Now we can go to our newly created pages and see results
All collection
{hostname}/api/extended_services
One service {hostname}/api/extended_services/57
.png)


0 comments: