From c960f06c1a63a492372579bf9eee2a443d5c47df Mon Sep 17 00:00:00 2001 From: "Jingwei.Zhang" Date: Tue, 24 Aug 2021 14:26:19 +0800 Subject: [PATCH] feat: Support client module 1. Add client module to request openstack api 2. Remove window.request, stores use client to request api 3. Remove window.globals, use globalRootStore to deal with user info Change-Id: I5657cfd8cf142dbacce8716991f805bbbb4a9222 --- config/webpack.common.js | 1 + package.json | 3 +- src/api/cinder/base.js | 2 +- src/api/glance/base.js | 2 +- src/api/gocron/base.js | 2 +- src/api/heat/base.js | 2 +- src/api/ironic-inspector/base.js | 2 +- src/api/ironic/base.js | 2 +- src/api/keystone/base.js | 2 +- src/api/neutron/base.js | 2 +- src/api/nova/base.js | 2 +- src/api/octavia/base.js | 2 +- src/api/panko/base.js | 2 +- src/api/placement/base.js | 2 +- src/api/skyline/base.js | 2 +- src/api/swift/base.js | 2 +- src/client/cinder/index.js | 155 +++++++ src/client/client/base.js | 420 ++++++++++++++++++ src/client/client/constants.js | 125 ++++++ src/client/client/request.js | 181 ++++++++ src/client/glance/index.js | 87 ++++ src/client/heat/index.js | 93 ++++ src/client/index.js | 41 ++ src/client/ironic/index.js | 96 ++++ src/client/keystone/index.js | 149 +++++++ src/client/neutron/index.js | 201 +++++++++ src/client/nova/index.js | 134 ++++++ src/client/octavia/index.js | 62 +++ src/client/placement/index.js | 42 ++ src/client/skyline/index.js | 123 +++++ src/components/Form/index.jsx | 11 +- .../FormItem/NetworkSelectTable/index.jsx | 2 +- .../FormItem/VolumeSelectTable/index.jsx | 2 +- .../Layout/GlobalHeader/AvatarDropdown.jsx | 2 +- .../Layout/GlobalHeader/RightContent.jsx | 4 +- src/components/StepForm/index.jsx | 16 +- .../Tables/Base/ActionButton/index.jsx | 9 +- .../Tables/Base/BatchActionButtons/index.jsx | 9 +- .../Base/PrimaryActionButtons/index.jsx | 4 +- src/components/Tables/SimpleTable/index.jsx | 8 +- src/containers/BaseDetail/index.jsx | 11 +- src/containers/List/index.jsx | 13 +- src/containers/TabDetail/index.jsx | 12 +- src/core/index.jsx | 21 - src/layouts/Base/index.jsx | 4 + src/layouts/admin-menu.jsx | 2 +- src/locales/en.json | 4 +- src/locales/zh.json | 4 +- src/pages/auth/containers/Login/index.jsx | 16 +- .../Overview/components/ResourceStatistic.jsx | 3 +- .../containers/BareMetalNode/index.jsx | 2 +- .../Hypervisors/Hypervisor/Detail/index.jsx | 11 - .../actions/CreateIronic/SystemStep/index.jsx | 4 - .../Instance/actions/CreateIronic/index.jsx | 5 +- .../actions/StepCreate/SystemStep/index.jsx | 4 - .../Instance/actions/StepCreate/index.jsx | 3 +- .../Detail/PortForwarding/index.jsx | 17 +- .../network/containers/FloatingIp/index.jsx | 6 +- .../LoadBalancerInstance/index.jsx | 2 +- .../containers/QoSPolicy/AdminQoSPolicy.js | 4 - .../containers/QoSPolicy/ProjectQoSPolicy.js | 4 - .../containers/QoSPolicy/SharedQoSPolicy.js | 4 - .../Router/actions/AssociateFip.jsx | 2 +- src/pages/network/containers/VPN/index.jsx | 2 +- src/resources/image.jsx | 14 +- src/resources/instance.jsx | 49 -- src/resources/network.js | 2 + src/resources/policy.js | 13 +- src/stores/base.js | 253 +++++------ src/stores/cinder/backup.js | 23 +- src/stores/cinder/extra-spec.js | 58 +-- src/stores/cinder/pool.js | 24 +- src/stores/cinder/qos-spec-key.js | 58 +-- src/stores/cinder/qos-spec.js | 33 +- src/stores/cinder/service.js | 27 +- src/stores/cinder/snapshot.js | 48 +- src/stores/cinder/volume-type.js | 104 +++-- src/stores/cinder/volume.js | 139 +++--- src/stores/glance/image.js | 70 +-- src/stores/glance/instance-snapshot.js | 50 +-- src/stores/glance/metadata.js | 40 +- src/stores/heat/event.js | 19 +- src/stores/heat/resource.js | 19 +- src/stores/heat/service.js | 17 +- src/stores/heat/stack.js | 37 +- src/stores/index.jsx | 43 ++ src/stores/ironic/ironic.js | 62 ++- src/stores/ironic/port-group.js | 22 +- src/stores/ironic/port.js | 22 +- src/stores/keystone/catalog.js | 12 +- src/stores/keystone/domain.js | 117 +++-- src/stores/keystone/project.js | 175 ++++---- src/stores/keystone/role.js | 18 +- src/stores/keystone/tag.js | 28 +- src/stores/keystone/user-group.js | 231 +++++----- src/stores/keystone/user.js | 337 ++++++++------ src/stores/neutron/agent-network.js | 39 +- src/stores/neutron/agent-router.js | 36 +- src/stores/neutron/agent.js | 14 +- src/stores/neutron/fixed-ip.js | 25 +- src/stores/neutron/floatingIp.js | 40 +- src/stores/neutron/network.js | 220 +++------ src/stores/neutron/neutron.js | 12 +- src/stores/neutron/port-forwarding.js | 148 ++---- src/stores/neutron/port.js | 53 ++- src/stores/neutron/qos-policy.js | 80 ++-- src/stores/neutron/router.js | 97 +--- src/stores/neutron/security-group.js | 18 +- src/stores/neutron/security-rule.js | 14 +- src/stores/neutron/static-route.js | 22 +- src/stores/neutron/subnet.js | 18 +- src/stores/neutron/virtual-adapter.js | 127 ++---- src/stores/neutron/vpn-endpoint-group.js | 28 +- src/stores/neutron/vpn-ike-policy.js | 28 +- src/stores/neutron/vpn-ipsec-connection.js | 31 +- src/stores/neutron/vpn-ipsec-policy.js | 28 +- src/stores/neutron/vpn-service.js | 28 +- src/stores/nova/aggregate.js | 20 +- src/stores/nova/compute-host.js | 18 +- src/stores/nova/flavor.js | 50 +-- src/stores/nova/hypervisor.js | 44 +- src/stores/nova/instance-volume.js | 49 +- src/stores/nova/instance.js | 109 ++--- src/stores/nova/keypair.js | 14 +- src/stores/nova/server-group.js | 49 +- src/stores/nova/zone.js | 28 +- src/stores/octavia/health-monitor.js | 18 +- src/stores/octavia/listener.js | 22 +- src/stores/octavia/loadbalancer.js | 57 +-- src/stores/octavia/pool-member.js | 44 +- src/stores/octavia/pool.js | 18 +- src/stores/overview-admin.js | 124 +++--- src/stores/project.js | 15 +- src/stores/root.js | 148 ++---- src/stores/skyline/recycle-server.js | 13 +- src/stores/skyline/server-group-instance.js | 28 +- src/stores/skyline/setting.js | 18 +- src/stores/skyline/skyline.js | 16 +- src/utils/constants.js | 107 ----- src/utils/request.js | 366 --------------- 140 files changed, 3632 insertions(+), 3181 deletions(-) create mode 100644 src/client/cinder/index.js create mode 100644 src/client/client/base.js create mode 100644 src/client/client/constants.js create mode 100644 src/client/client/request.js create mode 100644 src/client/glance/index.js create mode 100644 src/client/heat/index.js create mode 100644 src/client/index.js create mode 100644 src/client/ironic/index.js create mode 100644 src/client/keystone/index.js create mode 100644 src/client/neutron/index.js create mode 100644 src/client/nova/index.js create mode 100644 src/client/octavia/index.js create mode 100644 src/client/placement/index.js create mode 100644 src/client/skyline/index.js create mode 100644 src/stores/index.jsx delete mode 100644 src/utils/request.js diff --git a/config/webpack.common.js b/config/webpack.common.js index ce1e68ab..82b26ccc 100644 --- a/config/webpack.common.js +++ b/config/webpack.common.js @@ -127,6 +127,7 @@ module.exports = { locales: root('src/locales'), styles: root('src/styles'), resources: root('src/resources'), + client: root('src/client'), }, }, plugins: [ diff --git a/package.json b/package.json index 7dda56c7..97cd403c 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,8 @@ "react-highcharts": "^16.0.2", "react-router": "^4.3.1", "react-router-dom": "^4.3.1", - "react-sortable-hoc": "1.11.0" + "react-sortable-hoc": "1.11.0", + "uuid": "^8.3.2" }, "devDependencies": { "@babel/core": "^7.14.3", diff --git a/src/api/cinder/base.js b/src/api/cinder/base.js index a232d23b..441ba487 100644 --- a/src/api/cinder/base.js +++ b/src/api/cinder/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { cinderBase } from 'utils/constants'; +import { cinderBase } from 'client/client/constants'; const getCinderBaseUrl = (key) => `${cinderBase()}/${key}`; diff --git a/src/api/glance/base.js b/src/api/glance/base.js index 485ed93a..06de3c80 100644 --- a/src/api/glance/base.js +++ b/src/api/glance/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { glanceBase } from 'utils/constants'; +import { glanceBase } from 'client/client/constants'; const getGlanceBaseUrl = (key) => `${glanceBase()}/${key}`; diff --git a/src/api/gocron/base.js b/src/api/gocron/base.js index 47e29752..fbee73db 100644 --- a/src/api/gocron/base.js +++ b/src/api/gocron/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { gocronBase } from 'utils/constants'; +import { gocronBase } from 'client/client/constants'; const getGocronBaseUrl = (key) => `${gocronBase()}/${key}`; diff --git a/src/api/heat/base.js b/src/api/heat/base.js index aee33519..a6fcd3c4 100644 --- a/src/api/heat/base.js +++ b/src/api/heat/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { heatBase } from 'utils/constants'; +import { heatBase } from 'client/client/constants'; const getHeatBaseUrl = (key) => `${heatBase()}/${key}`; diff --git a/src/api/ironic-inspector/base.js b/src/api/ironic-inspector/base.js index 61d557ba..296d6328 100644 --- a/src/api/ironic-inspector/base.js +++ b/src/api/ironic-inspector/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { ironicInspectorBase } from 'utils/constants'; +import { ironicInspectorBase } from 'client/client/constants'; const getIronicInspectorBaseUrl = (key) => `${ironicInspectorBase()}/${key}`; diff --git a/src/api/ironic/base.js b/src/api/ironic/base.js index bc909b08..efe92619 100644 --- a/src/api/ironic/base.js +++ b/src/api/ironic/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { ironicBase } from 'utils/constants'; +import { ironicBase } from 'client/client/constants'; const getIronicBaseUrl = (key) => `${ironicBase()}/${key}`; diff --git a/src/api/keystone/base.js b/src/api/keystone/base.js index cb7cad0e..df7a5a01 100644 --- a/src/api/keystone/base.js +++ b/src/api/keystone/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { keystoneBase } from 'utils/constants'; +import { keystoneBase } from 'client/client/constants'; const getKeystoneBaseUrl = (key) => `${keystoneBase()}/${key}`; diff --git a/src/api/neutron/base.js b/src/api/neutron/base.js index d30feb56..dacb44c7 100644 --- a/src/api/neutron/base.js +++ b/src/api/neutron/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { neutronBase } from 'utils/constants'; +import { neutronBase } from 'client/client/constants'; const getNeutronBaseUrl = (key) => `${neutronBase()}/${key}`; diff --git a/src/api/nova/base.js b/src/api/nova/base.js index 0d4a3366..0f29be21 100644 --- a/src/api/nova/base.js +++ b/src/api/nova/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { novaBase } from 'utils/constants'; +import { novaBase } from 'client/client/constants'; const getNovaBaseUrl = (key) => `${novaBase()}/${key}`; diff --git a/src/api/octavia/base.js b/src/api/octavia/base.js index 85f0e052..942537f0 100644 --- a/src/api/octavia/base.js +++ b/src/api/octavia/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { octaviaBase } from 'utils/constants'; +import { octaviaBase } from 'client/client/constants'; const getOctaviaBaseUrl = (key) => `${octaviaBase()}/${key}`; diff --git a/src/api/panko/base.js b/src/api/panko/base.js index e3b159d3..3e2ab460 100644 --- a/src/api/panko/base.js +++ b/src/api/panko/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { pankoBase } from 'utils/constants'; +import { pankoBase } from 'client/client/constants'; const getPankoBaseUrl = (key) => `${pankoBase()}/${key}`; diff --git a/src/api/placement/base.js b/src/api/placement/base.js index 30017b79..1d5d9f55 100644 --- a/src/api/placement/base.js +++ b/src/api/placement/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { placementBase } from 'utils/constants'; +import { placementBase } from 'client/client/constants'; const getPlacementBaseUrl = (key) => `${placementBase()}/${key}`; diff --git a/src/api/skyline/base.js b/src/api/skyline/base.js index 19765c92..3c52be07 100644 --- a/src/api/skyline/base.js +++ b/src/api/skyline/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { skylineBase } from 'utils/constants'; +import { skylineBase } from 'client/client/constants'; const getSkylineBaseUrl = (key) => `${skylineBase()}/${key}`; diff --git a/src/api/swift/base.js b/src/api/swift/base.js index 1b78587a..b7a0ee4e 100644 --- a/src/api/swift/base.js +++ b/src/api/swift/base.js @@ -16,7 +16,7 @@ * @param {String} key api url * @returns {String} */ -import { swiftBase } from 'utils/constants'; +import { swiftBase } from 'client/client/constants'; const getSwiftBaseUrl = (key) => `${swiftBase()}/${key}`; diff --git a/src/client/cinder/index.js b/src/client/cinder/index.js new file mode 100644 index 00000000..578b81cf --- /dev/null +++ b/src/client/cinder/index.js @@ -0,0 +1,155 @@ +// 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 '../client/base'; +import { cinderBase } from '../client/constants'; + +class CinderClient extends Base { + get baseUrl() { + return cinderBase(); + } + + get projectInUrl() { + return true; + } + + get resources() { + return [ + { + key: 'volumes', + responseKey: 'volume', + extendOperations: [ + { + key: 'action', + method: 'post', + }, + ], + }, + { + key: 'types', + responseKey: 'volume_type', + extendOperations: [ + { + key: 'action', + method: 'post', + }, + { + name: 'getAccess', + key: 'os-volume-type-access', + }, + ], + subResources: [ + { + name: 'extraSpecs', + key: 'extra_specs', + responseKey: 'extra_spec', + }, + { + key: 'encryption', + }, + ], + }, + { + key: 'snapshots', + responseKey: 'snapshot', + }, + { + key: 'backups', + responseKey: 'backup', + extendOperations: [ + { + key: 'restore', + isDetail: true, + method: 'post', + }, + ], + }, + { + name: 'backupChains', + key: 'backup_chains', + responseKey: 'backup_chain', + extendOperations: [ + { + key: 'restore', + isDetail: true, + method: 'post', + }, + ], + }, + { + name: 'pools', + key: 'scheduler-stats/get_pools', + responseKey: 'pool', + }, + { + name: 'qosSpecs', + key: 'qos-specs', + responseKey: 'qos_spec', + extendOperations: [ + { + name: 'deleteKeys', + key: 'delete_keys', + method: 'put', + }, + { + key: 'associate', + }, + { + key: 'disassociate', + }, + ], + }, + { + name: 'services', + key: 'os-services', + responseKey: 'service', + extendOperations: [ + { + key: 'enable', + isDetail: false, + method: 'put', + }, + { + name: 'reason', + key: 'disable-log-reason', + isDetail: false, + method: 'put', + }, + ], + }, + { + name: 'quotaSets', + key: 'os-quota-sets', + responseKey: 'quota_set', + }, + { + name: 'azones', + key: 'os-availability-zone', + }, + { + name: 'volumeTransfers', + key: 'volume-transfers', + extendOperations: [ + { + key: 'accept', + method: 'post', + }, + ], + }, + ]; + } +} + +const cinderClient = new CinderClient(); +export default cinderClient; diff --git a/src/client/client/base.js b/src/client/client/base.js new file mode 100644 index 00000000..b6e0e3f6 --- /dev/null +++ b/src/client/client/base.js @@ -0,0 +1,420 @@ +// 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 globalRootStore from 'stores/root'; +import clientRequest from './request'; + +export default class BaseClient { + constructor() { + this.generateAll(); + } + + getUrl = (url) => { + if (this.projectInUrl) { + return `${this.baseUrl}/${this.project}/${url}`; + } + return `${this.baseUrl}/${url}`; + }; + + get request() { + const { request } = clientRequest; + return { + get: (url, params, conf) => request.get(this.getUrl(url), params, conf), + post: (url, data, params, conf) => + request.post(this.getUrl(url), data, params, conf), + put: (url, data, params, conf) => + request.put(this.getUrl(url), data, params, conf), + delete: (url, data, params, conf) => + request.delete(this.getUrl(url), data, params, conf), + patch: (url, data, params, conf) => + request.patch(this.getUrl(url), data, params, conf), + head: (url, params, conf) => request.head(this.getUrl(url), params, conf), + }; + } + + get originRequest() { + const { request } = clientRequest; + return request; + } + + get params() { + return []; + } + + get baseUrl() { + return ''; + } + + get projectInUrl() { + return false; + } + + get project() { + if (!this.projectInUrl) { + return ''; + } + const { project: { id } = {} } = globalRootStore.user || {}; + return id || ''; + } + + get enabled() { + return true; + } + + get resources() { + return []; + } + + getListUrl(resourceName) { + return resourceName; + } + + getDetailUrl(resourceName, id) { + if (!id) { + return resourceName; + } + if (resourceName[resourceName.length - 1] === '/') { + return `${resourceName.substr(0, resourceName.length - 1)}/${id}`; + } + return `${resourceName}/${id}`; + } + + getSubResourceUrl(resourceName, subResourceName) { + if (!resourceName) { + return subResourceName; + } + if (resourceName[resourceName.length - 1] === '/') { + return `${resourceName.substr( + 0, + resourceName.length - 1 + )}/${subResourceName}`; + } + return `${resourceName}/${subResourceName}`; + } + + getSubResourceUrlById(resourceName, subResourceName, id) { + return `${this.getDetailUrl(resourceName, id)}/${subResourceName}`; + } + + getSubResourceUrlBySubId(resourceName, subResourceName, id, subId) { + return `${this.getSubResourceUrlById( + resourceName, + subResourceName, + id + )}/${subId}`; + } + + getSubSubResourceListUrl( + resourceName, + subResourceName, + subSubResourceName, + id, + subId + ) { + return `${this.getSubResourceUrlBySubId( + resourceName, + subResourceName, + id, + subId + )}/${subSubResourceName}`; + } + + getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResourceName, + id, + subId, + subSubId + ) { + return `${this.getSubSubResourceListUrl( + resourceName, + subResourceName, + subSubResourceName, + id, + subId + )}/${subSubId}`; + } + + generateResource = (resourceName, responseKey, enabled = true) => { + const listUrl = this.getListUrl(resourceName); + return { + list: (params, conf) => this.request.get(listUrl, params, conf), + listDetail: (params, conf) => + this.request.get(`${listUrl}/detail`, params, conf), + show: (id, params, conf) => { + return this.request.get( + this.getDetailUrl(resourceName, id), + params, + conf + ); + }, + create: (data, ...args) => this.request.post(listUrl, data, ...args), + update: (id, data, ...args) => + this.request.put(this.getDetailUrl(resourceName, id), data, ...args), + patch: (id, data, ...args) => + this.request.patch(this.getDetailUrl(resourceName, id), data, ...args), + delete: (id, ...args) => + this.request.delete(this.getDetailUrl(resourceName, id), ...args), + responseKey, + enabled, + }; + }; + + generateSubResource = ( + resourceName, + subResourceName, + responseKey, + enabled + ) => ({ + list: (id, params, ...args) => + this.request.get( + this.getSubResourceUrlById(resourceName, subResourceName, id), + params, + ...args + ), + listDetail: (id, params, ...args) => + this.request.get( + `${this.getSubResourceUrlById( + resourceName, + subResourceName, + id + )}/detail`, + params, + ...args + ), + show: (id, subId, params, ...args) => + this.request.get( + this.getSubResourceUrlBySubId(resourceName, subResourceName, id, subId), + params, + ...args + ), + create: (id, data, ...args) => + this.request.post( + this.getSubResourceUrlById(resourceName, subResourceName, id), + data, + ...args + ), + update: (id, subId, data, ...args) => + this.request.put( + this.getSubResourceUrlBySubId(resourceName, subResourceName, id, subId), + data, + ...args + ), + patch: (id, subId, data, ...args) => + this.request.patch( + this.getSubResourceUrlBySubId(resourceName, subResourceName, id, subId), + data, + ...args + ), + delete: (id, subId, ...args) => + this.request.delete( + this.getSubResourceUrlBySubId(resourceName, subResourceName, id, subId), + ...args + ), + responseKey, + enabled, + }); + + generateSubSonResource = ( + resourceName, + subResourceName, + subSubResonseName, + responseKey + ) => ({ + list: (id, subId, params, ...args) => + this.request.get( + this.getSubSubResourceListUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId + ), + params, + ...args + ), + show: (id, subId, subSubId, params, ...args) => + this.request.get( + this.getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId, + subSubId + ), + params, + ...args + ), + create: (id, subId, data, ...args) => + this.request.post( + this.getSubSubResourceListUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId + ), + data, + ...args + ), + update: (id, subId, subSubId, data, ...args) => + this.request.put( + this.getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId, + subSubId + ), + data, + ...args + ), + patch: (id, subId, subSubId, data, ...args) => + this.request.patch( + this.getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId, + subSubId + ), + data, + ...args + ), + delete: (id, subId, subSubId, ...args) => + this.request.delete( + this.getSubSubResourceDetailUrl( + resourceName, + subResourceName, + subSubResonseName, + id, + subId, + subSubId + ), + ...args + ), + responseKey, + }); + + setRequest = (url, method, ...restArgs) => { + const lowerMethod = method.toLowerCase(); + return this.request[lowerMethod](url, ...restArgs); + }; + + generateAll = () => { + this.resources.forEach((resource) => { + const { + name, + key, + responseKey, + enabled, + subResources = [], + isResource = true, + extendOperations = [], + } = resource; + const result = isResource + ? this.generateResource(key, responseKey, enabled) + : {}; + const realName = name || key; + extendOperations.forEach((other) => { + const { + name: otherName, + key: otherKey, + method = 'get', + isDetail, + generate, + url, + } = other; + const otherRealName = otherName || otherKey; + const otherUrl = url && url(); + const otherIsDetail = isResource + ? isDetail === undefined + ? true + : isDetail + : isDetail === undefined + ? false + : isDetail; + if (generate) { + result[otherRealName] = generate; + } else if (otherIsDetail) { + result[otherRealName] = (id, ...args) => { + return this.setRequest( + otherUrl || this.getSubResourceUrlById(key, otherKey, id), + method, + ...args + ); + }; + } else { + result[otherRealName] = (...args) => { + return this.setRequest( + otherUrl || this.getSubResourceUrl(key, otherKey), + method, + ...args + ); + }; + } + }); + subResources.forEach((sub) => { + let subResult = {}; + const { + name: subName, + key: subKey, + responseKey: subResponseKey, + method: subMethod, + enabled: subEnabled, + subResources: subSubResources = [], + } = sub; + const subRealName = subName || subKey; + if (!subMethod) { + subResult = this.generateSubResource( + key, + subKey, + subResponseKey, + subEnabled + ); + } else { + subResult = (id, ...args) => { + const url = this.getSubResourceUrlById(key, subKey, id); + return this.setRequest(url, subMethod, ...args); + }; + } + subSubResources.forEach((son) => { + const { + key: sonKey, + name: sonName, + responseKey: sonResponseKey, + } = son; + subResult[sonName || sonKey] = this.generateSubSonResource( + key, + subKey, + sonKey, + sonResponseKey + ); + }); + result[subRealName] = subResult; + }); + if (realName) { + this[realName] = result; + } else { + Object.keys(result).forEach((resultKey) => { + this[resultKey] = result[resultKey]; + }); + } + }); + }; +} diff --git a/src/client/client/constants.js b/src/client/client/constants.js new file mode 100644 index 00000000..14392b27 --- /dev/null +++ b/src/client/client/constants.js @@ -0,0 +1,125 @@ +// 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 globalRootStore from 'stores/root'; +import { toJS } from 'mobx'; + +export const groupNameVersionMap = { + core: 'v1', + system: 'v1', +}; + +const endpointVersionMap = { + keystone: 'v3', + nova: 'v2.1', + cinder: 'v3', + glance: 'v2', + neutron: 'v2.0', + ironic: 'v1', + ironicInspector: 'v1', + heat: 'v1', + swift: 'v1', + octavia: 'v2', + courier: 'v1', + prometheus: 'api/v1', + prometheus_sidecar: 'api/v1', + gocron: 'api', + panko: 'v2', + billing_system: 'api/core.io/v1', + workflow: 'api/core.io/v1', +}; + +const endpointsDefault = { + ironic: '/api/openstack/ironic', + ironicInspector: '/api/openstack/ironic-inspector', + swift: '/api/openstack/swift/swift', + octavia: '/api/openstack/octavia', +}; + +export const getOpenstackEndpoint = (key) => { + const { endpoints = {} } = globalRootStore || {}; + const version = endpointVersionMap[key]; + const endpoint = endpoints[key] || endpointsDefault[key] || ''; + return version ? `${endpoint}/${version}` : endpoint; +}; + +export const getOriginEndpoint = (key) => { + const endpoints = toJS((globalRootStore && globalRootStore.endpoints) || {}); + return endpoints[key]; +}; + +export const skylineBase = () => '/api/openstack/skyline/api/v1'; +export const keystoneBase = () => getOpenstackEndpoint('keystone'); +export const novaBase = () => getOpenstackEndpoint('nova'); +export const cinderBase = () => getOpenstackEndpoint('cinder'); +export const glanceBase = () => getOpenstackEndpoint('glance'); +export const neutronBase = () => getOpenstackEndpoint('neutron'); +export const ironicBase = () => getOpenstackEndpoint('ironic'); +export const ironicInspectorBase = () => + getOpenstackEndpoint('ironicInspector'); +export const placementBase = () => getOpenstackEndpoint('placement'); +export const heatBase = () => getOpenstackEndpoint('heat'); +export const swiftBase = () => getOpenstackEndpoint('swift'); +export const octaviaBase = () => getOpenstackEndpoint('octavia'); +export const alertmanagerBase = () => getOpenstackEndpoint('alertmanager'); +export const prometheusBase = () => getOpenstackEndpoint('prometheus'); +export const prometheusSidecarBase = () => + getOpenstackEndpoint('prometheus_sidecar'); +export const courierBase = () => getOpenstackEndpoint('courier'); +export const gocronBase = () => getOpenstackEndpoint('gocron'); +export const pankoBase = () => getOpenstackEndpoint('panko'); +export const s3Base = () => getOpenstackEndpoint('s3'); +export const billingBase = () => getOpenstackEndpoint('billing_system'); +export const workflowBase = () => getOpenstackEndpoint('workflow'); + +export const ironicOriginEndpoint = () => getOriginEndpoint('ironic'); +export const s3OriginEndpoint = () => getOriginEndpoint('s3'); +export const billingEndpoint = () => getOriginEndpoint('billing_system'); +export const firewallEndpoint = () => getOriginEndpoint('neutron_firewall'); +export const vpnEndpoint = () => getOriginEndpoint('neutron_vpn'); +export const lbEndpoint = () => getOriginEndpoint('octavia'); + +export const apiVersionMaps = { + nova: { + key: 'Openstack-Api-Version', + value: 'compute 2.79', + }, + placement: { + key: 'Openstack-Api-Version', + value: 'placement 1.28', + }, + cinder: { + key: 'Openstack-Api-Version', + value: 'volume 3.59', + }, + ironic: { + key: 'X-Openstack-Ironic-Api-Version', + value: '1.58', + }, + 'ironic-inspect': { + key: 'X-OpenStack-Ironic-Inspector-API-Version', + value: '1.15', + }, +}; + +export const getOpenstackApiVersion = (url) => { + const key = Object.keys(apiVersionMaps).find((it) => url.indexOf(it) > -1); + if (!key) { + return null; + } + return apiVersionMaps[key]; +}; + +export const getK8sTypeEndpoint = (groupName, baseUrl) => + `${baseUrl}/${groupName}/${groupNameVersionMap[groupName]}`; diff --git a/src/client/client/request.js b/src/client/client/request.js new file mode 100644 index 00000000..88943b75 --- /dev/null +++ b/src/client/client/request.js @@ -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 Axios from 'axios'; +import { getLocalStorageItem } from 'utils/local-storage'; +import { isEqual } from 'lodash'; +import qs from 'qs'; +import globalRootStore from 'stores/root'; +import { v4 as uuidv4 } from 'uuid'; +import { getOpenstackApiVersion } from './constants'; + +const METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD']; +/** + * @class HttpRequest + * request with axios + */ +export class HttpRequest { + constructor() { + this.request = {}; + } + + /** + * @param instance instance of axios + * @param url request url + * interceptors includes request & response + * @returns {void} + */ + interceptors(instance, url) { + instance.interceptors.request.use( + (config) => { + const uuid = uuidv4(); + config.headers['X-Openstack-Request-Id'] = `req-${uuid}`; + const keystoneToken = getLocalStorageItem('keystone_token') || ''; + const apiVersionMap = getOpenstackApiVersion(url); + if (keystoneToken) { + config.headers['X-Auth-Token'] = keystoneToken; + } + if (apiVersionMap) { + config.headers[apiVersionMap.key] = apiVersionMap.value; + } + const { options: { headers, isFormData, ...rest } = {} } = config; + if (!isEqual(headers)) { + config.headers = { + ...config.headers, + ...headers, + }; + } + if (isFormData) { + delete config.headers['Content-Type']; + } + Object.keys(rest).forEach((key) => { + config[key] = rest[key]; + }); + return config; + }, + (err) => Promise.reject(err) + ); + + instance.interceptors.response.use( + (response) => { + // request is finished + const { data, status } = response; + const disposition = response.headers['content-disposition'] || ''; + const contentType = response.headers['content-type'] || ''; + if (contentType.includes('application/octet-stream')) { + return response; + } + if (disposition.includes('attachment')) { + return response; + } + if (status < 200 || status >= 300) { + return Promise.reject(data); + } + return data; + }, + (error) => { + // request is finished + // eslint-disable-next-line no-console + console.log('error.response', error.response, error); + if (error.response) { + const { status } = error.response; + if (status === 401) { + const currentPath = window.location.pathname; + if (currentPath.indexOf('login') < 0) { + globalRootStore.gotoLoginPage(currentPath); + } + } + } + return Promise.reject(error); + } + ); + } + + /** + * create a new instance of axios with a custom config + */ + create() { + const conf = { + baseURL: '/', + headers: { + 'Content-Type': 'application/json;charset=utf-8', + 'cache-control': 'no-cache', + pragma: 'no-cache', + }, + }; + return Axios.create(conf); + } + + /** + * @param {Object} obj translated object + * @returns {Object} trim undefined & null + */ + omitNil(obj) { + if (typeof obj !== 'object') return obj; + return Object.keys(obj).reduce((acc, v) => { + if (obj[v] !== undefined && obj[v] !== null && obj[v] !== '') + acc[v] = obj[v]; + return acc; + }, {}); + } + + /** + * build request + * @param {Object} config requests config + * @returns {Promise} axios instance return promise + */ + buildRequest(config) { + const method = config.method ? config.method.toLowerCase() : 'get'; + const options = { ...config }; + // Only get and head, we need to use null for some posts requests + if (options.params && ['get', 'head'].includes(method)) { + options.params = this.omitNil(options.params); + options.paramsSerializer = (p) => + qs.stringify(p, { arrayFormat: 'repeat' }); + } + const instance = this.create(); + this.interceptors(instance, options.url); + return instance(options); + } + + generateRequestMap = () => { + METHODS.forEach((method) => { + const lowerMethod = method.toLowerCase(); + if (lowerMethod === 'get' || lowerMethod === 'head') { + this.request[lowerMethod] = (url, params = {}, options) => { + return this.buildRequest({ + method: lowerMethod, + url, + params, + options, + }); + }; + } else { + this.request[lowerMethod] = (url, data, params, options) => { + return this.buildRequest({ + method: lowerMethod, + url, + data, + params, + options, + }); + }; + } + }); + }; +} + +const httpRequest = new HttpRequest(); +httpRequest.generateRequestMap(); +export default httpRequest; diff --git a/src/client/glance/index.js b/src/client/glance/index.js new file mode 100644 index 00000000..8ac50522 --- /dev/null +++ b/src/client/glance/index.js @@ -0,0 +1,87 @@ +// 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 '../client/base'; +import { glanceBase } from '../client/constants'; + +class GlanceClient extends Base { + get baseUrl() { + return glanceBase(); + } + + get resources() { + return [ + { + key: 'images', + responseKey: 'image', + extendOperations: [ + { + key: 'count', + isDetail: false, + }, + { + key: 'uploadFile', + generate: (id, body, conf = {}) => { + return this.request.put( + `${this.getDetailUrl('images', id)}/file`, + body, + null, + { + headers: { + 'content-type': 'application/octet-stream', + }, + ...conf, + } + ); + }, + }, + { + key: 'patch', + generate: (id, data) => + this.request.patch(this.getDetailUrl('images', id), data, null, { + headers: { + 'content-type': + 'application/openstack-images-v2.1-json-patch', + }, + }), + }, + ], + subResources: [ + { + key: 'members', + }, + ], + }, + { + name: 'namespaces', + key: 'metadefs/namespaces', + responseKey: 'namespace', + subResources: [ + { + name: 'resourceTypes', + key: 'resource_types', + }, + ], + }, + { + name: 'resourceTypes', + key: 'metadefs/resource_types', + responseKey: 'resource_type', + }, + ]; + } +} + +const glanceClient = new GlanceClient(); +export default glanceClient; diff --git a/src/client/heat/index.js b/src/client/heat/index.js new file mode 100644 index 00000000..20c1470a --- /dev/null +++ b/src/client/heat/index.js @@ -0,0 +1,93 @@ +// 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 '../client/base'; +import { heatBase } from '../client/constants'; + +class HeatClient extends Base { + get baseUrl() { + return heatBase(); + } + + get projectInUrl() { + return true; + } + + getDetailUrlForStack = ({ id, name }) => `stacks/${name}/${id}`; + + get resources() { + return [ + { + key: 'stacks', + responseKey: 'stack', + extendOperations: [ + { + key: 'show', + generate: ({ id, name }, params) => { + return this.request.get( + this.getDetailUrlForStack({ id, name }), + params + ); + }, + }, + { + key: 'update', + generate: ({ id, name }, data) => + this.request.put(this.getDetailUrlForStack({ id, name }), data), + }, + { + key: 'delete', + generate: ({ id, name }) => + this.request.delete(this.getDetailUrlForStack({ id, name })), + }, + { + key: 'abandon', + generate: ({ id, name }) => + this.request.delete( + `${this.getDetailUrlForStack({ id, name })}/abandon` + ), + }, + { + key: 'template', + generate: ({ id, name }) => + this.request.get( + `${this.getDetailUrlForStack({ id, name })}/template` + ), + }, + { + key: 'events', + generate: ({ id, name }) => + this.request.get( + `${this.getDetailUrlForStack({ id, name })}/events` + ), + }, + { + key: 'resources', + generate: ({ id, name }) => + this.request.get( + `${this.getDetailUrlForStack({ id, name })}/resources` + ), + }, + ], + }, + { + key: 'services', + responseKey: 'service', + }, + ]; + } +} + +const heatClient = new HeatClient(); +export default heatClient; diff --git a/src/client/index.js b/src/client/index.js new file mode 100644 index 00000000..e5e6b59d --- /dev/null +++ b/src/client/index.js @@ -0,0 +1,41 @@ +// 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 skyline from './skyline'; +import nova from './nova'; +import cinder from './cinder'; +import glance from './glance'; +import neutron from './neutron'; +import keystone from './keystone'; +import heat from './heat'; +import octavia from './octavia'; +import placement from './placement'; +import ironic from './ironic'; + +const client = { + skyline, + nova, + cinder, + glance, + neutron, + keystone, + heat, + octavia, + placement, + ironic, +}; + +window.client = client; + +export default client; diff --git a/src/client/ironic/index.js b/src/client/ironic/index.js new file mode 100644 index 00000000..b54adf71 --- /dev/null +++ b/src/client/ironic/index.js @@ -0,0 +1,96 @@ +// 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 '../client/base'; +import { ironicBase } from '../client/constants'; + +class IronicClient extends Base { + get baseUrl() { + return ironicBase(); + } + + get resources() { + return [ + { + key: 'nodes', + responseKey: 'node', + extendOperations: [ + { + name: 'updateStatesProvision', + key: 'states/provision', + method: 'put', + }, + { + name: 'UpdateStatesPower', + key: 'states/power', + method: 'put', + }, + { + name: 'updateMaintenance', + key: 'maintenance', + method: 'put', + }, + { + name: 'deleteMaintenance', + key: 'maintenance', + method: 'delete', + }, + { + name: 'getManagementBootDevice', + key: 'management/boot_device', + }, + { + name: 'updateManagementBootDevice', + key: 'management/boot_device', + method: 'put', + }, + { + name: 'getManagementBootDeviceSupported', + key: 'management/boot_device/supported', + }, + { + key: 'updateTraits', + method: 'put', + }, + ], + subResources: [ + { + key: 'states', + }, + { + key: 'validate', + }, + { + key: 'ports', + }, + { + key: 'portgroups', + responseKey: 'portgroup', + }, + ], + }, + { + key: 'ports', + responseKey: 'port', + }, + { + key: 'portgroups', + responseKey: 'portgroup', + }, + ]; + } +} + +const ironicClient = new IronicClient(); +export default ironicClient; diff --git a/src/client/keystone/index.js b/src/client/keystone/index.js new file mode 100644 index 00000000..6578223c --- /dev/null +++ b/src/client/keystone/index.js @@ -0,0 +1,149 @@ +// 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 '../client/base'; +import { keystoneBase } from '../client/constants'; + +class KeystoneClient extends Base { + get baseUrl() { + return keystoneBase(); + } + + get resources() { + return [ + { + name: 'catalog', + key: 'auth/catalog', + responseKey: 'catalog', + }, + { + key: 'projects', + responseKey: 'project', + extendOperations: [ + { + name: 'updateTags', + key: 'tags', + method: 'put', + }, + ], + subResources: [ + { + key: 'tags', + responseKey: 'tag', + }, + { + key: 'groups', + subResources: [ + { + key: 'roles', + }, + ], + }, + { + key: 'users', + subResources: [ + { + key: 'roles', + }, + ], + }, + ], + }, + { + key: 'domains', + responseKey: 'domain', + subResources: [ + { + key: 'groups', + subResources: [ + { + key: 'roles', + }, + ], + }, + { + key: 'users', + subResources: [ + { + key: 'roles', + }, + ], + }, + ], + }, + { + key: 'roles', + responseKey: 'role', + subResources: [ + { + key: 'implies', + }, + ], + }, + { + name: 'roleAssignments', + key: 'role_assignments', + }, + { + key: 'users', + responseKey: 'user', + subResources: [ + { + key: 'projects', + }, + { + key: 'groups', + }, + ], + extendOperations: [ + { + name: 'updatePassword', + key: 'password', + method: 'post', + }, + ], + }, + { + key: 'groups', + responseKey: 'group', + subResources: [ + { + key: 'users', + }, + ], + }, + { + name: 'systemGroups', + key: 'system/groups', + subResources: [ + { + key: 'roles', + }, + ], + }, + { + name: 'systemUsers', + key: 'system/users', + subResources: [ + { + key: 'roles', + }, + ], + }, + ]; + } +} + +const keystoneClient = new KeystoneClient(); +export default keystoneClient; diff --git a/src/client/neutron/index.js b/src/client/neutron/index.js new file mode 100644 index 00000000..1d7c085d --- /dev/null +++ b/src/client/neutron/index.js @@ -0,0 +1,201 @@ +// 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 '../client/base'; +import { neutronBase } from '../client/constants'; + +class NeutronClient extends Base { + get baseUrl() { + return neutronBase(); + } + + get resources() { + return [ + { + key: 'networks', + responseKey: 'network', + subResources: [ + { + name: 'dhcpAgents', + key: 'dhcp-agents', + }, + ], + }, + { + key: 'subnets', + responseKey: 'subnet', + }, + { + key: 'extensions', + }, + { + key: 'ports', + responseKey: 'port', + }, + { + key: 'routers', + responseKey: 'router', + extendOperations: [ + { + name: 'addRouterInterface', + key: 'add_router_interface', + method: 'put', + }, + { + name: 'removeRouterInterface', + key: 'remove_router_interface', + method: 'put', + }, + { + name: 'addExtraRoutes', + key: 'add_extraroutes', + method: 'put', + }, + { + name: 'removeExtraRoutes', + key: 'remove_extraroutes', + method: 'put', + }, + ], + }, + { + key: 'floatingips', + responseKey: 'floatingip', + subResources: [ + { + name: 'portForwardings', + key: 'port_forwardings', + responseKey: 'port_forwarding', + }, + ], + }, + { + key: 'agents', + responseKey: 'agent', + subResources: [ + { + name: 'dhcpNetworks', + key: 'dhcp-networks', + responseKey: 'network', + }, + { + name: 'l3Routers', + key: 'l3-routers', + responseKey: 'router', + }, + ], + }, + { + name: 'firewalls', + key: 'fwaas/firewall_groups', + responseKey: 'firewall_group', + }, + { + name: 'firewallPolicies', + key: 'fwaas/firewall_policies', + responseKey: 'firewall_policy', + extendOperations: [ + { + name: 'insertRule', + key: 'insert_rule', + method: 'put', + }, + { + name: 'removeRule', + key: 'remove_rule', + method: 'put', + }, + ], + }, + { + name: 'firewallRules', + key: 'fwaas/firewall_rules', + responseKey: 'firewall_rule', + }, + { + name: 'networkIpAvailabilities', + key: 'network-ip-availabilities', + }, + { + name: 'azones', + key: 'availability_zones', + }, + { + name: 'qosPolicies', + key: 'qos/policies', + responseKey: 'policy', + subResources: [ + { + name: 'bandwidthLimitRules', + key: 'bandwidth_limit_rules', + }, + { + name: 'dscpMarkingRules', + key: 'dscp_marking_rules', + }, + ], + }, + { + name: 'securityGroups', + key: 'security-groups', + responseKey: 'security_group', + }, + { + name: 'securityGroupRules', + key: 'security-group-rules', + responseKey: 'security_group_rule', + }, + { + key: 'subnets', + responseKey: 'subnet', + }, + { + name: 'endpointGroups', + key: 'endpoint-groups', + responseKey: 'endpoint_group', + }, + { + name: 'ikePolicies', + key: 'ikepolicies', + responseKey: 'ikepolicy', + }, + { + name: 'ipsecPolicies', + key: 'ipsecpolicies', + responseKey: 'ipsecpolicy', + }, + { + name: 'ipsecSiteConnections', + key: 'ipsec_site_connections', + responseKey: 'ipsec_site_connection', + }, + { + key: 'vpnservices', + responseKey: 'vpnservice', + }, + { + key: 'quotas', + responseKey: 'quota', + extendOperations: [ + { + key: 'details', + }, + ], + }, + ]; + } +} + +const neutronClient = new NeutronClient(); +export default neutronClient; diff --git a/src/client/nova/index.js b/src/client/nova/index.js new file mode 100644 index 00000000..f05ef111 --- /dev/null +++ b/src/client/nova/index.js @@ -0,0 +1,134 @@ +// 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 '../client/base'; +import { novaBase } from '../client/constants'; + +class NovaClient extends Base { + get baseUrl() { + return novaBase(); + } + + get resources() { + return [ + { + key: 'servers', + responseKey: 'server', + subResources: [ + { + name: 'interfaces', + key: 'os-interface', + }, + { + name: 'volumeAttachments', + key: 'os-volume_attachments', + responseKey: 'volumeAttachment', + }, + { + name: 'instanceActions', + key: 'os-instance-actions', + responseKey: 'instanceAction', + }, + ], + extendOperations: [ + { + name: 'createConsole', + key: 'remote-consoles', + method: 'post', + }, + { + key: 'action', + method: 'post', + }, + ], + }, + { + name: 'zone', + key: 'os-availability-zone', + responseKey: 'availabilityZoneInfo', + }, + { + key: 'flavors', + responseKey: 'flavor', + extendOperations: [ + { + name: 'action', + key: 'action', + method: 'post', + }, + ], + subResources: [ + { + name: 'access', + key: 'os-flavor-access', + }, + { + name: 'extraSpecs', + key: 'os-extra_specs', + }, + ], + }, + { + name: 'keypairs', + key: 'os-keypairs', + responseKey: 'keypair', + }, + { + name: 'serverGroups', + key: 'os-server-groups', + responseKey: 'server_group', + }, + { + name: 'aggregates', + key: 'os-aggregates', + responseKey: 'aggregate', + extendOperations: [ + { + name: 'action', + key: 'action', + method: 'post', + }, + ], + }, + { + name: 'services', + key: 'os-services', + responseKey: 'service', + }, + { + name: 'quotaSets', + key: 'os-quota-sets', + responseKey: 'quota_set', + extendOperations: [ + { + key: 'detail', + }, + ], + }, + { + name: 'hypervisors', + key: 'os-hypervisors', + responseKey: 'hypervisor', + }, + { + name: 'pciDevices', + key: 'os-pci-devices', + responseKey: 'pci_device', + }, + ]; + } +} + +const novaClient = new NovaClient(); +export default novaClient; diff --git a/src/client/octavia/index.js b/src/client/octavia/index.js new file mode 100644 index 00000000..b10e789d --- /dev/null +++ b/src/client/octavia/index.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 '../client/base'; +import { octaviaBase } from '../client/constants'; + +class OctaviaClient extends Base { + get baseUrl() { + return octaviaBase(); + } + + get resources() { + return [ + { + name: 'healthMonitors', + key: 'lbaas/healthmonitors', + responseKey: 'healthmonitor', + }, + { + name: 'listeners', + key: 'lbaas/listeners', + responseKey: 'listener', + }, + { + name: 'loadbalancers', + key: 'lbaas/loadbalancers', + responseKey: 'loadbalancer', + }, + { + name: 'pools', + key: 'lbaas/pools', + responseKey: 'pool', + extendOperations: [ + { + name: 'batchUpdateMembers', + key: 'members', + method: 'put', + }, + ], + subResources: [ + { + key: 'members', + }, + ], + }, + ]; + } +} + +const octaviaClient = new OctaviaClient(); +export default octaviaClient; diff --git a/src/client/placement/index.js b/src/client/placement/index.js new file mode 100644 index 00000000..7e522830 --- /dev/null +++ b/src/client/placement/index.js @@ -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 '../client/base'; +import { placementBase } from '../client/constants'; + +class PlacementClient extends Base { + get baseUrl() { + return placementBase(); + } + + get resources() { + return [ + { + name: 'resourceProviders', + key: 'resource_providers', + subResources: [ + { + key: 'inventories', + }, + ], + }, + { + key: 'traits', + }, + ]; + } +} + +const placementClient = new PlacementClient(); +export default placementClient; diff --git a/src/client/skyline/index.js b/src/client/skyline/index.js new file mode 100644 index 00000000..9d4055cb --- /dev/null +++ b/src/client/skyline/index.js @@ -0,0 +1,123 @@ +// 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 '../client/base'; +import { skylineBase } from '../client/constants'; + +class SkylineClient extends Base { + get baseUrl() { + return skylineBase(); + } + + get resources() { + return [ + { + key: 'contrib', + isResource: false, + extendOperations: [ + { + key: 'domains', + }, + { + key: 'regions', + }, + { + name: 'keystoneEndpoints', + key: 'keystone_endpoints', + }, + ], + }, + { + key: 'extension', + isResource: false, + extendOperations: [ + { + key: 'servers', + }, + { + name: 'recycleServers', + key: 'recycle_servers', + }, + { + key: 'volumes', + }, + { + name: 'volumeSnapshots', + key: 'volume_snapshots', + }, + { + key: 'ports', + }, + ], + }, + { + name: 'policies', + key: 'policies', + extendOperations: [ + { + name: 'check', + key: 'check', + isDetail: false, + }, + ], + }, + { + name: '', + key: '', + isResource: false, + extendOperations: [ + { + key: 'login', + method: 'post', + }, + { + key: 'logout', + method: 'post', + }, + { + key: 'profile', + }, + { + name: 'switchProject', + method: 'post', + generate: (projectId, domainId) => { + const url = `switch_project/${projectId}`; + const data = { + project_id: projectId, + project_domain_id: domainId, + }; + const params = { + project_domain_id: domainId, + }; + return this.request.post(url, data, params); + }, + }, + ], + }, + { + key: 'setting', + responseKey: 'setting', + extendOperations: [ + { + key: 'list', + url: () => 'settings', + }, + ], + }, + ]; + } +} + +const skylineClient = new SkylineClient(); +export default skylineClient; diff --git a/src/components/Form/index.jsx b/src/components/Form/index.jsx index e90950b2..a28234eb 100644 --- a/src/components/Form/index.jsx +++ b/src/components/Form/index.jsx @@ -104,6 +104,11 @@ export default class BaseForm extends React.Component { return ''; } + get currentUser() { + const { user } = this.props.rootStore || {}; + return user || {}; + } + get isAdminPage() { const { pathname = '' } = this.props.location || {}; return isAdminPage(pathname); @@ -114,11 +119,11 @@ export default class BaseForm extends React.Component { } get currentProjectId() { - return globals.user.project.id; + return this.props.rootStore.projectId; } get currentProjectName() { - return globals.user.project.name; + return this.props.rootStore.projectName; } getUrl(path, adminStr) { @@ -258,7 +263,7 @@ export default class BaseForm extends React.Component { this.responseError = err; this.showNotice && Notify.errorWithDetail(err, this.errorText); // eslint-disable-next-line no-console - console.log(err); + console.log('err', err); if (callback && isFunction(callback)) { callback(false, true); } diff --git a/src/components/FormItem/NetworkSelectTable/index.jsx b/src/components/FormItem/NetworkSelectTable/index.jsx index 9ac5d90f..d6926e91 100644 --- a/src/components/FormItem/NetworkSelectTable/index.jsx +++ b/src/components/FormItem/NetworkSelectTable/index.jsx @@ -44,7 +44,7 @@ export default class NetworkSelectTable extends Component { } get currentProjectId() { - return globals.user.project.id; + return this.props.rootStore.projectId; } get hasAdminRole() { diff --git a/src/components/FormItem/VolumeSelectTable/index.jsx b/src/components/FormItem/VolumeSelectTable/index.jsx index 9e17f95e..75020075 100644 --- a/src/components/FormItem/VolumeSelectTable/index.jsx +++ b/src/components/FormItem/VolumeSelectTable/index.jsx @@ -34,7 +34,7 @@ export default class VolumeSelectTable extends Component { } get currentProjectId() { - return globals.user.project.id; + return this.props.rootStore.projectId; } get hasAdminRole() { diff --git a/src/components/Layout/GlobalHeader/AvatarDropdown.jsx b/src/components/Layout/GlobalHeader/AvatarDropdown.jsx index 1acff3fc..dfb42d6b 100644 --- a/src/components/Layout/GlobalHeader/AvatarDropdown.jsx +++ b/src/components/Layout/GlobalHeader/AvatarDropdown.jsx @@ -126,7 +126,7 @@ class AvatarDropdown extends React.Component { diff --git a/src/components/Layout/GlobalHeader/RightContent.jsx b/src/components/Layout/GlobalHeader/RightContent.jsx index 08ad311c..db87ee6f 100644 --- a/src/components/Layout/GlobalHeader/RightContent.jsx +++ b/src/components/Layout/GlobalHeader/RightContent.jsx @@ -28,7 +28,7 @@ const gotoConsole = (type, props) => { }; const GlobalHeaderRight = (props) => { - const { isAdminPage = false, rootStore: { hasAdminRole = false } = {} } = + const { isAdminPage = false, rootStore: { hasAdminPageRole = false } = {} } = props; let linkRender = null; if (isAdminPage) { @@ -41,7 +41,7 @@ const GlobalHeaderRight = (props) => { {t('Console')} ); - } else if (hasAdminRole) { + } else if (hasAdminPageRole) { linkRender = (