Merge "feat: Adding log feature to Nova instance detail page."

This commit is contained in:
Zuul 2024-03-26 11:39:07 +00:00 committed by Gerrit Code Review
commit e65405bd8a
4 changed files with 123 additions and 1 deletions

View File

@ -0,0 +1,5 @@
---
features:
- |
feat: Adding log feature to Nova instance detail page.
Log tab has been added to the Nova instance detail interface.

View File

@ -0,0 +1,102 @@
import React, { useEffect, useState } from 'react';
import globalInstanceLogStore from 'src/stores/nova/instance';
import { Button, Col, Form, InputNumber, Row, Skeleton } from 'antd';
import { SearchOutlined, SettingOutlined } from '@ant-design/icons';
import classnames from 'classnames';
import styles from 'src/components/Tables/Base/index.less';
export default function Log(props) {
const [logs, setLogs] = useState('');
const [loading, setLoading] = useState(true);
useEffect(() => {
getLogs(35);
}, []);
const getLogs = async (tailSize) => {
setLoading(true);
const data = await globalInstanceLogStore.fetchLogs(props.detail.id, tailSize);
setLogs(data.output);
setLoading(false);
};
function onFinish(value) {
getLogs(value.number);
}
async function viewFullLog() {
setLoading(true);
const data = await globalInstanceLogStore.fetchLogs(props.detail.id, null);
const newWindow = window.open('console', '_blank');
const title = t('Console Log');
const htmlContent = `
<html>
<head>
<title>${title}</title>
</head>
<body>
<pre>${data.output}</pre>
</body>
</html>`;
newWindow.document.write(htmlContent);
newWindow.document.close();
setLoading(false);
}
return (
<div>
<Form
initialValues={{ number: 35 }}
onFinish={onFinish}>
<Row gutter={16}>
<Col className="gutter-row" span={16}>
<h2 style={{ paddingLeft: 16 }}>{t('Instance Console Log')}</h2>
</Col>
<Col className="gutter-row" span={4}>
<Form.Item name="number" label={t('Log Length')}>
<InputNumber
min={1}
max={100000}
placeholder={t("Log Length")}
style={{ width: "100%" }}
addonafter={<SettingOutlined />}
/>
</Form.Item>
</Col>
<Col className="gutter-row" span={4}>
<div
className={classnames(
styles['table-header-btns'])}
>
<Button
type="primary"
htmlType="submit">
<SearchOutlined />
</Button>
<Button
type="primary"
onClick={() => viewFullLog()}>
{t("View Full Log")}
</Button>
</div>
</Col>
</Row>
</Form>
<div
style={{
margin: 'auto 16px 16px 16px',
padding: 16,
backgroundColor: '#90a4ae',
borderRadius: 4,
color: '#fff',
fontSize: 12,
}}
>
{loading ? <Skeleton loading={loading} active /> : logs ? <pre>{logs}</pre> : t('No Logs...')}
</div>
</div>
);
}

View File

@ -30,6 +30,7 @@ import SecurityGroup from './SecurityGroup';
import ActionLog from './ActionLog';
import Snapshots from '../../InstanceSnapshot';
import actionConfigs from '../actions';
import Log from './Log';
export class InstanceDetail extends Base {
get name() {
@ -148,6 +149,11 @@ export class InstanceDetail extends Base {
key: 'action',
component: ActionLog,
},
{
title: t('Logs'),
key: 'logs',
component: Log,
},
];
if (this.enableCinder) {
tabs.splice(1, 0, {

View File

@ -179,6 +179,15 @@ export class ServerStore extends Base {
}));
}
async fetchLogs(id, tailSize) {
const logs = await this.client.action(id, {
"os-getConsoleOutput": {
"length": tailSize
}
});
return logs;
}
@action
async fetchInterface({ id }) {
this.interface.isLoading = true;
@ -226,7 +235,7 @@ export class ServerStore extends Base {
sgItems = result.map((it) =>
this.mapperSecurityGroupRule(it.security_group)
);
} catch (e) {}
} catch (e) { }
this.securityGroups = {
data: sgItems || [],
interfaces: ports,