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
|
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.
|
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
|
.. image:: images/cloud_biganimal_instance.png
|
||||||
:alt: Cloud Deployment Provider
|
:alt: Cloud Deployment Provider
|
||||||
:align: center
|
:align: center
|
||||||
@ -48,8 +60,11 @@ details.
|
|||||||
|
|
||||||
* Use the *Volume type* field to select the instance storage type.
|
* 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
|
.. 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 *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
|
.. image:: images/cloud_biganimal_review.png
|
||||||
:alt: Cloud Deployment Provider
|
: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',
|
parser_create_instance.add_argument('--volume-properties',
|
||||||
required=True,
|
required=True,
|
||||||
help='storage properties')
|
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,
|
parser_create_instance.add_argument('--private-network', required=True,
|
||||||
help='Private or Public Network')
|
help='Private or Public Network')
|
||||||
parser_create_instance.add_argument('--public-ip', default='',
|
parser_create_instance.add_argument('--public-ip', default='',
|
||||||
@ -76,6 +82,12 @@ class BigAnimalProvider(AbsProvider):
|
|||||||
parser_create_instance.add_argument('--nodes',
|
parser_create_instance.add_argument('--nodes',
|
||||||
required=True,
|
required=True,
|
||||||
help='No of Nodes')
|
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):
|
def cmd_create_instance(self, args):
|
||||||
""" Create a biganimal cluster """
|
""" Create a biganimal cluster """
|
||||||
@ -105,12 +117,14 @@ class BigAnimalProvider(AbsProvider):
|
|||||||
'pgType': {'pgTypeId': args.db_type},
|
'pgType': {'pgTypeId': args.db_type},
|
||||||
'pgVersion': {'pgVersionId': args.db_version},
|
'pgVersion': {'pgVersionId': args.db_version},
|
||||||
'privateNetworking': private_network,
|
'privateNetworking': private_network,
|
||||||
'provider': {'cloudProviderId': 'azure'},
|
'provider': {'cloudProviderId': args.cloud_provider},
|
||||||
'region': {'regionId': args.region},
|
'region': {'regionId': args.region},
|
||||||
'replicas': 1,
|
'replicas': int(args.replicas),
|
||||||
'storage': {
|
'storage': {
|
||||||
'volumePropertiesId': args.volume_properties,
|
'volumePropertiesId': args.volume_properties,
|
||||||
'volumeTypeId': args.volume_type
|
'volumeTypeId': args.volume_type,
|
||||||
|
'iops': args.volume_IOPS,
|
||||||
|
'size': args.volume_size + ' Gi'
|
||||||
},
|
},
|
||||||
'clusterArchitecture': {
|
'clusterArchitecture': {
|
||||||
'clusterArchitectureId': args.cluster_arch,
|
'clusterArchitectureId': args.cluster_arch,
|
||||||
|
@ -50,7 +50,8 @@ class BigAnimalModule(PgAdminModule):
|
|||||||
'biganimal.db_versions',
|
'biganimal.db_versions',
|
||||||
'biganimal.instance_types',
|
'biganimal.instance_types',
|
||||||
'biganimal.volume_types',
|
'biganimal.volume_types',
|
||||||
'biganimal.volume_properties']
|
'biganimal.volume_properties',
|
||||||
|
'biganimal.providers']
|
||||||
|
|
||||||
|
|
||||||
blueprint = BigAnimalModule(MODULE_NAME, __name__,
|
blueprint = BigAnimalModule(MODULE_NAME, __name__,
|
||||||
@ -82,17 +83,27 @@ def verification():
|
|||||||
return make_json_response(data=verification_uri)
|
return make_json_response(data=verification_uri)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route('/regions/',
|
@blueprint.route('/regions/<provider_id>',
|
||||||
methods=['GET'], endpoint='regions')
|
methods=['GET'], endpoint='regions')
|
||||||
@login_required
|
@login_required
|
||||||
def biganimal_regions():
|
def biganimal_regions(provider_id):
|
||||||
"""Get Regions."""
|
"""Get Regions."""
|
||||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
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)
|
session['biganimal']['provider_obj'] = pickle.dumps(biganimal_obj, -1)
|
||||||
return make_json_response(data=regions)
|
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/',
|
@blueprint.route('/db_types/',
|
||||||
methods=['GET'], endpoint='db_types')
|
methods=['GET'], endpoint='db_types')
|
||||||
@login_required
|
@login_required
|
||||||
@ -103,50 +114,52 @@ def biganimal_db_types():
|
|||||||
return make_json_response(data=pg_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')
|
methods=['GET'], endpoint='db_versions')
|
||||||
@login_required
|
@login_required
|
||||||
def biganimal_db_versions(db_type):
|
def biganimal_db_versions(cluster_type, pg_type):
|
||||||
"""Get Database Version."""
|
"""Get Database Version."""
|
||||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
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)
|
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')
|
methods=['GET'], endpoint='instance_types')
|
||||||
@login_required
|
@login_required
|
||||||
def biganimal_instance_types(region_id):
|
def biganimal_instance_types(region_id, provider_id):
|
||||||
"""Get Instance Types."""
|
"""Get Instance Types."""
|
||||||
if not region_id:
|
if not region_id or not provider_id:
|
||||||
return make_json_response(data=[])
|
return make_json_response(data=[])
|
||||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
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)
|
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')
|
methods=['GET'], endpoint='volume_types')
|
||||||
@login_required
|
@login_required
|
||||||
def biganimal_volume_types(region_id):
|
def biganimal_volume_types(region_id, provider_id):
|
||||||
"""Get Volume Types."""
|
"""Get Volume Types."""
|
||||||
if not region_id:
|
if not region_id or not provider_id:
|
||||||
return make_json_response(data=[])
|
return make_json_response(data=[])
|
||||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
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)
|
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')
|
methods=['GET'], endpoint='volume_properties')
|
||||||
@login_required
|
@login_required
|
||||||
def biganimal_volume_properties(region_id, volume_type):
|
def biganimal_volume_properties(region_id, provider_id, volume_type):
|
||||||
"""Get Volume Properties."""
|
"""Get Volume Properties."""
|
||||||
if not region_id:
|
if not region_id or not provider_id:
|
||||||
return make_json_response(data=[])
|
return make_json_response(data=[])
|
||||||
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
|
||||||
biganimal_volume_properties = biganimal_obj.get_volume_properties(
|
biganimal_volume_properties = biganimal_obj.get_volume_properties(
|
||||||
region_id,
|
region_id,
|
||||||
|
provider_id,
|
||||||
volume_type)
|
volume_type)
|
||||||
return make_json_response(data=biganimal_volume_properties)
|
return make_json_response(data=biganimal_volume_properties)
|
||||||
|
|
||||||
@ -261,19 +274,42 @@ class BigAnimalProvider():
|
|||||||
# so all the existing clusters moved to the default Project.
|
# so all the existing clusters moved to the default Project.
|
||||||
# For now, we can get the Proj Id by replacing 'org' to 'prj'
|
# For now, we can get the Proj Id by replacing 'org' to 'prj'
|
||||||
# in organization ID: org_1234 -> prj_1234
|
# in organization ID: org_1234 -> prj_1234
|
||||||
proj_Id = content['data']['organizationId'].replace('org',
|
proj_id = content['data']['organizationId'].replace('org',
|
||||||
'prj')
|
'prj')
|
||||||
for permission in content['data']['scopedPermissions']:
|
for permission in content['data']['scopedPermissions']:
|
||||||
if proj_Id == permission['scope'] and\
|
if proj_id == permission['scope'] and\
|
||||||
'create:clusters' in permission['permissions']:
|
'create:clusters' in permission['permissions']:
|
||||||
return True
|
return True
|
||||||
return False
|
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"""
|
"""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,
|
self.BASE_URL,
|
||||||
'cloud-providers/azure/regions')
|
provider_id)
|
||||||
regions = []
|
regions = []
|
||||||
resp = requests.get(_url, headers=self._get_headers())
|
resp = requests.get(_url, headers=self._get_headers())
|
||||||
if resp.status_code == 200 and resp.content:
|
if resp.status_code == 200 and resp.content:
|
||||||
@ -309,12 +345,15 @@ class BigAnimalProvider():
|
|||||||
})
|
})
|
||||||
return pg_types
|
return pg_types
|
||||||
|
|
||||||
def get_postgres_versions(self, db_type):
|
def get_postgres_versions(self, cluster_type, pg_type):
|
||||||
"""Get Postgres Versions."""
|
"""Get Postgres Versions."""
|
||||||
_url = "{0}/pg-versions?pgTypeIds={1}".format(
|
if not cluster_type or not pg_type:
|
||||||
self.BASE_URL,
|
return []
|
||||||
db_type
|
|
||||||
)
|
_url = "{0}/pg-versions?clusterArchitectureIds={1}" \
|
||||||
|
"&pgTypeIds={2}".format(self.BASE_URL,
|
||||||
|
cluster_type,
|
||||||
|
pg_type)
|
||||||
pg_versions = []
|
pg_versions = []
|
||||||
resp = requests.get(_url, headers=self._get_headers())
|
resp = requests.get(_url, headers=self._get_headers())
|
||||||
if resp.status_code == 200 and resp.content:
|
if resp.status_code == 200 and resp.content:
|
||||||
@ -326,48 +365,55 @@ class BigAnimalProvider():
|
|||||||
})
|
})
|
||||||
return pg_versions
|
return pg_versions
|
||||||
|
|
||||||
def get_instance_types(self, region_id):
|
def get_instance_types(self, region_id, provider_id):
|
||||||
"""GEt Instance Types."""
|
"""GEt Instance Types."""
|
||||||
if region_id not in self.regions:
|
if region_id not in self.regions or not provider_id:
|
||||||
return []
|
return []
|
||||||
_url = "{0}/{1}".format(
|
_url = '{0}/cloud-providers/{1}/regions/{2}/instance-types?' \
|
||||||
self.BASE_URL,
|
'sort=instanceTypeName'.format(self.BASE_URL,
|
||||||
'cloud-providers/azure/regions/'
|
provider_id,
|
||||||
'{0}/instance-types'.format(region_id))
|
region_id)
|
||||||
resp = requests.get(_url, headers=self._get_headers())
|
resp = requests.get(_url, headers=self._get_headers())
|
||||||
if resp.status_code == 200 and resp.content:
|
if resp.status_code == 200 and resp.content:
|
||||||
pg_types = json.loads(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 []
|
return []
|
||||||
|
|
||||||
def get_volume_types(self, region_id):
|
def get_volume_types(self, region_id, provider_id):
|
||||||
"""Get Volume Types."""
|
"""Get Volume Types."""
|
||||||
if region_id not in self.regions:
|
if region_id not in self.regions:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
_url = "{0}/{1}".format(
|
_url = '{0}/cloud-providers/{1}/regions/{2}/volume-types'.format(
|
||||||
self.BASE_URL,
|
self.BASE_URL,
|
||||||
'cloud-providers/azure/regions/{0}/volume-types'.format(region_id))
|
provider_id,
|
||||||
|
region_id)
|
||||||
volume_types = []
|
volume_types = []
|
||||||
resp = requests.get(_url, headers=self._get_headers())
|
resp = requests.get(_url, headers=self._get_headers())
|
||||||
if resp.status_code == 200 and resp.content:
|
if resp.status_code == 200 and resp.content:
|
||||||
volume_resp = json.loads(resp.content)
|
volume_resp = json.loads(resp.content)
|
||||||
for value in volume_resp['data']:
|
for value in volume_resp['data']:
|
||||||
|
if value['enabledInRegion']:
|
||||||
volume_types.append({
|
volume_types.append({
|
||||||
'label': value['volumeTypeName'],
|
'label': value['volumeTypeName'],
|
||||||
'value': value['volumeTypeId']
|
'value': value['volumeTypeId'],
|
||||||
|
'supportedInstanceFamilyNames': value[
|
||||||
|
'supportedInstanceFamilyNames']
|
||||||
})
|
})
|
||||||
return volume_types
|
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."""
|
"""Get Volume Properties."""
|
||||||
if region_id not in self.regions:
|
if region_id not in self.regions:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
_url = "{0}/{1}".format(
|
_url = '{0}/cloud-providers/{1}/regions/{2}/volume-types/{3}/' \
|
||||||
self.BASE_URL,
|
'volume-properties'.format(self.BASE_URL,
|
||||||
'cloud-providers/azure/regions/{0}/volume-types'
|
provider_id,
|
||||||
'/{1}/volume-properties'.format(region_id, volume_type))
|
region_id,
|
||||||
|
volume_type)
|
||||||
volume_properties = []
|
volume_properties = []
|
||||||
resp = requests.get(_url, headers=self._get_headers())
|
resp = requests.get(_url, headers=self._get_headers())
|
||||||
if resp.status_code == 200 and resp.content:
|
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_network = '1' if str(data['instance_details']['cloud_type']
|
||||||
) == 'private' else '0'
|
) == 'private' else '0'
|
||||||
_instance_size = data['instance_details']['instance_size'].split('||')[1]
|
_instance_size = data['instance_details']['instance_size'].split('||')[1]
|
||||||
|
|
||||||
cluster_arch = SINGLE_CLUSTER_ARCH
|
|
||||||
nodes = 1
|
nodes = 1
|
||||||
|
|
||||||
if data['db_details']['high_availability']:
|
if data['cluster_details']['cluster_type'] == HA_CLUSTER_ARCH:
|
||||||
cluster_arch = HA_CLUSTER_ARCH
|
nodes = int(data['cluster_details']['replicas']) + nodes
|
||||||
nodes = int(data['db_details']['replicas']) + nodes
|
elif data['cluster_details']['cluster_type'] == EHA_CLUSTER_ARCH:
|
||||||
|
nodes = 5
|
||||||
|
|
||||||
args = [_cmd_script,
|
args = [_cmd_script,
|
||||||
data['cloud'],
|
data['cloud'],
|
||||||
@ -416,15 +461,25 @@ def deploy_on_biganimal(data):
|
|||||||
'--volume-type',
|
'--volume-type',
|
||||||
str(data['instance_details']['volume_type']),
|
str(data['instance_details']['volume_type']),
|
||||||
'--volume-properties',
|
'--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',
|
'--instance-type',
|
||||||
str(_instance_size),
|
str(_instance_size),
|
||||||
'--private-network',
|
'--private-network',
|
||||||
_private_network,
|
_private_network,
|
||||||
'--cluster-arch',
|
'--cluster-arch',
|
||||||
cluster_arch,
|
data['cluster_details']['cluster_type'],
|
||||||
'--nodes',
|
'--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']:
|
if 'biganimal_public_ip' in data['instance_details']:
|
||||||
|
@ -58,6 +58,7 @@ blueprint = RDSModule(MODULE_NAME, __name__,
|
|||||||
@login_required
|
@login_required
|
||||||
def verify_credentials():
|
def verify_credentials():
|
||||||
"""Verify Credentials."""
|
"""Verify Credentials."""
|
||||||
|
msg = ''
|
||||||
data = json.loads(request.data, encoding='utf-8')
|
data = json.loads(request.data, encoding='utf-8')
|
||||||
|
|
||||||
session_token = data['secret']['session_token'] if\
|
session_token = data['secret']['session_token'] if\
|
||||||
|
@ -22,11 +22,13 @@ import pgAdmin from 'sources/pgadmin';
|
|||||||
import {ToggleButtons, FinalSummary} from './cloud_components';
|
import {ToggleButtons, FinalSummary} from './cloud_components';
|
||||||
import { PrimaryButton } from '../../../../static/js/components/Buttons';
|
import { PrimaryButton } from '../../../../static/js/components/Buttons';
|
||||||
import {AwsCredentials, AwsInstanceDetails, AwsDatabaseDetails, validateCloudStep1, validateCloudStep2, validateCloudStep3} from './aws';
|
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 { isEmptyString } from 'sources/validators';
|
||||||
import { AWSIcon, BigAnimalIcon, AzureIcon } from '../../../../static/js/components/ExternalIcon';
|
import { AWSIcon, BigAnimalIcon, AzureIcon } from '../../../../static/js/components/ExternalIcon';
|
||||||
import {AzureCredentials, AzureInstanceDetails, AzureDatabaseDetails, checkClusternameAvailbility, validateAzureStep2, validateAzureStep3} from './azure';
|
import {AzureCredentials, AzureInstanceDetails, AzureDatabaseDetails, checkClusternameAvailbility, validateAzureStep2, validateAzureStep3} from './azure';
|
||||||
import EventBus from '../../../../static/js/helpers/EventBus';
|
import EventBus from '../../../../static/js/helpers/EventBus';
|
||||||
|
import { CLOUD_PROVIDERS } from './cloud_constants';
|
||||||
|
|
||||||
|
|
||||||
const useStyles = makeStyles(() =>
|
const useStyles = makeStyles(() =>
|
||||||
({
|
({
|
||||||
@ -67,7 +69,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
|
|
||||||
const eventBus = React.useRef(new EventBus());
|
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')];
|
gettext('Instance Specification'), gettext('Database Details'), gettext('Review')];
|
||||||
const [currentStep, setCurrentStep] = React.useState('');
|
const [currentStep, setCurrentStep] = React.useState('');
|
||||||
const [selectionVal, setCloudSelection] = 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 [verificationIntiated, setVerificationIntiated] = React.useState(false);
|
||||||
const [bigAnimalInstanceData, setBigAnimalInstanceData] = React.useState({});
|
const [bigAnimalInstanceData, setBigAnimalInstanceData] = React.useState({});
|
||||||
const [bigAnimalDatabaseData, setBigAnimalDatabaseData] = React.useState({});
|
const [bigAnimalDatabaseData, setBigAnimalDatabaseData] = React.useState({});
|
||||||
|
const [bigAnimalClusterTypeData, setBigAnimalClusterTypeData] = React.useState({});
|
||||||
|
const [bigAnimalProviders, setBigAnimalProviders] = React.useState({});
|
||||||
|
|
||||||
const [azureCredData, setAzureCredData] = React.useState({});
|
const [azureCredData, setAzureCredData] = React.useState({});
|
||||||
const [azureInstanceData, setAzureInstanceData] = 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'),
|
let _url = url_for('cloud.deploy_on_cloud'),
|
||||||
post_data = {};
|
post_data = {};
|
||||||
|
|
||||||
if (cloudProvider == 'rds') {
|
if (cloudProvider == CLOUD_PROVIDERS.RDS) {
|
||||||
post_data = {
|
post_data = {
|
||||||
gid: nodeInfo.server_group._id,
|
gid: nodeInfo.server_group._id,
|
||||||
cloud: cloudProvider,
|
cloud: cloudProvider,
|
||||||
@ -132,7 +136,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
instance_details:cloudInstanceDetails,
|
instance_details:cloudInstanceDetails,
|
||||||
db_details: cloudDBDetails
|
db_details: cloudDBDetails
|
||||||
};
|
};
|
||||||
} else if(cloudProvider == 'azure'){
|
} else if(cloudProvider == CLOUD_PROVIDERS.AZURE){
|
||||||
post_data = {
|
post_data = {
|
||||||
gid: nodeInfo.server_group._id,
|
gid: nodeInfo.server_group._id,
|
||||||
secret: azureCredData,
|
secret: azureCredData,
|
||||||
@ -145,6 +149,7 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
post_data = {
|
post_data = {
|
||||||
gid: nodeInfo.server_group._id,
|
gid: nodeInfo.server_group._id,
|
||||||
cloud: cloudProvider,
|
cloud: cloudProvider,
|
||||||
|
cluster_details: bigAnimalClusterTypeData,
|
||||||
instance_details: bigAnimalInstanceData,
|
instance_details: bigAnimalInstanceData,
|
||||||
db_details: bigAnimalDatabaseData
|
db_details: bigAnimalDatabaseData
|
||||||
};
|
};
|
||||||
@ -165,54 +170,61 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
setCallRDSAPI(currentStep);
|
setCallRDSAPI(currentStep);
|
||||||
let isError = (cloudProvider == '');
|
let isError = (cloudProvider == '');
|
||||||
switch(cloudProvider) {
|
switch(cloudProvider) {
|
||||||
case 'rds':
|
case CLOUD_PROVIDERS.RDS:
|
||||||
switch (currentStep) {
|
switch (currentStep) {
|
||||||
case 0:
|
case 0:
|
||||||
setCloudSelection('rds');
|
setCloudSelection(CLOUD_PROVIDERS.RDS);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
isError = validateCloudStep1(cloudDBCred);
|
isError = validateCloudStep1(cloudDBCred);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
isError = validateCloudStep2(cloudInstanceDetails, hostIP);
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
isError = validateCloudStep2(cloudInstanceDetails, hostIP);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
isError = validateCloudStep3(cloudDBDetails, nodeInfo);
|
isError = validateCloudStep3(cloudDBDetails, nodeInfo);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'biganimal':
|
case CLOUD_PROVIDERS.BIGANIMAL:
|
||||||
switch (currentStep) {
|
switch (currentStep) {
|
||||||
case 0:
|
case 0:
|
||||||
setCloudSelection('biganimal');
|
setCloudSelection(CLOUD_PROVIDERS.BIGANIMAL);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
isError = !verificationIntiated;
|
isError = !verificationIntiated;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
isError = validateBigAnimalStep2(bigAnimalInstanceData);
|
isError = validateBigAnimalStep2(bigAnimalClusterTypeData);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
isError = validateBigAnimalStep3(bigAnimalDatabaseData, nodeInfo);
|
isError = validateBigAnimalStep3(bigAnimalInstanceData);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
isError = validateBigAnimalStep4(bigAnimalDatabaseData, nodeInfo);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'azure':
|
case CLOUD_PROVIDERS.AZURE:
|
||||||
switch (currentStep) {
|
switch (currentStep) {
|
||||||
case 0:
|
case 0:
|
||||||
setCloudSelection('azure');
|
setCloudSelection(CLOUD_PROVIDERS.AZURE);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
isError = !verificationIntiated;
|
isError = !verificationIntiated;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
isError = validateAzureStep2(azureInstanceData);
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
isError = validateAzureStep2(azureInstanceData);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
isError = validateAzureStep3(azureDatabaseData, nodeInfo);
|
isError = validateAzureStep3(azureDatabaseData, nodeInfo);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -223,9 +235,18 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
return isError;
|
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) => {
|
const onBeforeNext = (activeStep) => {
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject)=>{
|
||||||
if(activeStep == 1 && cloudProvider == 'rds') {
|
if(activeStep == 1 && cloudProvider == CLOUD_PROVIDERS.RDS) {
|
||||||
setErrMsg([MESSAGE_TYPE.INFO, gettext('Validating credentials...')]);
|
setErrMsg([MESSAGE_TYPE.INFO, gettext('Validating credentials...')]);
|
||||||
let _url = url_for('rds.verify_credentials');
|
let _url = url_for('rds.verify_credentials');
|
||||||
const post_data = {
|
const post_data = {
|
||||||
@ -239,14 +260,18 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
reject();
|
reject();
|
||||||
} else {
|
} else {
|
||||||
setErrMsg(['', '']);
|
setErrMsg(['', '']);
|
||||||
resolve();
|
if (activeStep == 1) {
|
||||||
|
resolve(true);
|
||||||
|
} else {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext('Error while checking cloud credentials')]);
|
setErrMsg([MESSAGE_TYPE.ERROR, gettext('Error while checking cloud credentials')]);
|
||||||
reject();
|
reject();
|
||||||
});
|
});
|
||||||
} else if(activeStep == 0 && cloudProvider == 'biganimal') {
|
} else if(activeStep == 0 && cloudProvider == CLOUD_PROVIDERS.BIGANIMAL) {
|
||||||
if (!isEmptyString(verificationURI)) { resolve(); return; }
|
if (!isEmptyString(verificationURI)) { resolve(); return; }
|
||||||
setErrMsg([MESSAGE_TYPE.INFO, gettext('Getting EDB BigAnimal verification URL...')]);
|
setErrMsg([MESSAGE_TYPE.INFO, gettext('Getting EDB BigAnimal verification URL...')]);
|
||||||
validateBigAnimal()
|
validateBigAnimal()
|
||||||
@ -260,7 +285,21 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]);
|
setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]);
|
||||||
reject();
|
reject();
|
||||||
});
|
});
|
||||||
} else if(activeStep == 2 && cloudProvider == 'azure'){
|
} else if (activeStep == 1 && cloudProvider == CLOUD_PROVIDERS.BIGANIMAL ) {
|
||||||
|
getProviderOptions()
|
||||||
|
.then((res)=>{
|
||||||
|
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...')]);
|
setErrMsg([MESSAGE_TYPE.INFO, gettext('Checking cluster name availability...')]);
|
||||||
checkClusternameAvailbility(azureInstanceData.name)
|
checkClusternameAvailbility(azureInstanceData.name)
|
||||||
.then((res)=>{
|
.then((res)=>{
|
||||||
@ -274,6 +313,9 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]);
|
setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]);
|
||||||
reject();
|
reject();
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setErrMsg(['', '']);
|
setErrMsg(['', '']);
|
||||||
@ -333,9 +375,9 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
setErrMsg([]);
|
setErrMsg([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
let cloud_providers = [{label: gettext('Amazon RDS'), value: 'rds', icon: <AWSIcon className={classes.icon} />},
|
let cloud_providers = [{label: gettext('Amazon RDS'), value: CLOUD_PROVIDERS.RDS, icon: <AWSIcon className={classes.icon} />},
|
||||||
{label: gettext('EDB BigAnimal'), value: 'biganimal', icon: <BigAnimalIcon className={classes.icon} />},
|
{label: gettext('EDB BigAnimal'), value: CLOUD_PROVIDERS.BIGANIMAL, icon: <BigAnimalIcon className={classes.icon} />},
|
||||||
{'label': gettext('Azure PostgreSQL'), value: 'azure', icon: <AzureIcon className={classes.icon} /> }];
|
{'label': gettext('Azure PostgreSQL'), value: CLOUD_PROVIDERS.AZURE, icon: <AzureIcon className={classes.icon} /> }];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CloudWizardEventsContext.Provider value={eventBus.current}>
|
<CloudWizardEventsContext.Provider value={eventBus.current}>
|
||||||
@ -347,7 +389,8 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
onStepChange={wizardStepChange}
|
onStepChange={wizardStepChange}
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
onHelp={onDialogHelp}
|
onHelp={onDialogHelp}
|
||||||
beforeNext={onBeforeNext}>
|
beforeNext={onBeforeNext}
|
||||||
|
beforeBack={onBeforeBack}>
|
||||||
<WizardStep stepId={0}>
|
<WizardStep stepId={0}>
|
||||||
<Box className={classes.messageBox}>
|
<Box className={classes.messageBox}>
|
||||||
<Box className={classes.messagePadding}>{gettext('Select a cloud provider.')}</Box>
|
<Box className={classes.messagePadding}>{gettext('Select a cloud provider.')}</Box>
|
||||||
@ -361,39 +404,51 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
</WizardStep>
|
</WizardStep>
|
||||||
<WizardStep stepId={1} >
|
<WizardStep stepId={1} >
|
||||||
<Box className={classes.buttonMarginEDB}>
|
<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>
|
<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.')}
|
<br/>{gettext('By clicking the below button, you will be redirected to the EDB BigAnimal authentication page in a new tab.')}
|
||||||
</Box>
|
</Box>
|
||||||
</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')}
|
{gettext('Click here to authenticate yourself to EDB BigAnimal')}
|
||||||
</PrimaryButton>}
|
</PrimaryButton>}
|
||||||
{cloudProvider == 'biganimal' && <Box className={classes.messageBox}>
|
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && <Box className={classes.messageBox}>
|
||||||
<Box ></Box>
|
<Box ></Box>
|
||||||
</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}>
|
<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>
|
</Box>
|
||||||
<FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} />
|
<FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} />
|
||||||
</WizardStep>
|
</WizardStep>
|
||||||
<WizardStep stepId={2} >
|
<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}
|
cloudProvider={cloudProvider}
|
||||||
nodeInfo={nodeInfo}
|
nodeInfo={nodeInfo}
|
||||||
nodeData={nodeData}
|
nodeData={nodeData}
|
||||||
setCloudInstanceDetails={setCloudInstanceDetails}
|
setCloudInstanceDetails={setCloudInstanceDetails}
|
||||||
hostIP={hostIP} /> }
|
hostIP={hostIP} /> }
|
||||||
{cloudProvider == 'biganimal' && callRDSAPI == 2 && <BigAnimalInstance
|
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 3 && <BigAnimalInstance
|
||||||
cloudProvider={cloudProvider}
|
cloudProvider={cloudProvider}
|
||||||
nodeInfo={nodeInfo}
|
nodeInfo={nodeInfo}
|
||||||
nodeData={nodeData}
|
nodeData={nodeData}
|
||||||
setBigAnimalInstanceData={setBigAnimalInstanceData}
|
setBigAnimalInstanceData={setBigAnimalInstanceData}
|
||||||
hostIP={hostIP}
|
hostIP={hostIP}
|
||||||
|
bigAnimalClusterTypeData={bigAnimalClusterTypeData}
|
||||||
/> }
|
/> }
|
||||||
{cloudProvider == 'azure' && callRDSAPI == 2 && <AzureInstanceDetails
|
{cloudProvider == CLOUD_PROVIDERS.AZURE && callRDSAPI == 3 && <AzureInstanceDetails
|
||||||
cloudProvider={cloudProvider}
|
cloudProvider={cloudProvider}
|
||||||
nodeInfo={nodeInfo}
|
nodeInfo={nodeInfo}
|
||||||
nodeData={nodeData}
|
nodeData={nodeData}
|
||||||
@ -403,22 +458,23 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
/> }
|
/> }
|
||||||
<FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} />
|
<FormFooterMessage type={errMsg[0]} message={errMsg[1]} onClose={onErrClose} />
|
||||||
</WizardStep>
|
</WizardStep>
|
||||||
<WizardStep stepId={3} >
|
<WizardStep stepId={4} >
|
||||||
{cloudProvider == 'rds' && <AwsDatabaseDetails
|
{cloudProvider == CLOUD_PROVIDERS.RDS && <AwsDatabaseDetails
|
||||||
cloudProvider={cloudProvider}
|
cloudProvider={cloudProvider}
|
||||||
nodeInfo={nodeInfo}
|
nodeInfo={nodeInfo}
|
||||||
nodeData={nodeData}
|
nodeData={nodeData}
|
||||||
setCloudDBDetails={setCloudDBDetails}
|
setCloudDBDetails={setCloudDBDetails}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{cloudProvider == 'biganimal' && callRDSAPI == 3 && <BigAnimalDatabase
|
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 4 && <BigAnimalDatabase
|
||||||
cloudProvider={cloudProvider}
|
cloudProvider={cloudProvider}
|
||||||
nodeInfo={nodeInfo}
|
nodeInfo={nodeInfo}
|
||||||
nodeData={nodeData}
|
nodeData={nodeData}
|
||||||
setBigAnimalDatabaseData={setBigAnimalDatabaseData}
|
setBigAnimalDatabaseData={setBigAnimalDatabaseData}
|
||||||
|
bigAnimalClusterTypeData={bigAnimalClusterTypeData}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{cloudProvider == 'azure' && <AzureDatabaseDetails
|
{cloudProvider == CLOUD_PROVIDERS.AZURE && <AzureDatabaseDetails
|
||||||
cloudProvider={cloudProvider}
|
cloudProvider={cloudProvider}
|
||||||
nodeInfo={nodeInfo}
|
nodeInfo={nodeInfo}
|
||||||
nodeData={nodeData}
|
nodeData={nodeData}
|
||||||
@ -426,22 +482,23 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</WizardStep>
|
</WizardStep>
|
||||||
<WizardStep stepId={4} >
|
<WizardStep stepId={5} >
|
||||||
<Box className={classes.boxText}>{gettext('Please review the details before creating the cloud instance.')}</Box>
|
<Box className={classes.boxText}>{gettext('Please review the details before creating the cloud instance.')}</Box>
|
||||||
<Paper variant="outlined" elevation={0} className={classes.summaryContainer}>
|
<Paper variant="outlined" elevation={0} className={classes.summaryContainer}>
|
||||||
{cloudProvider == 'rds' && callRDSAPI == 4 && <FinalSummary
|
{cloudProvider == CLOUD_PROVIDERS.RDS && callRDSAPI == 5 && <FinalSummary
|
||||||
cloudProvider={cloudProvider}
|
cloudProvider={cloudProvider}
|
||||||
instanceData={cloudInstanceDetails}
|
instanceData={cloudInstanceDetails}
|
||||||
databaseData={cloudDBDetails}
|
databaseData={cloudDBDetails}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{cloudProvider == 'biganimal' && callRDSAPI == 4 && <FinalSummary
|
{cloudProvider == CLOUD_PROVIDERS.BIGANIMAL && callRDSAPI == 5 && <FinalSummary
|
||||||
cloudProvider={cloudProvider}
|
cloudProvider={cloudProvider}
|
||||||
instanceData={bigAnimalInstanceData}
|
instanceData={bigAnimalInstanceData}
|
||||||
databaseData={bigAnimalDatabaseData}
|
databaseData={bigAnimalDatabaseData}
|
||||||
|
clusterTypeData={bigAnimalClusterTypeData}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{cloudProvider == 'azure' && callRDSAPI == 4 && <FinalSummary
|
{cloudProvider == CLOUD_PROVIDERS.AZURE && callRDSAPI == 5 && <FinalSummary
|
||||||
cloudProvider={cloudProvider}
|
cloudProvider={cloudProvider}
|
||||||
instanceData={azureInstanceData}
|
instanceData={azureInstanceData}
|
||||||
databaseData={azureDatabaseData}
|
databaseData={azureDatabaseData}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import pgAdmin from 'sources/pgadmin';
|
import pgAdmin from 'sources/pgadmin';
|
||||||
import { getNodeAjaxOptions, getNodeListById } from 'pgbrowser/node_ajax';
|
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 SchemaView from '../../../../static/js/SchemaView';
|
||||||
import url_for from 'sources/url_for';
|
import url_for from 'sources/url_for';
|
||||||
import getApiInstance from '../../../../static/js/api_instance';
|
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 React from 'react';
|
||||||
import pgAdmin from 'sources/pgadmin';
|
import pgAdmin from 'sources/pgadmin';
|
||||||
import { getNodeAjaxOptions, getNodeListById } from 'pgbrowser/node_ajax';
|
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 SchemaView from '../../../../static/js/SchemaView';
|
||||||
import url_for from 'sources/url_for';
|
import url_for from 'sources/url_for';
|
||||||
import getApiInstance from '../../../../static/js/api_instance';
|
import getApiInstance from '../../../../static/js/api_instance';
|
||||||
import { isEmptyString } from 'sources/validators';
|
import { isEmptyString } from 'sources/validators';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import gettext from 'sources/gettext';
|
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();
|
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
|
// BigAnimal Instance
|
||||||
export function BigAnimalInstance(props) {
|
export function BigAnimalInstance(props) {
|
||||||
const [bigAnimalInstance, setBigAnimalInstance] = React.useState();
|
const [bigAnimalInstance, setBigAnimalInstance] = React.useState();
|
||||||
|
|
||||||
React.useMemo(() => {
|
React.useMemo(() => {
|
||||||
const bigAnimalSchema = new BigAnimalClusterSchema({
|
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,
|
useCache:false,
|
||||||
cacheNode: 'server',
|
cacheNode: 'server',
|
||||||
customGenerateUrl: ()=>{
|
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 [];
|
if (isEmptyString(region_id)) return [];
|
||||||
return getNodeAjaxOptions('biganimal_instance_types', pgAdmin.Browser.Nodes['server'],
|
return getNodeAjaxOptions('biganimal_instance_types', pgAdmin.Browser.Nodes['server'],
|
||||||
props.nodeInfo, props.nodeData, {
|
props.nodeInfo, props.nodeData, {
|
||||||
useCache:false,
|
useCache:false,
|
||||||
cacheNode: 'server',
|
cacheNode: 'server',
|
||||||
customGenerateUrl: ()=>{
|
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 [];
|
if (isEmptyString(region_id)) return [];
|
||||||
return getNodeAjaxOptions('biganimal_volume_types', pgAdmin.Browser.Nodes['server'],
|
return getNodeAjaxOptions('biganimal_volume_types', pgAdmin.Browser.Nodes['server'],
|
||||||
props.nodeInfo, props.nodeData, {
|
props.nodeInfo, props.nodeData, {
|
||||||
useCache:false,
|
useCache:false,
|
||||||
cacheNode: 'server',
|
cacheNode: 'server',
|
||||||
customGenerateUrl: ()=>{
|
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 [];
|
if (isEmptyString(region_id) || isEmptyString(volume_type)) return [];
|
||||||
return getNodeAjaxOptions('biganimal_volume_properties', pgAdmin.Browser.Nodes['server'],
|
return getNodeAjaxOptions('biganimal_volume_properties', pgAdmin.Browser.Nodes['server'],
|
||||||
props.nodeInfo, props.nodeData, {
|
props.nodeInfo, props.nodeData, {
|
||||||
useCache:false,
|
useCache:false,
|
||||||
cacheNode: 'server',
|
cacheNode: 'server',
|
||||||
customGenerateUrl: ()=>{
|
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,
|
nodeInfo: props.nodeInfo,
|
||||||
nodeData: props.nodeData,
|
nodeData: props.nodeData,
|
||||||
hostIP: props.hostIP,
|
hostIP: props.hostIP,
|
||||||
|
provider: props.bigAnimalClusterTypeData.provider,
|
||||||
});
|
});
|
||||||
setBigAnimalInstance(bigAnimalSchema);
|
setBigAnimalInstance(bigAnimalSchema);
|
||||||
}, [props.cloudProvider]);
|
}, [props.cloudProvider]);
|
||||||
@ -92,10 +177,11 @@ BigAnimalInstance.propTypes = {
|
|||||||
cloudProvider: PropTypes.string,
|
cloudProvider: PropTypes.string,
|
||||||
setBigAnimalInstanceData: PropTypes.func,
|
setBigAnimalInstanceData: PropTypes.func,
|
||||||
hostIP: PropTypes.string,
|
hostIP: PropTypes.string,
|
||||||
|
bigAnimalClusterTypeData: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// BigAnimal Instance
|
// BigAnimal Database
|
||||||
export function BigAnimalDatabase(props) {
|
export function BigAnimalDatabase(props) {
|
||||||
const [bigAnimalDatabase, setBigAnimalDatabase] = React.useState();
|
const [bigAnimalDatabase, setBigAnimalDatabase] = React.useState();
|
||||||
|
|
||||||
@ -108,15 +194,18 @@ export function BigAnimalDatabase(props) {
|
|||||||
return url_for('biganimal.db_types');
|
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,
|
useCache:false,
|
||||||
cacheNode: 'server',
|
cacheNode: 'server',
|
||||||
customGenerateUrl: ()=>{
|
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),
|
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);
|
setBigAnimalDatabase(bigAnimalDBSchema);
|
||||||
}, [props.cloudProvider]);
|
}, [props.cloudProvider]);
|
||||||
|
|
||||||
@ -137,6 +226,7 @@ BigAnimalDatabase.propTypes = {
|
|||||||
nodeData: PropTypes.object,
|
nodeData: PropTypes.object,
|
||||||
cloudProvider: PropTypes.string,
|
cloudProvider: PropTypes.string,
|
||||||
setBigAnimalDatabaseData: PropTypes.func,
|
setBigAnimalDatabaseData: PropTypes.func,
|
||||||
|
bigAnimalClusterTypeData: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -162,7 +252,7 @@ function createData(name, value) {
|
|||||||
return { name, value };
|
return { name, value };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBigAnimalSummary(cloud, bigAnimalInstanceData, bigAnimalDatabaseData) {
|
export function getBigAnimalSummary(cloud, bigAnimalClusterTypeData, bigAnimalInstanceData, bigAnimalDatabaseData) {
|
||||||
const rows1 = [
|
const rows1 = [
|
||||||
createData(gettext('Cloud'), cloud),
|
createData(gettext('Cloud'), cloud),
|
||||||
createData(gettext('Instance name'), bigAnimalInstanceData.name),
|
createData(gettext('Instance name'), bigAnimalInstanceData.name),
|
||||||
@ -174,49 +264,72 @@ export function getBigAnimalSummary(cloud, bigAnimalInstanceData, bigAnimalDatab
|
|||||||
let instance_size = bigAnimalInstanceData.instance_size.split('||');
|
let instance_size = bigAnimalInstanceData.instance_size.split('||');
|
||||||
|
|
||||||
const rows2 = [
|
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 type'), bigAnimalInstanceData.instance_type),
|
||||||
createData(gettext('Instance series'), bigAnimalInstanceData.instance_series),
|
createData(gettext('Instance series'), bigAnimalInstanceData.instance_series),
|
||||||
createData(gettext('Instance size'), instance_size[0]),
|
createData(gettext('Instance size'), instance_size[0]),
|
||||||
];
|
];
|
||||||
|
|
||||||
const rows3 = [
|
const rows4 = [
|
||||||
createData(gettext('Volume type'), bigAnimalInstanceData.volume_type),
|
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('Password'), 'xxxxxxx'),
|
||||||
createData(gettext('Database Type'), bigAnimalDatabaseData.database_type),
|
createData(gettext('Database Type'), bigAnimalDatabaseData.database_type),
|
||||||
createData(gettext('Database Version'), bigAnimalDatabaseData.postgres_version),
|
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;
|
let isError = false;
|
||||||
if (isEmptyString(cloudInstanceDetails.name) ||
|
if (isEmptyString(cloudTypeDetails.cluster_type) || isEmptyString(cloudTypeDetails.provider) ||
|
||||||
isEmptyString(cloudInstanceDetails.region) || isEmptyString(cloudInstanceDetails.instance_type) ||
|
(cloudTypeDetails.cluster_type == 'ha' && cloudTypeDetails.replicas == 0)
|
||||||
isEmptyString(cloudInstanceDetails.instance_series)|| isEmptyString(cloudInstanceDetails.instance_size) ||
|
) {
|
||||||
isEmptyString(cloudInstanceDetails.volume_type)|| isEmptyString(cloudInstanceDetails.volume_properties)) {
|
|
||||||
isError = true;
|
isError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isError;
|
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;
|
let isError = false;
|
||||||
if (isEmptyString(cloudDBDetails.password) ||
|
if (isEmptyString(cloudDBDetails.password) ||
|
||||||
isEmptyString(cloudDBDetails.database_type) || isEmptyString(cloudDBDetails.postgres_version)) {
|
isEmptyString(cloudDBDetails.database_type) || isEmptyString(cloudDBDetails.postgres_version)) {
|
||||||
isError = true;
|
isError = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cloudDBDetails.high_availability && (isEmptyString(cloudDBDetails.replicas) || cloudDBDetails.replicas <= 0)) {
|
|
||||||
isError = true;
|
|
||||||
}
|
|
||||||
if (isEmptyString(cloudDBDetails.gid)) cloudDBDetails.gid = nodeInfo['server_group']._id;
|
if (isEmptyString(cloudDBDetails.gid)) cloudDBDetails.gid = nodeInfo['server_group']._id;
|
||||||
return isError;
|
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'];
|
summaryHeader = ['Cloud Details', 'Version and Instance Details', 'Storage Details', 'Database Details'];
|
||||||
|
|
||||||
if (props.cloudProvider == 'biganimal') {
|
if (props.cloudProvider == 'biganimal') {
|
||||||
summary = getBigAnimalSummary(props.cloudProvider, props.instanceData, props.databaseData);
|
summary = getBigAnimalSummary(props.cloudProvider, props.clusterTypeData, props.instanceData, props.databaseData);
|
||||||
summaryHeader[1] = 'Version Details';
|
summaryHeader = ['Cloud Details', 'Cluster Details' ,'Version Details', 'Storage Details', 'Database Details'];
|
||||||
} else if(props.cloudProvider == 'azure') {
|
} else if(props.cloudProvider == 'azure') {
|
||||||
summaryHeader.push('Network Connectivity','Availability');
|
summaryHeader.push('Network Connectivity','Availability');
|
||||||
summary = getAzureSummary(props.cloudProvider, props.instanceData, props.databaseData);
|
summary = getAzureSummary(props.cloudProvider, props.instanceData, props.databaseData);
|
||||||
@ -107,4 +107,5 @@ FinalSummary.propTypes = {
|
|||||||
cloudProvider: PropTypes.string,
|
cloudProvider: PropTypes.string,
|
||||||
instanceData: PropTypes.object,
|
instanceData: PropTypes.object,
|
||||||
databaseData: 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 Azure from '../../img/azure.svg?svgr';
|
||||||
import SQLFileSvg from '../../img/sql_file.svg?svgr';
|
import SQLFileSvg from '../../img/sql_file.svg?svgr';
|
||||||
import MagicSvg from '../../img/magic.svg?svgr';
|
import MagicSvg from '../../img/magic.svg?svgr';
|
||||||
|
import MsAzure from '../../img/ms_azure.svg?svgr';
|
||||||
|
|
||||||
export default function ExternalIcon({Icon, ...props}) {
|
export default function ExternalIcon({Icon, ...props}) {
|
||||||
return <Icon className={'MuiSvgIcon-root'} {...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" />;
|
export const MagicIcon = ({style})=><ExternalIcon Icon={MagicSvg} style={{height: '1rem', ...style}} data-label="MagicIcon" />;
|
||||||
MagicIcon.propTypes = {style: PropTypes.object};
|
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 = () => {
|
const handleNext = () => {
|
||||||
// beforeNext should always return a promise
|
// beforeNext should always return a promise
|
||||||
if(props.beforeNext) {
|
if(props.beforeNext) {
|
||||||
props.beforeNext(activeStep).then(()=>{
|
props.beforeNext(activeStep).then((skipCurrentStep=false)=>{
|
||||||
|
if (skipCurrentStep) {
|
||||||
|
setActiveStep((prevActiveStep) => prevActiveStep + 2);
|
||||||
|
} else {
|
||||||
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||||
|
}
|
||||||
}).catch(()=>{/*This is intentional (SonarQube)*/});
|
}).catch(()=>{/*This is intentional (SonarQube)*/});
|
||||||
} else {
|
} else {
|
||||||
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
setActiveStep((prevActiveStep) => prevActiveStep + 1);
|
||||||
@ -150,8 +154,12 @@ function Wizard({ stepList, onStepChange, onSave, className, ...props }) {
|
|||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
// beforeBack should always return a promise
|
// beforeBack should always return a promise
|
||||||
if(props.beforeBack) {
|
if(props.beforeBack) {
|
||||||
props.beforeBack(activeStep).then(()=>{
|
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);
|
setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 1);
|
||||||
|
}
|
||||||
}).catch(()=>{/*This is intentional (SonarQube)*/});
|
}).catch(()=>{/*This is intentional (SonarQube)*/});
|
||||||
} else {
|
} else {
|
||||||
setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 1);
|
setActiveStep((prevActiveStep) => prevActiveStep - 1 < 0 ? prevActiveStep : prevActiveStep - 1);
|
||||||
|