1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { Dispatch } from "redux";
6 import { RootState } from 'store/store';
7 import { ServiceRepository } from "services/services";
8 import { navigateToUserVirtualMachines, navigateToAdminVirtualMachines, navigateToRootProject } from "store/navigation/navigation-action";
9 import { bindDataExplorerActions } from 'store/data-explorer/data-explorer-action';
10 import { formatDate } from "common/formatters";
11 import { unionize, ofType, UnionOf } from "common/unionize";
12 import { VirtualMachineLogins } from 'models/virtual-machines';
13 import { FilterBuilder } from "services/api/filter-builder";
14 import { ListResults } from "services/common-service/common-service";
15 import { dialogActions } from 'store/dialog/dialog-actions';
16 import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
17 import { PermissionLevel } from "models/permission";
18 import { deleteResources, updateResources } from 'store/resources/resources-actions';
19 import { Participant } from "views-components/sharing-dialog/participant-select";
20 import { initialize, reset } from "redux-form";
21 import { getUserDisplayName } from "models/user";
23 export const virtualMachinesActions = unionize({
24 SET_REQUESTED_DATE: ofType<string>(),
25 SET_VIRTUAL_MACHINES: ofType<ListResults<any>>(),
26 SET_LOGINS: ofType<VirtualMachineLogins>(),
27 SET_LINKS: ofType<ListResults<any>>()
30 export type VirtualMachineActions = UnionOf<typeof virtualMachinesActions>;
32 export const VIRTUAL_MACHINES_PANEL = 'virtualMachinesPanel';
33 export const VIRTUAL_MACHINE_ATTRIBUTES_DIALOG = 'virtualMachineAttributesDialog';
34 export const VIRTUAL_MACHINE_REMOVE_DIALOG = 'virtualMachineRemoveDialog';
35 export const VIRTUAL_MACHINE_ADD_LOGIN_DIALOG = 'virtualMachineAddLoginDialog';
36 export const VIRTUAL_MACHINE_ADD_LOGIN_FORM = 'virtualMachineAddLoginForm';
37 export const VIRTUAL_MACHINE_REMOVE_LOGIN_DIALOG = 'virtualMachineRemoveLoginDialog';
39 export const VIRTUAL_MACHINE_UPDATE_LOGIN_UUID_FIELD = 'uuid';
40 export const VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD = 'vmUuid';
41 export const VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD = 'user';
42 export const VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD = 'groups';
43 export const VIRTUAL_MACHINE_ADD_LOGIN_EXCLUDE = 'excludedPerticipants';
45 export const openUserVirtualMachines = () =>
46 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
47 dispatch<any>(navigateToUserVirtualMachines);
50 export const openAdminVirtualMachines = () =>
51 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
52 const user = getState().auth.user;
53 if (user && user.isAdmin) {
54 dispatch<any>(navigateToAdminVirtualMachines);
56 dispatch<any>(navigateToRootProject);
57 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "You don't have permissions to view this page", hideDuration: 2000, kind: SnackbarKind.ERROR }));
61 export const openVirtualMachineAttributes = (uuid: string) =>
62 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
63 const virtualMachineData = getState().virtualMachines.virtualMachines.items.find(it => it.uuid === uuid);
64 dispatch(dialogActions.OPEN_DIALOG({ id: VIRTUAL_MACHINE_ATTRIBUTES_DIALOG, data: { virtualMachineData } }));
67 const loadRequestedDate = () =>
68 (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
69 const date = services.virtualMachineService.getRequestedDate();
70 dispatch(virtualMachinesActions.SET_REQUESTED_DATE(date));
73 export const loadVirtualMachinesAdminData = () =>
74 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
75 dispatch<any>(loadRequestedDate());
77 const virtualMachines = await services.virtualMachineService.list();
78 dispatch(updateResources(virtualMachines.items));
79 dispatch(virtualMachinesActions.SET_VIRTUAL_MACHINES(virtualMachines));
82 const logins = await services.permissionService.list({
83 filters: new FilterBuilder()
84 .addIn('head_uuid', virtualMachines.items.map(item => item.uuid))
85 .addEqual('name', PermissionLevel.CAN_LOGIN)
88 dispatch(updateResources(logins.items));
89 dispatch(virtualMachinesActions.SET_LINKS(logins));
91 const users = await services.userService.list({
92 filters: new FilterBuilder()
93 .addIn('uuid', logins.items.map(item => item.tailUuid))
97 dispatch(updateResources(users.items));
99 const getAllLogins = await services.virtualMachineService.getAllLogins();
100 dispatch(virtualMachinesActions.SET_LOGINS(getAllLogins));
103 export const loadVirtualMachinesUserData = () =>
104 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
105 dispatch<any>(loadRequestedDate());
106 const virtualMachines = await services.virtualMachineService.list();
107 const virtualMachinesUuids = virtualMachines.items.map(it => it.uuid);
108 const links = await services.linkService.list({
109 filters: new FilterBuilder()
110 .addIn("head_uuid", virtualMachinesUuids)
113 dispatch(virtualMachinesActions.SET_VIRTUAL_MACHINES(virtualMachines));
114 dispatch(virtualMachinesActions.SET_LINKS(links));
117 export const openAddVirtualMachineLoginDialog = (vmUuid: string) =>
118 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
119 // Get login permissions of vm
120 const virtualMachines = await services.virtualMachineService.list();
121 dispatch(updateResources(virtualMachines.items));
122 const logins = await services.permissionService.list({
123 filters: new FilterBuilder()
124 .addIn('head_uuid', virtualMachines.items.map(item => item.uuid))
125 .addEqual('name', PermissionLevel.CAN_LOGIN)
128 dispatch(updateResources(logins.items));
130 dispatch(initialize(VIRTUAL_MACHINE_ADD_LOGIN_FORM, {
131 [VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD]: vmUuid,
132 [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: [],
134 dispatch(dialogActions.OPEN_DIALOG( {id: VIRTUAL_MACHINE_ADD_LOGIN_DIALOG, data: {excludedParticipants: logins.items.map(it => it.tailUuid)}} ));
137 export const openEditVirtualMachineLoginDialog = (permissionUuid: string) =>
138 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
139 const login = await services.permissionService.get(permissionUuid);
140 const user = await services.userService.get(login.tailUuid);
141 dispatch(initialize(VIRTUAL_MACHINE_ADD_LOGIN_FORM, {
142 [VIRTUAL_MACHINE_UPDATE_LOGIN_UUID_FIELD]: permissionUuid,
143 [VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD]: {name: getUserDisplayName(user, true, true), uuid: login.tailUuid},
144 [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: login.properties.groups,
146 dispatch(dialogActions.OPEN_DIALOG( {id: VIRTUAL_MACHINE_ADD_LOGIN_DIALOG, data: {updating: true}} ));
149 export interface AddLoginFormData {
150 [VIRTUAL_MACHINE_UPDATE_LOGIN_UUID_FIELD]: string;
151 [VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD]: string;
152 [VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD]: Participant;
153 [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: string[];
157 export const addUpdateVirtualMachineLogin = ({uuid, vmUuid, user, groups}: AddLoginFormData) =>
158 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
161 const userResource = await services.userService.get(user.uuid);
164 const permission = await services.permissionService.update(uuid, {
165 tailUuid: userResource.uuid,
166 name: PermissionLevel.CAN_LOGIN,
168 username: userResource.username,
172 dispatch(updateResources([permission]));
174 const permission = await services.permissionService.create({
176 tailUuid: userResource.uuid,
177 name: PermissionLevel.CAN_LOGIN,
179 username: userResource.username,
183 dispatch(updateResources([permission]));
186 dispatch(reset(VIRTUAL_MACHINE_ADD_LOGIN_FORM));
187 dispatch(dialogActions.CLOSE_DIALOG({ id: VIRTUAL_MACHINE_ADD_LOGIN_DIALOG }));
188 dispatch<any>(loadVirtualMachinesAdminData());
190 dispatch(snackbarActions.OPEN_SNACKBAR({
191 message: `Permission updated`,
192 kind: SnackbarKind.SUCCESS
195 dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
199 export const openRemoveVirtualMachineLoginDialog = (uuid: string) =>
200 (dispatch: Dispatch, getState: () => RootState) => {
201 dispatch(dialogActions.OPEN_DIALOG({
202 id: VIRTUAL_MACHINE_REMOVE_LOGIN_DIALOG,
204 title: 'Remove login permission',
205 text: 'Are you sure you want to remove this permission?',
206 confirmButtonLabel: 'Remove',
212 export const removeVirtualMachineLogin = (uuid: string) =>
213 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
215 await services.permissionService.delete(uuid);
216 dispatch<any>(deleteResources([uuid]));
218 dispatch<any>(loadVirtualMachinesAdminData());
220 dispatch(snackbarActions.OPEN_SNACKBAR({
221 message: `Login permission removed`,
222 kind: SnackbarKind.SUCCESS
225 dispatch(snackbarActions.OPEN_SNACKBAR({ message: e.message, hideDuration: 2000, kind: SnackbarKind.ERROR }));
229 export const saveRequestedDate = () =>
230 (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
231 const date = formatDate((new Date()).toISOString());
232 services.virtualMachineService.saveRequestedDate(date);
233 dispatch<any>(loadRequestedDate());
236 export const openRemoveVirtualMachineDialog = (uuid: string) =>
237 (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
238 dispatch(dialogActions.OPEN_DIALOG({
239 id: VIRTUAL_MACHINE_REMOVE_DIALOG,
241 title: 'Remove virtual machine',
242 text: 'Are you sure you want to remove this virtual machine?',
243 confirmButtonLabel: 'Remove',
249 export const removeVirtualMachine = (uuid: string) =>
250 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
251 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...', kind: SnackbarKind.INFO }));
252 await services.virtualMachineService.delete(uuid);
253 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
254 dispatch<any>(loadVirtualMachinesAdminData());
257 const virtualMachinesBindedActions = bindDataExplorerActions(VIRTUAL_MACHINES_PANEL);
259 export const loadVirtualMachinesPanel = () =>
260 (dispatch: Dispatch) => {
261 dispatch(virtualMachinesBindedActions.REQUEST_ITEMS());