Merge "Add Magnum UI to Skyline-Console"

This commit is contained in:
Zuul 2022-05-23 05:32:22 +00:00 committed by Gerrit Code Review
commit 54ce0336b9
32 changed files with 2415 additions and 0 deletions

View File

@ -33,6 +33,7 @@ export const endpointVersionMap = {
trove: 'v1.0',
manilav2: 'v2',
barbican: 'v1',
magnum : 'v1',
};
export const endpointsDefault = {
@ -71,6 +72,7 @@ export const swiftBase = () => getOpenstackEndpoint('swift');
export const troveBase = () => getOpenstackEndpoint('trove');
export const manilaBase = () => getOpenstackEndpoint('manilav2');
export const barbicanBase = () => getOpenstackEndpoint('barbican');
export const magnumBase = () => getOpenstackEndpoint('magnum');
export const ironicOriginEndpoint = () => getOriginEndpoint('ironic');
export const vpnEndpoint = () => getOriginEndpoint('neutron_vpn');

View File

@ -26,6 +26,7 @@ import swift from './swift';
import trove from './trove';
import manila from './manila';
import barbican from './barbican';
import magnum from './magnum';
const client = {
skyline,
@ -42,6 +43,7 @@ const client = {
trove,
manila,
barbican,
magnum
};
window.client = client;

View 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 { magnumBase } from '../client/constants';
class MagnumClient extends Base {
get baseUrl() {
return magnumBase();
}
get resources() {
return [
{
name: 'clusters',
key: 'clusters',
responseKey: 'cluster',
extendOperations: [
{
name: 'resize',
key: 'actions/resize',
method: 'post',
},
]
},
{
name: 'clusterTemplates',
key: 'clustertemplates',
responseKey: 'clustertemplate',
},
];
}
}
const magnumClient = new MagnumClient();
export default magnumClient;

View File

@ -22,6 +22,7 @@ import {
DatabaseFilled,
AppstoreOutlined,
SwitcherOutlined,
ContainerOutlined,
} from '@ant-design/icons';
const renderMenu = (t) => {
@ -580,6 +581,63 @@ const renderMenu = (t) => {
},
],
},
{
path: '/container-infra',
name: t('Container Infra'),
key: 'containerInfra',
icon: <ContainerOutlined />,
children: [
{
path: '/container-infra/clusters',
name: t('Clusters'),
key: 'containerInfraClusters',
level: 1,
children: [
{
path: /^\/container-infra\/clusters\/detail\/.[^/]+$/,
name: t('Cluster Detail'),
key: 'containerInfraClusterDetail',
level: 2,
routePath: '/container-infra/clusters/detail/:id',
},
{
path: '/container-infra/clusters/create',
name: t('Create Cluster'),
key: 'containerInfraCreateCluster',
level: 2,
},
],
},
{
path: '/container-infra/cluster-template',
name: t('Cluster Template'),
key: 'clusterTemplate',
level: 1,
children: [
{
path: /^\/container-infra\/cluster-template\/detail\/.[^/]+$/,
name: t('Cluster Template Detail'),
key: 'containerInfraClusterTemplateDetail',
level: 2,
routePath: '/container-infra/cluster-template/detail/:id',
},
{
path: '/container-infra/cluster-template/create',
name: t('Create Cluster Template'),
key: 'containerInfraCreateClusterTemplate',
level: 2,
},
{
path: /^\/container-infra\/cluster-template\/update\/.[^/]+\/.[^/]+$/,
name: t('Update Cluster Template'),
key: 'containerInfraUpdateClusterTemplate',
level: 2,
routePath: '/container-infra/cluster-template/update/:id',
},
],
},
],
},
];
return menu;
};

View File

@ -50,6 +50,9 @@ const Database = lazy(() =>
const Share = lazy(() =>
import(/* webpackChunkName: "share" */ 'pages/share/App')
);
const ContainerInfra = lazy(() =>
import(/* webpackChunkName: "container-infra" */ 'pages/container-infra/App')
);
const E404 = lazy(() =>
import(/* webpackChunkName: "E404" */ 'pages/base/containers/404')
);
@ -102,6 +105,10 @@ export default [
path: `/share`,
component: Share,
},
{
path: `/container-infra`,
component: ContainerInfra,
},
{ path: '*', component: E404 },
],
},

View File

@ -0,0 +1,21 @@
// 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 renderRoutes from 'utils/RouterConfig';
import routes from './routes';
const App = (props) => renderRoutes(routes, props);
export default App;

View File

@ -0,0 +1,183 @@
// 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 React from 'react';
import { Link } from 'react-router-dom';
import { inject, observer } from 'mobx-react';
export class BaseDetail extends Base {
get leftCards() {
return [this.baseInfoCard, this.networkCard];
}
get rightCards() {
return [this.specCard, this.labelCard];
}
get baseInfoCard() {
const options = [
{
label: t('COE'),
dataIndex: 'coe'
},
{
label: t('Cluster Distro'),
dataIndex: 'cluster_distro'
},
{
label: t('Server Type'),
dataIndex: 'server_type'
},
{
label: t('Public'),
dataIndex: 'public',
valueRender: 'yesNo'
},
{
label: t('Registry Enabled'),
dataIndex: 'registry_enabled',
valueRender: 'yesNo'
},
{
label: t('TLS Disabled'),
dataIndex: 'tls_disabled',
valueRender: 'yesNo'
},
];
return {
title: t('Cluster Type'),
options
};
}
get networkCard() {
const options = [
{
label: t('Network Driver'),
dataIndex: 'network_driver'
},
{
label: t('HTTP Proxy'),
dataIndex: 'http_proxy'
},
{
label: t('HTTPS Proxy'),
dataIndex: 'https_proxy'
},
{
label: t('No Proxy'),
dataIndex: 'no_proxy'
},
{
label: t('External Network ID'),
dataIndex: 'external_network_id'
},
{
label: t('Fixed Network'),
dataIndex: 'fixed_network'
},
{
label: t('Fixed Subnet'),
dataIndex: 'fixed_subnet'
},
{
label: t('DNS'),
dataIndex: 'dns_nameserver'
},
{
label: t('Master LB Enabled'),
dataIndex: 'master_lb_enabled',
valueRender: 'yesNo'
},
{
label: t('Floating IP Enabled'),
dataIndex: 'floating_ip_enabled',
valueRender: 'yesNo'
},
];
return {
title: t('Network'),
options,
};
}
get specCard() {
const image = this.detailData.image_id;
const imageUrl = this.getRoutePath('imageDetail', { id: image });
const keypair = this.detailData.keypair_id;
const keypairUrl = this.getRoutePath('keypairDetail', { id: keypair });
const options = [
{
label: t('Image ID'),
content: <Link to={imageUrl}>{image}</Link>
},
{
label: t('Keypair'),
content: <Link to={keypairUrl}>{keypair}</Link>
},
{
label: t('Flavor ID'),
dataIndex: 'flavor_id'
},
{
label: t('Master Flavor ID'),
dataIndex: 'master_flavor_id'
},
{
label: t('Volume Driver'),
dataIndex: 'volume_driver'
},
{
label: t('Docker Storage Driver'),
dataIndex: 'docker_storage_driver'
},
{
label: t('Docker Volume Size'),
dataIndex: 'docker_volume_size'
},
{
label: t('Insecure Registry'),
dataIndex: 'insecure_registry'
},
];
return {
title: t('Node Spec'),
options,
};
}
get labelCard() {
const options = [
{
label: t('labels'),
dataIndex: 'labels',
render: (value) => value ? Object.entries(value).map(([key, val]) => {
return <div key={key}><ul><li>{key} : {val}</li></ul></div>
}) : '-'
},
];
return {
title: t('Labels'),
labelCol: 2,
options,
};
}
}
export default inject("rootStore")(observer(BaseDetail))

View File

@ -0,0 +1,65 @@
// 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 globalClusterTemplateStore from 'src/stores/magnum/clusterTemplates';
export class ClusterTemplateDetail extends Base {
init() {
this.store = globalClusterTemplateStore;
}
get name() {
return t('Cluster Template Detail');
}
get listUrl() {
return this.getRoutePath('clusterTemplate');
}
get policy() {
return 'container-infra:clustertemplate: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'
},
];
}
get tabs() {
return [
{
title: t('General Info'),
key: 'general_info',
component: BaseDetail,
},
];
}
}
export default inject("rootStore")(observer(ClusterTemplateDetail))

View File

@ -0,0 +1,42 @@
// 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 globalClusterTemplateStore from 'stores/magnum/clusterTemplates';
export default class DeleteClusterTemplates extends ConfirmAction {
get id() {
return 'delete';
}
get title() {
return t('Delete Template')
}
get actionName() {
return t('Delete Clusters Templates');
}
get buttonType() {
return 'danger';
}
get buttonText() {
return t('Delete');
}
policy = 'container-infra:clustertemplate:delete';
allowedCheckFunc = () => true;
onSubmit = (data) => globalClusterTemplateStore.delete({ id: data.uuid });
}

View File

@ -0,0 +1,45 @@
// 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 { getPath } from 'src/utils/route-map';
import StepCreate from './StepCreate';
@inject('rootStore')
@observer
export default class Edit extends StepCreate {
static id = 'update-cluster-template';
static title = t('Update Cluster Template');
get name() {
return t('Update Cluster Template');
}
get listUrl() {
return this.getRoutePath('clusterTemplate');
}
static policy = 'container-infra:clustertemplate:update';
static path = (item) => {
const key = 'containerInfraUpdateClusterTemplate';
const { id } = item;
return getPath({ key, params: { id } });
};
static allowed() {
return Promise.resolve(true);
}
}

View File

@ -0,0 +1,118 @@
// 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 StepInfo extends Base {
get title() {
return t("Info")
}
get name() {
return t("Info")
}
get isEdit() {
return !!this.props.extra;
}
get isStep() {
return true;
}
onCOEChange = (value) => {
this.updateContext({
coeSelectRows: value,
});
}
get defaultValue() {
const values = {};
if (this.isEdit) {
values.clusterTemplateName = this.props.extra.name;
values.coe = this.props.extra.coe;
values.cluster_template_public = this.props.extra.public;
values.cluster_template_hidden = this.props.extra.hidden;
values.docker_registry_enabled = this.props.extra.registry_enabled;
values.tls_disabled = this.props.extra.tls_disabled;
}
return values;
}
get formItems() {
return [
{
name: "clusterTemplateName",
label: t("Cluster Template Name"),
type: "input",
placeholder: t("Cluster Template Name"),
required: true
},
{
name: "coe",
label: t("Container Orchestration Engine"),
type: "select",
options: [
{
label: t("Kubernetes"),
value: "kubernetes"
},
{
label: t("Docker Swarm"),
value: "swarm"
},
{
label: t("Docker Swarm Mode"),
value: "swarm-mode"
},
{
label: t("Mesos"),
value: "mesos"
},
{
label: t("DC/OS"),
value: "dcos"
},
],
onChange: this.onCOEChange,
allowClear: true,
showSearch: true
},
{
name: "cluster_template_public",
label: t("Public"),
type: "check"
},
{
name: "cluster_template_hidden",
label: t("Hidden"),
type: "check"
},
{
name: "docker_registry_enabled",
label: t("Enable Registry"),
type: "check"
},
{
name: "tls_disabled",
label: t("Disable TLS"),
type: "check"
}
]
}
}
export default inject("rootStore")(observer(StepInfo))

View File

@ -0,0 +1,39 @@
// 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
//
// Unles //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 StepLabel extends Base {
get title() {
return t('Labels');
}
get name() {
return t('Labels');
}
get formItems() {
return [
{
name: 'additionalLabels',
label: t('Additional Labels'),
type: 'add-select',
itemComponent: KeyValueInput,
addText: t('Add Label'),
},
]
}
}
export default inject("rootStore")(observer(StepLabel))

View File

@ -0,0 +1,207 @@
// 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';
import globalSubnetStore from 'src/stores/neutron/subnet';
export class StepNetwork extends Base {
init() {
this.getFloatingIps();
this.getSubnets();
this.state = { selectedSubnetId: '' };
}
get title() {
return t('Network');
}
get name() {
return t('Network');
}
get isStep() {
return true;
}
get isEdit() {
return !!this.props.extra;
}
async getFloatingIps() {
globalNetworkStore.fetchList();
}
get getFloatingIpList() {
return (globalNetworkStore.list.data || [])
.filter((it) => it['router:external'] === true && it.project_id === this.currentProjectId)
.map((it) => ({
value: it.id,
label: it.name,
}));
}
get getPrivateFloatingIpList() {
return (globalNetworkStore.list.data || [])
.filter((it) => it['router:external'] === false && it.project_id === this.currentProjectId)
.map((it) => ({
value: it.id,
label: it.name,
subnetId: it.subnets,
}));
}
async getSubnets() {
globalSubnetStore.fetchList();
}
get getSubnetList() {
return (globalSubnetStore.list.data || [])
.filter((it) => this.state.selectedSubnetId === it.network_id)
.map((it) => ({
value: it.id,
label: it.name,
}));
}
onSelectChangeFixedNetwork(value) {
this.setState({
selectedSubnetId: value,
});
this.resetFormValue(['fixedSubnet']);
}
get getNetworkDriver() {
const { context = {} } = this.props;
const {
coeSelectRows = "",
coe = ""
} = context;
let networkDriver = [];
if (!coeSelectRows || !coe) {
networkDriver.push({ val: "docker", name: "Docker" }, { val: "flannel", name: "Flannel" }, { val: "calico", name: "Calico" })
}
if (coeSelectRows === "swarm" || coeSelectRows === "swarm-mode") {
networkDriver.push({ val: "docker", name: "Docker" }, { val: "flannel", name: "Flannel" })
}
if (coeSelectRows === "kubernetes") {
networkDriver.push({ val: "calico", name: "Calico" }, { val: "flannel", name: "Flannel" })
}
if (coeSelectRows === "mesos" || coeSelectRows === "dcos") {
networkDriver.push({ val: "docker", name: "Docker" })
}
return (networkDriver || [])
.map((it) => ({
value: it.val,
label: it.name,
}));
}
get defaultValue() {
const values = {};
if (this.isEdit) {
values.networkDriver = this.props.extra.network_driver;
values.HTTPProxy = this.props.extra.http_proxy;
values.HTTPSProxy = this.props.extra.https_proxy;
values.noProxy = this.props.extra.no_proxy;
values.externalNetworkID = this.props.extra.external_network_id;
values.fixedNetwork = this.props.extra.fixed_network;
values.fixedSubnet = this.props.extra.fixed_subnet;
values.DNS = this.props.extra.dns_nameserver;
values.masterLB = this.props.extra.master_lb_enabled;
values.floatingIP = this.props.extra.floating_ip_enabled;
}
return values;
}
get formItems() {
return [
{
name: "networkDriver",
label: t("Network Driver"),
placeholder: t("Choose a Network Driver"),
type: "select",
options: this.getNetworkDriver,
allowClear: true,
showSearch: true
},
{
name: "HTTPProxy",
label: t("HTTP Proxy"),
placeholder: t("The http_proxy address to use for nodes in cluster"),
type: "input"
},
{
name: "HTTPSProxy",
label: t("HTTPS Proxy"),
placeholder: t("The https_proxy address to use for nodes in cluster"),
type: "input"
},
{
name: "noProxy",
label: t("No Proxy"),
placeholder: t("The no_proxy address to use for nodes in cluster"),
type: "input"
},
{
name: "externalNetworkID",
label: t("External Network ID"),
placeholder: t("Choose a External Network ID"),
type: "select",
options: this.getFloatingIpList,
allowClear: true,
showSearch: true
},
{
name: "fixedNetwork",
label: t("Fixed Network"),
placeholder: t("Choose a Private Network ID"),
type: "select",
options: this.getPrivateFloatingIpList,
onChange: (val) => this.onSelectChangeFixedNetwork(val),
allowClear: true,
showSearch: true
},
{
name: "fixedSubnet",
label: t("Fixed Subnet"),
placeholder: t("Choose a Private Network at first"),
type: "select",
options: this.getSubnetList,
allowClear: true,
showSearch: true
},
{
name: "DNS",
label: t("DNS"),
placeholder: t("The DNS nameserver to use for this cluster template"),
type: "input"
},
{
name: "masterLB",
label: t("Master LB"),
type: "check"
},
{
name: "floatingIP",
label: t("Floating IP"),
type: "check"
}
]
}
}
export default inject("rootStore")(observer(StepNetwork))

View File

@ -0,0 +1,198 @@
// 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 globalImageStore from "src/stores/glance/image";
import globalKeypairStore from "src/stores/nova/keypair";
import Base from "components/Form";
import { inject, observer } from "mobx-react";
import React from 'react';
import FlavorSelectTable from "src/pages/compute/containers/Instance/components/FlavorSelectTable";
export class StepNodeSpec extends Base {
init() {
this.getImageOsDistro();
this.getKeypairs();
}
get title() {
return t("Node Spec");
}
get name() {
return t("Node Spec");
}
async getImageOsDistro() {
globalImageStore.fetchList();
}
get isStep() {
return true;
}
get isEdit() {
return !!this.props.extra;
}
get getImageOsDistroList() {
return (globalImageStore.list.data || [])
.filter((it) => it.name.indexOf('coreos') >= 0)
.filter((it) => it.owner === this.currentProjectId)
.map((it) => ({
value: it.id,
label: it.name,
}));
}
async getKeypairs() {
globalKeypairStore.fetchList();
}
get getKeypairList() {
return (globalKeypairStore.list.data || []).map((it) => ({
value: it.name,
label: it.name,
}));
}
get getVolumeDriver() {
const { context = {} } = this.props;
const {
coeSelectRows = "",
coe = ""
} = context;
let volumeDriver = [];
if (!coeSelectRows || !coe) {
volumeDriver.push({ val: "cinder", name: "Cinder" }, { val: "rexray", name: "Rexray" })
}
if (coeSelectRows === "kubernetes") {
volumeDriver.push({ val: "cinder", name: "Cinder" })
}
else if (coeSelectRows) {
volumeDriver.push({ val: "rexray", name: "Rexray" })
}
return (volumeDriver || [])
.map((it) => ({
value: it.val,
label: it.name,
}));
}
onFlavorChange = (value) => {
this.updateContext({
flavor: value,
});
};
get defaultValue() {
const values = {};
if (this.isEdit) {
values.image = this.props.extra.image_id;
values.keypair = this.props.extra.keypair_id;
values.flavorCurrent = this.props.extra.flavor_id;
values.flavor = this.props.extra.flavor_id;
values.masterFlavor = this.props.extra.master_flavor_id;
values.masterFlavorCurrent = this.props.extra.master_flavor_id;
values.volumeDriver = this.props.extra.volume_driver;
values.dockerStorageDriver = this.props.extra.docker_storage_driver;
values.dockerVolumeSize = this.props.extra.docker_volume_size;
}
return values;
}
get formItems() {
return [
{
name: "image",
label: t("Image"),
type: "select",
options: this.getImageOsDistroList,
allowClear: true,
showSearch: true
},
{
name: "keypair",
label: t("Keypair"),
type: "select",
options: this.getKeypairList,
allowClear: true,
showSearch: true
},
{
name: 'flavorCurrent',
label: t('Current Flavor'),
type: 'label',
iconType: 'flavor',
},
{
name: 'flavor',
label: t('Flavor'),
type: 'select-table',
component: (
<FlavorSelectTable onChange={this.onFlavorChange} />
),
},
{
name: 'masterFlavorCurrent',
label: t('Current Master Flavor'),
type: 'label',
iconType: 'flavor',
},
{
name: "masterFlavor",
label: t("Master Flavor"),
type: "select-table",
component: (
<FlavorSelectTable onChange={this.onFlavorChange} />
),
},
{
name: "volumeDriver",
label: t("Volume Driver"),
type: "select",
options: this.getVolumeDriver,
allowClear: true,
showSearch: true
},
{
name: "dockerStorageDriver",
label: t("Docker Storage Driver"),
type: "select",
options: [
{
label: t("Overlay"),
value: "overlay"
},
{
label: t("Overlay2"),
value: "overlay2"
}
],
allowClear: true,
showSearch: true
},
{
name: "dockerVolumeSize",
label: t("Docker Volume Size (GB)"),
type: "input-number",
min: "1",
placeholder: "Spec"
}
]
}
}
export default inject("rootStore")(observer(StepNodeSpec))

View File

@ -0,0 +1,147 @@
// 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 StepInfo from "./StepInfo";
import StepNodeSpec from "./StepNodeSpec";
import StepNetwork from "./StepNetwork";
import StepLabel from "./StepLabel";
import { inject, observer } from "mobx-react";
import { StepAction } from "src/containers/Action";
import globalClusterTemplateStore from "src/stores/magnum/clusterTemplates";
import { toJS } from "mobx";
export class StepCreate extends StepAction {
init() {
this.store = globalClusterTemplateStore;
this.getDetail();
}
static id = "create-cluster-template";
static title = t("Create Cluster Template");
static path = "/container-infra/cluster-template/create";
static policy = "container-infra:clustertemplate:create";
static allowed() {
return Promise.resolve(true);
}
get name() {
return t("Create Cluster Template");
}
get listUrl() {
return this.getRoutePath("clusterTemplate");
}
get isEdit() {
const { pathname } = this.props.location;
return pathname.indexOf('update') >= 0;
}
get hasExtraProps() {
return this.isEdit;
}
get hasConfirmStep() {
return false;
}
get params() {
const { id } = this.props.match.params;
return { id };
}
async getDetail() {
if (this.isEdit) {
const result = await globalClusterTemplateStore.fetchDetail(this.params);
this.setState({
extra: toJS(result),
});
}
}
get steps() {
return [
{
title: t("Info *"),
component: StepInfo
},
{
title: t("Node Spec *"),
component: StepNodeSpec
},
{
title: t("Network"),
component: StepNetwork
},
{
title: t("Labels"),
component: StepLabel
}
]
}
onSubmit = (values) => {
const requestLabels = {};
const { additionalLabels } = values;
if (additionalLabels) {
additionalLabels.forEach(item => {
const labelKey = item.value.key.toLowerCase().trim();
const labelValue = item.value.value.toLowerCase().trim();
requestLabels[labelKey] = labelValue;
})
}
const body = {
labels: {
...requestLabels,
admission_control_list: 'NodeRestriction,NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,TaintNodesByCondition,Priority,DefaultTolerationSeconds,DefaultStorageClass,StorageObjectInUseProtection,PersistentVolumeClaimResize,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,RuntimeClass'
},
fixed_subnet: values.fixedSubnet,
master_flavor_id: values.masterFlavor,
http_proxy: values.HTTPProxy != null ? values.HTTPProxy : null,
https_proxy: values.HTTPSProxy != null ? values.HTTPSProxy : null,
no_proxy: values.noProxy != null ? values.noProxy : null,
keypair_id: values.keypair,
docker_volume_size: values.dockerVolumeSize,
external_network_id: values.externalNetworkID,
image_id: values.image,
volume_driver: values.volumeDriver,
public: values.cluster_template_public,
hidden: values.cluster_template_hidden,
tls_disabled: values.tls_disabled,
registry_enabled: values.docker_registry_enabled,
master_lb_enabled: values.masterLB,
floating_ip_enabled: values.floatingIP,
docker_storage_driver: values.dockerStorageDriver,
name: values.clusterTemplateName,
network_driver: values.networkDriver,
fixed_network: values.fixedNetwork,
coe: values.coe,
flavor_id: values.flavor,
dns_nameserver: values.DNS,
}
if (this.isEdit) {
return this.store.update({ id: this.params.id }, body);
} else {
return this.store.create(body);
}
}
}
export default inject("rootStore")(observer(StepCreate))

View File

@ -0,0 +1,27 @@
// 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 DeleteClusterTemplates from './Delete';
import EditClusterTemplateStep from './EditStep';
const actionConfigs = {
rowActions: {
firstAction: DeleteClusterTemplates,
moreActions: [
{ action: EditClusterTemplateStep },
],
},
batchActions: [DeleteClusterTemplates],
primaryActions: [StepCreate],
};
export default actionConfigs;

View File

@ -0,0 +1,65 @@
// 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 globalClusterTemplateStore from 'src/stores/magnum/clusterTemplates';
import actionConfigs from './actions';
export class ClusterTemplates extends Base {
init() {
this.store = globalClusterTemplateStore;
this.downloadStore = globalClusterTemplateStore;
}
get name() {
return t('clustertemplates');
}
get policy() {
return 'container-infra:clustertemplate:get_all';
}
get actionConfigs() {
return actionConfigs;
}
getColumns = () => [
{
title: t('ID'),
dataIndex: 'uuid',
render: (data) => {
return this.getLinkRender("containerInfraClusterTemplateDetail", data, { id: data })
},
},
{
title: t('COE'),
isHideable: true,
dataIndex: 'coe',
},
{
title: t('Network Driver'),
isHideable: true,
dataIndex: 'network_driver',
},
{
title: t('Keypair'),
isHideable: true,
dataIndex: 'keypair_id',
render: (value) => {
return this.getLinkRender("keypairDetail", value, { id: value })
}
},
];
}
export default inject("rootStore")(observer(ClusterTemplates))

View File

@ -0,0 +1,181 @@
// 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/BaseDetail';
import { inject, observer } from 'mobx-react';
import React from 'react';
export class BaseDetail extends Base {
get leftCards() {
return [this.baseInfoCard, this.miscellaneousCard];
}
get rightCards() {
return [this.nodesCard, this.labelCard, this.stackCard];
}
get baseInfoCard() {
const options = [
{
label: t('Name'),
dataIndex: "template.name",
render: (value) => {
return this.getLinkRender('containerInfraClusterTemplateDetail', value, { id: this.detailData.template.uuid })
},
},
{
label: t("ID"),
dataIndex: "template.uuid"
},
{
label: t("COE"),
dataIndex: "template.coe",
},
{
label: t("Image ID"),
dataIndex: "template.image_id",
}
];
return {
title: t('Cluster Template'),
options,
};
}
get miscellaneousCard() {
const options = [
{
label: t('Discovery URL'),
dataIndex: 'discovery_url',
},
{
label: t('Cluster Create Timeout'),
dataIndex: 'create_timeout',
},
{
label: t('Keypair'),
dataIndex: 'keypair',
},
{
label: t('Docker Volume Size'),
dataIndex: 'docker_volume_size',
},
{
label: t('Master Flavor ID'),
dataIndex: 'master_flavor_id',
},
{
label: t('Node Flavor ID'),
dataIndex: 'flavor_id',
},
{
label: t('COE Version'),
dataIndex: 'coe_version',
},
{
label: t('Container Version'),
dataIndex: 'container_version',
},
];
return {
title: t('Miscellaneous'),
options,
};
}
get nodesCard() {
const options = [
{
label: t('Master Count'),
dataIndex: 'master_count',
},
{
label: t('Node Count'),
dataIndex: 'node_count',
},
{
label: t('API Address'),
dataIndex: 'api_address',
},
{
label: t('Master Addresses'),
dataIndex: 'master_addresses',
},
{
label: t('Node Addresses'),
dataIndex: 'node_addresses',
},
];
return {
title: t('Nodes'),
labelCol: 3,
options,
};
}
get labelCard() {
const options = [
{
label: t('Labels'),
dataIndex: 'labels',
render: (value) =>
Object.entries(value).map(([key, val]) => {
return (
<React.Fragment>
<ul>
<li>
{key} : {val}
</li>
</ul>
</React.Fragment>
);
}),
},
];
return {
title: t('Labels'),
labelCol: 2,
options,
};
}
get stackCard() {
const options = [
{
label: t('Stack ID'),
dataIndex: 'stack_id',
},
{
label: t('Stack Faults'),
dataIndex: 'faults',
render: (value) => value ? Object.entries(value).map(([key, val]) => {
return <React.Fragment><ul><li>{key} : {val}</li></ul></React.Fragment>
}) : " - "
},
];
return {
title: t('Stack'),
labelCol: 2,
options,
};
}
}
export default inject("rootStore")(observer(BaseDetail))

View File

@ -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))

View File

@ -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 });
}

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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 <FlavorSelectTable onChange={this.onFlavorChange} />;
}
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));

View File

@ -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))

View File

@ -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;

View File

@ -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))

View File

@ -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 },
],
},
];

View File

@ -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;

View File

@ -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;