Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kubernetes: virtual machines - add Vm Detail page #9466

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion pkg/kubernetes/scripts/virtual-machines.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
require('angular-dialog.js');
require('./kube-client');
require('./listing');
var vmsReact = require('./virtual-machines/index.jsx');
var vmsReact = require('./virtual-machines/entry-points/virtual-machines.jsx');
var vmReact = require('./virtual-machines/entry-points/virtual-machine.jsx');

require('../views/virtual-machines-page.html');
require('../views/virtual-machine-page.html');

angular.module('kubernetes.virtualMachines', [
'ngRoute',
Expand All @@ -45,6 +47,13 @@
.when('/vms', {
templateUrl: 'views/virtual-machines-page.html',
controller: 'VirtualMachinesCtrl'
})
.when('/vms/:namespace/:name', {
templateUrl: 'views/virtual-machine-page.html',
controller: 'VirtualMachineCtrl'
})
.when('/vms/:namespace', {
redirectTo: '/vms'
});
/*
Links rewriting is enabled by default. It does two things:
Expand Down Expand Up @@ -76,6 +85,18 @@
function($scope, loader, select, methods, request) {
vmsReact.init($scope, loader, select, methods, request);
}]
)

.controller('VirtualMachineCtrl', [
'$scope',
'$routeParams',
'kubeLoader',
'kubeSelect',
'kubeMethods',
'KubeRequest',
function($scope, $routeParams, loader, select, methods, request) {
vmReact.init($scope, $routeParams, loader, select, methods, request);
}]
);

}());
22 changes: 12 additions & 10 deletions pkg/kubernetes/scripts/virtual-machines/components/VmActions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@ import { vmActionFailed } from '../action-creators.jsx';

// import DropdownButtons from '../../../../machines/components/dropdownButtons.jsx';

const VmActions = ({ vm, onFailure }: { vm: Vm, onFailure: Function }) => {
const VmActions = ({ vm, onDeleteSuccess, onDeleteFailure }: { vm: Vm, onDeleteSuccess: Function, onDeleteFailure: Function }) => {
const id = vmIdPrefx(vm);

const onDelete = () => {
vmDelete({ vm }).catch((error) => {
console.info('VmActions: delete failed: ', error);
onFailure({
message: _("VM DELETE failed."),
detail: error,
});
});
vmDelete({ vm }).then(onDeleteSuccess)
.catch((error) => {
console.info('VmActions: delete failed: ', error);
onDeleteFailure({
message: _("VM DELETE failed."),
detail: error,
});
});
};

const buttonDelete = (
Expand All @@ -56,12 +57,13 @@ const VmActions = ({ vm, onFailure }: { vm: Vm, onFailure: Function }) => {

VmActions.propTypes = {
vm: PropTypes.object.isRequired,
onFailure: PropTypes.func.isRequired,
onDeleteSuccess: PropTypes.func.isRequired,
onDeleteFailure: PropTypes.func.isRequired,
};

export default connect(
() => ({}),
(dispatch, { vm }) => ({
onFailure: ({ message, detail }) => dispatch(vmActionFailed({ vm, message, detail })),
onDeleteFailure: ({ message, detail }) => dispatch(vmActionFailed({ vm, message, detail })),
}),
)(VmActions);
104 changes: 104 additions & 0 deletions pkg/kubernetes/scripts/virtual-machines/components/VmDetail.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2018 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Cockpit is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/

// @flow

import React, { PropTypes } from 'react';
import cockpit, { gettext as _ } from 'cockpit';
import { connect } from "react-redux";

import { DetailPage, DetailPageRow, DetailPageHeader } from 'cockpit-components-detail-page.jsx';
import { getPod, getPodMetrics } from '../selectors.jsx';
import VmOverviewTab from './VmOverviewTabKubevirt.jsx';
import VmActions from './VmActions.jsx';
import VmMetricsTab from './VmMetricsTab.jsx';
import VmDisksTab from './VmDisksTabKubevirt.jsx';

import type { Vm, VmMessages, PersistenVolumes, Pod } from '../types.jsx';
import { vmIdPrefx, prefixedId } from '../utils.jsx';

const navigateToVms = () => {
cockpit.location.go([ 'vms' ]);
};

const VmDetail = ({ vm, pageParams, vmMessages, pvs, pod, podMetrics }:
{ vm: Vm, vmMessages: VmMessages, pageParams: Object, pvs: PersistenVolumes, pod: Pod}) => {
const mainTitle = vm ? `${vm.metadata.namespace}:${vm.metadata.name}` : null;
const actions = vm ? <VmActions vm={vm} onDeleteSuccess={navigateToVms} /> : null;
const header = (<DetailPageHeader title={mainTitle}
navigateUpTitle={_("Show all VMs")}
onNavigateUp={navigateToVms}
actions={actions}
idPrefix={'vm-header'}
iconClass='fa pficon-virtual-machine fa-fw' />);

if (!vm) {
return (
<div>
<DetailPage>
{header}
<DetailPageRow title={cockpit.format(_("VM $0:$1 does not exist."), pageParams.namespace, pageParams.name)}
idPrefix={'vm-not-found'} />
</DetailPage>
</div>
);
}
const idPrefix = vmIdPrefx(vm);

return (
<div>
<DetailPage>
{header}
<DetailPageRow title={_("VM")} idPrefix={prefixedId(idPrefix, 'vm')} >
<VmOverviewTab vm={vm} vmMessages={vmMessages} pod={pod} showState />
</DetailPageRow>
<DetailPageRow title={_("Usage")} idPrefix={prefixedId(idPrefix, 'usage')} >
<VmMetricsTab idPrefix={idPrefix} podMetrics={podMetrics} />
</DetailPageRow>
<DetailPageRow title={_("Disks")} idPrefix={prefixedId(idPrefix, 'disks')} >
<VmDisksTab vm={vm} pvs={pvs} />
</DetailPageRow>
</DetailPage>
</div>
);
};

VmDetail.propTypes = {
vm: PropTypes.object.isRequired,
pageParams: PropTypes.object,
vmMessages: PropTypes.object,
pvs: PropTypes.array.isRequired,
pod: PropTypes.object.isRequired,
podMetrics: PropTypes.object,
};

export default connect(
({vms, pvs, pods, vmsMessages, nodeMetrics}) => {
const vm = vms.length > 0 ? vms[0] : null;
const pod = getPod(vm, pods);
const podMetrics = getPodMetrics(pod, nodeMetrics);
return {
vm,
vmMessages: vm ? vmsMessages[vm.metadata.uid] : null,
pvs,
pod,
podMetrics,
};
},
)(VmDetail);
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const prepareDiskData = (disk, vm, pvs, idPrefix) => {
if (pv) {
bus = _("iSCSI");
onNavigate = () => cockpit.jump(`/kubernetes#/volumes/${pv.metadata.name}`);
} else if (disk.disk.bus) {
} else if (disk.disk && disk.disk.bus) {
bus = disk.disk.bus;
}

Expand Down
35 changes: 18 additions & 17 deletions pkg/kubernetes/scripts/virtual-machines/components/VmMetricsTab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import React, { PropTypes } from 'react';
import { DonutChart } from 'patternfly-react';
import cockpit, { gettext as _ } from 'cockpit';
import { prefixedId } from '../utils.jsx';

import './VmMetricsTab.less';

Expand All @@ -38,11 +39,11 @@ const MetricColumn = ({ type, children, className }) => {
const MetricColumnContent = ({ id, title, smallTitle }) => {
return (
<div id={id} className="centered">
<span id={`${id}-title`} className="block donut-title-big-pf">
<span id={prefixedId(id, 'title')} className="block donut-title-big-pf">
{title}
</span>
<div className="container small-text-layout">
<span id={`${id}-small-title`} className="donut-title-small-pf small-text">
<span id={prefixedId(id, 'small-title')} className="donut-title-small-pf small-text">
{smallTitle}
</span>
</div>
Expand Down Expand Up @@ -79,61 +80,61 @@ const CPUChart = ({ id, podMetrics }) => {
);
};

const CpuColumn = ({ podMetrics }) => {
const CpuColumn = ({ idPrefix, podMetrics }) => {
return (
<MetricColumn type={_("CPU")}>
<CPUChart id="cpu-metric" podMetrics={podMetrics} />
<CPUChart id={prefixedId(idPrefix, 'cpu-metric')} podMetrics={podMetrics} />
</MetricColumn>
);
};

const MemoryColumn = ({ podMetrics }) => {
const MemoryColumn = ({ idPrefix, podMetrics }) => {
const usage = cockpit.format_bytes(podMetrics.memory.usageBytes);
return (
<MetricColumn type={_("Memory")}>
<MetricColumnContent id="memory-metric" title={usage} />
<MetricColumnContent id={prefixedId(idPrefix, 'memory-metric')} title={usage} />
</MetricColumn>
);
};

const NetworkColumn = ({ podMetrics }) => {
const NetworkColumn = ({ idPrefix, podMetrics }) => {
const received = cockpit.format_bytes_per_sec(podMetrics.network.rxBytes);
const transmitted = cockpit.format_bytes_per_sec(podMetrics.network.txBytes);
const title = (
<div>
<div className="inline-block" id="download-metric">
<div className="inline-block" id={prefixedId(idPrefix, 'download-metric')}>
<div className="next-to-left fa fa-arrow-down" />
{received}
</div>
<div className="inline-block" id="upload-metric">
<div className="inline-block" id={prefixedId(idPrefix, 'upload-metric')}>
<div className="next-clear fa fa-arrow-up" />
{transmitted}
</div>
</div>
);
return (
<MetricColumn type={_("Network")} className="no-padding-left">
<MetricColumnContent id="network-metric" title={title} />
<MetricColumnContent id={prefixedId(idPrefix, 'network-metric')} title={title} />
</MetricColumn>
);
};

const PodMetrics = ({ podMetrics }) => {
const PodMetrics = ({ idPrefix, podMetrics }) => {
return (
<div className="row">
<CpuColumn podMetrics={podMetrics} />
<MemoryColumn podMetrics={podMetrics} />
<NetworkColumn podMetrics={podMetrics} />
<CpuColumn idPrefix={idPrefix} podMetrics={podMetrics} />
<MemoryColumn idPrefix={idPrefix} podMetrics={podMetrics} />
<NetworkColumn idPrefix={idPrefix} podMetrics={podMetrics} />
</div>
);
};

const VmMetricsTab = ({podMetrics}) => {
const content = podMetrics ? (<PodMetrics podMetrics={podMetrics} />)
const VmMetricsTab = ({idPrefix, podMetrics}) => {
const content = podMetrics ? (<PodMetrics idPrefix={idPrefix} podMetrics={podMetrics} />)
: _("Usage metrics are available after the pod starts");

return (
<div id="usage-metrics">
<div id={prefixedId(idPrefix, 'usage-metrics')}>
{content}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const PodLink = ({ pod }) => {
return (<a href={`#/l/pods/${pod.metadata.namespace}/${pod.metadata.name}`}>{pod.metadata.name}</a>);
};

const VmOverviewTabKubevirt = ({ vm, vmMessages, pod }: { vm: Vm, vmMessages: VmMessages, pod: Pod }) => {
const VmOverviewTabKubevirt = ({ vm, vmMessages, pod, showState }: { vm: Vm, vmMessages: VmMessages, pod: Pod, showState: boolean }) => {
const idPrefix = vmIdPrefx(vm);

const message = (<VmMessage vmMessages={vmMessages} vm={vm} />);
Expand All @@ -73,12 +73,25 @@ const VmOverviewTabKubevirt = ({ vm, vmMessages, pod }: { vm: Vm, vmMessages: Vm
const nodeLink = nodeName ? (<a href={`#/nodes/${nodeName}`}>{nodeName}</a>) : '-';
const podLink = (<PodLink pod={pod} />);

const items = [
{title: commonTitles.MEMORY, value: getMemory(vm), idPostfix: 'memory'},
{title: _("Node:"), value: nodeLink, idPostfix: 'node'},
{title: commonTitles.CPUS, value: _(getValueOrDefault(() => vm.spec.domain.cpu.cores, 1)), idPostfix: 'vcpus'},
{title: _("Labels:"), value: getLabels(vm), idPostfix: 'labels'},
{title: _("Pod:"), value: podLink, idPostfix: 'pod'},
const memoryItem = {title: commonTitles.MEMORY, value: getMemory(vm), idPostfix: 'memory'};
const vCpusItem = {title: commonTitles.CPUS, value: _(getValueOrDefault(() => vm.spec.domain.cpu.cores, 1)), idPostfix: 'vcpus'};
const podItem = {title: _("Pod:"), value: podLink, idPostfix: 'pod'};
const nodeItem = {title: _("Node:"), value: nodeLink, idPostfix: 'node'};
const labelsItem = {title: _("Labels:"), value: getLabels(vm), idPostfix: 'labels'};

const items = showState ? [
memoryItem,
{title: _("State"), value: getValueOrDefault(() => vm.status.phase, _("n/a")), idPostfix: 'state'},
vCpusItem,
nodeItem,
podItem,
labelsItem,
] : [
memoryItem,
nodeItem,
vCpusItem,
labelsItem,
podItem,
];

return (<VmOverviewTab message={message}
Expand Down
Loading