defmodule Meta.OpenNebula do @moduledoc """ The OpenNebula context and XML-RPC Interface. See http://docs.opennebula.io/5.12/integration/system_interfaces/api.html for details. """ alias Meta.OpenNebula.{VM, VMPool} require Logger # OpenNebula daemon. @endpoint "/RPC2" defp get_opennebula_config(key) do Application.get_env(:meta, :opennebula, []) |> Keyword.get(key) end def get_locations() do get_opennebula_config(:locations) |> Enum.map(fn m -> Map.get(m, :name) end) end def get_location_config(name) do get_opennebula_config(:locations) |> Enum.find(fn m -> Map.get(m, :name) == name end) end ## # Related to XML-RPC calls defp post(url, request) do opts = [] headers = [] body = request |> XMLRPC.encode! query = HTTPoison.post(url, body, headers, opts) case query do {:ok, response} -> case response.body |> XMLRPC.decode! do %{fault_code: _, fault_string: err} -> {:error, err} %{param: [false, err | _]} -> {:error, err} %{param: [true, result | _]} -> {:ok, result} end {:error, %HTTPoison.Error{id: nil, reason: reason}} -> Logger.warn("Error querying OpenNebula: #{inspect(reason)}") {:error, reason} end end def query(location, method, params) do user = Map.get(get_location_config(location), :user) password = Map.get(get_location_config(location), :password) auth_string = "#{user}:#{password}" request = %XMLRPC.MethodCall{ method_name: method, params: [auth_string | params] } address = get_location_config(location) |> Map.get(:xmlrpc_address) post(address <> @endpoint, request) end ## # Public / external calls. def get_vm(location, id), do: VM.get(id).(location) def get_vms_for_user(location, username) do call = VMPool.get(%{ filter_flag: :all, range_start: :infinite, range_end: :infinite, state_filter: VM.state_for(:any_except_done), kv_filter: "" }) case call.(location) do {:ok, %{VM: vms}} -> Enum.filter(vms, fn vm -> List.to_string(Map.get(vm, :UNAME)) == username end) {:error, _err} -> %{} end end def get_vms_for_user(username) do get_locations() |> Enum.map(fn l -> %{l => get_vms_for_user(l, username)} end) |> Enum.reduce(%{}, &Map.merge/2) end end