diff --git a/src/client/client/constants.js b/src/client/client/constants.js index ec061be8..b92757dd 100644 --- a/src/client/client/constants.js +++ b/src/client/client/constants.js @@ -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'); diff --git a/src/client/index.js b/src/client/index.js index 5155ac5f..137f5894 100644 --- a/src/client/index.js +++ b/src/client/index.js @@ -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; diff --git a/src/client/masakari/index.js b/src/client/masakari/index.js new file mode 100644 index 00000000..11153da3 --- /dev/null +++ b/src/client/masakari/index.js @@ -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; + diff --git a/src/pages/basic/routes/index.js b/src/pages/basic/routes/index.js index ff58ee7e..5efa288f 100644 --- a/src/pages/basic/routes/index.js +++ b/src/pages/basic/routes/index.js @@ -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 }, ], }, diff --git a/src/pages/ha/App.jsx b/src/pages/ha/App.jsx new file mode 100644 index 00000000..5bf31315 --- /dev/null +++ b/src/pages/ha/App.jsx @@ -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; diff --git a/src/pages/ha/containers/Hosts/Detail/BaseDetail.jsx b/src/pages/ha/containers/Hosts/Detail/BaseDetail.jsx new file mode 100644 index 00000000..4e997a35 --- /dev/null +++ b/src/pages/ha/containers/Hosts/Detail/BaseDetail.jsx @@ -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 {row.failover_segment.name} + } + }, + { + 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)); diff --git a/src/pages/ha/containers/Hosts/Detail/index.jsx b/src/pages/ha/containers/Hosts/Detail/index.jsx new file mode 100644 index 00000000..2653b87a --- /dev/null +++ b/src/pages/ha/containers/Hosts/Detail/index.jsx @@ -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)); diff --git a/src/pages/ha/containers/Hosts/actions/Delete.jsx b/src/pages/ha/containers/Hosts/actions/Delete.jsx new file mode 100644 index 00000000..835766f8 --- /dev/null +++ b/src/pages/ha/containers/Hosts/actions/Delete.jsx @@ -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 }); + }; +} diff --git a/src/pages/ha/containers/Hosts/actions/Update.jsx b/src/pages/ha/containers/Hosts/actions/Update.jsx new file mode 100644 index 00000000..44012849 --- /dev/null +++ b/src/pages/ha/containers/Hosts/actions/Update.jsx @@ -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)); \ No newline at end of file diff --git a/src/pages/ha/containers/Hosts/actions/index.jsx b/src/pages/ha/containers/Hosts/actions/index.jsx new file mode 100644 index 00000000..b397b58d --- /dev/null +++ b/src/pages/ha/containers/Hosts/actions/index.jsx @@ -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; diff --git a/src/pages/ha/containers/Hosts/index.jsx b/src/pages/ha/containers/Hosts/index.jsx new file mode 100644 index 00000000..07aa986b --- /dev/null +++ b/src/pages/ha/containers/Hosts/index.jsx @@ -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 {value}; + } + }, + { + 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 {row.failover_segment.name} + } + } + ]; + +} + +export default inject('rootStore')(observer(Hosts)); diff --git a/src/pages/ha/containers/Notifications/Detail/BaseDetail.jsx b/src/pages/ha/containers/Notifications/Detail/BaseDetail.jsx new file mode 100644 index 00000000..3f9c2c07 --- /dev/null +++ b/src/pages/ha/containers/Notifications/Detail/BaseDetail.jsx @@ -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)); diff --git a/src/pages/ha/containers/Notifications/Detail/index.jsx b/src/pages/ha/containers/Notifications/Detail/index.jsx new file mode 100644 index 00000000..e2b1aa8f --- /dev/null +++ b/src/pages/ha/containers/Notifications/Detail/index.jsx @@ -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)); diff --git a/src/pages/ha/containers/Notifications/index.jsx b/src/pages/ha/containers/Notifications/index.jsx new file mode 100644 index 00000000..10b7e8d0 --- /dev/null +++ b/src/pages/ha/containers/Notifications/index.jsx @@ -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 {value} + }, + 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 =>
{it}: {value[it]}
) + }, + ]; +} + +export default inject('rootStore')(observer(Notifications)); diff --git a/src/pages/ha/containers/Segments/Detail/BaseDetail.jsx b/src/pages/ha/containers/Segments/Detail/BaseDetail.jsx new file mode 100644 index 00000000..6352eb8f --- /dev/null +++ b/src/pages/ha/containers/Segments/Detail/BaseDetail.jsx @@ -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)); diff --git a/src/pages/ha/containers/Segments/Detail/HostDetail.jsx b/src/pages/ha/containers/Segments/Detail/HostDetail.jsx new file mode 100644 index 00000000..46228aea --- /dev/null +++ b/src/pages/ha/containers/Segments/Detail/HostDetail.jsx @@ -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 {value}; + } + }, + { + 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 {row.failover_segment.name} + } + } + ]; + return columns; + }; + +} + +export default inject('rootStore')(observer(HostDetail)); diff --git a/src/pages/ha/containers/Segments/Detail/index.jsx b/src/pages/ha/containers/Segments/Detail/index.jsx new file mode 100644 index 00000000..619e4949 --- /dev/null +++ b/src/pages/ha/containers/Segments/Detail/index.jsx @@ -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)); diff --git a/src/pages/ha/containers/Segments/actions/AddHost.jsx b/src/pages/ha/containers/Segments/actions/AddHost.jsx new file mode 100644 index 00000000..8d52ad02 --- /dev/null +++ b/src/pages/ha/containers/Segments/actions/AddHost.jsx @@ -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)); \ No newline at end of file diff --git a/src/pages/ha/containers/Segments/actions/Delete.jsx b/src/pages/ha/containers/Segments/actions/Delete.jsx new file mode 100644 index 00000000..dfa8b1af --- /dev/null +++ b/src/pages/ha/containers/Segments/actions/Delete.jsx @@ -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 }); + }; +} diff --git a/src/pages/ha/containers/Segments/actions/StepCreate/StepHost.jsx b/src/pages/ha/containers/Segments/actions/StepCreate/StepHost.jsx new file mode 100644 index 00000000..6e89c275 --- /dev/null +++ b/src/pages/ha/containers/Segments/actions/StepCreate/StepHost.jsx @@ -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) => ( + { + 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) => ( + { + 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) => ( + { + 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) => ( + { + 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)); diff --git a/src/pages/ha/containers/Segments/actions/StepCreate/StepSegment.jsx b/src/pages/ha/containers/Segments/actions/StepCreate/StepSegment.jsx new file mode 100644 index 00000000..758bd2d8 --- /dev/null +++ b/src/pages/ha/containers/Segments/actions/StepCreate/StepSegment.jsx @@ -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)); diff --git a/src/pages/ha/containers/Segments/actions/StepCreate/index.jsx b/src/pages/ha/containers/Segments/actions/StepCreate/index.jsx new file mode 100644 index 00000000..652a24dd --- /dev/null +++ b/src/pages/ha/containers/Segments/actions/StepCreate/index.jsx @@ -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 ( + + ); + } + + getPrevBtn() { + const { current } = this.state; + if (current === 0) { + return null; + } + const preTitle = this.steps[current - 1].title; + return ( + + ); + } + + 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: , + 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)); diff --git a/src/pages/ha/containers/Segments/actions/Update.jsx b/src/pages/ha/containers/Segments/actions/Update.jsx new file mode 100644 index 00000000..c83eeb96 --- /dev/null +++ b/src/pages/ha/containers/Segments/actions/Update.jsx @@ -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)); \ No newline at end of file diff --git a/src/pages/ha/containers/Segments/actions/index.jsx b/src/pages/ha/containers/Segments/actions/index.jsx new file mode 100644 index 00000000..0a3c6fc7 --- /dev/null +++ b/src/pages/ha/containers/Segments/actions/index.jsx @@ -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; diff --git a/src/pages/ha/containers/Segments/index.jsx b/src/pages/ha/containers/Segments/index.jsx new file mode 100644 index 00000000..e3b9ed2d --- /dev/null +++ b/src/pages/ha/containers/Segments/index.jsx @@ -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)); diff --git a/src/pages/ha/routes/index.js b/src/pages/ha/routes/index.js new file mode 100644 index 00000000..5fe1bd78 --- /dev/null +++ b/src/pages/ha/routes/index.js @@ -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 }, + ], + }, +]; diff --git a/src/stores/masakari/hosts.js b/src/stores/masakari/hosts.js new file mode 100644 index 00000000..0c64d55b --- /dev/null +++ b/src/stores/masakari/hosts.js @@ -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; diff --git a/src/stores/masakari/notifications.js b/src/stores/masakari/notifications.js new file mode 100644 index 00000000..6910c2eb --- /dev/null +++ b/src/stores/masakari/notifications.js @@ -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; diff --git a/src/stores/masakari/segments.js b/src/stores/masakari/segments.js new file mode 100644 index 00000000..ca402b40 --- /dev/null +++ b/src/stores/masakari/segments.js @@ -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;