diff --git a/lib/opennebula.ex b/lib/opennebula.ex new file mode 100644 index 0000000..e69de29 diff --git a/lib/recycledcloud/opennebula/vm.ex b/lib/recycledcloud/opennebula/vm.ex index 5a2da9d..f06f1a6 100644 --- a/lib/recycledcloud/opennebula/vm.ex +++ b/lib/recycledcloud/opennebula/vm.ex @@ -22,12 +22,88 @@ defmodule RecycledCloud.OpenNebula.VM do {:cloning_failure, 11} ] + @actions [ + "terminate-hard", + "terminate", + "undeploy-hard", + "undeploy", + "poweroff-hard", + "poweroff", + "reboot-hard", + "reboot", + "hold", + "release", + "stop", + "suspend", + "resume", + "resched", + "unresched" + ] + + @events [ + {:NONE_ACTION , 0}, + {:MIGRATE_ACTION , 1}, + {:LIVE_MIGRATE_ACTION , 2}, + {:SHUTDOWN_ACTION , 3}, + {:SHUTDOWN_HARD_ACTION , 4}, + {:UNDEPLOY_ACTION , 5}, + {:UNDEPLOY_HARD_ACTION , 6}, + {:HOLD_ACTION , 7}, + {:RELEASE_ACTION , 8}, + {:STOP_ACTION , 9}, + {:SUSPEND_ACTION , 10}, + {:RESUME_ACTION , 11}, + {:BOOT_ACTION , 12}, + {:DELETE_ACTION , 13}, + {:DELETE_RECREATE_ACTION , 14}, + {:REBOOT_ACTION , 15}, + {:REBOOT_HARD_ACTION , 16}, + {:RESCHED_ACTION , 17}, + {:UNRESCHED_ACTION , 18}, + {:POWEROFF_ACTION , 19}, + {:POWEROFF_HARD_ACTION , 20}, + {:DISK_ATTACH_ACTION , 21}, + {:DISK_DETACH_ACTION , 22}, + {:NIC_ATTACH_ACTION , 23}, + {:NIC_DETACH_ACTION , 24}, + {:DISK_SNAPSHOT_CREATE_ACTION , 25}, + {:DISK_SNAPSHOT_DELETE_ACTION , 26}, + {:TERMINATE_ACTION , 27}, + {:TERMINATE_HARD_ACTION , 28}, + {:DISK_RESIZE_ACTION , 29}, + {:DEPLOY_ACTION , 30}, + {:CHOWN_ACTION , 31}, + {:CHMOD_ACTION , 32}, + {:UPDATECONF_ACTION , 33}, + {:RENAME_ACTION , 34}, + {:RESIZE_ACTION , 35}, + {:UPDATE_ACTION , 36}, + {:SNAPSHOT_CREATE_ACTION , 37}, + {:SNAPSHOT_DELETE_ACTION , 38}, + {:SNAPSHOT_REVERT_ACTION , 39}, + {:DISK_SAVEAS_ACTION , 40}, + {:DISK_SNAPSHOT_REVERT_ACTION , 41}, + {:RECOVER_ACTION , 42}, + {:RETRY_ACTION , 43}, + {:MONITOR_ACTION , 44}, + {:DISK_SNAPSHOT_RENAME_ACTION , 45}, + {:ALIAS_ATTACH_ACTION , 46}, + {:ALIAS_DETACH_ACTION , 47}, + ] + def state_for(state) when is_atom(state) do @states |> Keyword.get(state) end def state_for(state) when is_integer(state) do - case Enum.find(@states, fn {atom, value} -> value == state end) do + case Enum.find(@states, fn {_atom, value} -> value == state end) do + {atom, _value} -> atom + nil -> nil + end + end + + def event_for(event) when is_integer(event) do + case Enum.find(@events, fn {_atom, value} -> value == event end) do {atom, _value} -> atom nil -> nil end @@ -43,4 +119,23 @@ defmodule RecycledCloud.OpenNebula.VM do {:error, err} -> {:error, err} end end + + def get_by_username(username) do + {:ok, %{VM: vms}} = RecycledCloud.OpenNebula.VMPool.get(%{ + filter_flag: :all, + range_start: :infinite, + range_end: :infinite, + state_filter: state_for(:any_except_done), + kv_filter: "" + }) + + Enum.filter(vms, fn vm -> List.to_string(Map.get(vm, :UNAME)) == username end) + end + + def execute(vm_id, action) when action in @actions do + case ONE.query("one.vm.action", [action, vm_id]) do + {:ok, _raw} -> :ok + {:error, err} -> {:error, err} + end + end end diff --git a/lib/recycledcloud_web/controllers/virtual_machine_hosting_controller.ex b/lib/recycledcloud_web/controllers/virtual_machine_hosting_controller.ex new file mode 100644 index 0000000..c9d056e --- /dev/null +++ b/lib/recycledcloud_web/controllers/virtual_machine_hosting_controller.ex @@ -0,0 +1,58 @@ +defmodule RecycledCloudWeb.VirtualMachineHostingController do + use RecycledCloudWeb, :controller + + alias RecycledCloud.OpenNebula.VM + + def index(conn, _params) do + username = conn.assigns.current_user.username + vms = VM.get_by_username(username) + + render(conn, "index.html", vms: vms) + end + + def start(conn, %{"id" => id}) do + case VM.execute(String.to_integer(id), "resume") do + :ok -> + conn + |> put_flash(:info, "Start request sent to VMM.") + |> redirect(to: Routes.virtual_machine_hosting_path(conn, :index)) + {:error, err} -> + conn + |> put_flash(:error, "Something went wrong: #{inspect(err)}") + |> redirect(to: Routes.virtual_machine_hosting_path(conn, :index)) + end + end + + def stop(conn, %{"id" => id}) do + case VM.execute(String.to_integer(id), "poweroff") do + :ok -> + conn + |> put_flash(:stop, "Stop request sent to VMM.") + |> redirect(to: Routes.virtual_machine_hosting_path(conn, :index)) + {:error, err} -> + conn + |> put_flash(:error, "Something went wrong: #{inspect(err)}") + |> redirect(to: Routes.virtual_machine_hosting_path(conn, :index)) + end + end + + def show(conn, %{"id" => id}) do + case VM.get(String.to_integer(id)) do + {:error, err} -> + conn + |> put_flash(:error, "Could not fetch VM details: #{inspect(err)}") + |> redirect(to: Routes.virtual_machine_hosting_path(conn, :index)) + {:ok, vm} -> + owner = vm |> Map.get(:UNAME) |> List.to_string + if owner == conn.assigns.current_user.username do + conn + |> assign(:vm, vm) + |> render("show.html") + else + conn + |> put_flash(:error, "You are not the owner of this machine.") + |> redirect(to: Routes.virtual_machine_hosting_path(conn, :index)) + end + end + end +end diff --git a/lib/recycledcloud_web/router.ex b/lib/recycledcloud_web/router.ex index 254ea79..f409e64 100644 --- a/lib/recycledcloud_web/router.ex +++ b/lib/recycledcloud_web/router.ex @@ -74,6 +74,11 @@ defmodule RecycledCloudWeb.Router do get "/users/settings/keys/:key_id/delete", UserKeysController, :delete get "/billing", BillingController, :index post "/billing/partner/update", BillingController, :update + + get "/hosting/vm", VirtualMachineHostingController, :index + get "/hosting/vm/:id", VirtualMachineHostingController, :show + get "/hosting/vm/:id/start", VirtualMachineHostingController, :start + get "/hosting/vm/:id/stop", VirtualMachineHostingController, :stop end scope "/", RecycledCloudWeb do diff --git a/lib/recycledcloud_web/templates/layout/_sidebar_nav.html.eex b/lib/recycledcloud_web/templates/layout/_sidebar_nav.html.eex index 5bcb884..d6ed1f5 100644 --- a/lib/recycledcloud_web/templates/layout/_sidebar_nav.html.eex +++ b/lib/recycledcloud_web/templates/layout/_sidebar_nav.html.eex @@ -6,13 +6,25 @@
  • <%= link "Settings", to: Routes.user_settings_path(@conn, :edit) %>
  • <%= link "Billing", to: Routes.billing_path(@conn, :index) %>
  • <%= link "Log out", to: Routes.user_session_path(@conn, :delete), method: :delete %>
  • +
    + +
  • <%= link "Virtual Machines", to: Routes.virtual_machine_hosting_path(@conn, :index) %>
  • <% else %>
  • <%= link "Log in", to: Routes.user_session_path(@conn, :new) %>
  • <%= link "Register", to: Routes.user_registration_path(@conn, :new) %>
  • <% end %>
  • + + Infrastructure status + +
  • Back to recycled.cloud ↵ + diff --git a/lib/recycledcloud_web/templates/virtual_machine_hosting/index.html.eex b/lib/recycledcloud_web/templates/virtual_machine_hosting/index.html.eex new file mode 100644 index 0000000..9a134ad --- /dev/null +++ b/lib/recycledcloud_web/templates/virtual_machine_hosting/index.html.eex @@ -0,0 +1,39 @@ +

    Virtual Machines

    + +

    This page list all the Virtual Machines linked to your account. It is +not possible to interect with them yet.

    + +<%= for location <- ["LNTH"] do %> +

    Location: <%= location %>

    + + + + + + + + + + + + <%= for vm <- @vms do %> + + + + + + + <% end %> + +
    #NameStateActions
    <%= Map.get(vm, :ID) %><%= Map.get(vm, :NAME) %><%= VM.state_for(Map.get(vm, :STATE)) %> + <%= case VM.state_for(Map.get(vm, :STATE)) do + :poweroff -> + link "start", to: Routes.virtual_machine_hosting_path(@conn, :start, Map.get(vm, :ID)) + :active -> + link "stop", to: Routes.virtual_machine_hosting_path(@conn, :stop, Map.get(vm, :ID)) + _ -> "" + end %> + + <%= link "show details", to: Routes.virtual_machine_hosting_path(@conn, :show, Map.get(vm, :ID)) %> +
    +<% end %> diff --git a/lib/recycledcloud_web/templates/virtual_machine_hosting/show.html.eex b/lib/recycledcloud_web/templates/virtual_machine_hosting/show.html.eex new file mode 100644 index 0000000..2f8e427 --- /dev/null +++ b/lib/recycledcloud_web/templates/virtual_machine_hosting/show.html.eex @@ -0,0 +1,27 @@ +

    VM#<%= Map.get(@vm, :ID) %> - <%= Map.get(@vm, :NAME) %>

    + +