Added support of AWS provider for BigAnimal cloud deployment. #5569
@ -23,6 +23,18 @@ will be redirected to the new tab for the verification.
|
||||
Once you confirm the one time code, the pgAdmin will automatically detect it
|
||||
and the next button will be enabled. To proceed further, click on the next button.
|
||||
|
||||
.. image:: images/cloud_biganimal_cluster.png
|
||||
:alt: Cloud Deployment Provider
|
||||
:align: center
|
||||
|
||||
|
||||
* Use the *Cluster type* field to choose a cluster type.
|
||||
|
||||
* Use the *No. of Standby Replicas* field to specify the replicas if you have selected the High Availability cluster.
|
||||
|
||||
* Use the *Cluster provider* field to coose the provider.
|
||||
|
||||
|
||||
.. image:: images/cloud_biganimal_instance.png
|
||||
:alt: Cloud Deployment Provider
|
||||
:align: center
|
||||
@ -48,8 +60,11 @@ details.
|
||||
|
||||
* Use the *Volume type* field to select the instance storage type.
|
||||
|
||||
* Use the *Volume properties* field to specify the storage capacity.
|
||||
* Use the *Volume properties* field to specify the storage capacity. This field is specific to Azure.
|
||||
|
||||
* Use the *Volume size* field to specify the storage size. This field is specific to AWS.
|
||||
|
||||
* Use the *Volume IOPS* field to specify the storage IOPS. This field is specific to AWS.
|
||||
|
||||
|
||||
.. image:: images/cloud_biganimal_database.png
|
||||
@ -72,11 +87,6 @@ Use the fields from the Database Details tab to specify the Instance details.
|
||||
|
||||
* Use the *Confirm password* field to repeat the password.
|
||||
|
||||
* Use the *High Availability* field to create the cluster with high availability, which creates a cluster
|
||||
with one primary and up to two standby replicas in different availability zones.
|
||||
|
||||
* Use the *Number of standby replicas* field to specify the standby replicas.
|
||||
|
||||
|
||||
.. image:: images/cloud_biganimal_review.png
|
||||
:alt: Cloud Deployment Provider
|
||||
|
BIN
docs/en_US/images/cloud_biganimal_cluster.png
Normal file
After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 113 KiB |
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 134 KiB |
@ -65,6 +65,12 @@ class BigAnimalProvider(AbsProvider):
|
||||
parser_create_instance.add_argument('--volume-properties',
|
||||
required=True,
|
||||
help='storage properties')
|
||||
parser_create_instance.add_argument('--volume-size',
|
||||
required=True,
|
||||
help='storage Size')
|
||||
parser_create_instance.add_argument('--volume-IOPS',
|
||||
required=True,
|
||||
help='storage IOPS')
|
||||
parser_create_instance.add_argument('--private-network', required=True,
|
||||
help='Private or Public Network')
|
||||
parser_create_instance.add_argument('--public-ip', default='',
|
||||
@ -76,6 +82,12 @@ class BigAnimalProvider(AbsProvider):
|
||||
parser_create_instance.add_argument('--nodes',
|
||||
required=True,
|
||||
help='No of Nodes')
|
||||
parser_create_instance.add_argument('--replicas',
|
||||
required=True,
|
||||
help='No. of Stand By Replicas')
|
||||
parser_create_instance.add_argument('--cloud-provider',
|
||||
required=True,
|
||||
help='Provider')
|
||||
|
||||
def cmd_create_instance(self, args):
|
||||
""" Create a biganimal cluster """
|
||||
@ -105,12 +117,14 @@ class BigAnimalProvider(AbsProvider):
|
||||
'pgType': {'pgTypeId': args.db_type},
|
||||
'pgVersion': {'pgVersionId': args.db_version},
|
||||
'privateNetworking': private_network,
|
||||
'provider': {'cloudProviderId': 'azure'},
|
||||
'provider': {'cloudProviderId': args.cloud_provider},
|
||||
'region': {'regionId': args.region},
|
||||
'replicas': 1,
|
||||
'replicas': int(args.replicas),
|
||||
'storage': {
|
||||
'volumePropertiesId': args.volume_properties,
|
||||
'volumeTypeId': args.volume_type
|
||||
'volumeTypeId': args.volume_type,
|
||||
'iops': args.volume_IOPS,
|
||||
'size': args.volume_size + ' Gi'
|
||||
},
|
||||
'clusterArchitecture': {
|
||||
'clusterArchitectureId': args.cluster_arch,
|
||||
|
@ -50,7 +50,8 @@ class BigAnimalModule(PgAdminModule):
|
||||
'biganimal.db_versions',
|
||||
'biganimal.instance_types',
|
||||
'biganimal.volume_types',
|
||||
'biganimal.volume_properties']
|
||||
'biganimal.volume_properties',
|
||||
'biganimal.providers']
|
||||
|
||||
|
||||
blueprint = BigAnimalModule(MODULE_NAME, __name__,
|
||||
@ -82,17 +83,27 @@ def verification():
|
||||
return make_json_response(data=verification_uri)
|
||||
|
||||
|
||||
@blueprint.route('/regions/',
|
||||
@blueprint.route('/regions/<provider_id>',
|
||||
methods=['GET'], endpoint='regions')
|
||||
@login_required
|
||||
def biganimal_regions():
|
||||
def biganimal_regions(provider_id):
|
||||
"""Get Regions."""
|
||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
||||
status, regions = biganimal_obj.get_regions()
|
||||
status, regions = biganimal_obj.get_regions(provider_id)
|
||||
session['biganimal']['provider_obj'] = pickle.dumps(biganimal_obj, -1)
|
||||
return make_json_response(data=regions)
|
||||
|
||||
|
||||
@blueprint.route('/providers/',
|
||||
methods=['GET'], endpoint='providers')
|
||||
@login_required
|
||||
def biganimal_providers():
|
||||
"""Get Providers."""
|
||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
||||
status, providers = biganimal_obj.get_providers()
|
||||
return make_json_response(data=providers)
|
||||
|
||||
|
||||
@blueprint.route('/db_types/',
|
||||
methods=['GET'], endpoint='db_types')
|
||||
@login_required
|
||||
@ -103,50 +114,52 @@ def biganimal_db_types():
|
||||
return make_json_response(data=pg_types)
|
||||
|
||||
|
||||
@blueprint.route('/db_versions/<db_type>',
|
||||
@blueprint.route('/db_versions/<cluster_type>/<pg_type>',
|
||||
methods=['GET'], endpoint='db_versions')
|
||||
@login_required
|
||||
def biganimal_db_versions(db_type):
|
||||
def biganimal_db_versions(cluster_type, pg_type):
|
||||
"""Get Database Version."""
|
||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
||||
pg_versions = biganimal_obj.get_postgres_versions(db_type)
|
||||
pg_versions = biganimal_obj.get_postgres_versions(cluster_type, pg_type)
|
||||
return make_json_response(data=pg_versions)
|
||||
|
||||
|
||||
@blueprint.route('/instance_types/<region_id>',
|
||||
@blueprint.route('/instance_types/<region_id>/<provider_id>',
|
||||
methods=['GET'], endpoint='instance_types')
|
||||
@login_required
|
||||
def biganimal_instance_types(region_id):
|
||||
def biganimal_instance_types(region_id, provider_id):
|
||||
"""Get Instance Types."""
|
||||
if not region_id:
|
||||
if not region_id or not provider_id:
|
||||
return make_json_response(data=[])
|
||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
||||
biganimal_instances = biganimal_obj.get_instance_types(region_id)
|
||||
biganimal_instances = biganimal_obj.get_instance_types(region_id,
|
||||
provider_id)
|
||||
return make_json_response(data=biganimal_instances)
|
||||
|
||||
|
||||
@blueprint.route('/volume_types/<region_id>',
|
||||
@blueprint.route('/volume_types/<region_id>/<provider_id>',
|
||||
methods=['GET'], endpoint='volume_types')
|
||||
@login_required
|
||||
def biganimal_volume_types(region_id):
|
||||
def biganimal_volume_types(region_id, provider_id):
|
||||
"""Get Volume Types."""
|
||||
if not region_id:
|
||||
if not region_id or not provider_id:
|
||||
return make_json_response(data=[])
|
||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
||||
biganimal_volumes = biganimal_obj.get_volume_types(region_id)
|
||||
biganimal_volumes = biganimal_obj.get_volume_types(region_id, provider_id)
|
||||
return make_json_response(data=biganimal_volumes)
|
||||
|
||||
|
||||
@blueprint.route('/volume_properties/<region_id>/<volume_type>',
|
||||
@blueprint.route('/volume_properties/<region_id>/<provider_id>/<volume_type>',
|
||||
methods=['GET'], endpoint='volume_properties')
|
||||
@login_required
|
||||
def biganimal_volume_properties(region_id, volume_type):
|
||||
def biganimal_volume_properties(region_id, provider_id, volume_type):
|
||||
"""Get Volume Properties."""
|
||||
if not region_id:
|
||||
if not region_id or not provider_id:
|
||||
return make_json_response(data=[])
|
||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
||||
biganimal_volume_properties = biganimal_obj.get_volume_properties(
|
||||
region_id,
|
||||
provider_id,
|
||||
volume_type)
|
||||
return make_json_response(data=biganimal_volume_properties)
|
||||
|
||||
@ -261,19 +274,42 @@ class BigAnimalProvider():
|
||||
# so all the existing clusters moved to the default Project.
|
||||
# For now, we can get the Proj Id by replacing 'org' to 'prj'
|
||||
# in organization ID: org_1234 -> prj_1234
|
||||
proj_Id = content['data']['organizationId'].replace('org',
|
||||
proj_id = content['data']['organizationId'].replace('org',
|
||||
'prj')
|
||||
for permission in content['data']['scopedPermissions']:
|
||||
if proj_Id == permission['scope'] and\
|
||||
if proj_id == permission['scope'] and\
|
||||
'create:clusters' in permission['permissions']:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_regions(self):
|
||||
def get_providers(self):
|
||||
"""Get cloud providers"""
|
||||
_url = '{0}/cloud-providers'.format(
|
||||
self.BASE_URL)
|
||||
providers = []
|
||||
resp = requests.get(_url, headers=self._get_headers())
|
||||
if resp.status_code == 200 and resp.content:
|
||||
provider_resp = json.loads(resp.content)
|
||||
for value in provider_resp['data']:
|
||||
providers.append({
|
||||
'label': value['cloudProviderName'],
|
||||
'value': value['cloudProviderId'],
|
||||
'connected': value['connected']
|
||||
})
|
||||
return True, providers
|
||||
elif resp.content:
|
||||
provider_resp = json.loads(resp.content)
|
||||
return False, provider_resp['error']['message']
|
||||
else:
|
||||
return False, gettext('Error retrieving providers.')
|
||||
|
||||
def get_regions(self, provider_id):
|
||||
"""Get regions"""
|
||||
_url = "{0}/{1}".format(
|
||||
if not provider_id:
|
||||
return False, gettext('Provider not provided.')
|
||||
_url = '{0}/cloud-providers/{1}/regions'.format(
|
||||
self.BASE_URL,
|
||||
'cloud-providers/azure/regions')
|
||||
provider_id)
|
||||
regions = []
|
||||
resp = requests.get(_url, headers=self._get_headers())
|
||||
if resp.status_code == 200 and resp.content:
|
||||
@ -309,12 +345,15 @@ class BigAnimalProvider():
|
||||
})
|
||||
return pg_types
|
||||
|
||||
def get_postgres_versions(self, db_type):
|
||||
def get_postgres_versions(self, cluster_type, pg_type):
|
||||
"""Get Postgres Versions."""
|
||||
_url = "{0}/pg-versions?pgTypeIds={1}".format(
|
||||
self.BASE_URL,
|
||||
db_type
|
||||
)
|
||||
if not cluster_type or not pg_type:
|
||||
return []
|
||||
|
||||
_url = "{0}/pg-versions?clusterArchitectureIds={1}" \
|
||||
"&pgTypeIds={2}".format(self.BASE_URL,
|
||||
cluster_type,
|
||||
pg_type)
|
||||
pg_versions = []
|
||||
resp = requests.get(_url, headers=self._get_headers())
|
||||
if resp.status_code == 200 and resp.content:
|
||||
@ -326,48 +365,55 @@ class BigAnimalProvider():
|
||||
})
|
||||
return pg_versions
|
||||
|
||||
def get_instance_types(self, region_id):
|
||||
def get_instance_types(self, region_id, provider_id):
|
||||
"""GEt Instance Types."""
|
||||
if region_id not in self.regions:
|
||||
if region_id not in self.regions or not provider_id:
|
||||
return []
|
||||
_url = "{0}/{1}".format(
|
||||
self.BASE_URL,
|
||||
'cloud-providers/azure/regions/'
|
||||
'{0}/instance-types'.format(region_id))
|
||||
_url = '{0}/cloud-providers/{1}/regions/{2}/instance-types?' \
|
||||
'sort=instanceTypeName'.format(self.BASE_URL,
|
||||
provider_id,
|
||||
region_id)
|
||||
resp = requests.get(_url, headers=self._get_headers())
|
||||
if resp.status_code == 200 and resp.content:
|
||||
pg_types = json.loads(resp.content)
|
||||
return pg_types['data']
|
||||
_sorted_data = sorted(pg_types['data'], key=lambda x: int(x['cpu'])
|
||||
)
|
||||
return _sorted_data
|
||||
return []
|
||||
|
||||
def get_volume_types(self, region_id):
|
||||
def get_volume_types(self, region_id, provider_id):
|
||||
"""Get Volume Types."""
|
||||
if region_id not in self.regions:
|
||||
return []
|
||||
|
||||
_url = "{0}/{1}".format(
|
||||
_url = '{0}/cloud-providers/{1}/regions/{2}/volume-types'.format(
|
||||
self.BASE_URL,
|
||||
'cloud-providers/azure/regions/{0}/volume-types'.format(region_id))
|
||||
provider_id,
|
||||
region_id)
|
||||
volume_types = []
|
||||
resp = requests.get(_url, headers=self._get_headers())
|
||||
if resp.status_code == 200 and resp.content:
|
||||
volume_resp = json.loads(resp.content)
|
||||
for value in volume_resp['data']:
|
||||
volume_types.append({
|
||||
'label': value['volumeTypeName'],
|
||||
'value': value['volumeTypeId']
|
||||
})
|
||||
if value['enabledInRegion']:
|
||||
volume_types.append({
|
||||
'label': value['volumeTypeName'],
|
||||
'value': value['volumeTypeId'],
|
||||
'supportedInstanceFamilyNames': value[
|
||||
'supportedInstanceFamilyNames']
|
||||
})
|
||||
return volume_types
|
||||
|
||||
def get_volume_properties(self, region_id, volume_type):
|
||||
def get_volume_properties(self, region_id, provider_id, volume_type):
|
||||
"""Get Volume Properties."""
|
||||
if region_id not in self.regions:
|
||||
return []
|
||||
|
||||
_url = "{0}/{1}".format(
|
||||
self.BASE_URL,
|
||||
'cloud-providers/azure/regions/{0}/volume-types'
|
||||
'/{1}/volume-properties'.format(region_id, volume_type))
|
||||
_url = '{0}/cloud-providers/{1}/regions/{2}/volume-types/{3}/' \
|
||||
'volume-properties'.format(self.BASE_URL,
|
||||
provider_id,
|
||||
region_id,
|
||||
volume_type)
|
||||
volume_properties = []
|
||||
resp = requests.get(_url, headers=self._get_headers())
|
||||
if resp.status_code == 200 and resp.content:
|
||||
@ -394,13 +440,12 @@ def deploy_on_biganimal(data):
|
||||
_private_network = '1' if str(data['instance_details']['cloud_type']
|
||||
) == 'private' else '0'
|
||||
_instance_size = data['instance_details']['instance_size'].split('||')[1]
|
||||
|
||||
cluster_arch = SINGLE_CLUSTER_ARCH
|
||||
nodes = 1
|
||||
|
||||
if data['db_details']['high_availability']:
|
||||
cluster_arch = HA_CLUSTER_ARCH
|
||||
nodes = int(data['db_details']['replicas']) + nodes
|
||||
if data['cluster_details']['cluster_type'] == HA_CLUSTER_ARCH:
|
||||
nodes = int(data['cluster_details']['replicas']) + nodes
|
||||
elif data['cluster_details']['cluster_type'] == EHA_CLUSTER_ARCH:
|
||||
nodes = 5
|
||||
|
||||
args = [_cmd_script,
|
||||
data['cloud'],
|
||||
@ -416,15 +461,25 @@ def deploy_on_biganimal(data):
|
||||
'--volume-type',
|
||||
str(data['instance_details']['volume_type']),
|
||||
'--volume-properties',
|
||||
str(data['instance_details']['volume_properties']),
|
||||
str(data['instance_details'].get('volume_properties',
|
||||
data['instance_details'][
|
||||
'volume_type'])),
|
||||
'--volume-size',
|
||||
str(data['instance_details'].get('volume_size', None)),
|
||||
'--volume-IOPS',
|
||||
str(data['instance_details'].get('volume_IOPS', None)),
|
||||
'--instance-type',
|
||||
str(_instance_size),
|
||||
'--private-network',
|
||||
_private_network,
|
||||
'--cluster-arch',
|
||||
cluster_arch,
|
||||
data['cluster_details']['cluster_type'],
|
||||
'--nodes',
|
||||
str(nodes)
|
||||
str(nodes),
|
||||
'--replicas',
|
||||
str(data['cluster_details']['replicas']),
|
||||
'--cloud-provider',
|
||||
str(data['cluster_details']['provider']),
|
||||
]
|
||||
|
||||
if 'biganimal_public_ip' in data['instance_details']:
|
||||
|
@ -58,6 +58,7 @@ blueprint = RDSModule(MODULE_NAME, __name__,
|
||||
@login_required
|
||||
def verify_credentials():
|
||||
"""Verify Credentials."""
|
||||
msg = ''
|
||||
data = json.loads(request.data, encoding='utf-8')
|
||||
|
||||
session_token = data['secret']['session_token'] if\
|
||||
|
@ -22,11 +22,13 @@ import pgAdmin from 'sources/pgadmin';
|
||||
import {ToggleButtons, FinalSummary} from './cloud_components';
|
||||
import { PrimaryButton } from '../../../../static/js/components/Buttons';
|
||||
import {AwsCredentials, AwsInstanceDetails, AwsDatabaseDetails, validateCloudStep1, validateCloudStep2, validateCloudStep3} from './aws';
|
||||
import {BigAnimalInstance, BigAnimalDatabase, validateBigAnimal,validateBigAnimalStep2, validateBigAnimalStep3} from './biganimal';
|
||||
import {BigAnimalInstance, BigAnimalDatabase, BigAnimalClusterType, getProviderOptions, validateBigAnimal, validateBigAnimalStep2, validateBigAnimalStep3, validateBigAnimalStep4} from './biganimal';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import { AWSIcon, BigAnimalIcon, AzureIcon } from '../../../../static/js/components/ExternalIcon';
|
||||
import {AzureCredentials, AzureInstanceDetails, AzureDatabaseDetails, checkClusternameAvailbility, validateAzureStep2, validateAzureStep3} from './azure';
|
||||
import EventBus from '../../../../static/js/helpers/EventBus';
|
||||
import { CLOUD_PROVIDERS } from './cloud_constants';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
({
|
||||
@ -67,7 +69,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
|
||||
const eventBus = React.useRef(new EventBus());
|
||||
|
||||
let steps = [gettext('Cloud Provider'), gettext('Credentials'),
|
||||
let steps = [gettext('Cloud Provider'), gettext('Credentials'), gettext('Cluster Type'),
|
||||
gettext('Instance Specification'), gettext('Database Details'), gettext('Review')];
|
||||
const [currentStep, setCurrentStep] = React.useState('');
|
||||
const [selectionVal, setCloudSelection] = React.useState('');
|
||||
@ -81,6 +83,8 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
const [verificationIntiated, setVerificationIntiated] = React.useState(false);
|
||||
const [bigAnimalInstanceData, setBigAnimalInstanceData] = React.useState({});
|
||||
const [bigAnimalDatabaseData, setBigAnimalDatabaseData] = React.useState({});
|
||||
const [bigAnimalClusterTypeData, setBigAnimalClusterTypeData] = React.useState({});
|
||||
const [bigAnimalProviders, setBigAnimalProviders] = React.useState({});
|
||||
|
||||
const [azureCredData, setAzureCredData] = React.useState({});
|
||||
const [azureInstanceData, setAzureInstanceData] = React.useState({});
|
||||
@ -124,7 +128,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
let _url = url_for('cloud.deploy_on_cloud'),
|
||||
post_data = {};
|
||||
|
||||
if (cloudProvider == 'rds') {
|
||||
if (cloudProvider == CLOUD_PROVIDERS.RDS) {
|
||||
post_data = {
|
||||
gid: nodeInfo.server_group._id,
|
||||
cloud: cloudProvider,
|
||||
@ -132,7 +136,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
instance_details:cloudInstanceDetails,
|
||||
db_details: cloudDBDetails
|
||||
};
|
||||
} else if(cloudProvider == 'azure'){
|
||||
} else if(cloudProvider == CLOUD_PROVIDERS.AZURE){
|
||||
post_data = {
|
||||
gid: nodeInfo.server_group._id,
|
||||
secret: azureCredData,
|
||||
@ -145,7 +149,8 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
post_data = {
|
||||
gid: nodeInfo.server_group._id,
|
||||
cloud: cloudProvider,
|
||||
instance_details:bigAnimalInstanceData,
|
||||
cluster_details: bigAnimalClusterTypeData,
|
||||
instance_details: bigAnimalInstanceData,
|
||||
db_details: bigAnimalDatabaseData
|
||||
};
|
||||
}
|
||||
@ -165,54 +170,61 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
setCallRDSAPI(currentStep);
|
||||
let isError = (cloudProvider == '');
|
||||
switch(cloudProvider) {
|
||||
case 'rds':
|
||||
case CLOUD_PROVIDERS.RDS:
|
||||
switch (currentStep) {
|
||||
case 0:
|
||||
setCloudSelection('rds');
|
||||
setCloudSelection(CLOUD_PROVIDERS.RDS);
|
||||
break;
|
||||
case 1:
|
||||
isError = validateCloudStep1(cloudDBCred);
|
||||
break;
|
||||
case 2:
|
||||
isError = validateCloudStep2(cloudInstanceDetails, hostIP);
|
||||
break;
|
||||
case 3:
|
||||
isError = validateCloudStep2(cloudInstanceDetails, hostIP);
|
||||
break;
|
||||
case 4:
|
||||
isError = validateCloudStep3(cloudDBDetails, nodeInfo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'biganimal':
|
||||
case CLOUD_PROVIDERS.BIGANIMAL:
|
||||
switch (currentStep) {
|
||||
case 0:
|
||||
setCloudSelection('biganimal');
|
||||
setCloudSelection(CLOUD_PROVIDERS.BIGANIMAL);
|
||||
break;
|
||||
case 1:
|
||||
isError = !verificationIntiated;
|
||||
break;
|
||||
case 2:
|
||||
isError = validateBigAnimalStep2(bigAnimalInstanceData);
|
||||
isError = validateBigAnimalStep2(bigAnimalClusterTypeData);
|
||||
break;
|
||||
case 3:
|
||||
isError = validateBigAnimalStep3(bigAnimalDatabaseData, nodeInfo);
|
||||
isError = validateBigAnimalStep3(bigAnimalInstanceData);
|
||||
break;
|
||||
case 4:
|
||||
isError = validateBigAnimalStep4(bigAnimalDatabaseData, nodeInfo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'azure':
|
||||
case CLOUD_PROVIDERS.AZURE:
|
||||
switch (currentStep) {
|
||||
case 0:
|
||||
setCloudSelection('azure');
|
||||
setCloudSelection(CLOUD_PROVIDERS.AZURE);
|
||||
break;
|
||||
case 1:
|
||||
isError = !verificationIntiated;
|
||||
break;
|
||||
case 2:
|
||||
isError = validateAzureStep2(azureInstanceData);
|
||||
break;
|
||||
case 3:
|
||||
isError = validateAzureStep2(azureInstanceData);
|
||||
break;
|
||||
case 4:
|
||||
isError = validateAzureStep3(azureDatabaseData, nodeInfo);
|
||||
break;
|
||||
default:
|
||||
@ -223,9 +235,18 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
return isError;
|
||||
};
|
||||
|
||||
const onBeforeBack = (activeStep) => {
|
||||
return new Promise((resolve)=>{
|
||||
if(activeStep == 3 && (cloudProvider == CLOUD_PROVIDERS.RDS || cloudProvider == CLOUD_PROVIDERS.AZURE)) {
|
||||
resolve(true);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
const onBeforeNext = (activeStep) => {
|
||||
return new Promise((resolve, reject)=>{
|
||||
if(activeStep == 1 && cloudProvider == 'rds') {
|
||||
if(activeStep == 1 && cloudProvider == CLOUD_PROVIDERS.RDS) {
|
||||
setErrMsg([MESSAGE_TYPE.INFO, gettext('Validating credentials...')]);
|
||||
let _url = url_for('rds.verify_credentials');
|
||||
const post_data = {
|
||||
@ -239,14 +260,18 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
reject();
|
||||
} else {
|
||||
setErrMsg(['', '']);
|
||||
resolve();
|
||||
if (activeStep == 1) {
|
||||
resolve(true);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext('Error while checking cloud credentials')]);
|
||||
reject();
|
||||
});
|
||||
} else if(activeStep == 0 && cloudProvider == 'biganimal') {
|
||||
} else if(activeStep == 0 && cloudProvider == CLOUD_PROVIDERS.BIGANIMAL) {
|
||||
if (!isEmptyString(verificationURI)) { resolve(); return; }
|
||||
setErrMsg([MESSAGE_TYPE.INFO, gettext('Getting EDB BigAnimal verification URL...')]);
|
||||
validateBigAnimal()
|
||||
@ -260,20 +285,37 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]);
|
||||
reject();
|
||||
});
|
||||
} else if(activeStep == 2 && cloudProvider == 'azure'){
|
||||
setErrMsg([MESSAGE_TYPE.INFO, gettext('Checking cluster name availability...')]);
|
||||
checkClusternameAvailbility(azureInstanceData.name)
|
||||
} else if (activeStep == 1 && cloudProvider == CLOUD_PROVIDERS.BIGANIMAL ) {
|
||||
getProviderOptions()
|
||||
.then((res)=>{
|
||||
if (res.data && res.data.success == 0 ) {
|
||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext('Specified cluster name is already used.')]);
|
||||
}else{
|
||||
setErrMsg(['', '']);
|
||||
}
|
||||
setBigAnimalProviders(res);
|
||||
setErrMsg(['', '']);
|
||||
resolve();
|
||||
}).catch((error)=>{
|
||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]);
|
||||
reject();
|
||||
});
|
||||
} else if (cloudProvider == CLOUD_PROVIDERS.AZURE) {
|
||||
if (activeStep == 1) {
|
||||
// Skip the current step
|
||||
resolve(true);
|
||||
} else if (activeStep == 2) {
|
||||
setErrMsg([MESSAGE_TYPE.INFO, gettext('Checking cluster name availability...')]);
|
||||
checkClusternameAvailbility(azureInstanceData.name)
|
||||
.then((res)=>{
|
||||
if (res.data && res.data.success == 0 ) {
|
||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext('Specified cluster name is already used.')]);
|
||||
}else{
|
||||
setErrMsg(['', '']);
|
||||
}
|
||||
resolve();
|
||||
}).catch((error)=>{
|
||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]);
|
||||
reject();
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
else {
|
||||
setErrMsg(['', '']);
|
||||
@ -333,9 +375,9 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
setErrMsg([]);
|
||||
});
|
||||
|
||||
let cloud_providers = [{label: gettext('Amazon RDS'), value: 'rds', icon: <AWSIcon className={classes.icon} />},
|
||||
{label: gettext('EDB BigAnimal'), value: 'biganimal', icon: <BigAnimalIcon className={classes.icon} />},
|
||||
{'label': gettext('Azure PostgreSQL'), value: 'azure', icon: <AzureIcon className={classes.icon} /> }];
|
||||
let cloud_providers = [{label: gettext('Amazon RDS'), value: CLOUD_PROVIDERS.RDS, icon: <AWSIcon className={classes.icon} />},
|
||||
{label: gettext('EDB BigAnimal'), value: CLOUD_PROVIDERS.BIGANIMAL, icon: <BigAnimalIcon className={classes.icon} />},
|
||||
{'label': gettext('Azure PostgreSQL'), value: CLOUD_PROVIDERS.AZURE, icon: <AzureIcon className={classes.icon} /> }];
|
||||
|
||||
return (
|
||||
<CloudWizardEventsContext.Provider value={eventBus.current}>
|
||||
@ -347,7 +389,8 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
onStepChange={wizardStepChange}
|
||||
onSave={onSave}
|
||||
onHelp={onDialogHelp}
|
||||
beforeNext={onBeforeNext}>
|
||||
beforeNext={onBeforeNext}
|
||||
beforeBack={onBeforeBack}>
|
||||
<WizardStep stepId={0}>
|
||||
<Box className={classes.messageBox}>
|
||||
<Box className={classes.messagePadding}>{gettext('Select a cloud provider.')}</Box>
|
||||
@ -361,39 +404,51 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
</WizardStep>
|
||||
<WizardStep stepId={1} >
|
||||
<Box className={classes.buttonMarginEDB}>
|
||||
{cloudProvider == 'biganimal' && <Box className={classes.messageBox}>
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box className={classes.messageBox}>
|
||||
<Box>{gettext('The verification code to authenticate the pgAdmin to EDB BigAnimal is: ')} <strong>{verificationCode}</strong>
|
||||
<br/>{gettext('By clicking the below button, you will be redirected to the EDB BigAnimal authentication page in a new tab.')}
|
||||
</Box>
|
||||
</Box>}
|
||||
{cloudProvider == 'biganimal' && <PrimaryButton onClick={authenticateBigAnimal} disabled={verificationIntiated ? true: false}>
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <PrimaryButton onClick={authenticateBigAnimal} disabled={verificationIntiated ? true: false}>
|
||||
{gettext('Click here to authenticate yourself to EDB BigAnimal')}
|
||||
</PrimaryButton>}
|
||||
{cloudProvider == 'biganimal' && <Box className={classes.messageBox}>
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box className={classes.messageBox}>
|
||||
<Box ></Box>
|
||||
</Box>}
|
||||
</Box>
|
||||
{cloudProvider == 'rds' && <AwsCredentials cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setCloudDBCred={setCloudDBCred}/>}
|
||||
{cloudProvider == CLOUD_PROVIDERS.RDS && <AwsCredentials cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setCloudDBCred={setCloudDBCred}/>}
|
||||
<Box flexGrow={1}>
|
||||
{cloudProvider == 'azure' && <AzureCredentials cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setAzureCredData={setAzureCredData}/>}
|
||||
{cloudProvider == CLOUD_PROVIDERS.AZURE && <AzureCredentials cloudProvider={cloudProvider} nodeInfo={nodeInfo} nodeData={nodeData} setAzureCredData={setAzureCredData}/>}
|
||||
</Box>
|
||||
<FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} />
|
||||
</WizardStep>
|
||||
<WizardStep stepId={2} >
|
||||
{cloudProvider == 'rds' && callRDSAPI == 2 && <AwsInstanceDetails
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 2 && <BigAnimalClusterType
|
||||
cloudProvider={cloudProvider}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeData={nodeData}
|
||||
bigAnimalProviders={bigAnimalProviders}
|
||||
setBigAnimalClusterTypeData={setBigAnimalClusterTypeData}
|
||||
hostIP={hostIP}
|
||||
/> }
|
||||
<FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} />
|
||||
</WizardStep>
|
||||
<WizardStep stepId={3} >
|
||||
{cloudProvider == CLOUD_PROVIDERS.RDS && callRDSAPI == 3 && <AwsInstanceDetails
|
||||
cloudProvider={cloudProvider}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeData={nodeData}
|
||||
setCloudInstanceDetails={setCloudInstanceDetails}
|
||||
hostIP={hostIP} /> }
|
||||
{cloudProvider == 'biganimal' && callRDSAPI == 2 && <BigAnimalInstance
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 3 && <BigAnimalInstance
|
||||
cloudProvider={cloudProvider}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeData={nodeData}
|
||||
setBigAnimalInstanceData={setBigAnimalInstanceData}
|
||||
hostIP={hostIP}
|
||||
bigAnimalClusterTypeData={bigAnimalClusterTypeData}
|
||||
/> }
|
||||
{cloudProvider == 'azure' && callRDSAPI == 2 && <AzureInstanceDetails
|
||||
{cloudProvider == CLOUD_PROVIDERS.AZURE && callRDSAPI == 3 && <AzureInstanceDetails
|
||||
cloudProvider={cloudProvider}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeData={nodeData}
|
||||
@ -403,22 +458,23 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
/> }
|
||||
<FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} />
|
||||
</WizardStep>
|
||||
<WizardStep stepId={3} >
|
||||
{cloudProvider == 'rds' && <AwsDatabaseDetails
|
||||
<WizardStep stepId={4} >
|
||||
{cloudProvider == CLOUD_PROVIDERS.RDS && <AwsDatabaseDetails
|
||||
cloudProvider={cloudProvider}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeData={nodeData}
|
||||
setCloudDBDetails={setCloudDBDetails}
|
||||
/>
|
||||
}
|
||||
{cloudProvider == 'biganimal' && callRDSAPI == 3 && <BigAnimalDatabase
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 4 && <BigAnimalDatabase
|
||||
cloudProvider={cloudProvider}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeData={nodeData}
|
||||
setBigAnimalDatabaseData={setBigAnimalDatabaseData}
|
||||
bigAnimalClusterTypeData={bigAnimalClusterTypeData}
|
||||
/>
|
||||
}
|
||||
{cloudProvider == 'azure' && <AzureDatabaseDetails
|
||||
{cloudProvider == CLOUD_PROVIDERS.AZURE && <AzureDatabaseDetails
|
||||
cloudProvider={cloudProvider}
|
||||
nodeInfo={nodeInfo}
|
||||
nodeData={nodeData}
|
||||
@ -426,22 +482,23 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
||||
/>
|
||||
}
|
||||
</WizardStep>
|
||||
<WizardStep stepId={4} >
|
||||
<WizardStep stepId={5} >
|
||||
<Box className={classes.boxText}>{gettext('Please review the details before creating the cloud instance.')}</Box>
|
||||
<Paper variant="outlined" elevation={0} className={classes.summaryContainer}>
|
||||
{cloudProvider == 'rds' && callRDSAPI == 4 && <FinalSummary
|
||||
{cloudProvider == CLOUD_PROVIDERS.RDS && callRDSAPI == 5 && <FinalSummary
|
||||
cloudProvider={cloudProvider}
|
||||
instanceData={cloudInstanceDetails}
|
||||
databaseData={cloudDBDetails}
|
||||
/>
|
||||
}
|
||||
{cloudProvider == 'biganimal' && callRDSAPI == 4 && <FinalSummary
|
||||
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 5 && <FinalSummary
|
||||
cloudProvider={cloudProvider}
|
||||
instanceData={bigAnimalInstanceData}
|
||||
databaseData={bigAnimalDatabaseData}
|
||||
clusterTypeData={bigAnimalClusterTypeData}
|
||||
/>
|
||||
}
|
||||
{cloudProvider == 'azure' && callRDSAPI == 4 && <FinalSummary
|
||||
{cloudProvider == CLOUD_PROVIDERS.AZURE && callRDSAPI == 5 && <FinalSummary
|
||||
cloudProvider={cloudProvider}
|
||||
instanceData={azureInstanceData}
|
||||
databaseData={azureDatabaseData}
|
||||
|
@ -10,7 +10,7 @@
|
||||
import React from 'react';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { getNodeAjaxOptions, getNodeListById } from 'pgbrowser/node_ajax';
|
||||
import {CloudInstanceDetailsSchema, CloudDBCredSchema, DatabaseSchema} from './cloud_db_details_schema.ui';
|
||||
import {CloudInstanceDetailsSchema, CloudDBCredSchema, DatabaseSchema} from './aws_schema.ui';
|
||||
import SchemaView from '../../../../static/js/SchemaView';
|
||||
import url_for from 'sources/url_for';
|
||||
import getApiInstance from '../../../../static/js/api_instance';
|
||||
|
334
web/pgadmin/misc/cloud/static/js/aws_schema.ui.js
Normal file
@ -0,0 +1,334 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
|
||||
|
||||
class CloudInstanceDetailsSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
name: '',
|
||||
public_ip: initValues.hostIP,
|
||||
high_availability: false,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'name', label: gettext('Instance name'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'public_ip', label: gettext('Public IP range'), type: 'text',
|
||||
mode: ['create'],
|
||||
helpMessage: gettext('IP address range for allowed inbound traffic, for example: 127.0.0.1/32. Add multiple IP addresses/ranges separated with commas.'),
|
||||
}, {
|
||||
type: 'nested-fieldset', label: gettext('Version & Instance'),
|
||||
mode: ['create'],
|
||||
schema: new InstanceSchema(this.fieldOptions.version,
|
||||
this.fieldOptions.instance_type,
|
||||
this.fieldOptions.getInstances),
|
||||
}, {
|
||||
type: 'nested-fieldset', label: gettext('Storage'),
|
||||
mode: ['create'],
|
||||
schema: new StorageSchema(),
|
||||
}, {
|
||||
type: 'nested-fieldset', label: gettext('Availability'),
|
||||
mode: ['create'],
|
||||
schema: new HighAvailablity(),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CloudDBCredSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: null,
|
||||
region: '',
|
||||
access_key: '',
|
||||
secret_access_key: '',
|
||||
session_token: '',
|
||||
is_valid_cred: false,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'region', label: gettext('Region'),
|
||||
type: 'select',
|
||||
options: this.fieldOptions.regions,
|
||||
controlProps: { allowClear: false },
|
||||
noEmpty: true,
|
||||
helpMessage: gettext('The cloud instance will be deployed in the selected region.')
|
||||
},{
|
||||
id: 'access_key', label: gettext('AWS access key'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'secret_access_key', label: gettext('AWS secret access key'), type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'session_token', label: gettext('AWS session token'), type: 'multiline',
|
||||
mode: ['create'], noEmpty: false,
|
||||
helpMessage: gettext('Temporary AWS session required session token.')
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DatabaseSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues={}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
gid: undefined,
|
||||
db_name: '',
|
||||
db_username: '',
|
||||
db_password: '',
|
||||
db_confirm_password: '',
|
||||
db_port: 5432,
|
||||
...initValues,
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
validate(data, setErrMsg) {
|
||||
if(!isEmptyString(data.db_password) && !isEmptyString(data.db_confirm_password)
|
||||
&& data.db_password != data.db_confirm_password) {
|
||||
setErrMsg('db_confirm_password', gettext('Passwords do not match.'));
|
||||
return true;
|
||||
}
|
||||
if (!isEmptyString(data.db_confirm_password) && data.db_confirm_password.length < 8) {
|
||||
setErrMsg('db_confirm_password', gettext('Password must be 8 characters or more.'));
|
||||
return true;
|
||||
}
|
||||
if (data.db_confirm_password.includes('\'') || data.db_confirm_password.includes('"') ||
|
||||
data.db_confirm_password.includes('@') || data.db_confirm_password.includes('/')) {
|
||||
setErrMsg('db_confirm_password', gettext('Invalid passowrd.'));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [{
|
||||
id: 'gid', label: gettext('pgAdmin server group'), type: 'select',
|
||||
options: this.fieldOptions.server_groups,
|
||||
mode: ['create'],
|
||||
controlProps: { allowClear: false },
|
||||
noEmpty: true,
|
||||
}, {
|
||||
id: 'db_name', label: gettext('Database name'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'db_username', label: gettext('Username'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'db_password', label: gettext('Password'), type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
helpMessage: gettext('At least 8 printable ASCII characters. Cannot contain any of the following: / \(slash\), \'\(single quote\), "\(double quote\) and @ \(at sign\).')
|
||||
}, {
|
||||
id: 'db_confirm_password', label: gettext('Confirm password'),
|
||||
type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'db_port', label: gettext('Port'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class InstanceSchema extends BaseUISchema {
|
||||
constructor(versionOpts, instanceOpts, getInstances) {
|
||||
super({
|
||||
db_version: '',
|
||||
db_instance_class: 'm',
|
||||
instance_type: '',
|
||||
reload_instances: true,
|
||||
});
|
||||
this.versionOpts = versionOpts;
|
||||
this.instanceOpts = instanceOpts;
|
||||
this.getInstances = getInstances;
|
||||
this.instanceData = [];
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [{
|
||||
id: 'db_version', label: gettext('Database version'),
|
||||
type: 'select',
|
||||
options: this.versionOpts,
|
||||
controlProps: { allowClear: false },
|
||||
deps: ['name'],
|
||||
noEmpty: true,
|
||||
},{
|
||||
id: 'db_instance_class', label: gettext('Instance class'),
|
||||
type: 'select',
|
||||
options: [
|
||||
{'label': gettext('Standard classes (includes m classes)'), value: 'm'},
|
||||
{'label': gettext('Memory optimized classes (includes r & x classes)'), value: 'x'},
|
||||
{'label': gettext('Burstable classes (includes t classes)'), value: 't'},
|
||||
], noEmpty: true
|
||||
},{
|
||||
id: 'instance_type', label: gettext('Instance type'),
|
||||
options: this.instanceOpts,
|
||||
deps: ['db_version', 'db_instance_class'],
|
||||
depChange: (state, source)=> {
|
||||
if (source[0] == 'db_instance_class') {
|
||||
return {reload_instances: false};
|
||||
} else {
|
||||
state.instanceData = [];
|
||||
return {reload_instances: true};
|
||||
}
|
||||
},
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.getInstances(state.db_version,
|
||||
state.reload_instances, state.instanceData),
|
||||
optionsLoaded: (options) => { state.instanceData = options; },
|
||||
optionsReloadBasis: state.db_version + (state.db_instance_class || 'm'),
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
let pattern = 'db.m';
|
||||
let pattern_1 = 'db.m';
|
||||
|
||||
if (state.db_instance_class) {
|
||||
pattern = 'db.' + state.db_instance_class;
|
||||
pattern_1 = 'db.' + state.db_instance_class;
|
||||
}
|
||||
if (state.db_instance_class == 'x') {
|
||||
pattern_1 = 'db.' + 'r';
|
||||
}
|
||||
return options.filter((option) => {
|
||||
return (option.value.includes(pattern) || option.value.includes(pattern_1));
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class StorageSchema extends BaseUISchema {
|
||||
constructor() {
|
||||
super({
|
||||
storage_type: 'io1',
|
||||
storage_size: 100,
|
||||
storage_IOPS: 3000,
|
||||
storage_msg: 'Minimum: 20 GiB. Maximum: 16,384 GiB.'
|
||||
});
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'storage_type', label: gettext('Storage type'), type: 'select',
|
||||
mode: ['create'],
|
||||
options: [
|
||||
{'label': gettext('General Purpose SSD (gp2)'), 'value': 'gp2'},
|
||||
{'label': gettext('Provisioned IOPS SSD (io1)'), 'value': 'io1'},
|
||||
{'label': gettext('Magnetic'), 'value': 'standard'}
|
||||
], noEmpty: true,
|
||||
},{
|
||||
id: 'storage_size', label: gettext('Allocated storage'), type: 'text',
|
||||
mode: ['create'], noEmpty: true, deps: ['storage_type'],
|
||||
depChange: (state, source)=> {
|
||||
if (source[0] !== 'storage_size')
|
||||
if(state.storage_type === 'io1') {
|
||||
return {storage_size: 100};
|
||||
} else if(state.storage_type === 'gp2') {
|
||||
return {storage_size: 20};
|
||||
} else {
|
||||
return {storage_size: 5};
|
||||
}
|
||||
},
|
||||
helpMessage: gettext('Size in GiB.')
|
||||
}, {
|
||||
id: 'storage_IOPS', label: gettext('Provisioned IOPS'), type: 'text',
|
||||
mode: ['create'],
|
||||
visible: (state) => {
|
||||
return state.storage_type === 'io1';
|
||||
} , deps: ['storage_type'],
|
||||
depChange: (state, source) => {
|
||||
if (source[0] !== 'storage_IOPS') {
|
||||
if(state.storage_type === 'io1') {
|
||||
return {storage_IOPS: 3000};
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class HighAvailablity extends BaseUISchema {
|
||||
constructor() {
|
||||
super({
|
||||
high_availability: false
|
||||
});
|
||||
}
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'high_availability',
|
||||
label: gettext('High availability'),
|
||||
type: 'switch',
|
||||
mode: ['create'],
|
||||
helpMessage: gettext(
|
||||
'Creates a standby in a different Availability Zone (AZ) to provide data redundancy, eliminate I/O freezes, and minimize latency spikes during system backups.'
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
CloudInstanceDetailsSchema,
|
||||
CloudDBCredSchema,
|
||||
DatabaseSchema
|
||||
};
|
@ -10,59 +10,143 @@
|
||||
import React from 'react';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import { getNodeAjaxOptions, getNodeListById } from 'pgbrowser/node_ajax';
|
||||
import {BigAnimalClusterSchema, BigAnimalDatabaseSchema} from './cloud_db_details_schema.ui';
|
||||
import {BigAnimalClusterSchema, BigAnimalDatabaseSchema, BigAnimalClusterTypeSchema} from './biganimal_schema.ui';
|
||||
import SchemaView from '../../../../static/js/SchemaView';
|
||||
import url_for from 'sources/url_for';
|
||||
import getApiInstance from '../../../../static/js/api_instance';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import PropTypes from 'prop-types';
|
||||
import gettext from 'sources/gettext';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { AWSIcon, MSAzureIcon } from '../../../../static/js/components/ExternalIcon';
|
||||
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
({
|
||||
providerHeight: {
|
||||
height: '5em',
|
||||
},
|
||||
AwsIcon: {
|
||||
width: '6rem',
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
const axiosApi = getApiInstance();
|
||||
|
||||
export function getProviderOptions() {
|
||||
return new Promise((resolve, reject) => {
|
||||
axiosApi.get(url_for('biganimal.providers'))
|
||||
.then((res) => {
|
||||
if (res.data.data) {
|
||||
let _options= [],
|
||||
_options_label = {'azure': <MSAzureIcon key='1' />,
|
||||
'aws': <AWSIcon style={{width: '6rem'}} key = '2'/>};
|
||||
_.forEach(res.data.data, (val) => {
|
||||
_options.push({
|
||||
'label': _options_label[val['value']],
|
||||
'value': val['value'],
|
||||
'disabled': !val['connected']
|
||||
});
|
||||
});
|
||||
resolve(_options);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(gettext(`Error while getting the biganimal providers: ${error.response.data.errormsg}`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// BigAnimal Cluster Type
|
||||
export function BigAnimalClusterType(props) {
|
||||
const [bigAnimalClusterType, setBigAnimalClusterType] = React.useState();
|
||||
const classes = useStyles();
|
||||
|
||||
React.useMemo(() => {
|
||||
const bigAnimalClusterTypeSchema = new BigAnimalClusterTypeSchema({
|
||||
providers: ()=>getNodeAjaxOptions('biganimal_providers', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
|
||||
useCache:false,
|
||||
cacheNode: 'server',
|
||||
customGenerateUrl: ()=>{
|
||||
return url_for('biganimal.providers');
|
||||
}
|
||||
}),
|
||||
}, {
|
||||
nodeInfo: props.nodeInfo,
|
||||
nodeData: props.nodeData,
|
||||
hostIP: props.hostIP,
|
||||
classes: classes,
|
||||
bigAnimalProviders: props.bigAnimalProviders,
|
||||
});
|
||||
setBigAnimalClusterType(bigAnimalClusterTypeSchema);
|
||||
}, [props.cloudProvider]);
|
||||
|
||||
return <SchemaView
|
||||
formType={'dialog'}
|
||||
getInitData={() => { /*This is intentional (SonarQube)*/ }}
|
||||
viewHelperProps={{ mode: 'create' }}
|
||||
schema={bigAnimalClusterType}
|
||||
showFooter={false}
|
||||
isTabView={false}
|
||||
onDataChange={(isChanged, changedData) => {
|
||||
props.setBigAnimalClusterTypeData(changedData);
|
||||
}}
|
||||
/>;
|
||||
}
|
||||
BigAnimalClusterType.propTypes = {
|
||||
nodeInfo: PropTypes.object,
|
||||
nodeData: PropTypes.object,
|
||||
cloudProvider: PropTypes.string,
|
||||
setBigAnimalClusterTypeData: PropTypes.func,
|
||||
hostIP: PropTypes.string,
|
||||
bigAnimalProviders: PropTypes.object,
|
||||
};
|
||||
|
||||
|
||||
// BigAnimal Instance
|
||||
export function BigAnimalInstance(props) {
|
||||
const [bigAnimalInstance, setBigAnimalInstance] = React.useState();
|
||||
|
||||
React.useMemo(() => {
|
||||
const bigAnimalSchema = new BigAnimalClusterSchema({
|
||||
regions: ()=>getNodeAjaxOptions('biganimal_regions', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
|
||||
regions: (provider_id)=>getNodeAjaxOptions('biganimal_regions', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
|
||||
useCache:false,
|
||||
cacheNode: 'server',
|
||||
customGenerateUrl: ()=>{
|
||||
return url_for('biganimal.regions');
|
||||
return url_for('biganimal.regions', {'provider_id': provider_id || 0});
|
||||
}
|
||||
}),
|
||||
instance_types: (region_id)=>{
|
||||
instance_types: (region_id, provider_id)=>{
|
||||
if (isEmptyString(region_id)) return [];
|
||||
return getNodeAjaxOptions('biganimal_instance_types', pgAdmin.Browser.Nodes['server'],
|
||||
props.nodeInfo, props.nodeData, {
|
||||
useCache:false,
|
||||
cacheNode: 'server',
|
||||
customGenerateUrl: ()=>{
|
||||
return url_for('biganimal.instance_types', {'region_id': region_id || 0});
|
||||
return url_for('biganimal.instance_types', {'region_id': region_id || 0, 'provider_id': provider_id || 0});
|
||||
}
|
||||
});
|
||||
},
|
||||
volume_types: (region_id)=>{
|
||||
volume_types: (region_id, provider_id)=>{
|
||||
if (isEmptyString(region_id)) return [];
|
||||
return getNodeAjaxOptions('biganimal_volume_types', pgAdmin.Browser.Nodes['server'],
|
||||
props.nodeInfo, props.nodeData, {
|
||||
useCache:false,
|
||||
cacheNode: 'server',
|
||||
customGenerateUrl: ()=>{
|
||||
return url_for('biganimal.volume_types', {'region_id': region_id || 0});
|
||||
return url_for('biganimal.volume_types', {'region_id': region_id || 0, 'provider_id': provider_id || 0});
|
||||
}
|
||||
});
|
||||
},
|
||||
volume_properties: (region_id, volume_type)=>{
|
||||
volume_properties: (region_id, provider_id, volume_type)=>{
|
||||
if (isEmptyString(region_id) || isEmptyString(volume_type)) return [];
|
||||
return getNodeAjaxOptions('biganimal_volume_properties', pgAdmin.Browser.Nodes['server'],
|
||||
props.nodeInfo, props.nodeData, {
|
||||
useCache:false,
|
||||
cacheNode: 'server',
|
||||
customGenerateUrl: ()=>{
|
||||
return url_for('biganimal.volume_properties', {'region_id': region_id || 0, 'volume_type': volume_type || ''});
|
||||
return url_for('biganimal.volume_properties', {'region_id': region_id || 0, 'provider_id': provider_id || 0, 'volume_type': volume_type || ''});
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -70,6 +154,7 @@ export function BigAnimalInstance(props) {
|
||||
nodeInfo: props.nodeInfo,
|
||||
nodeData: props.nodeData,
|
||||
hostIP: props.hostIP,
|
||||
provider: props.bigAnimalClusterTypeData.provider,
|
||||
});
|
||||
setBigAnimalInstance(bigAnimalSchema);
|
||||
}, [props.cloudProvider]);
|
||||
@ -92,10 +177,11 @@ BigAnimalInstance.propTypes = {
|
||||
cloudProvider: PropTypes.string,
|
||||
setBigAnimalInstanceData: PropTypes.func,
|
||||
hostIP: PropTypes.string,
|
||||
bigAnimalClusterTypeData: PropTypes.object,
|
||||
};
|
||||
|
||||
|
||||
// BigAnimal Instance
|
||||
// BigAnimal Database
|
||||
export function BigAnimalDatabase(props) {
|
||||
const [bigAnimalDatabase, setBigAnimalDatabase] = React.useState();
|
||||
|
||||
@ -108,15 +194,18 @@ export function BigAnimalDatabase(props) {
|
||||
return url_for('biganimal.db_types');
|
||||
}
|
||||
}),
|
||||
db_versions: (db_type)=>getNodeAjaxOptions('biganimal_db_versions', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
|
||||
db_versions: (cluster_type, pg_type)=>getNodeAjaxOptions('biganimal_db_versions', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
|
||||
useCache:false,
|
||||
cacheNode: 'server',
|
||||
customGenerateUrl: ()=>{
|
||||
return url_for('biganimal.db_versions', {'db_type': db_type || 'pg'});
|
||||
return url_for('biganimal.db_versions', {'cluster_type': cluster_type || 'single', 'pg_type': pg_type || 'pg'});
|
||||
}
|
||||
}),
|
||||
server_groups: ()=>getNodeListById(pgAdmin.Browser.Nodes['server_group'], props.nodeInfo, props.nodeData),
|
||||
}, {gid: props.nodeInfo['server_group']._id});
|
||||
}, {
|
||||
gid: props.nodeInfo['server_group']._id,
|
||||
cluster_type: props.bigAnimalClusterTypeData.cluster_type,
|
||||
});
|
||||
setBigAnimalDatabase(bigAnimalDBSchema);
|
||||
}, [props.cloudProvider]);
|
||||
|
||||
@ -137,12 +226,13 @@ BigAnimalDatabase.propTypes = {
|
||||
nodeData: PropTypes.object,
|
||||
cloudProvider: PropTypes.string,
|
||||
setBigAnimalDatabaseData: PropTypes.func,
|
||||
bigAnimalClusterTypeData: PropTypes.object
|
||||
};
|
||||
|
||||
|
||||
export function validateBigAnimal() {
|
||||
return new Promise((resolve, reject)=>{
|
||||
let _url = url_for('biganimal.verification') ;
|
||||
let _url = url_for('biganimal.verification');
|
||||
axiosApi.get(_url)
|
||||
.then((res) => {
|
||||
if (res.data.data) {
|
||||
@ -162,7 +252,7 @@ function createData(name, value) {
|
||||
return { name, value };
|
||||
}
|
||||
|
||||
export function getBigAnimalSummary(cloud, bigAnimalInstanceData, bigAnimalDatabaseData) {
|
||||
export function getBigAnimalSummary(cloud, bigAnimalClusterTypeData, bigAnimalInstanceData, bigAnimalDatabaseData) {
|
||||
const rows1 = [
|
||||
createData(gettext('Cloud'), cloud),
|
||||
createData(gettext('Instance name'), bigAnimalInstanceData.name),
|
||||
@ -174,49 +264,72 @@ export function getBigAnimalSummary(cloud, bigAnimalInstanceData, bigAnimalDatab
|
||||
let instance_size = bigAnimalInstanceData.instance_size.split('||');
|
||||
|
||||
const rows2 = [
|
||||
createData(gettext('Cluster type'), bigAnimalClusterTypeData.cluster_type),
|
||||
createData(gettext('No. of Standby Replicas'), bigAnimalClusterTypeData.replicas),
|
||||
createData(gettext('Provider'), bigAnimalClusterTypeData.provider),
|
||||
];
|
||||
|
||||
const rows3 = [
|
||||
createData(gettext('Instance type'), bigAnimalInstanceData.instance_type),
|
||||
createData(gettext('Instance series'), bigAnimalInstanceData.instance_series),
|
||||
createData(gettext('Instance size'), instance_size[0]),
|
||||
];
|
||||
|
||||
const rows3 = [
|
||||
const rows4 = [
|
||||
createData(gettext('Volume type'), bigAnimalInstanceData.volume_type),
|
||||
createData(gettext('Volume properties'), bigAnimalInstanceData.volume_properties),
|
||||
createData(gettext('Volume size'), bigAnimalInstanceData.volume_size),
|
||||
createData(gettext('Volume IOPS'), bigAnimalInstanceData.volume_IOPS),
|
||||
];
|
||||
|
||||
const rows4 = [
|
||||
const rows5 = [
|
||||
createData(gettext('Password'), 'xxxxxxx'),
|
||||
createData(gettext('Database Type'), bigAnimalDatabaseData.database_type),
|
||||
createData(gettext('Database Version'), bigAnimalDatabaseData.postgres_version),
|
||||
createData(gettext('High Availability'), bigAnimalDatabaseData.high_availability),
|
||||
createData(gettext('No. of Standby Replicas'), bigAnimalDatabaseData.replicas),
|
||||
];
|
||||
|
||||
return [rows1, rows2, rows3, rows4];
|
||||
return [rows1, rows2, rows3, rows4, rows5];
|
||||
}
|
||||
|
||||
export function validateBigAnimalStep2(cloudInstanceDetails) {
|
||||
export function validateBigAnimalStep2(cloudTypeDetails) {
|
||||
let isError = false;
|
||||
if (isEmptyString(cloudInstanceDetails.name) ||
|
||||
isEmptyString(cloudInstanceDetails.region) || isEmptyString(cloudInstanceDetails.instance_type) ||
|
||||
isEmptyString(cloudInstanceDetails.instance_series)|| isEmptyString(cloudInstanceDetails.instance_size) ||
|
||||
isEmptyString(cloudInstanceDetails.volume_type)|| isEmptyString(cloudInstanceDetails.volume_properties)) {
|
||||
if (isEmptyString(cloudTypeDetails.cluster_type) || isEmptyString(cloudTypeDetails.provider) ||
|
||||
(cloudTypeDetails.cluster_type == 'ha' && cloudTypeDetails.replicas == 0)
|
||||
) {
|
||||
isError = true;
|
||||
}
|
||||
|
||||
return isError;
|
||||
}
|
||||
|
||||
export function validateBigAnimalStep3(cloudDBDetails, nodeInfo) {
|
||||
export function validateBigAnimalStep3(cloudDetails) {
|
||||
let isError = false;
|
||||
if (isEmptyString(cloudDetails.name) ||
|
||||
isEmptyString(cloudDetails.region) || isEmptyString(cloudDetails.instance_type) ||
|
||||
isEmptyString(cloudDetails.instance_series)|| isEmptyString(cloudDetails.instance_size) ||
|
||||
isEmptyString(cloudDetails.volume_type) || (cloudDetails.provider != 'aws' && isEmptyString(cloudDetails.volume_properties)) ) {
|
||||
isError = true;
|
||||
}
|
||||
|
||||
if (cloudDetails.provider == 'aws') {
|
||||
if (isEmptyString(cloudDetails.volume_IOPS) || isEmptyString(cloudDetails.volume_IOPS) || (cloudDetails.volume_IOPS != 'io2' &&
|
||||
(cloudDetails.volume_size < 1 || cloudDetails.volume_size > 16384)) ||
|
||||
(cloudDetails.volume_IOPS == 'io2' && (cloudDetails.volume_size < 4 || cloudDetails.volume_size > 16384)) ||
|
||||
(cloudDetails.volume_IOPS != 'io2' && cloudDetails.volume_IOPS != 3000) ||
|
||||
(cloudDetails.volume_IOPS == 'io2' && (cloudDetails.volume_IOPS < 100 || cloudDetails.volume_IOPS > 2000))) {
|
||||
isError = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isError;
|
||||
}
|
||||
|
||||
export function validateBigAnimalStep4(cloudDBDetails, nodeInfo) {
|
||||
let isError = false;
|
||||
if (isEmptyString(cloudDBDetails.password) ||
|
||||
isEmptyString(cloudDBDetails.database_type) || isEmptyString(cloudDBDetails.postgres_version)) {
|
||||
isError = true;
|
||||
}
|
||||
|
||||
if(cloudDBDetails.high_availability && (isEmptyString(cloudDBDetails.replicas) || cloudDBDetails.replicas <= 0)) {
|
||||
isError = true;
|
||||
}
|
||||
if (isEmptyString(cloudDBDetails.gid)) cloudDBDetails.gid = nodeInfo['server_group']._id;
|
||||
return isError;
|
||||
}
|
||||
|
468
web/pgadmin/misc/cloud/static/js/biganimal_schema.ui.js
Normal file
@ -0,0 +1,468 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2022, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import { CLOUD_PROVIDERS } from './cloud_constants';
|
||||
|
||||
class BigAnimalClusterTypeSchema extends BaseUISchema {
|
||||
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
cluster_type: '',
|
||||
replicas: 0,
|
||||
provider: '',
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
validate(data, setErrMsg) {
|
||||
if (data.cluster_type == 'ha' && data.replicas == 0) {
|
||||
setErrMsg('replicas', gettext('Please select number of stand by replicas.'));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'cluster_type', label: gettext('Cluster type'), noEmpty: true,
|
||||
type: () => {
|
||||
return {
|
||||
type: 'toggle',
|
||||
options: [
|
||||
{'label': gettext('Single Node'), value: 'single'},
|
||||
{'label': gettext('High Availability'), value: 'ha'},
|
||||
{'label': gettext('Extreme High Availability'), value: 'eha'},
|
||||
],
|
||||
};
|
||||
},
|
||||
}, {
|
||||
id: 'replicas', label: gettext('Number of standby replicas'), type: 'select',
|
||||
mode: ['create'], deps: ['cluster_type'],
|
||||
controlProps: { allowClear: false },
|
||||
helpMessage: gettext('Adding standby replicas will increase your number of CPUs, as well as your cost.'),
|
||||
options: [
|
||||
{'label': gettext('1'), 'value': 1},
|
||||
{'label': gettext('2'), 'value': 2},
|
||||
],
|
||||
disabled: (state) => {
|
||||
return state.cluster_type != 'ha';
|
||||
}
|
||||
}, { id: 'provider', label: gettext('Cluster provider'), noEmpty: true,
|
||||
type: 'toggle', className: this.initValues.classes.providerHeight,
|
||||
options: this.initValues.bigAnimalProviders,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class BigAnimalInstanceSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues={}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
instance_type: '',
|
||||
instance_series: '',
|
||||
instance_size: '',
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'instance_type', label: gettext('Instance type'),
|
||||
mode: ['create'],
|
||||
deps: [['region']],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.instance_types(state.region, state.provider),
|
||||
optionsReloadBasis: state.region,
|
||||
optionsLoaded: (options) => { state.instanceData = options; },
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
let _types = _.uniq(_.map(options, 'category')),
|
||||
_options = [];
|
||||
_.forEach(_types, (region) => {
|
||||
_options.push({
|
||||
'label': region,
|
||||
'value': region
|
||||
});
|
||||
});
|
||||
return _options;
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
noEmpty: true,
|
||||
},{
|
||||
id: 'instance_series', label: gettext('Instance series'),
|
||||
mode: ['create'], deps: ['instance_type'],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: state.instanceData,
|
||||
optionsReloadBasis: state.instance_type,
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
let _types = _.filter(options, {'category': state.instance_type}),
|
||||
_options = [];
|
||||
_types = _.uniq(_.map(_types, 'familyName'));
|
||||
_.forEach(_types, (value) => {
|
||||
_options.push({
|
||||
'label': value,
|
||||
'value': value
|
||||
});
|
||||
});
|
||||
return _options;
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
noEmpty: true,
|
||||
},{
|
||||
id: 'instance_size', label: gettext('Instance size'),
|
||||
mode: ['create'], deps: ['instance_series'],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: state.instanceData,
|
||||
optionsReloadBasis: state.instance_series,
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
let _types = _.filter(options, {'familyName': state.instance_series}),
|
||||
_options = [];
|
||||
_.forEach(_types, (value) => {
|
||||
_options.push({
|
||||
'label': value.instanceTypeName + ' (' + value.cpu + 'vCPU, ' + value.ram + 'GB RAM)',
|
||||
'value': value.instanceTypeName + ' (' + value.cpu + 'vCPU, ' + value.ram + 'GB RAM)' + '||' + value.instanceTypeId,
|
||||
});
|
||||
});
|
||||
return _options;
|
||||
},
|
||||
}
|
||||
};
|
||||
}, noEmpty: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BigAnimalVolumeSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
volume_type: '',
|
||||
volume_properties: '',
|
||||
volume_size: 4,
|
||||
volume_IOPS: '',
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
this.volumeType = '';
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
validate(data, setErrMsg) {
|
||||
if (data.provider != CLOUD_PROVIDERS.AWS && isEmptyString(data.volume_properties)) {
|
||||
setErrMsg('replicas', gettext('Please select volume properties.'));
|
||||
return true;
|
||||
}
|
||||
if (data.provider == CLOUD_PROVIDERS.AWS) {
|
||||
if (isEmptyString(data.volume_IOPS)) {
|
||||
setErrMsg('replicas', gettext('Please select volume IOPS.'));
|
||||
return true;
|
||||
}
|
||||
if (!isEmptyString(data.volume_size)) {
|
||||
if( data.volume_IOPS != 'io2' && (data.volume_size < 1 || data.volume_size > 16384)) {
|
||||
setErrMsg('replicas', gettext('Please enter the volume size in the range between 1 tp 16384.'));
|
||||
return true;
|
||||
}
|
||||
if (data.volume_IOPS == 'io2' && (data.volume_size < 4 || data.volume_size > 16384)) {
|
||||
setErrMsg('replicas', gettext('Please enter the volume size in the range between 4 tp 16384.'));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!isEmptyString(data.volume_IOPS)) {
|
||||
if(data.volume_IOPS != 'io2' && data.volume_IOPS != 3000) {
|
||||
setErrMsg('replicas', gettext('Please enter the volume IOPS 3000.'));
|
||||
return true;
|
||||
}
|
||||
if(data.volume_IOPS == 'io2' && (data.volume_IOPS < 100 || data.volume_IOPS > 2000)) {
|
||||
setErrMsg('replicas', gettext('Please enter the volume IOPS in the range between 100 tp 2000.'));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
let obj = this;
|
||||
return [
|
||||
{
|
||||
id: 'volume_type', label: gettext('Volume type'),
|
||||
mode: ['create'], deps: [['region'], ['instance_series']],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.volume_types(state.region, state.provider),
|
||||
optionsReloadBasis: state.region,
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
return options.filter((option) => {
|
||||
return (option.supportedInstanceFamilyNames.includes(state.instance_series));
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
}, noEmpty: true,
|
||||
},{
|
||||
id: 'volume_properties', label: gettext('Volume properties'),
|
||||
mode: ['create'], deps: ['volume_type', 'provider'],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.volume_properties(state.region, state.provider, state.volume_type),
|
||||
optionsReloadBasis: state.volume_type,
|
||||
};
|
||||
},
|
||||
visible: (state) => {
|
||||
return state.provider !== CLOUD_PROVIDERS.AWS;
|
||||
},
|
||||
}, {
|
||||
id: 'volume_size', label: gettext('Size'), type: 'text',
|
||||
mode: ['create'], noEmpty: true, deps: ['volume_type'],
|
||||
depChange: (state, source)=> {
|
||||
obj.volumeType = state.volume_type;
|
||||
if (source[0] !== 'volume_size') {
|
||||
if(state.volume_type == 'io2' || state.provider === CLOUD_PROVIDERS.AZURE) {
|
||||
return {volume_size: 4};
|
||||
} else {
|
||||
return {volume_size: 1};
|
||||
}
|
||||
}
|
||||
},
|
||||
visible: (state) => {
|
||||
return state.provider === CLOUD_PROVIDERS.AWS;
|
||||
},
|
||||
helpMessage: obj.volumeType == 'io2' ? gettext('Size (4-16,384 GiB)') : gettext('Size (1-16,384 GiB)')
|
||||
}, {
|
||||
id: 'volume_IOPS', label: gettext('IOPS'), type: 'text',
|
||||
mode: ['create'],
|
||||
helpMessage: obj.volumeType == 'io2' ? gettext('IOPS (100-2,000)') : gettext('IOPS (3,000-3,000)'),
|
||||
visible: (state) => {
|
||||
return state.provider === CLOUD_PROVIDERS.AWS;
|
||||
}, deps: ['volume_type'],
|
||||
depChange: (state, source) => {
|
||||
obj.volumeType = state.volume_type;
|
||||
if (source[0] !== 'volume_IOPS') {
|
||||
if (state.provider === CLOUD_PROVIDERS.AWS) {
|
||||
if(state.volume_type === 'io2') {
|
||||
return {volume_IOPS: 100};
|
||||
} else {
|
||||
return {volume_IOPS: 3000};
|
||||
}
|
||||
} else {
|
||||
return {volume_IOPS: 120};
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BigAnimalDatabaseSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
password: '',
|
||||
confirm_password: '',
|
||||
database_type: '',
|
||||
postgres_version: '',
|
||||
high_availability: false,
|
||||
replicas: 0,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
|
||||
validate(data, setErrMsg) {
|
||||
if(!isEmptyString(data.password) && !isEmptyString(data.confirm_password)
|
||||
&& data.password != data.confirm_password) {
|
||||
setErrMsg('confirm_password', gettext('Passwords do not match.'));
|
||||
return true;
|
||||
}
|
||||
if (!isEmptyString(data.confirm_password) && data.confirm_password.length < 12) {
|
||||
setErrMsg('confirm_password', gettext('Password must be 12 characters or more.'));
|
||||
return true;
|
||||
}
|
||||
if (data.high_availability && (isEmptyString(data.replicas) || data.replicas <= 0)) {
|
||||
setErrMsg('replicas', gettext('Please select number of stand by replicas.'));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'gid', label: gettext('pgAdmin server group'), type: 'select',
|
||||
options: this.fieldOptions.server_groups,
|
||||
mode: ['create'],
|
||||
controlProps: { allowClear: false },
|
||||
noEmpty: true,
|
||||
}, {
|
||||
id: 'database_type', label: gettext('Database type'), mode: ['create'],
|
||||
type: 'select',
|
||||
options: this.fieldOptions.db_types,
|
||||
noEmpty: true, orientation: 'vertical',
|
||||
},{
|
||||
id: 'postgres_version', label: gettext('Database version'),
|
||||
mode: ['create'], noEmpty: true, deps: ['database_type'],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.db_versions(this.initValues.cluster_typ, state.database_type),
|
||||
optionsReloadBasis: state.database_type,
|
||||
};
|
||||
},
|
||||
},{
|
||||
id: 'password', label: gettext('Database password'), type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
},{
|
||||
id: 'confirm_password', label: gettext('Confirm password'), type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
},
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BigAnimalClusterSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
name: '',
|
||||
region: '',
|
||||
cloud_type: 'public',
|
||||
biganimal_public_ip: initValues.hostIP,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
|
||||
this.instance_types = new BigAnimalInstanceSchema({
|
||||
instance_types: this.fieldOptions.instance_types,
|
||||
});
|
||||
this.volume_types = new BigAnimalVolumeSchema({
|
||||
volume_types: this.fieldOptions.volume_types,
|
||||
volume_properties: this.fieldOptions.volume_properties
|
||||
});
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'name', label: gettext('Cluster name'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
},{
|
||||
id: 'region', label: gettext('Region'),
|
||||
controlProps: { allowClear: false },
|
||||
noEmpty: true,
|
||||
mode: ['create'],
|
||||
dep: [this.initValues.provider],
|
||||
type: () => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.regions(this.initValues.provider),
|
||||
optionsReloadBasis: this.initValues.provider,
|
||||
};
|
||||
},
|
||||
},{
|
||||
id: 'biganimal_public_ip', label: gettext('Public IP range'), type: 'text',
|
||||
mode: ['create'],
|
||||
helpMessage: gettext('IP address range for allowed inbound traffic, for example: 127.0.0.1/32. Add multiple IP addresses/ranges separated with commas. Leave blank for 0.0.0.0/0'),
|
||||
},{
|
||||
type: 'nested-fieldset', label: gettext('Instance Type'),
|
||||
mode: ['create'], deps: ['region'],
|
||||
schema: this.instance_types,
|
||||
},{
|
||||
type: 'nested-fieldset', label: gettext('Storage'),
|
||||
mode: ['create'], deps: ['region'],
|
||||
schema: this.volume_types,
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
BigAnimalClusterSchema,
|
||||
BigAnimalDatabaseSchema,
|
||||
BigAnimalClusterTypeSchema
|
||||
};
|
@ -69,8 +69,8 @@ export function FinalSummary(props) {
|
||||
summaryHeader = ['Cloud Details', 'Version and Instance Details', 'Storage Details', 'Database Details'];
|
||||
|
||||
if (props.cloudProvider == 'biganimal') {
|
||||
summary = getBigAnimalSummary(props.cloudProvider, props.instanceData, props.databaseData);
|
||||
summaryHeader[1] = 'Version Details';
|
||||
summary = getBigAnimalSummary(props.cloudProvider, props.clusterTypeData, props.instanceData, props.databaseData);
|
||||
summaryHeader = ['Cloud Details', 'Cluster Details' ,'Version Details', 'Storage Details', 'Database Details'];
|
||||
} else if(props.cloudProvider == 'azure') {
|
||||
summaryHeader.push('Network Connectivity','Availability');
|
||||
summary = getAzureSummary(props.cloudProvider, props.instanceData, props.databaseData);
|
||||
@ -107,4 +107,5 @@ FinalSummary.propTypes = {
|
||||
cloudProvider: PropTypes.string,
|
||||
instanceData: PropTypes.object,
|
||||
databaseData: PropTypes.object,
|
||||
clusterTypeData: PropTypes.object,
|
||||
};
|
||||
|
15
web/pgadmin/misc/cloud/static/js/cloud_constants.js
Normal file
@ -0,0 +1,15 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
export const CLOUD_PROVIDERS = {
|
||||
AZURE: 'azure',
|
||||
BIGANIMAL: 'biganimal',
|
||||
AWS: 'aws',
|
||||
RDS: 'rds',
|
||||
};
|
@ -1,678 +0,0 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
|
||||
|
||||
class CloudInstanceDetailsSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
name: '',
|
||||
public_ip: initValues.hostIP,
|
||||
high_availability: false,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'name', label: gettext('Instance name'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'public_ip', label: gettext('Public IP range'), type: 'text',
|
||||
mode: ['create'],
|
||||
helpMessage: gettext('IP address range for allowed inbound traffic, for example: 127.0.0.1/32. Add multiple IP addresses/ranges separated with commas.'),
|
||||
}, {
|
||||
type: 'nested-fieldset', label: gettext('Version & Instance'),
|
||||
mode: ['create'],
|
||||
schema: new InstanceSchema(this.fieldOptions.version,
|
||||
this.fieldOptions.instance_type,
|
||||
this.fieldOptions.getInstances),
|
||||
}, {
|
||||
type: 'nested-fieldset', label: gettext('Storage'),
|
||||
mode: ['create'],
|
||||
schema: new StorageSchema(),
|
||||
}, {
|
||||
type: 'nested-fieldset', label: gettext('Availability'),
|
||||
mode: ['create'],
|
||||
schema: new HighAvailablity(),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CloudDBCredSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: null,
|
||||
region: '',
|
||||
access_key: '',
|
||||
secret_access_key: '',
|
||||
session_token: '',
|
||||
is_valid_cred: false,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'region', label: gettext('Region'),
|
||||
type: 'select',
|
||||
options: this.fieldOptions.regions,
|
||||
controlProps: { allowClear: false },
|
||||
noEmpty: true,
|
||||
helpMessage: gettext('The cloud instance will be deployed in the selected region.')
|
||||
},{
|
||||
id: 'access_key', label: gettext('AWS access key'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'secret_access_key', label: gettext('AWS secret access key'), type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'session_token', label: gettext('AWS session token'), type: 'multiline',
|
||||
mode: ['create'], noEmpty: false,
|
||||
helpMessage: gettext('Temporary AWS session required session token.')
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DatabaseSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues={}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
gid: undefined,
|
||||
db_name: '',
|
||||
db_username: '',
|
||||
db_password: '',
|
||||
db_confirm_password: '',
|
||||
db_port: 5432,
|
||||
...initValues,
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
validate(data, setErrMsg) {
|
||||
if(!isEmptyString(data.db_password) && !isEmptyString(data.db_confirm_password)
|
||||
&& data.db_password != data.db_confirm_password) {
|
||||
setErrMsg('db_confirm_password', gettext('Passwords do not match.'));
|
||||
return true;
|
||||
}
|
||||
if (!isEmptyString(data.db_confirm_password) && data.db_confirm_password.length < 8) {
|
||||
setErrMsg('db_confirm_password', gettext('Password must be 8 characters or more.'));
|
||||
return true;
|
||||
}
|
||||
if (data.db_confirm_password.includes('\'') || data.db_confirm_password.includes('"') ||
|
||||
data.db_confirm_password.includes('@') || data.db_confirm_password.includes('/')) {
|
||||
setErrMsg('db_confirm_password', gettext('Invalid passowrd.'));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [{
|
||||
id: 'gid', label: gettext('pgAdmin server group'), type: 'select',
|
||||
options: this.fieldOptions.server_groups,
|
||||
mode: ['create'],
|
||||
controlProps: { allowClear: false },
|
||||
noEmpty: true,
|
||||
}, {
|
||||
id: 'db_name', label: gettext('Database name'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'db_username', label: gettext('Username'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'db_password', label: gettext('Password'), type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
helpMessage: gettext('At least 8 printable ASCII characters. Cannot contain any of the following: / \(slash\), \'\(single quote\), "\(double quote\) and @ \(at sign\).')
|
||||
}, {
|
||||
id: 'db_confirm_password', label: gettext('Confirm password'),
|
||||
type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}, {
|
||||
id: 'db_port', label: gettext('Port'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class InstanceSchema extends BaseUISchema {
|
||||
constructor(versionOpts, instanceOpts, getInstances) {
|
||||
super({
|
||||
db_version: '',
|
||||
db_instance_class: 'm',
|
||||
instance_type: '',
|
||||
reload_instances: true,
|
||||
});
|
||||
this.versionOpts = versionOpts;
|
||||
this.instanceOpts = instanceOpts;
|
||||
this.getInstances = getInstances;
|
||||
this.instanceData = [];
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [{
|
||||
id: 'db_version', label: gettext('Database version'),
|
||||
type: 'select',
|
||||
options: this.versionOpts,
|
||||
controlProps: { allowClear: false },
|
||||
deps: ['name'],
|
||||
noEmpty: true,
|
||||
},{
|
||||
id: 'db_instance_class', label: gettext('Instance class'),
|
||||
type: 'select',
|
||||
options: [
|
||||
{'label': gettext('Standard classes (includes m classes)'), value: 'm'},
|
||||
{'label': gettext('Memory optimized classes (includes r & x classes)'), value: 'x'},
|
||||
{'label': gettext('Burstable classes (includes t classes)'), value: 't'},
|
||||
], noEmpty: true
|
||||
},{
|
||||
id: 'instance_type', label: gettext('Instance type'),
|
||||
options: this.instanceOpts,
|
||||
deps: ['db_version', 'db_instance_class'],
|
||||
depChange: (state, source)=> {
|
||||
if (source[0] == 'db_instance_class') {
|
||||
return {reload_instances: false};
|
||||
} else {
|
||||
state.instanceData = [];
|
||||
return {reload_instances: true};
|
||||
}
|
||||
},
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.getInstances(state.db_version,
|
||||
state.reload_instances, state.instanceData),
|
||||
optionsLoaded: (options) => { state.instanceData = options; },
|
||||
optionsReloadBasis: state.db_version + (state.db_instance_class || 'm'),
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
let pattern = 'db.m';
|
||||
let pattern_1 = 'db.m';
|
||||
|
||||
if (state.db_instance_class) {
|
||||
pattern = 'db.' + state.db_instance_class;
|
||||
pattern_1 = 'db.' + state.db_instance_class;
|
||||
}
|
||||
if (state.db_instance_class == 'x') {
|
||||
pattern_1 = 'db.' + 'r';
|
||||
}
|
||||
return options.filter((option) => {
|
||||
return (option.value.includes(pattern) || option.value.includes(pattern_1));
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class StorageSchema extends BaseUISchema {
|
||||
constructor() {
|
||||
super({
|
||||
storage_type: 'io1',
|
||||
storage_size: 100,
|
||||
storage_IOPS: 3000,
|
||||
storage_msg: 'Minimum: 20 GiB. Maximum: 16,384 GiB.'
|
||||
});
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'storage_type', label: gettext('Storage type'), type: 'select',
|
||||
mode: ['create'],
|
||||
options: [
|
||||
{'label': gettext('General Purpose SSD (gp2)'), 'value': 'gp2'},
|
||||
{'label': gettext('Provisioned IOPS SSD (io1)'), 'value': 'io1'},
|
||||
{'label': gettext('Magnetic'), 'value': 'standard'}
|
||||
], noEmpty: true,
|
||||
},{
|
||||
id: 'storage_size', label: gettext('Allocated storage'), type: 'text',
|
||||
mode: ['create'], noEmpty: true, deps: ['storage_type'],
|
||||
depChange: (state, source)=> {
|
||||
if (source[0] !== 'storage_size')
|
||||
if(state.storage_type === 'io1') {
|
||||
return {storage_size: 100};
|
||||
} else if(state.storage_type === 'gp2') {
|
||||
return {storage_size: 20};
|
||||
} else {
|
||||
return {storage_size: 5};
|
||||
}
|
||||
},
|
||||
helpMessage: gettext('Size in GiB.')
|
||||
}, {
|
||||
id: 'storage_IOPS', label: gettext('Provisioned IOPS'), type: 'text',
|
||||
mode: ['create'],
|
||||
visible: (state) => {
|
||||
return state.storage_type === 'io1';
|
||||
} , deps: ['storage_type'],
|
||||
depChange: (state, source) => {
|
||||
if (source[0] !== 'storage_IOPS') {
|
||||
if(state.storage_type === 'io1') {
|
||||
return {storage_IOPS: 3000};
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class HighAvailablity extends BaseUISchema {
|
||||
constructor() {
|
||||
super({
|
||||
high_availability: false
|
||||
});
|
||||
}
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'high_availability',
|
||||
label: gettext('High availability'),
|
||||
type: 'switch',
|
||||
mode: ['create'],
|
||||
helpMessage: gettext(
|
||||
'Creates a standby in a different Availability Zone (AZ) to provide data redundancy, eliminate I/O freezes, and minimize latency spikes during system backups.'
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BigAnimalInstanceSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues={}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
instance_type: '',
|
||||
instance_series: '',
|
||||
instance_size: '',
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'instance_type', label: gettext('Instance type'),
|
||||
mode: ['create'],
|
||||
deps: [['region']],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.instance_types(state.region),
|
||||
optionsReloadBasis: state.region,
|
||||
optionsLoaded: (options) => { state.instanceData = options; },
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
let _types = _.uniq(_.map(options, 'category')),
|
||||
_options = [];
|
||||
_.forEach(_types, (region) => {
|
||||
_options.push({
|
||||
'label': region,
|
||||
'value': region
|
||||
});
|
||||
});
|
||||
return _options;
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
noEmpty: true,
|
||||
},{
|
||||
id: 'instance_series', label: gettext('Instance series'),
|
||||
mode: ['create'], deps: ['instance_type'],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: state.instanceData,
|
||||
optionsReloadBasis: state.instance_type,
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
let _types = _.filter(options, {'category': state.instance_type}),
|
||||
_options = [];
|
||||
_types = _.uniq(_.map(_types, 'familyName'));
|
||||
_.forEach(_types, (value) => {
|
||||
_options.push({
|
||||
'label': value,
|
||||
'value': value
|
||||
});
|
||||
});
|
||||
return _options;
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
noEmpty: true,
|
||||
},{
|
||||
id: 'instance_size', label: gettext('Instance size'),
|
||||
mode: ['create'], deps: ['instance_series'],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: state.instanceData,
|
||||
optionsReloadBasis: state.instance_series,
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
filter: (options) => {
|
||||
if (options.length == 0) return;
|
||||
let _types = _.filter(options, {'familyName': state.instance_series}),
|
||||
_options = [];
|
||||
_.forEach(_types, (value) => {
|
||||
_options.push({
|
||||
'label': value.instanceTypeName + ' (' + value.cpu + 'vCPU, ' + value.ram + 'GB RAM)',
|
||||
'value': value.instanceTypeName + ' (' + value.cpu + 'vCPU, ' + value.ram + 'GB RAM)' + '||' + value.instanceTypeId,
|
||||
});
|
||||
});
|
||||
return _options;
|
||||
},
|
||||
}
|
||||
};
|
||||
}, noEmpty: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BigAnimalVolumeSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
volume_type: '',
|
||||
volume_properties: '',
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'volume_type', label: gettext('Volume type'),
|
||||
mode: ['create'], deps: [['region']],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.volume_types(state.region),
|
||||
optionsReloadBasis: state.region,
|
||||
};
|
||||
}, noEmpty: true,
|
||||
},{
|
||||
id: 'volume_properties', label: gettext('Volume properties'),
|
||||
mode: ['create'], deps: ['volume_type'],
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.volume_properties(state.region, state.volume_type),
|
||||
optionsReloadBasis: state.volume_type,
|
||||
};
|
||||
}, noEmpty: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class BigAnimalDatabaseSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
password: '',
|
||||
confirm_password: '',
|
||||
database_type: '',
|
||||
postgres_version: '',
|
||||
high_availability: false,
|
||||
replicas: 0,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
|
||||
validate(data, setErrMsg) {
|
||||
if(!isEmptyString(data.password) && !isEmptyString(data.confirm_password)
|
||||
&& data.password != data.confirm_password) {
|
||||
setErrMsg('confirm_password', gettext('Passwords do not match.'));
|
||||
return true;
|
||||
}
|
||||
if (!isEmptyString(data.confirm_password) && data.confirm_password.length < 12) {
|
||||
setErrMsg('confirm_password', gettext('Password must be 12 characters or more.'));
|
||||
return true;
|
||||
}
|
||||
if (data.high_availability && (isEmptyString(data.replicas) || data.replicas <= 0)) {
|
||||
setErrMsg('replicas', gettext('Please select number of stand by replicas.'));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'gid', label: gettext('pgAdmin server group'), type: 'select',
|
||||
options: this.fieldOptions.server_groups,
|
||||
mode: ['create'],
|
||||
controlProps: { allowClear: false },
|
||||
noEmpty: true,
|
||||
}, {
|
||||
id: 'database_type', label: gettext('Database type'), mode: ['create'],
|
||||
type: 'select',
|
||||
options: this.fieldOptions.db_types,
|
||||
noEmpty: true, orientation: 'vertical',
|
||||
},{
|
||||
id: 'postgres_version', label: gettext('PostgreSQL version'),
|
||||
mode: ['create'], noEmpty: true, deps: ['database_type'],
|
||||
options: this.fieldOptions.db_versions,
|
||||
type: (state) => {
|
||||
return {
|
||||
type: 'select',
|
||||
options: ()=>this.fieldOptions.db_versions(state.database_type),
|
||||
optionsReloadBasis: state.database_type,
|
||||
};
|
||||
},
|
||||
},{
|
||||
id: 'password', label: gettext('Database password'), type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
},{
|
||||
id: 'confirm_password', label: gettext('Confirm password'), type: 'password',
|
||||
mode: ['create'], noEmpty: true,
|
||||
},{
|
||||
type: 'nested-fieldset', label: gettext('Availability'),
|
||||
mode: ['create'],
|
||||
schema: new BigAnimalHighAvailSchema(),
|
||||
},
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class BigAnimalClusterSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
name: '',
|
||||
region: '',
|
||||
cloud_type: 'public',
|
||||
biganimal_public_ip: initValues.hostIP,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
|
||||
this.instance_types = new BigAnimalInstanceSchema({
|
||||
instance_types: this.fieldOptions.instance_types,
|
||||
});
|
||||
this.volume_types = new BigAnimalVolumeSchema({
|
||||
volume_types: this.fieldOptions.volume_types,
|
||||
volume_properties: this.fieldOptions.volume_properties
|
||||
});
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'name', label: gettext('Cluster name'), type: 'text',
|
||||
mode: ['create'], noEmpty: true,
|
||||
},{
|
||||
id: 'region', label: gettext('Region'), type: 'select',
|
||||
options: this.fieldOptions.regions,
|
||||
controlProps: { allowClear: false },
|
||||
noEmpty: true,
|
||||
mode: ['create'],
|
||||
},{
|
||||
id: 'biganimal_public_ip', label: gettext('Public IP range'), type: 'text',
|
||||
mode: ['create'],
|
||||
helpMessage: gettext('IP address range for allowed inbound traffic, for example: 127.0.0.1/32. Add multiple IP addresses/ranges separated with commas. Leave blank for 0.0.0.0/0'),
|
||||
},{
|
||||
type: 'nested-fieldset', label: gettext('Instance Type'),
|
||||
mode: ['create'], deps: ['region'],
|
||||
schema: this.instance_types,
|
||||
},{
|
||||
type: 'nested-fieldset', label: gettext('Storage'),
|
||||
mode: ['create'], deps: ['region'],
|
||||
schema: this.volume_types,
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class BigAnimalHighAvailSchema extends BaseUISchema {
|
||||
constructor(fieldOptions = {}, initValues = {}) {
|
||||
super({
|
||||
oid: undefined,
|
||||
high_availability: false,
|
||||
replicas: 0,
|
||||
...initValues
|
||||
});
|
||||
|
||||
this.fieldOptions = {
|
||||
...fieldOptions,
|
||||
};
|
||||
this.initValues = initValues;
|
||||
}
|
||||
|
||||
get idAttribute() {
|
||||
return 'oid';
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
return [
|
||||
{
|
||||
id: 'high_availability_note', type: 'note',
|
||||
mode: ['create'],
|
||||
text: gettext('High availability clusters are configured with one primary and up to two '
|
||||
+ 'standby replicas. Clusters are configured across availability zones in regions with availability zones.'),
|
||||
},{
|
||||
id: 'high_availability', label: gettext('High availability'), type: 'switch',
|
||||
mode: ['create']
|
||||
},{
|
||||
id: 'replicas', label: gettext('Number of standby replicas'), type: 'select',
|
||||
mode: ['create'], deps: ['high_availability'],
|
||||
controlProps: { allowClear: false },
|
||||
helpMessage: gettext('Adding standby replicas will increase your number of CPUs, as well as your cost.'),
|
||||
options: [
|
||||
{'label': gettext('1'), 'value': 1},
|
||||
{'label': gettext('2'), 'value': 2},
|
||||
], noEmpty: true,
|
||||
disabled: (state) => {
|
||||
return !state.high_availability;
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
CloudInstanceDetailsSchema,
|
||||
CloudDBCredSchema,
|
||||
DatabaseSchema,
|
||||
BigAnimalClusterSchema,
|
||||
BigAnimalDatabaseSchema
|
||||
};
|
55
web/pgadmin/static/img/ms_azure.svg
Normal file
@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.0" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 800 600" style="enable-background:new 0 0 800 600;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#706D6E;}
|
||||
.st1{fill:#F1511B;}
|
||||
.st2{fill:#80CC28;}
|
||||
.st3{fill:#00ADEF;}
|
||||
.st4{fill:#FBBC09;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M367.7,261.5l-3.3,9.3h-0.2c-0.6-2.2-1.6-5.3-3.1-9.2l-17.7-44.4h-17.3v70.6h11.4v-43.4c0-2.7-0.1-5.9-0.2-9.6
|
||||
c0-1.9-0.3-3.4-0.3-4.5h0.3c0.4,2,1,4.1,1.6,6l21.2,51.5h8l21.1-52c0.5-1.2,1-3.5,1.4-5.6h0.2c-0.3,5.1-0.5,9.8-0.5,12.7v44.8h12.2
|
||||
v-70.6h-16.6L367.7,261.5z M413.9,287.8h11.9v-50.6h-11.9V287.8z M420,215.7c-2,0-3.7,0.7-5.1,2c-1.4,1.3-2.1,3-2.1,5
|
||||
c0,1.9,0.7,3.6,2.1,4.9c1.4,1.3,3.1,1.9,5.1,1.9c2,0,3.7-0.6,5.1-1.9c1.4-1.3,2.1-2.9,2.1-4.9c0-1.9-0.7-3.5-2.1-4.9
|
||||
C423.8,216.4,422.1,215.7,420,215.7 M468,236.7c-2.2-0.5-4.4-0.7-6.7-0.7c-5.4,0-10.3,1.2-14.5,3.5c-4.1,2.3-7.3,5.6-9.5,9.8
|
||||
c-2.2,4.2-3.3,9.1-3.3,14.5c0,4.8,1.1,9.1,3.2,13c2.1,3.9,5.1,6.9,8.9,9c3.8,2.1,8.2,3.2,13,3.2c5.7,0,10.5-1.1,14.3-3.3l0.2-0.1
|
||||
v-10.9l-0.5,0.4c-1.8,1.3-3.8,2.3-5.8,3c-2.1,0.7-4,1.1-5.7,1.1c-4.7,0-8.4-1.5-11.1-4.3c-2.7-2.9-4.1-6.9-4.1-12
|
||||
c0-5.1,1.4-9.3,4.3-12.4c2.8-3.1,6.6-4.6,11.1-4.6c3.9,0,7.7,1.3,11.3,3.9l0.5,0.4v-11.5l-0.2-0.1
|
||||
C472.1,237.8,470.2,237.2,468,236.7 M507.2,236.3c-3,0-5.7,1-8,2.8c-2,1.7-3.5,3.9-4.6,6.8h-0.1v-8.7h-11.9v50.6h11.9v-25.9
|
||||
c0-4.4,1-8,3-10.7c1.9-2.7,4.5-4.1,7.7-4.1c1.1,0,2.3,0.2,3.6,0.5c1.3,0.3,2.2,0.7,2.8,1.1l0.5,0.4v-12l-0.2-0.1
|
||||
C510.7,236.6,509.1,236.3,507.2,236.3 M549,274.9c-2.2,2.8-5.6,4.2-10,4.2c-4.4,0-7.8-1.4-10.2-4.3c-2.5-2.9-3.7-7-3.7-12.2
|
||||
c0-5.4,1.2-9.6,3.7-12.5c2.4-2.9,5.8-4.4,10.1-4.4c4.2,0,7.5,1.4,9.9,4.2c2.4,2.8,3.6,7,3.6,12.4
|
||||
C552.3,267.9,551.2,272.1,549,274.9 M539.5,236c-8.3,0-14.9,2.5-19.7,7.3c-4.7,4.8-7.1,11.5-7.1,19.8c0,7.9,2.3,14.3,6.9,18.9
|
||||
c4.6,4.6,10.9,7,18.6,7c8.1,0,14.6-2.5,19.3-7.4c4.7-4.9,7.1-11.5,7.1-19.6c0-8-2.2-14.5-6.7-19.1C553.6,238.3,547.3,236,539.5,236
|
||||
M591.1,258.1c-3.8-1.5-6.2-2.8-7.1-3.7c-1-0.9-1.4-2.2-1.4-3.9c0-1.5,0.6-2.6,1.8-3.6c1.2-1,2.9-1.4,5.1-1.4c2,0,4.1,0.3,6.1,0.9
|
||||
c2,0.6,3.8,1.4,5.3,2.5l0.5,0.3v-11l-0.2-0.1c-1.3-0.6-3.2-1.1-5.4-1.5c-2.2-0.4-4.2-0.6-5.9-0.6c-5.7,0-10.4,1.5-13.9,4.3
|
||||
c-3.6,2.9-5.5,6.7-5.5,11.2c0,2.4,0.4,4.5,1.2,6.3c0.8,1.8,2,3.4,3.6,4.7c1.6,1.3,4,2.7,7.3,4.1c2.7,1.1,4.8,2.1,6.1,2.8
|
||||
c1.3,0.7,2.2,1.5,2.7,2.2c0.5,0.7,0.8,1.7,0.8,2.9c0,3.4-2.5,5.1-7.8,5.1c-2,0-4.2-0.4-6.6-1.2c-2.4-0.8-4.7-1.9-6.7-3.4l-0.5-0.4
|
||||
v11.6l0.2,0.1c1.7,0.8,3.9,1.5,6.4,2c2.5,0.5,4.8,0.8,6.8,0.8c6.2,0,11.1-1.5,14.7-4.3c3.7-2.9,5.5-6.8,5.5-11.5c0-3.4-1-6.3-3-8.7
|
||||
C599.1,262.1,595.8,260,591.1,258.1 M645.5,274.9c-2.2,2.8-5.6,4.2-10,4.2c-4.4,0-7.8-1.4-10.3-4.3c-2.5-2.9-3.7-7-3.7-12.2
|
||||
c0-5.4,1.2-9.6,3.7-12.5c2.4-2.9,5.8-4.4,10.1-4.4c4.2,0,7.5,1.4,9.9,4.2c2.4,2.8,3.6,7,3.6,12.4
|
||||
C648.9,267.9,647.7,272.1,645.5,274.9 M636,236c-8.3,0-15,2.5-19.7,7.3c-4.7,4.8-7.1,11.5-7.1,19.8c0,7.9,2.3,14.3,6.9,18.9
|
||||
c4.6,4.6,10.9,7,18.6,7c8.1,0,14.6-2.5,19.3-7.4c4.7-4.9,7.1-11.5,7.1-19.6c0-8-2.2-14.5-6.7-19.1C650.1,238.3,643.9,236,636,236
|
||||
M724.7,246.9v-9.7h-12v-15.1l-0.4,0.1l-11.3,3.5l-0.2,0.1v11.4h-17.8v-6.4c0-3,0.7-5.2,2-6.7c1.3-1.5,3.2-2.2,5.5-2.2
|
||||
c1.7,0,3.5,0.4,5.2,1.2l0.4,0.2V213l-0.2-0.1c-1.6-0.6-3.9-0.9-6.7-0.9c-3.5,0-6.7,0.8-9.5,2.3c-2.8,1.5-5,3.7-6.5,6.4
|
||||
c-1.5,2.7-2.3,5.9-2.3,9.4v7h-8.4v9.7h8.4v40.9h12v-40.9h17.9v26c0,10.7,5,16.1,15,16.1c1.6,0,3.4-0.2,5.1-0.6
|
||||
c1.8-0.4,3-0.8,3.7-1.2l0.2-0.1v-9.8l-0.5,0.3c-0.7,0.4-1.5,0.8-2.4,1c-1,0.3-1.8,0.4-2.4,0.4c-2.3,0-4.1-0.6-5.1-1.9
|
||||
c-1.1-1.3-1.6-3.4-1.6-6.5v-23.9L724.7,246.9z"/>
|
||||
<path class="st1" d="M186.5,296.9H107v-79.5h79.4L186.5,296.9z"/>
|
||||
<path class="st2" d="M274.2,296.9h-79.5v-79.5h79.5V296.9z"/>
|
||||
<path class="st3" d="M186.5,384.6H107v-79.5h79.4L186.5,384.6z"/>
|
||||
<path class="st4" d="M274.2,384.6h-79.5v-79.5h79.5V384.6z"/>
|
||||
<path class="st0" d="M365.8,356.2l-9.9-28.4c-0.3-0.9-0.6-2.4-0.9-4.5h-0.2c-0.3,1.9-0.6,3.4-1,4.5l-9.8,28.4H365.8z M388.2,384
|
||||
h-12.9l-6.4-18.1h-28l-6.1,18.1h-12.9l26.6-71.1h13.3L388.2,384z M432.2,338l-27.4,37h27.3v9h-43.4v-4.3l28-37.5h-25.3v-9h40.9
|
||||
L432.2,338z M484.1,384h-11.5v-8h-0.2c-3.3,6.2-8.5,9.2-15.6,9.2c-12,0-18-7.2-18-21.6v-30.4h11.5v29.2c0,9.1,3.5,13.7,10.6,13.7
|
||||
c3.4,0,6.2-1.3,8.4-3.8c2.2-2.5,3.3-5.8,3.3-9.9v-29.2h11.5L484.1,384z M526.5,344.1c-1.4-1.1-3.4-1.6-6-1.6
|
||||
c-3.4,0-6.2,1.5-8.5,4.6c-2.3,3.1-3.4,7.3-3.4,12.5V384h-11.5v-50.8h11.5v10.5h0.2c1.1-3.6,2.9-6.4,5.2-8.4c2.3-2,4.9-3,7.8-3
|
||||
c2.1,0,3.7,0.3,4.8,0.9V344.1z M563.6,353.6c0-4.1-1-7.4-2.9-9.6c-1.9-2.3-4.6-3.4-7.9-3.4c-3.3,0-6.1,1.2-8.4,3.6
|
||||
c-2.3,2.4-3.7,5.6-4.2,9.5L563.6,353.6z M574.7,361.7h-34.6c0.1,4.7,1.6,8.3,4.3,10.9c2.8,2.5,6.6,3.8,11.4,3.8
|
||||
c5.4,0,10.4-1.6,14.9-4.9v9.3c-4.6,2.9-10.7,4.4-18.3,4.4c-7.5,0-13.3-2.3-17.6-6.9c-4.2-4.6-6.4-11.1-6.4-19.5
|
||||
c0-7.9,2.3-14.3,7-19.3c4.7-5,10.5-7.5,17.4-7.5c6.9,0,12.3,2.2,16.1,6.7c3.8,4.5,5.7,10.7,5.7,18.6V361.7z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
@ -19,6 +19,7 @@ import BigAnimal from '../../img/biganimal.svg?svgr';
|
||||
import Azure from '../../img/azure.svg?svgr';
|
||||
import SQLFileSvg from '../../img/sql_file.svg?svgr';
|
||||
import MagicSvg from '../../img/magic.svg?svgr';
|
||||
import MsAzure from '../../img/ms_azure.svg?svgr';
|
||||
|
||||
export default function ExternalIcon({Icon, ...props}) {
|
||||
return <Icon className={'MuiSvgIcon-root'} {...props} />;
|
||||
@ -84,3 +85,6 @@ SQLFileIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const MagicIcon = ({style})=><ExternalIcon Icon={MagicSvg} style={{height: '1rem', ...style}} data-label="MagicIcon" />;
|
||||
MagicIcon.propTypes = {style: PropTypes.object};
|
||||
|
||||
export const MSAzureIcon = ({style})=><ExternalIcon Icon={MsAzure} style={{height: '6rem', width: '7rem', ...style}} data-label="MSAzureIcon" />;
|
||||
MSAzureIcon.propTypes = {style: PropTypes.object};
|
||||
|
@ -139,8 +139,12 @@ function Wizard({ stepList, onStepChange, onSave, className, ...props }) {
|
||||
const handleNext = () => {
|
||||
// beforeNext should always return a promise
|
||||
if(props.beforeNext) {
|
||||
props.beforeNext(activeStep).then(()=>{
|
||||
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||
props.beforeNext(activeStep).then((skipCurrentStep=false)=>{
|
||||
if (skipCurrentStep) {
|
||||
setActiveStep((prevActiveStep) => prevActiveStep + 2);
|
||||
} else {
|
||||
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||
}
|
||||
}).catch(()=>{/*This is intentional (SonarQube)*/});
|
||||
} else {
|
||||
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||
@ -150,8 +154,12 @@ function Wizard({ stepList, onStepChange, onSave, className, ...props }) {
|
||||
const handleBack = () => {
|
||||
// beforeBack should always return a promise
|
||||
if(props.beforeBack) {
|
||||
props.beforeBack(activeStep).then(()=>{
|
||||
setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 1);
|
||||
props.beforeBack(activeStep).then((skipCurrentStep=false)=>{
|
||||
if (skipCurrentStep) {
|
||||
setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 2);
|
||||
} else {
|
||||
setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 1);
|
||||
}
|
||||
}).catch(()=>{/*This is intentional (SonarQube)*/});
|
||||
} else {
|
||||
setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 1);
|
||||
|