diff --git a/src/components/create-vm-dialog/createVmDialog.jsx b/src/components/create-vm-dialog/createVmDialog.jsx
index d1e9dfed1..5f21e2a78 100644
--- a/src/components/create-vm-dialog/createVmDialog.jsx
+++ b/src/components/create-vm-dialog/createVmDialog.jsx
@@ -21,9 +21,11 @@ import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { debounce } from 'throttle-debounce';
import { Divider } from "@patternfly/react-core/dist/esm/components/Divider";
+import { FileUpload } from '@patternfly/react-core/dist/esm/components/FileUpload';
import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex";
import { Form, FormGroup } from "@patternfly/react-core/dist/esm/components/Form";
import { FormSelect, FormSelectOption } from "@patternfly/react-core/dist/esm/components/FormSelect";
+import { Grid, GridItem } from "@patternfly/react-core/dist/esm/layouts/Grid";
import { InputGroup } from "@patternfly/react-core/dist/esm/components/InputGroup";
import { Modal } from "@patternfly/react-core/dist/esm/components/Modal";
import { Select as PFSelect, SelectGroup, SelectOption } from "@patternfly/react-core/dist/esm/deprecated/components/Select";
@@ -33,7 +35,7 @@ import { Button } from "@patternfly/react-core/dist/esm/components/Button";
import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip";
import { TextArea } from "@patternfly/react-core/dist/esm/components/TextArea";
import { Spinner } from "@patternfly/react-core/dist/esm/components/Spinner";
-import { ExternalLinkAltIcon } from '@patternfly/react-icons';
+import { ExternalLinkAltIcon, MinusIcon } from '@patternfly/react-icons';
import { DialogsContext } from 'dialogs.jsx';
import cockpit from 'cockpit';
@@ -84,6 +86,7 @@ import { domainCreate } from '../../libvirtApi/domain.js';
import { storagePoolRefresh } from '../../libvirtApi/storagePool.js';
import { getAccessToken } from '../../libvirtApi/rhel-images.js';
import { PasswordFormFields, password_quality } from 'cockpit-components-password.jsx';
+import { DynamicListForm } from '../common/DynamicListForm';
import './createVmDialog.scss';
@@ -248,6 +251,8 @@ function validateParams(vmParams) {
}
if (vmParams.userPassword && !vmParams.userLogin) {
validationFailed.userLogin = _("User login must not be empty when user password is set");
+ } else if (vmParams.sshKeys.length > 0 && !vmParams.userLogin) {
+ validationFailed.userLogin = _("User login must not be empty when SSH keys are set");
}
return validationFailed;
@@ -748,6 +753,47 @@ const UsersConfigurationRow = ({
);
};
+const SshKeysRow = ({
+ id, item, onChange, idx, removeitem,
+}) => {
+ const [isLoading, setIsLoading] = useState(false);
+
+ const handleClear = (_event) => {
+ onChange(idx, "file", "");
+ onChange(idx, "value", "");
+ };
+
+ return (
+
+
+ onChange(idx, "file", file)}
+ onDataChange={(_event, value) => onChange(idx, "value", value)}
+ onTextChange={(_event, value) => onChange(idx, "value", value)}
+ onReadStarted={() => setIsLoading(true)}
+ onReadFinished={() => setIsLoading(false)}
+ onClearClick={_event => handleClear(_event)}
+ isLoading={isLoading}
+ allowEditingUploadedText
+ browseButtonText="Upload" />
+
+
+ }
+ onClick={() => removeitem(idx)} />
+
+
+ );
+};
+
const CloudInitOptionsRow = ({
onValueChanged,
rootPassword,
@@ -755,13 +801,22 @@ const CloudInitOptionsRow = ({
validationFailed,
}) => {
return (
-
+ <>
+
+ onValueChanged('sshKeys', value)}
+ default={{ value: null, file: null }}
+ itemcomponent={ } />
+ >
);
};
@@ -979,6 +1034,7 @@ class CreateVmModal extends React.Component {
userLogin: '',
accessToken: '',
offlineToken: '',
+ sshKeys: [],
};
this.onCreateClicked = this.onCreateClicked.bind(this);
this.onValueChanged = this.onValueChanged.bind(this);
@@ -1172,6 +1228,7 @@ class CreateVmModal extends React.Component {
userPassword: this.state.userPassword,
rootPassword: this.state.rootPassword,
userLogin: this.state.userLogin,
+ sshKeys: this.state.sshKeys.map(key => key.value),
startVm,
accessToken: this.state.accessToken,
loggedUser
diff --git a/src/scripts/install_machine.py b/src/scripts/install_machine.py
index f5626cd26..21cbb6ea6 100755
--- a/src/scripts/install_machine.py
+++ b/src/scripts/install_machine.py
@@ -133,6 +133,10 @@ def prepare_cloud_init(args):
if args['userLogin']:
user_data_file.write("users:\n")
user_data_file.write(f" - name: {args['userLogin']}\n")
+ if args['sshKeys'] and len(args['sshKeys']) > 9:
+ user_data_file.write(" ssh_authorized_keys:\n")
+ for key in args['sshKeys']:
+ user_data_file.write(f" - {key}\n")
if args['rootPassword'] or args['userPassword']:
# enable SSH password login if any password is set