Added support of BigAnimal v3 API. #5805

This commit is contained in:
Yogesh Mahajan 2023-03-24 16:07:02 +05:30 committed by GitHub
parent 58aca506fe
commit 40013fb26c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 139 additions and 134 deletions

View File

@ -19,7 +19,7 @@ from utils.io import debug, error, output
class BigAnimalProvider(AbsProvider):
BASE_URL = 'https://portal.biganimal.com/api/v2'
BASE_URL = 'https://portal.biganimal.com/api/v3'
def __init__(self):
self._clients = {}
@ -47,6 +47,12 @@ class BigAnimalProvider(AbsProvider):
parser_create_instance = parsers.add_parser('create-instance',
help='create a new '
'instance')
parser_create_instance.add_argument('--project',
required=True,
help='Project')
parser_create_instance.add_argument('--cloud-provider',
required=True,
help='Provider')
parser_create_instance.add_argument('--region', required=True,
help='name of the region')
parser_create_instance.add_argument('--name', required=True,
@ -85,9 +91,6 @@ class BigAnimalProvider(AbsProvider):
parser_create_instance.add_argument('--replicas',
required=True,
help='No. of Stand By Replicas')
parser_create_instance.add_argument('--cloud-provider',
required=True,
help='Provider')
def cmd_create_instance(self, args):
""" Create a biganimal cluster """
@ -105,7 +108,8 @@ class BigAnimalProvider(AbsProvider):
debug('Creating BigAnimal cluster: {}...'.format(args.name))
_url = "{0}/{1}".format(self.BASE_URL, 'clusters')
_url = "{0}/projects/{1}/clusters".format(self.BASE_URL,
args.project)
_headers = {"content-type": "application/json",
"accept": "application/json",
'authorization': 'Bearer {0}'.format(self._access_key)}
@ -143,7 +147,7 @@ class BigAnimalProvider(AbsProvider):
if cluster_resp.status_code == 202 and cluster_resp.content:
cluster_info = json.loads(cluster_resp.content)
instance_id = cluster_info['data']['clusterId']
instance = self.get_instance_status(instance_id)
instance = self.get_instance_status(args.project, instance_id)
data = {'instance': {
'ImageName': instance['clusterName'],
'Database Type': instance['pgType']['pgTypeName'],
@ -163,13 +167,15 @@ class BigAnimalProvider(AbsProvider):
except Exception as e:
debug(str(e))
def get_instance_status(self, instance_id):
def get_instance_status(self, project_id, instance_id):
""" Get the biganimal cluster status """
running = True
status = None
while running:
_url = "{0}/{1}/{2}".format(self.BASE_URL, 'clusters', instance_id)
_url = "{0}/projects/{1}/clusters/{2}".format(self.BASE_URL,
project_id,
instance_id)
_headers = {"accept": "application/json",
'authorization': 'Bearer {0}'.format(self._access_key)}

View File

@ -50,7 +50,8 @@ class BigAnimalModule(PgAdminModule):
'biganimal.instance_types',
'biganimal.volume_types',
'biganimal.volume_properties',
'biganimal.providers']
'biganimal.providers',
'biganimal.projects']
blueprint = BigAnimalModule(MODULE_NAME, __name__,
@ -64,6 +65,7 @@ def biganimal_verification_ack():
"""Check the Verification is done or not."""
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
status, error = biganimal_obj.polling_for_token()
if status:
session['biganimal']['provider_obj'] = pickle.dumps(biganimal_obj, -1)
return make_json_response(success=status,
errormsg=error)
@ -82,25 +84,36 @@ def verification():
return make_json_response(data=verification_uri)
@blueprint.route('/regions/<provider_id>',
methods=['GET'], endpoint='regions')
@blueprint.route('/projects/',
methods=['GET'], endpoint='projects')
@login_required
def biganimal_regions(provider_id):
"""Get Regions."""
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
status, regions = biganimal_obj.get_regions(provider_id)
session['biganimal']['provider_obj'] = pickle.dumps(biganimal_obj, -1)
return make_json_response(data=regions)
@blueprint.route('/providers/',
methods=['GET'], endpoint='providers')
@login_required
def biganimal_providers():
def biganimal_projects():
"""Get Providers."""
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
status, providers = biganimal_obj.get_providers()
return make_json_response(data=providers)
projects, error = biganimal_obj.get_projects()
return make_json_response(data=projects, errormsg=error)
@blueprint.route('/providers/<project_id>',
methods=['GET'], endpoint='providers')
@login_required
def biganimal_providers(project_id):
"""Get Providers."""
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
providers, error = biganimal_obj.get_providers(project_id)
session['biganimal']['provider_obj'] = pickle.dumps(biganimal_obj, -1)
return make_json_response(data=providers, errormsg=error)
@blueprint.route('/regions/',
methods=['GET'], endpoint='regions')
@login_required
def biganimal_regions():
"""Get Regions."""
biganimal_obj = pickle.loads(session['biganimal']['provider_obj'])
status, regions = biganimal_obj.get_regions()
session['biganimal']['provider_obj'] = pickle.dumps(biganimal_obj, -1)
return make_json_response(data=regions)
@blueprint.route('/db_types/',
@ -165,7 +178,7 @@ def biganimal_volume_properties(region_id, provider_id, volume_type):
class BigAnimalProvider():
"""BigAnimal provider class"""
BASE_URL = 'https://portal.biganimal.com/api/v2'
BASE_URL = 'https://portal.biganimal.com/api/v3'
def __init__(self):
self.provider = {}
@ -177,6 +190,7 @@ class BigAnimalProvider():
self.token_status = -1
self.regions = []
self.get_auth_provider()
self.project_id = None
def _get_headers(self):
return {
@ -281,34 +295,33 @@ class BigAnimalProvider():
return True
return False
def get_providers(self):
def get_providers(self, project_id):
"""Get cloud providers"""
_url = '{0}/cloud-providers'.format(
self.BASE_URL)
if not project_id:
return False, gettext('Project not provided.')
_url = '{0}/projects/{1}/cloud-providers'.format(
self.BASE_URL, project_id)
providers = []
resp = requests.get(_url, headers=self._get_headers())
if resp.status_code == 200 and resp.content:
self.project_id = project_id
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
'connected': value['connected']})
return providers, None
elif resp.content:
provider_resp = json.loads(resp.content)
return False, provider_resp['error']['message']
return [], provider_resp['error']['message']
else:
return False, gettext('Error retrieving providers.')
return [], gettext('Error retrieving providers.')
def get_regions(self, provider_id):
def get_regions(self):
"""Get regions"""
if not provider_id:
return False, gettext('Provider not provided.')
_url = '{0}/cloud-providers/{1}/regions'.format(
self.BASE_URL,
provider_id)
_url = '{0}/projects/{1}/regions'.format(
self.BASE_URL, self.project_id)
regions = []
resp = requests.get(_url, headers=self._get_headers())
if resp.status_code == 200 and resp.content:
@ -328,9 +341,8 @@ class BigAnimalProvider():
def get_postgres_types(self):
"""Get Postgres Types."""
_url = "{0}/{1}".format(
self.BASE_URL,
'pg-types')
_url = "{0}/projects/{1}/pg-types".format(
self.BASE_URL, self.project_id)
pg_types = []
resp = requests.get(_url, headers=self._get_headers())
if resp.status_code == 200 and resp.content:
@ -349,10 +361,9 @@ class BigAnimalProvider():
if not cluster_type or not pg_type:
return []
_url = "{0}/pg-versions?clusterArchitectureIds={1}" \
"&pgTypeIds={2}".format(self.BASE_URL,
cluster_type,
pg_type)
_url = "{0}/projects/{1}/pg-versions?clusterArchitectureIds={2}" \
"&pgTypeIds={3}".format(self.BASE_URL, self.project_id,
cluster_type, pg_type)
pg_versions = []
resp = requests.get(_url, headers=self._get_headers())
if resp.status_code == 200 and resp.content:
@ -368,15 +379,14 @@ class BigAnimalProvider():
"""GEt Instance Types."""
if region_id not in self.regions or not provider_id:
return []
_url = '{0}/cloud-providers/{1}/regions/{2}/instance-types?' \
'sort=instanceTypeName'.format(self.BASE_URL,
provider_id,
region_id)
_url = '{0}/projects/{1}/cloud-providers/{2}/regions/{3}/' \
'instance-types?sort=instanceTypeName'.\
format(self.BASE_URL, self.project_id, provider_id, region_id)
resp = requests.get(_url, headers=self._get_headers())
if resp.status_code == 200 and resp.content:
pg_types = json.loads(resp.content)
_sorted_data = sorted(pg_types['data'], key=lambda x: int(x['cpu'])
)
_sorted_data = sorted(pg_types['data'],
key=lambda x: int(x['cpu']))
return _sorted_data
return []
@ -385,10 +395,8 @@ class BigAnimalProvider():
if region_id not in self.regions:
return []
_url = '{0}/cloud-providers/{1}/regions/{2}/volume-types'.format(
self.BASE_URL,
provider_id,
region_id)
_url = '{0}/projects/{1}/cloud-providers/{2}/regions/{3}/volume-types'\
.format(self.BASE_URL, self.project_id, provider_id, region_id)
volume_types = []
resp = requests.get(_url, headers=self._get_headers())
if resp.status_code == 200 and resp.content:
@ -399,8 +407,7 @@ class BigAnimalProvider():
'label': value['volumeTypeName'],
'value': value['volumeTypeId'],
'supportedInstanceFamilyNames': value[
'supportedInstanceFamilyNames']
})
'supportedInstanceFamilyNames']})
return volume_types
def get_volume_properties(self, region_id, provider_id, volume_type):
@ -408,10 +415,9 @@ class BigAnimalProvider():
if region_id not in self.regions:
return []
_url = '{0}/cloud-providers/{1}/regions/{2}/volume-types/{3}/' \
'volume-properties'.format(self.BASE_URL,
provider_id,
region_id,
_url = '{0}/projects/{1}/cloud-providers/{2}/regions/{3}/' \
'volume-types/{4}/volume-properties'\
.format(self.BASE_URL, self.project_id, provider_id, region_id,
volume_type)
volume_properties = []
resp = requests.get(_url, headers=self._get_headers())
@ -424,6 +430,24 @@ class BigAnimalProvider():
})
return volume_properties
def get_projects(self):
projects = []
_url = '{0}/projects'.format(self.BASE_URL)
resp = requests.get(_url, headers=self._get_headers())
if resp.status_code == 200 and resp.content:
project_resp = json.loads(resp.content)
for value in project_resp['data']:
projects.append({
'label': value['projectName'],
'value': value['projectId']
})
return projects, None
elif resp.content:
project_resp = json.loads(resp.content)
return [], project_resp['error']['message']
else:
return [], gettext('Error retrieving projects.')
def clear_biganimal_session():
"""Clear session data."""
@ -451,6 +475,10 @@ def deploy_on_biganimal(data):
'create-instance',
'--name',
data['instance_details']['name'],
'--project',
str(data['cluster_details']['project']),
'--cloud-provider',
str(data['cluster_details']['provider']),
'--region',
str(data['instance_details']['region']),
'--db-type',
@ -476,10 +504,7 @@ def deploy_on_biganimal(data):
'--nodes',
str(nodes),
'--replicas',
str(data['cluster_details']['replicas']),
'--cloud-provider',
str(data['cluster_details']['provider']),
]
str(data['cluster_details']['replicas'])]
if 'biganimal_public_ip' in data['instance_details']:
args.append('--public-ip')

View File

@ -22,7 +22,7 @@ import pgAdmin from 'sources/pgadmin';
import {ToggleButtons, FinalSummary} from './cloud_components';
import { PrimaryButton } from '../../../../static/js/components/Buttons';
import {AwsCredentials, AwsInstanceDetails, AwsDatabaseDetails, validateCloudStep1, validateCloudStep2, validateCloudStep3} from './aws';
import {BigAnimalInstance, BigAnimalDatabase, BigAnimalClusterType, getProviderOptions, validateBigAnimal, validateBigAnimalStep2, validateBigAnimalStep3, validateBigAnimalStep4} from './biganimal';
import {BigAnimalInstance, BigAnimalDatabase, BigAnimalClusterType, validateBigAnimal, validateBigAnimalStep2, validateBigAnimalStep3, validateBigAnimalStep4} from './biganimal';
import { isEmptyString } from 'sources/validators';
import { AWSIcon, BigAnimalIcon, AzureIcon, GoogleCloudIcon } from '../../../../static/js/components/ExternalIcon';
import {AzureCredentials, AzureInstanceDetails, AzureDatabaseDetails, checkClusternameAvailbility, validateAzureStep2, validateAzureStep3} from './azure';
@ -84,7 +84,6 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
const [bigAnimalInstanceData, setBigAnimalInstanceData] = React.useState({});
const [bigAnimalDatabaseData, setBigAnimalDatabaseData] = React.useState({});
const [bigAnimalClusterTypeData, setBigAnimalClusterTypeData] = React.useState({});
const [bigAnimalProviders, setBigAnimalProviders] = React.useState({});
const [azureCredData, setAzureCredData] = React.useState({});
const [azureInstanceData, setAzureInstanceData] = React.useState({});
@ -320,16 +319,6 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
setErrMsg([MESSAGE_TYPE.ERROR, gettext(error)]);
reject();
});
} 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
@ -477,7 +466,6 @@ export default function CloudWizard({ nodeInfo, nodeData, onClose, cloudPanel})
cloudProvider={cloudProvider}
nodeInfo={nodeInfo}
nodeData={nodeData}
bigAnimalProviders={bigAnimalProviders}
setBigAnimalClusterTypeData={setBigAnimalClusterTypeData}
hostIP={hostIP}
/> }

View File

@ -17,67 +17,33 @@ import getApiInstance from '../../../../static/js/api_instance';
import { isEmptyString } from 'sources/validators';
import PropTypes from 'prop-types';
import gettext from 'sources/gettext';
import { makeStyles } from '@material-ui/core/styles';
import { AWSIcon, MSAzureIcon } from '../../../../static/js/components/ExternalIcon';
const useStyles = makeStyles(() =>
({
providerHeight: {
height: '5em',
},
AwsIcon: {
width: '6rem',
}
}),
);
const axiosApi = getApiInstance();
export function getProviderOptions() {
return new Promise((resolve, reject) => {
axiosApi.get(url_for('biganimal.providers'))
.then((res) => {
if (res.data.data) {
let _options= [],
_options_label = {'azure': <MSAzureIcon key='1' />,
'aws': <AWSIcon style={{width: '6rem'}} key = '2'/>};
_.forEach(res.data.data, (val) => {
_options.push({
'label': _options_label[val['value']],
'value': val['value'],
'disabled': !val['connected']
});
});
resolve(_options);
}
})
.catch((error) => {
reject(gettext(`Error while getting the biganimal providers: ${error.response.data.errormsg}`));
});
});
}
// BigAnimal Cluster Type
export function BigAnimalClusterType(props) {
const [bigAnimalClusterType, setBigAnimalClusterType] = React.useState();
const classes = useStyles();
React.useMemo(() => {
const bigAnimalClusterTypeSchema = new BigAnimalClusterTypeSchema({
providers: ()=>getNodeAjaxOptions('biganimal_providers', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
projects: ()=>getNodeAjaxOptions('biganimal_projects', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
useCache:false,
cacheNode: 'server',
customGenerateUrl: ()=>{
return url_for('biganimal.providers');
return url_for('biganimal.projects');
}
}),
providers: (project)=>getNodeAjaxOptions('biganimal_providers', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
useCache:false,
cacheNode: 'server',
customGenerateUrl: ()=>{
return url_for('biganimal.providers', {'project_id':project});
}
}),
}, {
nodeInfo: props.nodeInfo,
nodeData: props.nodeData,
hostIP: props.hostIP,
classes: classes,
bigAnimalProviders: props.bigAnimalProviders,
});
setBigAnimalClusterType(bigAnimalClusterTypeSchema);
}, [props.cloudProvider]);
@ -99,8 +65,7 @@ BigAnimalClusterType.propTypes = {
nodeData: PropTypes.object,
cloudProvider: PropTypes.string,
setBigAnimalClusterTypeData: PropTypes.func,
hostIP: PropTypes.string,
bigAnimalProviders: PropTypes.object,
hostIP: PropTypes.string
};
@ -110,11 +75,11 @@ export function BigAnimalInstance(props) {
React.useMemo(() => {
const bigAnimalSchema = new BigAnimalClusterSchema({
regions: (provider_id)=>getNodeAjaxOptions('biganimal_regions', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
regions: ()=>getNodeAjaxOptions('biganimal_regions', pgAdmin.Browser.Nodes['server'], props.nodeInfo, props.nodeData, {
useCache:false,
cacheNode: 'server',
customGenerateUrl: ()=>{
return url_for('biganimal.regions', {'provider_id': provider_id || 0});
return url_for('biganimal.regions');
}
}),
instance_types: (region_id, provider_id)=>{

View File

@ -17,6 +17,7 @@ class BigAnimalClusterTypeSchema extends BaseUISchema {
constructor(fieldOptions = {}, initValues = {}) {
super({
oid: undefined,
project: '',
cluster_type: '',
replicas: 0,
provider: '',
@ -43,6 +44,18 @@ class BigAnimalClusterTypeSchema extends BaseUISchema {
get baseFields() {
return [
{ id: 'project',
label: gettext('Project'),
mode: ['create'],
noEmpty: true,
type: () => {
return {
type: 'select',
options: this.fieldOptions.projects
};
},
},
{
id: 'cluster_type', label: gettext('Cluster type'), noEmpty: true,
type: () => {
@ -68,8 +81,17 @@ class BigAnimalClusterTypeSchema extends BaseUISchema {
return state.cluster_type != 'ha';
}
}, { id: 'provider', label: gettext('Cluster provider'), noEmpty: true,
type: 'toggle', className: this.initValues.classes.providerHeight,
options: this.initValues.bigAnimalProviders,
deps:['project'],
type: (state) => {
return {
type: 'select',
options: state.project
? () => this.fieldOptions.providers(state.project)
: [],
optionsReloadBasis: state.project,
allowClear: false,
};
}
},
];
}
@ -379,7 +401,7 @@ class BigAnimalDatabaseSchema extends BaseUISchema {
type: (state) => {
return {
type: 'select',
options: ()=>this.fieldOptions.db_versions(this.initValues.cluster_typ, state.database_type),
options: ()=>this.fieldOptions.db_versions(this.initValues.cluster_type, state.database_type),
optionsReloadBasis: state.database_type,
};
},
@ -439,8 +461,7 @@ class BigAnimalClusterSchema extends BaseUISchema {
type: () => {
return {
type: 'select',
options: ()=>this.fieldOptions.regions(this.initValues.provider),
optionsReloadBasis: this.initValues.provider,
options: ()=>this.fieldOptions.regions()
};
},
},{