+ }) : " - "
+ },
+ ];
+
+ return {
+ title: t('Stack'),
+ labelCol: 2,
+ options,
+ };
+ }
+}
+
+export default inject("rootStore")(observer(BaseDetail))
\ No newline at end of file
diff --git a/src/pages/container-infra/containers/Clusters/Detail/index.jsx b/src/pages/container-infra/containers/Clusters/Detail/index.jsx
new file mode 100644
index 00000000..c90069f3
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/Detail/index.jsx
@@ -0,0 +1,84 @@
+// Copyright 2021 99cloud
+//
+// 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 globalClustersStore from 'src/stores/magnum/clusters';
+
+export class ClustersDetail extends Base {
+ init() {
+ this.store = globalClustersStore;
+ }
+
+ get name() {
+ return t('Cluster Detail');
+ }
+
+ get listUrl() {
+ return this.getRoutePath('containerInfraClusters');
+ }
+
+ get policy() {
+ return 'container-infra:cluster:detail';
+ }
+
+ get detailInfos() {
+ return [
+ {
+ title: t('Name'),
+ dataIndex: 'name',
+ },
+ {
+ title: t('Created'),
+ dataIndex: 'created_at',
+ valueRender: 'toLocalTime',
+ },
+ {
+ title: t('Updated'),
+ dataIndex: 'updated_at',
+ valueRender: 'toLocalTime',
+ },
+ {
+ title: t('Status'),
+ dataIndex: 'status',
+ },
+ {
+ title: t('Status Reason'),
+ dataIndex: 'status_reason',
+ },
+ {
+ title: t('Health Status'),
+ dataIndex: 'health_status',
+ },
+ {
+ title: t('Health Status Reason'),
+ dataIndex: 'health_status_reason',
+ render: (value) => (value.length > 0 ? value : '-'),
+ },
+ ];
+ }
+
+ get tabs() {
+ return [
+ {
+ title: t('General Info'),
+ key: 'general_info',
+ component: BaseDetail,
+ },
+ ];
+ }
+}
+
+export default inject("rootStore")(observer(ClustersDetail))
\ No newline at end of file
diff --git a/src/pages/container-infra/containers/Clusters/actions/Delete.jsx b/src/pages/container-infra/containers/Clusters/actions/Delete.jsx
new file mode 100644
index 00000000..5b58540e
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/Delete.jsx
@@ -0,0 +1,44 @@
+// Copyright 2021 99cloud
+//
+// 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 globalClustersStore from 'stores/magnum/clusters';
+
+export default class DeleteClusters extends ConfirmAction {
+ get id() {
+ return 'delete';
+ }
+
+ get title() {
+ return t('Delete Clusters')
+ }
+
+ get actionName() {
+ return t('Delete Clusters');
+ }
+
+ get buttonText() {
+ return t('Delete');
+ }
+
+ get buttonType() {
+ return 'danger';
+ }
+
+ policy = 'container-infra:cluster:delete';
+
+ allowedCheckFunc = () => true;
+
+ onSubmit = (data) => globalClustersStore.delete({ id: data.uuid });
+}
diff --git a/src/pages/container-infra/containers/Clusters/actions/Resize.jsx b/src/pages/container-infra/containers/Clusters/actions/Resize.jsx
new file mode 100644
index 00000000..7d6e06d9
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/Resize.jsx
@@ -0,0 +1,60 @@
+// 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 globalClustersStore from 'stores/magnum/clusters';
+
+export class ResizeClusters extends ModalAction {
+ init() {
+ this.store = globalClustersStore;
+ }
+
+ static id = 'resize-cluster';
+
+ static title = t('Resize Cluster');
+
+ policy = 'container-infra:cluster:resize';
+
+ static allowed() {
+ return Promise.resolve(true);
+ }
+
+ get name() {
+ return t('Resize Cluster');
+ }
+
+ get buttonText() {
+ return t('Resize');
+ }
+
+ get formItems() {
+ return [
+ {
+ name: 'node_count',
+ label: t('Instance'),
+ type: 'input-number',
+ min: 1,
+ required: true,
+ },
+ ];
+ }
+
+ onSubmit = (data) => {
+ this.store.update(
+ { id: this.props.item.uuid },
+ { node_count: data.node_count }
+ );
+ };
+}
+
+export default inject("rootStore")(observer(ResizeClusters))
diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepAdvanced/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepAdvanced/index.jsx
new file mode 100644
index 00000000..24419615
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepAdvanced/index.jsx
@@ -0,0 +1,42 @@
+// Copyright 2021 99cloud
+//
+// 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 "components/Form"
+import { inject, observer } from "mobx-react";
+import KeyValueInput from 'components/FormItem/KeyValueInput';
+
+export class StepAdvanced extends Base {
+
+ get title() {
+ return t("Cluster Advanced")
+ }
+
+ get name() {
+ return t("Cluster Advanced")
+ }
+
+ get formItems() {
+ return [
+ {
+ name: 'additionalLabels',
+ label: t('Additional Labels'),
+ type: 'add-select',
+ itemComponent: KeyValueInput,
+ addText: t('Add Label'),
+ }
+ ]
+ }
+}
+
+export default inject("rootStore")(observer(StepAdvanced))
\ No newline at end of file
diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepDetails/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepDetails/index.jsx
new file mode 100644
index 00000000..a9d58669
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepDetails/index.jsx
@@ -0,0 +1,112 @@
+// Copyright 2021 99cloud
+//
+// 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 'components/Form';
+import { inject, observer } from 'mobx-react';
+
+import globalClusterTemplateStore from 'stores/magnum/clusterTemplates';
+import globalAvailabilityZoneStore from 'stores/nova/zone';
+import globalKeypairStore from 'stores/nova/keypair';
+
+export class StepDetails extends Base {
+ init() {
+ this.getClustertemplates();
+ this.getAvailableZones();
+ this.getKeypairs();
+ }
+
+ get title() {
+ return t('Cluster Detail');
+ }
+
+ get name() {
+ return t('Cluster Detail');
+ }
+
+ async getClustertemplates() {
+ globalClusterTemplateStore.fetchList();
+ }
+
+ get getClusterTemplateList() {
+ return (globalClusterTemplateStore.list.data || []).map((it) => ({
+ value: it.uuid,
+ label: it.name,
+ }));
+ }
+
+ async getAvailableZones() {
+ globalAvailabilityZoneStore.fetchListWithoutDetail();
+ }
+
+ get getAvailableZonesList() {
+ return (globalAvailabilityZoneStore.list.data || [])
+ .filter((it) => it.zoneState.available)
+ .map((it) => ({
+ value: it.zoneName,
+ label: it.zoneName,
+ }));
+ }
+
+ async getKeypairs() {
+ globalKeypairStore.fetchList();
+ }
+
+ get getKeypairList() {
+ return (globalKeypairStore.list.data || []).map((it) => ({
+ value: it.name,
+ label: it.name,
+ }));
+ }
+
+ get formItems() {
+ return [
+ {
+ name: "clusterName",
+ label: t("Cluster Name"),
+ type: "input",
+ required: true
+ },
+ {
+ name: "clusterTemplateId",
+ label: t("Cluster Template"),
+ type: "select",
+ placeholder: t('Please select'),
+ options: this.getClusterTemplateList,
+ allowClear: true,
+ showSearch: true,
+ required: true
+ },
+ {
+ name: "availabilityZone",
+ label: t("Availability Zone"),
+ type: "select",
+ options: this.getAvailableZonesList,
+ allowClear: true,
+ showSearch: true,
+ required: true
+ },
+ {
+ name: "keypair",
+ label: t("Keypair"),
+ type: "select",
+ options: this.getKeypairList,
+ allowClear: true,
+ showSearch: true,
+ required: true
+ }
+ ]
+ }
+}
+
+export default inject("rootStore")(observer(StepDetails))
\ No newline at end of file
diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepManagement/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepManagement/index.jsx
new file mode 100644
index 00000000..90acc93d
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepManagement/index.jsx
@@ -0,0 +1,50 @@
+// Copyright 2021 99cloud
+//
+// 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 "components/Form";
+
+import { inject, observer } from "mobx-react";
+
+export class StepManagement extends Base {
+
+ get title() {
+ return t("Cluster Management")
+ }
+
+ get name() {
+ return t("Cluster Management")
+ }
+
+ get formItems() {
+ return [
+ {
+ name: "auto_healing_enabled",
+ label: t("Auto Healing"),
+ type: "check",
+ content: t("Automatically repair unhealhty nodes")
+ },
+ {
+ type: "divider"
+ },
+ {
+ name: "auto_scaling_enabled",
+ label: t("Auto Scaling"),
+ type: "check",
+ content: t("Auto scaling feature will be enabled")
+ }
+ ]
+ }
+}
+
+export default inject("rootStore")(observer(StepManagement))
\ No newline at end of file
diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNetworks/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNetworks/index.jsx
new file mode 100644
index 00000000..a2f55f4b
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepNetworks/index.jsx
@@ -0,0 +1,124 @@
+// Copyright 2021 99cloud
+//
+// 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 "components/Form";
+import { inject, observer } from "mobx-react";
+import globalNetworkStore from "src/stores/neutron/network";
+
+export class StepNetworks extends Base {
+
+ init() {
+ this.getNetworks();
+ }
+
+ get title() {
+ return t("Cluster Network")
+ }
+
+ get name() {
+ return t("Cluster Network")
+ }
+
+ allowed = () => Promise.resolve();
+
+ get defaultValue() {
+ return {
+ enableNetwork: true
+ };
+ }
+
+ get nameForStateUpdate() {
+ return ['enableNetwork']
+ }
+
+ async getNetworks() {
+ globalNetworkStore.fetchList();
+ }
+
+ get getNetworkList() {
+ return (globalNetworkStore.list.data || []).map((it) => ({
+ value: it.id,
+ label: it.name,
+ }));
+ }
+
+ get formItems() {
+
+ const { enableNetwork } = this.state;
+
+ return [
+ {
+ name: "enableLoadBalancer",
+ label: t("Enable Load Balancer"),
+ type: "check",
+ content: t("Enabled Load Balancer for Master Nodes"),
+ },
+ {
+ name: "enableNetwork",
+ label: t("Enabled Network"),
+ type: "check",
+ content: t("Create New Network"),
+ },
+ {
+ name: "network",
+ label: t("Use an Existing Network"),
+ type: 'network-select-table',
+ hidden: enableNetwork
+ },
+ {
+ type: "divider"
+ },
+ {
+ name: "floating_ip_enabled",
+ label: t("Cluster API"),
+ type: "select",
+ options: [
+ {
+ label: t("Accessible on private network only"),
+ value: "networkOnly"
+ },
+ {
+ label: t("Accessible on the public internet"),
+ value: "publicInternet"
+ }
+ ],
+ },
+ {
+ type: "divider"
+ },
+ {
+ name: "ingress_controller",
+ label: t("Ingress Controller"),
+ type: "select",
+ options: [
+ {
+ label: t("octavia"),
+ value: "octavia"
+ },
+ {
+ label: t("nginx"),
+ value: "nginx"
+ },
+ {
+ label: t("traefik"),
+ value: "traefik"
+ }
+ ],
+ placeholder: t("Choose an ingress controller"),
+ }
+ ]
+ }
+}
+
+export default inject("rootStore")(observer(StepNetworks))
\ No newline at end of file
diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepSize/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepSize/index.jsx
new file mode 100644
index 00000000..4c47626f
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/StepSize/index.jsx
@@ -0,0 +1,78 @@
+// Copyright 2021 99cloud
+//
+// 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 FlavorSelectTable from 'src/pages/compute/containers/Instance/components/FlavorSelectTable';
+
+export class StepSize extends Base {
+ get title() {
+ return t("Cluster Size")
+ }
+
+ get name() {
+ return t("Cluster Size")
+ }
+
+ allowed = () => Promise.resolve();
+
+ getFlavorComponent() {
+ return ;
+ }
+
+ onFlavorChange = (value) => {
+ this.updateContext({
+ flavor: value,
+ });
+ };
+
+ get formItems() {
+ return [
+ {
+ name: "numberOfMasterNodes",
+ label: t("Number of Master Nodes"),
+ type: "input-number",
+ min: 1,
+ required: true
+ },
+ {
+ name: "flavorOfMasterNodes",
+ label: t("Flavor of Master Nodes"),
+ type: 'select-table',
+ component: this.getFlavorComponent(),
+ required: true,
+ },
+ {
+ type: "divider"
+ },
+ {
+ name: "numberOfWorkerNodes",
+ label: t("Number of Worker Nodes"),
+ type: "input-number",
+ min: 1,
+ required: true,
+ },
+ {
+ name: "flavorOfWorkerNodes",
+ label: t("Flavor of Worker Nodes"),
+ type: 'select-table',
+ component: this.getFlavorComponent(),
+ required: true
+ }
+ ]
+ }
+}
+
+export default inject('rootStore')(observer(StepSize));
\ No newline at end of file
diff --git a/src/pages/container-infra/containers/Clusters/actions/StepCreate/index.jsx b/src/pages/container-infra/containers/Clusters/actions/StepCreate/index.jsx
new file mode 100644
index 00000000..3241b07a
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/StepCreate/index.jsx
@@ -0,0 +1,116 @@
+// 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 StepDetails from "./StepDetails";
+import StepSize from './StepSize';
+import StepNetworks from './StepNetworks';
+import StepManagement from './StepManagement';
+import StepAdvanced from './StepAdvanced';
+import { inject, observer } from "mobx-react";
+import { StepAction } from "src/containers/Action";
+import globalClustersStore from "src/stores/magnum/clusters";
+
+export class StepCreate extends StepAction {
+ init() {
+ this.store = globalClustersStore
+ }
+
+ static id = "create-cluster";
+
+ static title = t("Create Cluster");
+
+ static path = "/container-infra/clusters/create";
+
+ static policy = "container-infra:cluster:create";
+
+ static allowed() {
+ return Promise.resolve(true);
+ }
+
+ get name() {
+ return t("Create Instance");
+ }
+
+ get listUrl() {
+ return this.getRoutePath("containerInfraClusters");
+ }
+
+ get hasConfirmStep() {
+ return false;
+ }
+
+ get steps() {
+ return [
+ {
+ title: t("Details *"),
+ component: StepDetails
+ },
+ {
+ title: t("Size: *"),
+ component: StepSize
+ },
+ {
+ title: t("Networks"),
+ component: StepNetworks
+ },
+ {
+ title: t("Management"),
+ component: StepManagement
+ },
+ {
+ title: t("Advanced"),
+ component: StepAdvanced
+ }
+ ]
+ }
+
+ onSubmit = (values) => {
+
+ const { additionalLabels, auto_healing_enabled, auto_scaling_enabled } = values;
+ const requestLabels = {};
+
+ if (additionalLabels) {
+ additionalLabels.forEach((item) => {
+ const labelKey = item.value.key.toLowerCase().trim();
+ const labelValue = item.value.value.toLowerCase().trim();
+ requestLabels[labelKey] = labelValue;
+ })
+ }
+
+ const data = {
+ name: values.clusterName,
+ labels: {
+ ...requestLabels,
+ auto_healing_enabled: auto_healing_enabled,
+ auto_scaling_enabled: auto_scaling_enabled,
+ admission_control_list: 'NodeRestriction,NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,PersistentVolumeClaimResize,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,RuntimeClass'
+ },
+ cluster_template_id: values.clusterTemplateId,
+ create_timeout: 60,
+ master_count: values.numberOfMasterNodes,
+ node_count: values.numberOfWorkerNodes,
+ keypair: values.keypair,
+ master_flavor_id: values.flavorOfMasterNodes,
+ flavor_id: values.flavorOfWorkerNodes,
+ master_lb_enabled: values.enableLoadBalancer,
+ floating_ip_enabled: values.floating_ip_enabled === 'networkOnly' ? false : true,
+ };
+
+ if (!values.enableNetwork) {
+ data.fixed_network = values.network;
+ }
+
+ return this.store.create(data);
+ }
+}
+
+export default inject("rootStore")(observer(StepCreate))
\ No newline at end of file
diff --git a/src/pages/container-infra/containers/Clusters/actions/index.jsx b/src/pages/container-infra/containers/Clusters/actions/index.jsx
new file mode 100644
index 00000000..b803e513
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/actions/index.jsx
@@ -0,0 +1,25 @@
+// 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 DeleteClusters from './Delete';
+import ResizeClusters from './Resize';
+import StepCreate from './StepCreate';
+
+const actionConfigs = {
+ rowActions: {
+ firstAction: DeleteClusters,
+ moreActions: [{ action: ResizeClusters }],
+ },
+ batchActions: [DeleteClusters],
+ primaryActions: [StepCreate],
+};
+
+export default actionConfigs;
diff --git a/src/pages/container-infra/containers/Clusters/index.jsx b/src/pages/container-infra/containers/Clusters/index.jsx
new file mode 100644
index 00000000..19456546
--- /dev/null
+++ b/src/pages/container-infra/containers/Clusters/index.jsx
@@ -0,0 +1,66 @@
+// Copyright 2021 99cloud
+//
+// 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/List';
+import { inject, observer } from 'mobx-react';
+import globalClustersStore from 'src/stores/magnum/clusters';
+import actionConfigs from './actions';
+
+export class Clusters extends Base {
+ init() {
+ this.store = globalClustersStore;
+ this.downloadStore = globalClustersStore;
+ }
+
+ get name() {
+ return t('clusters');
+ }
+
+ get policy() {
+ return 'container-infra:cluster:get_all';
+ }
+
+ get actionConfigs() {
+ return actionConfigs;
+ }
+
+ get rowKey() {
+ return 'uuid';
+ }
+
+ getColumns = () => [
+ {
+ title: t('ID/Name'),
+ dataIndex: 'name',
+ routeName: this.getRouteName('containerInfraClusterDetail'),
+ },
+ {
+ title: t('Status'),
+ isHideable: true,
+ dataIndex: 'status',
+ },
+ {
+ title: t('Health Status'),
+ isHideable: true,
+ dataIndex: 'health_status',
+ },
+ {
+ title: t('Keypair'),
+ isHideable: true,
+ dataIndex: 'keypair',
+ },
+ ];
+}
+
+export default inject("rootStore")(observer(Clusters))
\ No newline at end of file
diff --git a/src/pages/container-infra/routes/index.js b/src/pages/container-infra/routes/index.js
new file mode 100644
index 00000000..0520d457
--- /dev/null
+++ b/src/pages/container-infra/routes/index.js
@@ -0,0 +1,49 @@
+// Copyright 2021 99cloud
+//
+// 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 Clusters from '../containers/Clusters';
+import ClustersDetail from '../containers/Clusters/Detail';
+import ClusterTemplates from '../containers/ClusterTemplates';
+import ClusterTemplateDetail from '../containers/ClusterTemplates/Detail';
+import ClustersCreate from '../containers/Clusters/actions/StepCreate';
+import ClustersTemplateCreate from '../containers/ClusterTemplates/actions/StepCreate'
+import StepUpdateClusterTemplate from '../containers/ClusterTemplates/actions/EditStep'
+
+const PATH = '/container-infra';
+export default [
+ {
+ path: PATH,
+ component: BaseLayout,
+ routes: [
+ { path: `${PATH}/clusters`, component: Clusters, exact: true },
+ { path: `${PATH}/clusters/detail/:id`, component: ClustersDetail, exact: true },
+ { path: `${PATH}/cluster-template`, component: ClusterTemplates, exact: true },
+ { path: `${PATH}/cluster-template/detail/:id`, component: ClusterTemplateDetail, exact: true },
+ {
+ path: `${PATH}/clusters/create`,
+ component: ClustersCreate,
+ exact: true,
+ },
+ {
+ path: `${PATH}/clusters-template/create`,
+ component: ClustersTemplateCreate,
+ exact: true,
+ },
+ { path: `${PATH}/cluster-template/update/:id`, component: StepUpdateClusterTemplate, exact: true },
+ { path: '*', component: E404 },
+ ],
+ },
+];
diff --git a/src/stores/magnum/clusterTemplates.js b/src/stores/magnum/clusterTemplates.js
new file mode 100644
index 00000000..0a4adc57
--- /dev/null
+++ b/src/stores/magnum/clusterTemplates.js
@@ -0,0 +1,51 @@
+// Copyright 2021 99cloud
+//
+// 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 'stores/base';
+import client from 'client';
+import { action } from 'mobx';
+
+export class ClusterTemplatesStore extends Base {
+ get client() {
+ return client.magnum.clusterTemplates;
+ }
+
+ @action
+ async create(newbody) {
+ return this.submitting(this.client.create(newbody));
+ }
+
+ @action
+ async delete({ id }) {
+ return this.client.delete(id);
+ }
+
+ @action
+ async get(data) {
+ return this.client.get(data);
+ }
+
+ @action
+ async update({ id }, newbody) {
+ return this.client.update(id, newbody);
+ }
+
+ async listDidFetch(items) {
+ if (!items.length) return items
+ return items.map(it => ({ ...it, id: it.uuid }));
+ }
+}
+
+const globalClusterTemplateStore = new ClusterTemplatesStore();
+export default globalClusterTemplateStore;
diff --git a/src/stores/magnum/clusters.js b/src/stores/magnum/clusters.js
new file mode 100644
index 00000000..5e62d6f9
--- /dev/null
+++ b/src/stores/magnum/clusters.js
@@ -0,0 +1,62 @@
+// Copyright 2021 99cloud
+//
+// 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 'stores/base';
+import client from 'client';
+import { action } from 'mobx';
+
+export class ClustersStore extends Base {
+
+ get client() {
+ return client.magnum.clusters;
+ }
+
+ get templateClient() {
+ return client.magnum.clusterTemplates;
+ }
+
+ get listWithDetail() {
+ return true;
+ }
+
+ @action
+ async create(newbody) {
+ return this.submitting(this.client.create(newbody));
+ }
+
+ @action
+ async delete({ id }) {
+ return this.client.delete(id);
+ }
+
+ @action
+ async update({ id }, newbody) {
+ return this.client.resize.create(id, newbody);
+ }
+
+ async detailDidFetch(item) {
+ const { cluster_template_id } = item || {};
+ const template = await this.templateClient.show(cluster_template_id);
+ item.template = template;
+ return item;
+ }
+
+ async listDidFetch(items) {
+ if (!items.length) return items
+ return items.map(it => ({ ...it, id: it.uuid }));
+ }
+}
+
+const globalClustersStore = new ClustersStore();
+export default globalClustersStore;