defmodule AzureAPI.VirtualMachineController do
@moduledoc """
Interface for making API calls.
This module uses a GenServer to store data required for API requests.
"""
alias AzureAPI.AzureCalls
use GenServer
########### GENSERVER ####################################################
########## GENSERVER CLIENT ###############
@doc section: :public
@doc """
Starts the GenServer.
Implicitly calls init/1 with the user passed in.
"""
def start_link(user) do
#IO.inspect(user.sub_id)
GenServer.start_link(__MODULE__, user, name: String.to_atom(user.email))
end
@doc false
def get_virtual_machines(pid) do
GenServer.call(pid, :get_virtual_machines)
end
@doc section: :public
@doc """
Public facing call for getting availability
"""
def get_availability(pid) do
GenServer.call(pid, {:get_availability})
end
@doc section: :public
@doc """
Public facing call for starting a virtual machine
"""
def start_virtual_machine(name, pid) do
GenServer.call(pid, {:start_virtual_machine, name})
end
@doc section: :public
@doc """
Public facing call for stopping (deallocating) a virtual machine
"""
def stop_virtual_machine(name, pid) do
IO.inspect("Stop call 1")
GenServer.call(pid, {:stop_virtual_machine, name})
end
@doc section: :public
@doc """
Public facing call for retreiving cost data
"""
def get_cost_data(vm, pid) do
GenServer.call(pid, {:get_cost_data, vm}, 1000000)
end
########## GENSERVER SERVER CALLBACKS ########################
@doc section: :callbacks
# Callbacks
@doc """
Server callbacks for messages
Possible msgs:
1. :get_virtual_machines (deprecated)
2. :get_availability - calls AzureCalls.get_availability/1
3. [:start_virtual_machine, name] - calls AzureCalls.start_azure_machine/1
4. [:stop_virtual_machine, name] - calls AzureCalls.stop_azure_machine/1
5. [:get_cost_data, vm] - calls AzureCalls.get_azure_cost_data/1
"""
def handle_call(:get_virtual_machines, _from, azure_keys) do
# Call list_VM endpoint
{_status, map} = AzureCalls.list_azure_machines_and_statuses(azure_keys, self())
{:reply, map, azure_keys}
# IO.inspect(body)
end
def handle_call({:get_availability}, _from, token) do
# Call availability function
data = AzureCalls.get_availability(token)
{:reply, data, token}
end
def handle_call({:start_virtual_machine, name}, _from, azure_keys) do
# Call start endpoint
data = AzureCalls.start_azure_machine(name, azure_keys)
{:reply, data, azure_keys}
# IO.inspect(body)
end
def handle_call({:stop_virtual_machine, name}, _from, token) do
IO.inspect("Stop call 2")
# Call Start Function
data = AzureCalls.stop_azure_machine(name, token)
{:reply, data, token}
end
def handle_call({:get_cost_data, vm}, _from, token) do
data = AzureCalls.get_azure_cost_data(vm, token)
IO.inspect(data)
{:reply, data, token, 1000000}
end
@doc section: :callbacks
@doc """
Handles messages sent to the server
Possible messages:
1. :refresh_token - calls AzureCalls.get_token/1
2. :refresh_sync - calls AzureCalls.list_azure_machines_and_statuses/1
"""
# Refresh Token
def handle_info(:refresh_token, azure_keys) do
token = AzureCalls.get_token(azure_keys, self())
Map.put(azure_keys, "token", token)
{:noreply, azure_keys}
end
def handle_info(:refresh_sync, token) do
# IO.inspect("refreshing sync")
{:ok, _map} = AzureCalls.list_azure_machines_and_statuses(token, self())
{:noreply, token}
end
def init(user) do
token_keys = %{"sub_id" => user.sub_id, "tenant_id" => user.tenant_id,
"client_secret" => user.client_secret, "client_id" => user.client_id,
"resource_group" => user.resource_group}
token = AzureCalls.get_token(token_keys, self())
if(token == nil) do
{:ok, token_keys}
else
# azure_keys = Map.put(token_keys ,:token, token)
azure_keys = %{"sub_id" => user.sub_id, "tenant_id" => user.tenant_id,
"client_secret" => user.client_secret, "client_id" => user.client_id,
"resource_group" => user.resource_group, "token" => token}
# IO.inspect("token here!")
# IO.inspect(Map.get(azure_keys, "token"))
{:ok, _map} = AzureCalls.list_azure_machines_and_statuses(azure_keys, self())
AzureCalls.get_availability(azure_keys)
{:ok, azure_keys}
end
end
"https://prices.azure.com/api/retail/prices?currencyCode='AUD'&$filter=serviceFamily eq 'Compute' and armSkuName eq 'Standard_A0'"
################ END GENSERVER #######################
# ################ API FUNCTIONS #######################
# def get_token() do
# # Call Token Endpoint
# HTTPoison.start
# response = HTTPoison.post! "https://login.microsoftonline.com/a6a9eda9-1fed-417d-bebb-fb86af8465d2/oauth2/token", "grant_type=client_credentials&client_id=4bcba93a-6e11-417f-b4dc-224b008a7385&client_secret=oNH8Q~Gw6j0DKSEkJYlz2Cy65AkTxiPsoSLWKbiZ&resource=https%3A%2F%2Fmanagement.azure.com%2F", [{"Content-Type", "application/x-www-form-urlencoded"}]
# {_status, body} = Poison.decode(response.body)
# IO.inspect(body["error"])
# token = body["access_token"]
# IO.inspect(token)
# # Schedule a new token after 1 hour
# Process.send_after(:virtual_machine_controller, :refresh_token, 1 * 60 * 60 * 1000)
# token
# end
# # LIST AZURE MACHINES
# def list_azure_machines_and_statuses(token) do
# # Construct Header
# header = ['Authorization': "Bearer " <> token]
# # Call List Endpoint
# response = HTTPoison.get! "https://management.azure.com/subscriptions/f2b523ec-c203-404c-8b3c-217fa4ce341e/resourceGroups/usyd-12a/providers/Microsoft.Compute/virtualMachines?api-version=2022-03-01", header, []
# IO.inspect(response.status_code)
# if response.status_code == 200 do
# body = Poison.Parser.parse!(response.body)
# # Extract names
# names = Enum.map(body["value"], fn (x) -> x["name"] end)
# IO.inspect(names)
# map = Enum.map(names, fn name ->
# instance_view = HTTPoison.get! "https://management.azure.com/subscriptions/f2b523ec-c203-404c-8b3c-217fa4ce341e/resourceGroups/usyd-12a/providers/Microsoft.Compute/virtualMachines/#{name}/instanceView?api-version=2022-03-01", header, []
# {_status, body} = Poison.decode(instance_view.body)
# [_provision, power] = Enum.map(body["statuses"], fn (x) -> x["displayStatus"] end)
# {name, power}
# end)
# {:ok, map}
# else
# {:error, response.status_code}
# end
# end
# # GET AZURE AVAILABILITIES
# def get_availability(token) do
# # Construct header
# header = ['Authorization': "Bearer " <> token]
# response = HTTPoison.get! "https://management.azure.com/subscriptions/f2b523ec-c203-404c-8b3c-217fa4ce341e/providers/Microsoft.ResourceHealth/availabilityStatuses/current?api-version=2018-07-01", header, []
# # response = HTTPoison.get! "https://management.azure.com/subscriptions/f2b523ec-c203-404c-8b3c-217fa4ce341e/resourceGroups/usyd-12a/providers/Microsoft.ResourceHealth/availabilityStatuses?api-version=2018-07-01&", header, []
# IO.inspect(response)
# if response.status_code == 200 do
# body = Poison.Parser.parse!(response.body)
# statuses = Enum.map(body["value"], fn (x) -> {x["properties"]["availabilityState"], x["properties"]["summary"]} end)
# IO.inspect(statuses)
# {:ok, statuses}
# else
# {:error, response.status_code}
# end
# end
# # START AZURE MACHINES
# def start_azure_machine(name, token) do
# # Construct Header
# header = ['Authorization': "Bearer " <> token]
# # Call Start Endpoint
# HTTPoison.post! "https://management.azure.com/subscriptions/f2b523ec-c203-404c-8b3c-217fa4ce341e/resourceGroups/usyd-12a/providers/Microsoft.Compute/virtualMachines/#{name}/start?api-version=2022-08-01", [], header
# end
# # STOP AZURE MACHINES
# def stop_azure_machine(name, token) do
# # Construct Header
# header = ['Authorization': "Bearer " <> token]
# # Call Start Endpoint
# HTTPoison.post! "https://management.azure.com/subscriptions/f2b523ec-c203-404c-8b3c-217fa4ce341e/resourceGroups/usyd-12a/providers/Microsoft.Compute/virtualMachines/#{name}/powerOff?api-version=2022-08-01", [], header
# end
# # GET COST DATA
# """
# TO CALL THIS FUNCTION, CALL THE GENSERVER FUNCTION
# For example, needed in the details page
# eg -> response = AzureAPI.VirtualMachineController.get_cost_data(vmName).
# get_cost_data/1 grabs the token from the genserver and sends it to this function along with the vmName
# """
# def get_azure_cost_data(name, token) do
# # Construct Header
# header = ['Authorization': "Bearer " <> token, 'content-type': "application/json"]
# scope = "subscriptions/f2b523ec-c203-404c-8b3c-217fa4ce341e/resourceGroups/usyd-12a"
# body = %{
# :type => "Usage",
# :timeframe => "MonthToDate",
# :dataset => %{
# :granularity => "Daily",
# :filter => %{
# :dimensions => %{
# :name => "ResourceId",
# :operator => "In",
# :values => [
# "/subscriptions/f2b523ec-c203-404c-8b3c-217fa4ce341e/resourcegroups/usyd-12a/providers/microsoft.compute/virtualmachines/#{name}"
# ]
# }
# },
# :grouping => [
# %{
# :type => "Dimension",
# :name => "ResourceId"
# }
# ],
# :aggregation => %{
# :totalCost => %{
# :name => "PreTaxCost",
# :function => "sum"
# }
# }
# }
# }
# # Call Start Endpoint
# response = HTTPoison.post! "https://management.azure.com/#{scope}/providers/Microsoft.CostManagement/query?api-version=2021-10-01", Poison.encode!(body), header
# # Return decoded body
# Poison.decode! response.body
# # "https://management.azure.com/#{scope}/providers/Microsoft.CostManagement/query?api-version=2021-10-01"
# end
end