Add Masakari UI to Skyline-Console
Change-Id: I74913bbf1c48823ba4a9b68539a2c26582e62939
This commit is contained in:
parent
a95c588a5d
commit
0bc19114c4
@ -36,6 +36,7 @@ export const endpointVersionMap = {
|
||||
zun: 'v1',
|
||||
magnum: 'v1',
|
||||
designate: 'v2',
|
||||
masakari: 'v1',
|
||||
};
|
||||
|
||||
export const endpointsDefault = {
|
||||
@ -77,6 +78,7 @@ export const barbicanBase = () => getOpenstackEndpoint('barbican');
|
||||
export const zunBase = () => getOpenstackEndpoint('zun');
|
||||
export const magnumBase = () => getOpenstackEndpoint('magnum');
|
||||
export const designateBase = () => getOpenstackEndpoint('designate');
|
||||
export const masakariBase = () => getOpenstackEndpoint('masakari');
|
||||
|
||||
export const ironicOriginEndpoint = () => getOriginEndpoint('ironic');
|
||||
export const vpnEndpoint = () => getOriginEndpoint('neutron_vpn');
|
||||
|
@ -28,6 +28,7 @@ import manila from './manila';
|
||||
import barbican from './barbican';
|
||||
import zun from './zun';
|
||||
import magnum from './magnum';
|
||||
import masakari from './masakari';
|
||||
|
||||
const client = {
|
||||
skyline,
|
||||
@ -46,6 +47,7 @@ const client = {
|
||||
barbican,
|
||||
zun,
|
||||
magnum,
|
||||
masakari,
|
||||
};
|
||||
|
||||
window.client = client;
|
||||
|
45
src/client/masakari/index.js
Normal file
45
src/client/masakari/index.js
Normal file
@ -0,0 +1,45 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import Base from '../client/base';
|
||||
import { masakariBase } from '../client/constants';
|
||||
|
||||
export class MasakariClient extends Base {
|
||||
get baseUrl() {
|
||||
return masakariBase();
|
||||
}
|
||||
|
||||
get resources() {
|
||||
return [
|
||||
{
|
||||
name: 'segments',
|
||||
key: 'segments',
|
||||
responseKey: 'segment',
|
||||
subResources: [
|
||||
{
|
||||
key: 'hosts',
|
||||
responseKey: 'host',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'notifications',
|
||||
key: 'notifications',
|
||||
responseKey: 'notification',
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const masakariClient = new MasakariClient();
|
||||
export default masakariClient;
|
||||
|
@ -51,13 +51,16 @@ const Share = lazy(() =>
|
||||
import(/* webpackChunkName: "share" */ 'pages/share/App')
|
||||
);
|
||||
const ContainerInfra = lazy(() =>
|
||||
import(/* webpackChunkName: "container-infra" */ 'pages/container-infra/App')
|
||||
import(/* webpackChunkName: "container-infra" */ 'pages/container-infra/App')
|
||||
);
|
||||
const ContainerService = lazy(() =>
|
||||
import(/* webpackChunkName: "Container" */ 'pages/container-service/App')
|
||||
import(/* webpackChunkName: "Container" */ 'pages/container-service/App')
|
||||
);
|
||||
const E404 = lazy(() =>
|
||||
import(/* webpackChunkName: "E404" */ 'pages/base/containers/404')
|
||||
import(/* webpackChunkName: "E404" */ 'pages/base/containers/404')
|
||||
);
|
||||
const InstanceHA = lazy(() =>
|
||||
import(/* webpackChunkName: "Inctance-HA" */ 'pages/ha/App')
|
||||
);
|
||||
const PATH = '/';
|
||||
|
||||
@ -116,6 +119,10 @@ export default [
|
||||
path: `/container-service`,
|
||||
component: ContainerService,
|
||||
},
|
||||
{
|
||||
path: `/ha`,
|
||||
component: InstanceHA,
|
||||
},
|
||||
{ path: '*', component: E404 },
|
||||
],
|
||||
},
|
||||
|
19
src/pages/ha/App.jsx
Normal file
19
src/pages/ha/App.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import renderRoutes from 'utils/RouterConfig';
|
||||
|
||||
import routes from './routes';
|
||||
|
||||
const App = (props) => renderRoutes(routes, props);
|
||||
|
||||
export default App;
|
64
src/pages/ha/containers/Hosts/Detail/BaseDetail.jsx
Normal file
64
src/pages/ha/containers/Hosts/Detail/BaseDetail.jsx
Normal file
@ -0,0 +1,64 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import Base from 'containers/BaseDetail';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export class BaseDetail extends Base {
|
||||
get leftCards() {
|
||||
const cards = [this.baseInfoCard];
|
||||
return cards;
|
||||
}
|
||||
|
||||
get baseInfoCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('UUID'),
|
||||
dataIndex: 'uuid',
|
||||
},
|
||||
{
|
||||
label: t('Failover Segment'),
|
||||
dataIndex: 'failover_segment_id',
|
||||
render: (value, row) => {
|
||||
return <Link to={this.getRoutePath('masakariSegmentDetail', { id: row.failover_segment_id })}>{row.failover_segment.name}</Link>
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('Reserved'),
|
||||
dataIndex: 'reserved',
|
||||
valueRender: 'yesNo'
|
||||
},
|
||||
{
|
||||
label: t('On Maintenance'),
|
||||
dataIndex: 'on_maintenance',
|
||||
valueRender: 'yesNo'
|
||||
},
|
||||
{
|
||||
label: t('Type'),
|
||||
dataIndex: 'type',
|
||||
},
|
||||
{
|
||||
label: t('Control Attribute'),
|
||||
dataIndex: 'control_attributes',
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
title: t('Host Detail'),
|
||||
options,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(BaseDetail));
|
73
src/pages/ha/containers/Hosts/Detail/index.jsx
Normal file
73
src/pages/ha/containers/Hosts/Detail/index.jsx
Normal file
@ -0,0 +1,73 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { parse } from 'qs';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import Base from 'containers/TabDetail';
|
||||
import globalHostStore from 'src/stores/masakari/hosts';
|
||||
import BaseDetail from './BaseDetail';
|
||||
import actionConfigs from '../actions';
|
||||
|
||||
export class HostsDetail extends Base {
|
||||
init() {
|
||||
this.store = globalHostStore;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('Host Detail');
|
||||
}
|
||||
|
||||
get listUrl() {
|
||||
return this.getRoutePath('masakariHosts');
|
||||
}
|
||||
|
||||
get policy() {
|
||||
return 'capsule:get_one_all_projects';
|
||||
}
|
||||
|
||||
get actionConfigs() {
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
get titleValue() {
|
||||
return parse(this.routing.location.search.slice(1)).uuid;
|
||||
}
|
||||
|
||||
get detailInfos() {
|
||||
return [
|
||||
{
|
||||
title: t('Name'),
|
||||
dataIndex: 'name',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
updateFetchParams = (params) => {
|
||||
const hostId = parse(this.routing.location.search.slice(1));
|
||||
return {
|
||||
id: params.id,
|
||||
uuid: hostId.uuid
|
||||
};
|
||||
};
|
||||
|
||||
get tabs() {
|
||||
return [
|
||||
{
|
||||
title: t('Detail'),
|
||||
key: 'general_info',
|
||||
component: BaseDetail,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(HostsDetail));
|
53
src/pages/ha/containers/Hosts/actions/Delete.jsx
Normal file
53
src/pages/ha/containers/Hosts/actions/Delete.jsx
Normal file
@ -0,0 +1,53 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { ConfirmAction } from 'containers/Action';
|
||||
import globalHostStore from 'src/stores/masakari/hosts';
|
||||
|
||||
export default class Delete extends ConfirmAction {
|
||||
get id() {
|
||||
return 'delete';
|
||||
}
|
||||
|
||||
get title() {
|
||||
return t('Delete');
|
||||
}
|
||||
|
||||
get actionName() {
|
||||
return t('delete host');
|
||||
}
|
||||
|
||||
get isDanger() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get isAsyncAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
policy = 'instance:delete';
|
||||
|
||||
allowedCheckFunction = () => true;
|
||||
|
||||
confirmContext = (data) => {
|
||||
const name = this.getName(data);
|
||||
return t('Are you sure to {action} (Host: {name})?', {
|
||||
action: this.actionNameDisplay || this.title,
|
||||
name,
|
||||
});
|
||||
};
|
||||
|
||||
onSubmit = (item) => {
|
||||
const { uuid, failover_segment_id } = item || this.item;
|
||||
return globalHostStore.delete({ segment_id: failover_segment_id, host_id: uuid });
|
||||
};
|
||||
}
|
83
src/pages/ha/containers/Hosts/actions/Update.jsx
Normal file
83
src/pages/ha/containers/Hosts/actions/Update.jsx
Normal file
@ -0,0 +1,83 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { ModalAction } from "src/containers/Action";
|
||||
import globalHostStore from 'src/stores/masakari/hosts';
|
||||
|
||||
export class Update extends ModalAction {
|
||||
init() {
|
||||
this.store = globalHostStore;
|
||||
}
|
||||
|
||||
static id = 'UpdateHost';
|
||||
|
||||
static title = t('Update');
|
||||
|
||||
get name() {
|
||||
return t('Update');
|
||||
}
|
||||
|
||||
static policy = 'baremetal:port:Update';
|
||||
|
||||
static allowed = () => Promise.resolve(true);
|
||||
|
||||
get defaultValue() {
|
||||
return {
|
||||
...this.item
|
||||
};
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
return [
|
||||
{
|
||||
name: 'name',
|
||||
label: t('Host Name'),
|
||||
type: 'input',
|
||||
disabled: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'reserved',
|
||||
label: t('Reserved'),
|
||||
type: 'switch',
|
||||
checkedText: '',
|
||||
uncheckedText: ''
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
label: t('Type'),
|
||||
type: 'input',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'control_attributes',
|
||||
label: t('Control Attribute'),
|
||||
type: 'input',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'on_maintenance',
|
||||
label: t('On Maintenance'),
|
||||
type: 'switch',
|
||||
checkedText: '',
|
||||
uncheckedText: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
onSubmit = (values) => {
|
||||
return this.store.update(this.item.failover_segment_id, this.item.uuid, { 'host': values });
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(Update));
|
28
src/pages/ha/containers/Hosts/actions/index.jsx
Normal file
28
src/pages/ha/containers/Hosts/actions/index.jsx
Normal file
@ -0,0 +1,28 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import Update from './Update';
|
||||
import Delete from './Delete';
|
||||
|
||||
const actionConfigs = {
|
||||
rowActions: {
|
||||
firstAction: Update,
|
||||
moreActions: [
|
||||
{
|
||||
action: Delete,
|
||||
},
|
||||
],
|
||||
},
|
||||
batchActions: [Delete]
|
||||
};
|
||||
|
||||
export default actionConfigs;
|
126
src/pages/ha/containers/Hosts/index.jsx
Normal file
126
src/pages/ha/containers/Hosts/index.jsx
Normal file
@ -0,0 +1,126 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import Base from 'containers/List';
|
||||
import actionConfigs from './actions';
|
||||
import globalHostStore, { HostStore } from 'src/stores/masakari/hosts';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export class Hosts extends Base {
|
||||
init() {
|
||||
this.store = globalHostStore;
|
||||
this.downloadStore = new HostStore();
|
||||
}
|
||||
|
||||
get policy() {
|
||||
if (this.isAdminPage) {
|
||||
return 'os_compute_api:servers:index:get_all_tenants';
|
||||
}
|
||||
return 'os_compute_api:servers:index';
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('hosts');
|
||||
}
|
||||
|
||||
get defaultSortKey() {
|
||||
return 'updated_at';
|
||||
}
|
||||
|
||||
get actionConfigs() {
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
get rowKey() {
|
||||
return 'uuid';
|
||||
}
|
||||
|
||||
get searchFilters() {
|
||||
return [
|
||||
{
|
||||
label: t('Segment ID'),
|
||||
name: 'id',
|
||||
},
|
||||
{
|
||||
label: t('Type'),
|
||||
name: 'type',
|
||||
},
|
||||
{
|
||||
label: t('On Maintenance'),
|
||||
name: 'on_maintenance',
|
||||
},
|
||||
{
|
||||
label: t('Reserved'),
|
||||
name: 'reserved',
|
||||
},
|
||||
...(this.isAdminPage
|
||||
? [
|
||||
{
|
||||
label: t('Project Name'),
|
||||
name: 'project_name',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
getColumns = () => [
|
||||
{
|
||||
title: t('Name'),
|
||||
dataIndex: 'name',
|
||||
render: (value, row) => {
|
||||
const path = this.getRoutePath('masakariHostDetail', { id: row.failover_segment_id }, { uuid: row.uuid });
|
||||
return <Link to={path}>{value}</Link>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t('UUID'),
|
||||
dataIndex: 'uuid',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Reserved'),
|
||||
dataIndex: 'reserved',
|
||||
isHideable: true,
|
||||
valueRender: 'yesNo'
|
||||
},
|
||||
{
|
||||
title: t('Type'),
|
||||
dataIndex: 'type',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Control Attribute'),
|
||||
dataIndex: 'control_attributes',
|
||||
isHideable: true
|
||||
},
|
||||
{
|
||||
title: t('On Maintenance'),
|
||||
dataIndex: 'on_maintenance',
|
||||
isHideable: true,
|
||||
valueRender: 'yesNo'
|
||||
},
|
||||
{
|
||||
title: t('Failover Segment'),
|
||||
dataIndex: 'failover_segment',
|
||||
isHideable: true,
|
||||
render: (value, row) => {
|
||||
return <Link to={this.getRoutePath('masakariSegmentDetail', { id: row.failover_segment_id })}>{row.failover_segment.name}</Link>
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(Hosts));
|
80
src/pages/ha/containers/Notifications/Detail/BaseDetail.jsx
Normal file
80
src/pages/ha/containers/Notifications/Detail/BaseDetail.jsx
Normal file
@ -0,0 +1,80 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import Base from 'containers/BaseDetail';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
|
||||
export class BaseDetail extends Base {
|
||||
get leftCards() {
|
||||
const cards = [this.baseInfoCard, this.payloadCard];
|
||||
return cards;
|
||||
}
|
||||
|
||||
get baseInfoCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('ID'),
|
||||
dataIndex: 'id',
|
||||
},
|
||||
{
|
||||
label: t('Host'),
|
||||
dataIndex: 'source_host_uuid',
|
||||
copyable: true
|
||||
},
|
||||
{
|
||||
label: t('Generated Time'),
|
||||
dataIndex: 'generated_time',
|
||||
valueRender: 'toLocalTime'
|
||||
},
|
||||
{
|
||||
label: t('Created At'),
|
||||
dataIndex: 'created_at',
|
||||
valueRender: 'toLocalTime'
|
||||
},
|
||||
{
|
||||
label: t('Updated At'),
|
||||
dataIndex: 'updated_at',
|
||||
valueRender: 'toLocalTime'
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
title: t('Notification Detail'),
|
||||
options,
|
||||
};
|
||||
}
|
||||
|
||||
get payloadCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('Event'),
|
||||
dataIndex: 'event'
|
||||
},
|
||||
{
|
||||
label: t('Instance UUID'),
|
||||
dataIndex: 'instance_uuid'
|
||||
},
|
||||
{
|
||||
label: t('VIR Domain Event'),
|
||||
dataIndex: 'vir_domain_event'
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
title: t('Payload'),
|
||||
sourceData: this.detailData.payload,
|
||||
options
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(BaseDetail));
|
59
src/pages/ha/containers/Notifications/Detail/index.jsx
Normal file
59
src/pages/ha/containers/Notifications/Detail/index.jsx
Normal file
@ -0,0 +1,59 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import Base from 'containers/TabDetail';
|
||||
import BaseDetail from './BaseDetail';
|
||||
import globalNotificationStore from 'stores/masakari/notifications';
|
||||
|
||||
export class NotificationsDetail extends Base {
|
||||
init() {
|
||||
this.store = globalNotificationStore;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('Host Detail');
|
||||
}
|
||||
|
||||
get listUrl() {
|
||||
return this.getRoutePath('masakariNotifications');
|
||||
}
|
||||
|
||||
get policy() {
|
||||
return 'capsule:get_one_all_projects';
|
||||
}
|
||||
|
||||
get detailInfos() {
|
||||
return [
|
||||
{
|
||||
title: t('Type'),
|
||||
dataIndex: 'type',
|
||||
},
|
||||
{
|
||||
title: t('Status'),
|
||||
dataIndex: 'status',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
get tabs() {
|
||||
return [
|
||||
{
|
||||
title: t('Detail'),
|
||||
key: 'baseDetail',
|
||||
component: BaseDetail,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(NotificationsDetail));
|
87
src/pages/ha/containers/Notifications/index.jsx
Normal file
87
src/pages/ha/containers/Notifications/index.jsx
Normal file
@ -0,0 +1,87 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import Base from 'containers/List';
|
||||
import globalNotificationStore, { NotificationStore } from 'stores/masakari/notifications';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export class Notifications extends Base {
|
||||
init() {
|
||||
this.store = globalNotificationStore;
|
||||
this.downloadStore = new NotificationStore();
|
||||
}
|
||||
|
||||
get policy() {
|
||||
if (this.isAdminPage) {
|
||||
return 'os_compute_api:servers:index:get_all_tenants';
|
||||
}
|
||||
return 'os_compute_api:servers:index';
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('segments');
|
||||
}
|
||||
|
||||
get defaultSortKey() {
|
||||
return 'updated_at';
|
||||
}
|
||||
|
||||
get searchFilters() {
|
||||
return [
|
||||
{
|
||||
label: t('Host'),
|
||||
name: 'source_host_uuid',
|
||||
},
|
||||
{
|
||||
label: t('UUID'),
|
||||
name: 'notification_uuid',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
getColumns = () => [
|
||||
{
|
||||
title: t('UUID'),
|
||||
dataIndex: 'notification_uuid',
|
||||
render: (value) => {
|
||||
const path = this.getRoutePath("masakariNotificationDetail", {id: value});
|
||||
return <Link to={path}>{value}</Link>
|
||||
},
|
||||
isHideable: true
|
||||
},
|
||||
{
|
||||
title: t('Host'),
|
||||
dataIndex: 'source_host_uuid',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Type'),
|
||||
dataIndex: 'type',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Status'),
|
||||
dataIndex: 'status',
|
||||
isHideable: true
|
||||
},
|
||||
{
|
||||
title: t('Payload'),
|
||||
dataIndex: 'payload',
|
||||
isHideable: true,
|
||||
render: (value) => Object.keys(value).map(it =><React.Fragment><div>{it}: {value[it]}</div></React.Fragment>)
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(Notifications));
|
56
src/pages/ha/containers/Segments/Detail/BaseDetail.jsx
Normal file
56
src/pages/ha/containers/Segments/Detail/BaseDetail.jsx
Normal file
@ -0,0 +1,56 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import Base from 'containers/BaseDetail';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
|
||||
export class BaseDetail extends Base {
|
||||
get leftCards() {
|
||||
const cards = [this.baseInfoCard];
|
||||
return cards;
|
||||
}
|
||||
|
||||
get baseInfoCard() {
|
||||
const options = [
|
||||
{
|
||||
label: t('Recovery Method'),
|
||||
dataIndex: 'recovery_method',
|
||||
},
|
||||
{
|
||||
label: t('Service Type'),
|
||||
dataIndex: 'service_type',
|
||||
},
|
||||
{
|
||||
label: t('Enabled'),
|
||||
dataIndex: 'enabled',
|
||||
valueRender: 'yesNo'
|
||||
},
|
||||
{
|
||||
label: t('Created At'),
|
||||
dataIndex: 'created_at',
|
||||
valueRender: 'toLocalTime',
|
||||
},
|
||||
{
|
||||
label: t('Updated At'),
|
||||
dataIndex: 'updated_at',
|
||||
valueRender: 'toLocalTime',
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
title: t('Capsule Type'),
|
||||
options,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(BaseDetail));
|
85
src/pages/ha/containers/Segments/Detail/HostDetail.jsx
Normal file
85
src/pages/ha/containers/Segments/Detail/HostDetail.jsx
Normal file
@ -0,0 +1,85 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import Base from 'containers/List';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import globalHostStore, { HostStore } from 'stores/masakari/hosts';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export class HostDetail extends Base {
|
||||
init() {
|
||||
this.store = globalHostStore;
|
||||
this.downloadStore = new HostStore();
|
||||
}
|
||||
|
||||
get policy() {
|
||||
return 'volume:get_all';
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('Host');
|
||||
}
|
||||
|
||||
getColumns = () => {
|
||||
const columns = [
|
||||
{
|
||||
title: t('Name'),
|
||||
dataIndex: 'name',
|
||||
render: (value, row) => {
|
||||
const path = this.getRoutePath('masakariHostDetail', { id: row.failover_segment_id }, { uuid: row.uuid });
|
||||
return <Link to={path}>{value}</Link>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: t('UUID'),
|
||||
dataIndex: 'uuid',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Reserved'),
|
||||
dataIndex: 'reserved',
|
||||
isHideable: true,
|
||||
valueRender: 'yesNo'
|
||||
},
|
||||
{
|
||||
title: t('Type'),
|
||||
dataIndex: 'type',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Control Attribute'),
|
||||
dataIndex: 'control_attributes',
|
||||
isHideable: true
|
||||
},
|
||||
{
|
||||
title: t('On Maintenance'),
|
||||
dataIndex: 'on_maintenance',
|
||||
isHideable: true,
|
||||
valueRender: 'yesNo'
|
||||
}
|
||||
,
|
||||
{
|
||||
title: t('Failover Segment'),
|
||||
dataIndex: 'failover_segment',
|
||||
isHideable: true,
|
||||
render: (value, row) => {
|
||||
return <Link to={this.getRoutePath('masakariSegmentDetail', { id: row.failover_segment_id })}>{row.failover_segment.name}</Link>
|
||||
}
|
||||
}
|
||||
];
|
||||
return columns;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(HostDetail));
|
70
src/pages/ha/containers/Segments/Detail/index.jsx
Normal file
70
src/pages/ha/containers/Segments/Detail/index.jsx
Normal file
@ -0,0 +1,70 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import Base from 'containers/TabDetail';
|
||||
import { SegmentStore } from 'src/stores/masakari/segments';
|
||||
import BaseDetail from './BaseDetail';
|
||||
import actionConfigs from '../actions';
|
||||
import HostDetail from '../../Hosts';
|
||||
|
||||
export class SegmentsDetail extends Base {
|
||||
init() {
|
||||
this.store = new SegmentStore;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('Segment Detail');
|
||||
}
|
||||
|
||||
get listUrl() {
|
||||
return this.getRoutePath('masakariSegments');
|
||||
}
|
||||
|
||||
get policy() {
|
||||
return 'capsule:get_one_all_projects';
|
||||
}
|
||||
|
||||
get actionConfigs() {
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
get detailInfos() {
|
||||
return [
|
||||
{
|
||||
title: t('Name'),
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: t('Description'),
|
||||
dataIndex: 'description',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
get tabs() {
|
||||
return [
|
||||
{
|
||||
title: t('Detail'),
|
||||
key: 'general_info',
|
||||
component: BaseDetail,
|
||||
},
|
||||
{
|
||||
title: t('Hosts'),
|
||||
key: 'host',
|
||||
component: HostDetail,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(SegmentsDetail));
|
131
src/pages/ha/containers/Segments/actions/AddHost.jsx
Normal file
131
src/pages/ha/containers/Segments/actions/AddHost.jsx
Normal file
@ -0,0 +1,131 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { ModalAction } from "src/containers/Action";
|
||||
import globalHostStore from 'src/stores/masakari/hosts';
|
||||
import globalComputeHostStore from 'src/stores/nova/compute-host';
|
||||
|
||||
export class AddHost extends ModalAction {
|
||||
init() {
|
||||
this.store = globalHostStore;
|
||||
this.state = {
|
||||
host: []
|
||||
};
|
||||
this.getHostList();
|
||||
}
|
||||
|
||||
static id = 'AddHost';
|
||||
|
||||
static title = t('Add Host');
|
||||
|
||||
get name() {
|
||||
return t('Add Host');
|
||||
}
|
||||
|
||||
static policy = 'baremetal:port:create';
|
||||
|
||||
static allowed = () => Promise.resolve(true);
|
||||
|
||||
async getHostList() {
|
||||
const response = await globalComputeHostStore.fetchList({ binary: 'nova-compute' });
|
||||
const hostList = await globalHostStore.fetchList();
|
||||
let flag = false;
|
||||
|
||||
if (hostList.length < 1) {
|
||||
this.setState({
|
||||
host: response
|
||||
});
|
||||
}
|
||||
else {
|
||||
response.forEach(newHost => {
|
||||
for (let i = 0; i < hostList.length; i++) {
|
||||
if (hostList[i].name === newHost.host) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
this.setState({
|
||||
host: [...this.state.host, newHost]
|
||||
});
|
||||
}
|
||||
flag = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get getHostName() {
|
||||
return (this.state.host || []).map((it) => ({
|
||||
value: it.host,
|
||||
label: it.host,
|
||||
}));
|
||||
}
|
||||
|
||||
get defaultValue() {
|
||||
return {
|
||||
segment_name: this.item.name,
|
||||
reserved: false,
|
||||
on_maintenance: false
|
||||
};
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
return [
|
||||
{
|
||||
name: 'segment_name',
|
||||
label: t('Segment Name'),
|
||||
type: 'input',
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: t('Host Name'),
|
||||
type: 'select',
|
||||
options: this.getHostName,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'reserved',
|
||||
label: t('Reserved'),
|
||||
type: 'switch',
|
||||
checkedText: '',
|
||||
uncheckedText: ''
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
label: t('Type'),
|
||||
type: 'input',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'control_attributes',
|
||||
label: t('Control Attributes'),
|
||||
type: 'input',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'on_maintenance',
|
||||
label: t('On Maintenance'),
|
||||
type: 'switch',
|
||||
checkedText: '',
|
||||
uncheckedText: ''
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
onSubmit = (values) => {
|
||||
const { segment_name, ...submitData } = values;
|
||||
return this.store.create(this.item.uuid, { 'host': { ...submitData } });
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(AddHost));
|
54
src/pages/ha/containers/Segments/actions/Delete.jsx
Normal file
54
src/pages/ha/containers/Segments/actions/Delete.jsx
Normal file
@ -0,0 +1,54 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { ConfirmAction } from 'containers/Action';
|
||||
import globalSegmentStore from 'src/stores/masakari/segments';
|
||||
|
||||
export default class Delete extends ConfirmAction {
|
||||
get id() {
|
||||
return 'delete';
|
||||
}
|
||||
|
||||
get title() {
|
||||
return t('Delete');
|
||||
}
|
||||
|
||||
get actionName() {
|
||||
return t('delete segments');
|
||||
}
|
||||
|
||||
get isDanger() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get isAsyncAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
policy = 'os_compute_api:os-deferred-delete:force';
|
||||
|
||||
allowedCheckFunction = () => true;
|
||||
|
||||
confirmContext = (data) => {
|
||||
const name = this.getName(data);
|
||||
return t('Are you sure to {action} (Segment: {name})?', {
|
||||
action: this.actionNameDisplay || this.title,
|
||||
name,
|
||||
});
|
||||
};
|
||||
|
||||
onSubmit = (item) => {
|
||||
const { uuid } = item || this.item;
|
||||
let id = uuid;
|
||||
return globalSegmentStore.delete({ id });
|
||||
};
|
||||
}
|
189
src/pages/ha/containers/Segments/actions/StepCreate/StepHost.jsx
Normal file
189
src/pages/ha/containers/Segments/actions/StepCreate/StepHost.jsx
Normal file
@ -0,0 +1,189 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import Base from 'components/Form';
|
||||
import globalHostStore from 'src/stores/masakari/hosts';
|
||||
import globalComputeHostStore from 'src/stores/nova/compute-host';
|
||||
import { Input, Switch } from 'antd';
|
||||
|
||||
export class StepHost extends Base {
|
||||
init() {
|
||||
this.store = globalHostStore;
|
||||
this.state = {
|
||||
host: [],
|
||||
hostLoading: true,
|
||||
...this.state
|
||||
};
|
||||
|
||||
this.getHostList();
|
||||
}
|
||||
|
||||
get title() {
|
||||
return 'StepHost';
|
||||
}
|
||||
|
||||
get name() {
|
||||
return 'StepHost';
|
||||
}
|
||||
|
||||
get isStep() {
|
||||
return true;
|
||||
}
|
||||
|
||||
allowed = () => Promise.resolve();
|
||||
|
||||
async getHostList() {
|
||||
|
||||
const response = await globalComputeHostStore.fetchList({ binary: 'nova-compute' });
|
||||
const hostList = await globalHostStore.fetchList();
|
||||
let flag = false;
|
||||
|
||||
if (hostList.length < 1) {
|
||||
this.setState({
|
||||
host: response
|
||||
});
|
||||
}
|
||||
else {
|
||||
response.forEach(newHost => {
|
||||
for (let i = 0; i < hostList.length; i++) {
|
||||
if (hostList[i].name === newHost.host) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
this.setState({ host: [...this.state.host, newHost] });
|
||||
}
|
||||
flag = false;
|
||||
});
|
||||
}
|
||||
|
||||
const hostMap = Object.fromEntries(
|
||||
this.state.host.map(host => [host.id, host])
|
||||
)
|
||||
this.setState({ hostMap: hostMap, hostLoading: false });
|
||||
}
|
||||
|
||||
get getHostName() {
|
||||
return (this.state.host || []).map((it) => ({
|
||||
value: it.host,
|
||||
label: it.host,
|
||||
}));
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
const columns = [
|
||||
{ title: t('Name'), dataIndex: 'host' },
|
||||
{ title: t('Zone'), dataIndex: 'zone' },
|
||||
{
|
||||
title: t('Updated'),
|
||||
dataIndex: 'updated_at',
|
||||
valueRender: 'toLocalTime'
|
||||
},
|
||||
{
|
||||
name: 'reserved',
|
||||
title: t('Reserved'),
|
||||
dataIndex: 'reserved',
|
||||
required: true,
|
||||
render: (reserved, row) => (
|
||||
<Switch
|
||||
checked={reserved}
|
||||
onChange={(checked) => {
|
||||
this.setState(prevState => {
|
||||
const host = prevState.hostMap
|
||||
host[row.id].reserved = checked
|
||||
return { hostMap: host }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
title: t('Type'),
|
||||
dataIndex: 'type',
|
||||
required: true,
|
||||
render: (type, row) => (
|
||||
<Input
|
||||
required={true}
|
||||
defaultValue={type}
|
||||
onChange={(e) => {
|
||||
const { value } = e.target
|
||||
this.setState(prevState => {
|
||||
const host = prevState.hostMap
|
||||
host[row.id].type = value
|
||||
return { hostMap: host }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'control_attributes',
|
||||
title: t('Control Attributes'),
|
||||
dataIndex: 'control_attributes',
|
||||
render: (control_attribute, row) => (
|
||||
<Input
|
||||
defaultValue={control_attribute}
|
||||
required={true}
|
||||
onChange={(e) => {
|
||||
const { value } = e.target
|
||||
this.setState(prevState => {
|
||||
const host = prevState.hostMap
|
||||
host[row.id].control_attributes = value
|
||||
return { hostMap: host }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'on_maintenance',
|
||||
title: t('On Maintenance'),
|
||||
dataIndex: 'on_maintenance',
|
||||
render: (maintain, row) => (
|
||||
<Switch
|
||||
checked={maintain}
|
||||
onChange={(checked) => {
|
||||
this.setState(prevState => {
|
||||
const host = prevState.hostMap
|
||||
host[row.id].on_maintenance = checked
|
||||
return { hostMap: host }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'name',
|
||||
label: t('Host Name'),
|
||||
type: 'select-table',
|
||||
required: true,
|
||||
data: this.state.host,
|
||||
isMulti: true,
|
||||
onRow: () => { },
|
||||
columns: columns,
|
||||
isLoading: this.state.hostLoading,
|
||||
filterParams: [
|
||||
{ label: t('Name'), name: 'host' },
|
||||
{ label: t('Zone'), name: 'zone' },
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(StepHost));
|
@ -0,0 +1,76 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import Base from 'components/Form';
|
||||
|
||||
export class StepSegment extends Base {
|
||||
|
||||
get title() {
|
||||
return 'StepSegment';
|
||||
}
|
||||
|
||||
get name() {
|
||||
return 'StepSegment';
|
||||
}
|
||||
|
||||
get isStep() {
|
||||
return true;
|
||||
}
|
||||
|
||||
get defaultValue() {
|
||||
return {
|
||||
recovery_method: 'auto',
|
||||
service_type: 'compute',
|
||||
};
|
||||
}
|
||||
|
||||
allowed = () => Promise.resolve();
|
||||
|
||||
get formItems() {
|
||||
return [
|
||||
{
|
||||
name: 'segment_name',
|
||||
label: t('Segment Name'),
|
||||
type: 'input',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'recovery_method',
|
||||
label: t('Recovery Method'),
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: t('auto'), value: 'auto' },
|
||||
{ label: t('auto_priority'), value: 'auto_priority' },
|
||||
{ label: t('reserved_host'), value: 'reserved_host' },
|
||||
{ label: t('rh_priority'), value: 'rh_priority' },
|
||||
],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'service_type',
|
||||
label: t('Service Type'),
|
||||
type: 'input',
|
||||
required: true,
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: t('Description'),
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(StepSegment));
|
151
src/pages/ha/containers/Segments/actions/StepCreate/index.jsx
Normal file
151
src/pages/ha/containers/Segments/actions/StepCreate/index.jsx
Normal file
@ -0,0 +1,151 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { StepAction } from 'containers/Action';
|
||||
import StepSegment from './StepSegment';
|
||||
import globalSegmentStore from 'src/stores/masakari/segments';
|
||||
import StepHost from './StepHost';
|
||||
import React from 'react';
|
||||
import { Button, Modal } from 'antd';
|
||||
import { toJS } from 'mobx';
|
||||
import { QuestionCircleFilled } from '@ant-design/icons';
|
||||
import stylesConfirm from 'src/components/Confirm/index.less';
|
||||
import globalHostStore from 'src/stores/masakari/hosts';
|
||||
import Notify from 'src/components/Notify';
|
||||
|
||||
export class StepCreate extends StepAction {
|
||||
static id = 'instance-ha-create';
|
||||
|
||||
static title = t('Create Segment');
|
||||
|
||||
static path = '/ha/segments-admin/create-step-admin';
|
||||
|
||||
init() {
|
||||
this.store = globalHostStore;
|
||||
this.state = { btnIsLoading: false, ...this.state };
|
||||
}
|
||||
|
||||
static policy = 'get_images';
|
||||
|
||||
static allowed() {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('Create Segment');
|
||||
}
|
||||
|
||||
get listUrl() {
|
||||
return this.getRoutePath('masakariSegments');
|
||||
}
|
||||
|
||||
get hasConfirmStep() {
|
||||
return false;
|
||||
}
|
||||
|
||||
next() {
|
||||
this.currentRef.current.wrappedInstance.checkFormInput((values) => {
|
||||
this.updateData(values);
|
||||
|
||||
if (this.state.current === 0) {
|
||||
this.setState({ btnIsLoading: true })
|
||||
const { segment_name, recovery_method, service_type, description } = this.state.data;
|
||||
|
||||
globalSegmentStore.create({ segment: { name: segment_name, recovery_method, service_type, description } }).then(item => {
|
||||
this.setState({ extra: toJS({ createdSegmentId: item.segment.uuid }) }, () => {
|
||||
this.setState((prev) => ({ current: prev.current + 1 }));
|
||||
})
|
||||
}, (err) => {
|
||||
this.responseError = err;
|
||||
const { response: { data: responseData } = {} } = err;
|
||||
Notify.errorWithDetail(responseData, this.errorText);
|
||||
}
|
||||
).finally(() => {
|
||||
this.setState({ btnIsLoading: false })
|
||||
});
|
||||
}
|
||||
|
||||
}, () => this.setState({ btnIsLoading: false }));
|
||||
}
|
||||
|
||||
getNextBtn() {
|
||||
const { current } = this.state;
|
||||
if (current >= this.steps.length - 1) {
|
||||
return null;
|
||||
}
|
||||
const { title } = this.steps[current + 1];
|
||||
return (
|
||||
<Button type="primary" onClick={() => this.next()} loading={this.state.btnIsLoading}>
|
||||
{`${t('Next')}: ${title}`}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
getPrevBtn() {
|
||||
const { current } = this.state;
|
||||
if (current === 0) {
|
||||
return null;
|
||||
}
|
||||
const preTitle = this.steps[current - 1].title;
|
||||
return (
|
||||
<Button style={{ margin: '0 8px' }} onClick={() => this.prev()}>
|
||||
{`${t('Previous')}: ${preTitle}`}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
prev() {
|
||||
this.currentRef.current.wrappedInstance.checkFormInput(
|
||||
this.updateDataOnPrev,
|
||||
this.updateDataOnPrev
|
||||
);
|
||||
globalSegmentStore.delete({ id: this.state.extra.createdSegmentId });
|
||||
}
|
||||
|
||||
onClickCancel = () => {
|
||||
if (this.state.current !== 0) {
|
||||
Modal.confirm({
|
||||
title: 'Confirm',
|
||||
icon: <QuestionCircleFilled className={stylesConfirm.warn} />,
|
||||
content: 'Segment will be deleted. Are you sure want to cancel this created segment?',
|
||||
okText: 'Confirm',
|
||||
cancelText: 'Cancel',
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
return globalSegmentStore.delete({ id: this.state.extra.createdSegmentId }).finally(() => this.routing.push(this.listUrl));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.routing.push(this.listUrl);
|
||||
}
|
||||
}
|
||||
|
||||
get steps() {
|
||||
return [
|
||||
{ title: t('Create Segment'), component: StepSegment },
|
||||
{ title: t('Add Host'), component: StepHost }
|
||||
];
|
||||
}
|
||||
|
||||
onSubmit = (values) => {
|
||||
const { name } = values;
|
||||
return Promise.resolve(
|
||||
name.selectedRows.forEach(item => {
|
||||
const { binary, forced_down, host, id, state, status, updated_at, zone, ...hostData } = item;
|
||||
this.store.create(this.state.extra.createdSegmentId, { 'host': { name: host, ...hostData } })
|
||||
})
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(StepCreate));
|
74
src/pages/ha/containers/Segments/actions/Update.jsx
Normal file
74
src/pages/ha/containers/Segments/actions/Update.jsx
Normal file
@ -0,0 +1,74 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { inject, observer } from 'mobx-react';
|
||||
import { ModalAction } from "src/containers/Action";
|
||||
import globalSegmentStore from 'src/stores/masakari/segments';
|
||||
|
||||
export class Update extends ModalAction {
|
||||
init() {
|
||||
this.store = globalSegmentStore;
|
||||
}
|
||||
|
||||
static id = 'UpdateSegment';
|
||||
|
||||
static title = t('Update');
|
||||
|
||||
get name() {
|
||||
return t('Update Segment');
|
||||
}
|
||||
|
||||
static policy = 'baremetal:port:Update';
|
||||
|
||||
static allowed = () => Promise.resolve(true);
|
||||
|
||||
get defaultValue() {
|
||||
return {
|
||||
...this.item
|
||||
};
|
||||
}
|
||||
|
||||
get formItems() {
|
||||
return [
|
||||
{
|
||||
name: 'name',
|
||||
label: t('Segment Name'),
|
||||
type: 'input',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'recovery_method',
|
||||
label: t('Recovery Method'),
|
||||
type: 'select',
|
||||
options: [
|
||||
{ label: t('auto'), value: 'auto' },
|
||||
{ label: t('auto_priority'), value: 'auto_priority' },
|
||||
{ label: t('reserved_host'), value: 'reserved_host' },
|
||||
{ label: t('rh_priority'), value: 'rh_priority' },
|
||||
],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: t('Description'),
|
||||
type: 'textarea',
|
||||
rows: 4
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
onSubmit = (values) => {
|
||||
return this.store.update(this.item.uuid, { 'segment': values });
|
||||
}
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(Update));
|
34
src/pages/ha/containers/Segments/actions/index.jsx
Normal file
34
src/pages/ha/containers/Segments/actions/index.jsx
Normal file
@ -0,0 +1,34 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import StepCreate from './StepCreate';
|
||||
import Update from './Update';
|
||||
import Delete from './Delete';
|
||||
import AddHost from './AddHost';
|
||||
|
||||
const actionConfigs = {
|
||||
rowActions: {
|
||||
firstAction: Update,
|
||||
moreActions: [
|
||||
{
|
||||
action: AddHost,
|
||||
},
|
||||
{
|
||||
action: Delete,
|
||||
},
|
||||
],
|
||||
},
|
||||
batchActions: [Delete],
|
||||
primaryActions: [StepCreate]
|
||||
};
|
||||
|
||||
export default actionConfigs;
|
97
src/pages/ha/containers/Segments/index.jsx
Normal file
97
src/pages/ha/containers/Segments/index.jsx
Normal file
@ -0,0 +1,97 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { observer, inject } from 'mobx-react';
|
||||
import Base from 'containers/List';
|
||||
import actionConfigs from './actions';
|
||||
import globalSegmentStore, { SegmentStore } from 'stores/masakari/segments';
|
||||
|
||||
export class Segments extends Base {
|
||||
init() {
|
||||
this.store = globalSegmentStore;
|
||||
this.downloadStore = new SegmentStore();
|
||||
}
|
||||
|
||||
get policy() {
|
||||
if (this.isAdminPage) {
|
||||
return 'os_compute_api:servers:index:get_all_tenants';
|
||||
}
|
||||
return 'os_compute_api:servers:index';
|
||||
}
|
||||
|
||||
get name() {
|
||||
return t('segments');
|
||||
}
|
||||
|
||||
get defaultSortKey() {
|
||||
return 'updated_at';
|
||||
}
|
||||
|
||||
get actionConfigs() {
|
||||
return actionConfigs;
|
||||
}
|
||||
|
||||
get searchFilters() {
|
||||
return [
|
||||
{
|
||||
label: t('Recovery Method'),
|
||||
name: 'recovery_method',
|
||||
},
|
||||
{
|
||||
label: t('Service Type'),
|
||||
name: 'service_type',
|
||||
},
|
||||
...(this.isAdminPage
|
||||
? [
|
||||
{
|
||||
label: t('Project Name'),
|
||||
name: 'project_name',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
get rowKey() {
|
||||
return 'uuid';
|
||||
}
|
||||
|
||||
getColumns = () => [
|
||||
{
|
||||
title: t('Name'),
|
||||
dataIndex: 'name',
|
||||
routeName: this.getRouteName('masakariSegmentDetail'),
|
||||
},
|
||||
{
|
||||
title: t('UUID'),
|
||||
dataIndex: 'uuid',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Recovery Method'),
|
||||
dataIndex: 'recovery_method',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Service Type'),
|
||||
dataIndex: 'service_type',
|
||||
isHideable: true,
|
||||
},
|
||||
{
|
||||
title: t('Description'),
|
||||
dataIndex: 'description',
|
||||
isHideable: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
export default inject('rootStore')(observer(Segments));
|
40
src/pages/ha/routes/index.js
Normal file
40
src/pages/ha/routes/index.js
Normal file
@ -0,0 +1,40 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import BaseLayout from 'layouts/Basic';
|
||||
import E404 from 'pages/base/containers/404';
|
||||
import Segments from '../containers/Segments';
|
||||
import Hosts from '../containers/Hosts';
|
||||
import Notifications from '../containers/Notifications';
|
||||
import SegmentsDetail from '../containers/Segments/Detail';
|
||||
import HostsDetail from '../containers/Hosts/Detail';
|
||||
import NotificationsDetail from '../containers/Notifications/Detail';
|
||||
import StepCreate from '../containers/Segments/actions/StepCreate';
|
||||
|
||||
const PATH = '/ha';
|
||||
export default [
|
||||
{
|
||||
path: PATH,
|
||||
component: BaseLayout,
|
||||
routes: [
|
||||
{ path: `${PATH}/segments-admin`, component: Segments, exact: true },
|
||||
{ path: `${PATH}/segments-admin`, component: Segments, exact: true },
|
||||
{ path: `${PATH}/segments-admin/create-step-admin`, component: StepCreate, exact: true },
|
||||
{ path: `${PATH}/segments-admin/detail/:id`, component: SegmentsDetail, exact: true },
|
||||
{ path: `${PATH}/hosts-admin`, component: Hosts, exact: true },
|
||||
{ path: `${PATH}/hosts-admin/detail/:id`, component: HostsDetail, exact: true, },
|
||||
{ path: `${PATH}/notifications-admin`, component: Notifications, exact: true },
|
||||
{ path: `${PATH}/notifications-admin/detail/:id`, component: NotificationsDetail, exact: true },
|
||||
{ path: '*', component: E404 },
|
||||
],
|
||||
},
|
||||
];
|
68
src/stores/masakari/hosts.js
Normal file
68
src/stores/masakari/hosts.js
Normal file
@ -0,0 +1,68 @@
|
||||
import Base from 'stores/base';
|
||||
import client from 'client';
|
||||
import { action } from 'mobx';
|
||||
|
||||
export class HostStore extends Base {
|
||||
|
||||
get client() {
|
||||
return client.masakari.segments.hosts;
|
||||
}
|
||||
|
||||
get segmentClient() {
|
||||
return client.masakari.segments;
|
||||
}
|
||||
|
||||
get isSubResource() {
|
||||
return true;
|
||||
}
|
||||
|
||||
detailFetchByClient(resourceParams) {
|
||||
return this.client.show(resourceParams.id, resourceParams.uuid);
|
||||
}
|
||||
|
||||
get paramsFunc() {
|
||||
return (params) => {
|
||||
const { id } = params;
|
||||
|
||||
return { segment_id: id };
|
||||
};
|
||||
}
|
||||
|
||||
async listFetchByClient(params) {
|
||||
const result = [];
|
||||
|
||||
if (params.segment_id) {
|
||||
await this.client.list(params.segment_id).then(response => {
|
||||
response.hosts.map(item => result.push(item))
|
||||
});
|
||||
} else {
|
||||
await this.segmentClient.list().then(async segmentList => {
|
||||
const segmentHosts = segmentList.segments.map((it) => this.client.list(it.uuid).then(getHost => getHost.hosts))
|
||||
await Promise.all(segmentHosts).then(hostList => {
|
||||
hostList.forEach(host => {
|
||||
host.forEach(item => {
|
||||
result.push(item);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
return { hosts: result }
|
||||
}
|
||||
|
||||
@action
|
||||
async create(segment_id, newbody) {
|
||||
return this.client.create(segment_id, newbody);
|
||||
}
|
||||
|
||||
@action
|
||||
delete = ({ segment_id, host_id }) => this.submitting(this.client.delete(segment_id, host_id));
|
||||
|
||||
@action
|
||||
update(segmentId, id, body) {
|
||||
return this.submitting(this.client.update(segmentId, id, body));
|
||||
}
|
||||
}
|
||||
|
||||
const globalHostStore = new HostStore();
|
||||
export default globalHostStore;
|
22
src/stores/masakari/notifications.js
Normal file
22
src/stores/masakari/notifications.js
Normal file
@ -0,0 +1,22 @@
|
||||
import Base from 'stores/base';
|
||||
import client from 'client';
|
||||
import { action } from 'mobx';
|
||||
|
||||
export class NotificationStore extends Base {
|
||||
get client() {
|
||||
return client.masakari.notifications;
|
||||
}
|
||||
|
||||
@action
|
||||
async create(newbody) {
|
||||
return this.client.create(newbody);
|
||||
}
|
||||
|
||||
@action
|
||||
async delete({ params }, newbody) {
|
||||
return this.client.delete(params, newbody);
|
||||
}
|
||||
}
|
||||
|
||||
const globalNotificationStore = new NotificationStore();
|
||||
export default globalNotificationStore;
|
27
src/stores/masakari/segments.js
Normal file
27
src/stores/masakari/segments.js
Normal file
@ -0,0 +1,27 @@
|
||||
import Base from 'stores/base';
|
||||
import client from 'client';
|
||||
import { action } from 'mobx';
|
||||
|
||||
export class SegmentStore extends Base {
|
||||
get client() {
|
||||
return client.masakari.segments;
|
||||
}
|
||||
|
||||
@action
|
||||
async create(newbody) {
|
||||
return this.client.create(newbody);
|
||||
}
|
||||
|
||||
@action
|
||||
async delete({ id }) {
|
||||
return this.client.delete(id);
|
||||
}
|
||||
|
||||
@action
|
||||
update(id, body) {
|
||||
return this.submitting(this.client.update(id, body));
|
||||
}
|
||||
}
|
||||
|
||||
const globalSegmentStore = new SegmentStore();
|
||||
export default globalSegmentStore;
|
Loading…
x
Reference in New Issue
Block a user