lib/azure_api/VirtualMachine_controller.ex

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