mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Added capability to deploy PostgreSQL servers on EDB BigAnimal. Fixes #7179
This commit is contained in:
committed by
Akshay Joshi
parent
0795b22ae6
commit
5677b1e5f8
@@ -40,8 +40,6 @@ def get_args(providers):
|
||||
""" Creates the parsers and returns the args """
|
||||
# Create the top-level parser
|
||||
parser = argparse.ArgumentParser(prog='pgacloud.py')
|
||||
parser.add_argument('--debug', action=argparse.BooleanOptionalAction,
|
||||
default=True, help='send debug messages to stderr')
|
||||
|
||||
# Create the provider sub-parser
|
||||
parsers = parser.add_subparsers(help='provider help', dest='provider')
|
||||
|
||||
181
web/pgacloud/providers/biganimal.py
Normal file
181
web/pgacloud/providers/biganimal.py
Normal file
@@ -0,0 +1,181 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2022, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
""" EDB BigAnimal PostgreSQL provider """
|
||||
|
||||
import os
|
||||
import time
|
||||
import requests
|
||||
import json
|
||||
|
||||
from providers._abstract import AbsProvider
|
||||
from utils.io import debug, error, output
|
||||
|
||||
|
||||
class BigAnimalProvider(AbsProvider):
|
||||
BASE_URL = 'https://portal.biganimal.com/api/v1'
|
||||
|
||||
def __init__(self):
|
||||
self._clients = {}
|
||||
self._access_key = None
|
||||
self._database_pass = None
|
||||
self._cluster_info = None
|
||||
|
||||
# Get the credentials
|
||||
if 'BIGANIMAL_ACCESS_KEY' in os.environ:
|
||||
self._access_key = os.environ['BIGANIMAL_ACCESS_KEY']
|
||||
if 'BIGANIMAL_DATABASE_PASSWORD' in os.environ:
|
||||
self._database_pass = os.environ['BIGANIMAL_DATABASE_PASSWORD']
|
||||
|
||||
def init_args(self, parsers):
|
||||
""" Create the command line parser for this provider """
|
||||
self.parser = parsers.add_parser('biganimal',
|
||||
help='Amazon AWS RDS PostgreSQL',
|
||||
epilog='...')
|
||||
|
||||
# Create the command sub-parser
|
||||
parsers = self.parser.add_subparsers(help='BigAnimal commands',
|
||||
dest='command')
|
||||
|
||||
# Create the create instance command parser
|
||||
parser_create_instance = parsers.add_parser('create-instance',
|
||||
help='create a new '
|
||||
'instance')
|
||||
parser_create_instance.add_argument('--region', required=True,
|
||||
help='name of the region')
|
||||
parser_create_instance.add_argument('--name', required=True,
|
||||
help='name of the cluster')
|
||||
parser_create_instance.add_argument('--db-type', required=True,
|
||||
help='database type (PostgreSQL'
|
||||
' or EPAS)')
|
||||
parser_create_instance.add_argument('--db-version', required=True,
|
||||
help='database version')
|
||||
parser_create_instance.add_argument('--instance-type', required=True,
|
||||
help='machine type for the '
|
||||
'instance nodes')
|
||||
parser_create_instance.add_argument('--volume-type', required=True,
|
||||
help='storage type for the data '
|
||||
'database')
|
||||
parser_create_instance.add_argument('--volume-properties',
|
||||
required=True,
|
||||
help='storage properties')
|
||||
parser_create_instance.add_argument('--private-network', required=True,
|
||||
help='Private or Public Network')
|
||||
parser_create_instance.add_argument('--public-ip', default='',
|
||||
help='Public IP '
|
||||
'(default: 127.0.0.1)')
|
||||
|
||||
def cmd_create_instance(self, args):
|
||||
""" Create a biganimal cluster """
|
||||
|
||||
try:
|
||||
private_network = True if args.private_network == '1' else False
|
||||
ip = args.public_ip if args.public_ip else '0.0.0.0/0'
|
||||
IpRanges = []
|
||||
|
||||
ip = ip.split(',')
|
||||
for i in ip:
|
||||
IpRanges.append([i, 'pgcloud client {}'.format(i)])
|
||||
|
||||
debug('Creating BigAnimal cluster: {}...'.format(args.name))
|
||||
|
||||
_url = "{0}/{1}".format(self.BASE_URL, 'clusters')
|
||||
_headers = {"content-type": "application/json",
|
||||
"accept": "application/json",
|
||||
'authorization': 'Bearer {0}'.format(self._access_key)}
|
||||
|
||||
_data = {
|
||||
'clusterName': args.name,
|
||||
'instanceTypeId': args.instance_type,
|
||||
'password': self._database_pass,
|
||||
'postgresTypeId': args.db_type,
|
||||
'postgresVersion': args.db_version,
|
||||
'privateNetworking': private_network,
|
||||
'providerId': 'azure',
|
||||
'regionId': args.region,
|
||||
'replicas': 3,
|
||||
'volumePropertiesId': args.volume_properties,
|
||||
'volumeTypeId': args.volume_type,
|
||||
'zoneRedundantHa': False,
|
||||
'pgConfigMap': [],
|
||||
}
|
||||
|
||||
if not private_network:
|
||||
_data['allowIpRangeMap'] = IpRanges
|
||||
|
||||
cluster_resp = requests.post(_url,
|
||||
headers=_headers,
|
||||
data=json.dumps(_data))
|
||||
|
||||
if cluster_resp.status_code == 202 and cluster_resp.content:
|
||||
cluster_info = json.loads(cluster_resp.content)
|
||||
instance_id = cluster_info['pgId']
|
||||
instance = self.get_instance_status(instance_id)
|
||||
data = {'instance': {
|
||||
'ImageName': instance['imageName'],
|
||||
'Database Type': instance['pgType']['name'],
|
||||
'Hostname': instance['clusterConnectionInfo'][
|
||||
'serviceName'],
|
||||
'Port': instance['clusterConnectionInfo']['port'],
|
||||
'Database': instance['clusterConnectionInfo'][
|
||||
'databaseName'],
|
||||
'Username': instance['clusterConnectionInfo'][
|
||||
'username']
|
||||
}}
|
||||
|
||||
output(data)
|
||||
else:
|
||||
error(str(cluster_resp.text))
|
||||
|
||||
except Exception as e:
|
||||
debug(str(e))
|
||||
|
||||
def get_instance_status(self, instance_id):
|
||||
""" Get the biganimal cluster status """
|
||||
|
||||
running = True
|
||||
status = None
|
||||
while running:
|
||||
_url = "{0}/{1}/{2}".format(self.BASE_URL, 'clusters', instance_id)
|
||||
_headers = {"accept": "application/json",
|
||||
'authorization': 'Bearer {0}'.format(self._access_key)}
|
||||
|
||||
cluster_resp = requests.get(_url,
|
||||
headers=_headers)
|
||||
|
||||
if cluster_resp.status_code == 200 and cluster_resp.content:
|
||||
cluster_info = json.loads(cluster_resp.content)
|
||||
|
||||
self._cluster_info = cluster_info[0]
|
||||
|
||||
if self._cluster_info['instance'] != 0 and\
|
||||
self._cluster_info['phase'] not in [
|
||||
'Cluster creation request received',
|
||||
'Setting up primary',
|
||||
'Creating CNP cluster'
|
||||
]:
|
||||
running = False
|
||||
|
||||
if status != self._cluster_info['phase']:
|
||||
status = self._cluster_info['phase']
|
||||
debug('BigAnimal cluster status: {}...'.format(
|
||||
status))
|
||||
else:
|
||||
running = False
|
||||
error(str(cluster_resp.text))
|
||||
|
||||
if running:
|
||||
time.sleep(5)
|
||||
|
||||
return self._cluster_info
|
||||
|
||||
|
||||
def load():
|
||||
""" Loads the current provider """
|
||||
return BigAnimalProvider()
|
||||
@@ -144,7 +144,7 @@ class RdsProvider(AbsProvider):
|
||||
name = 'pgacloud_{}_{}_{}'.format(args.name,
|
||||
ip[0].replace('.', '-'),
|
||||
get_random_id())
|
||||
debug(args, 'Creating security group: {}...'.format(name))
|
||||
debug('Creating security group: {}...'.format(name))
|
||||
output({'Creating': 'Creating security group: {}...'.format(name)})
|
||||
response = ec2.create_security_group(
|
||||
Description='Inbound access for {} to RDS instance {}'.format(
|
||||
@@ -152,7 +152,7 @@ class RdsProvider(AbsProvider):
|
||||
GroupName=name
|
||||
)
|
||||
except Exception as e:
|
||||
error(args, str(e))
|
||||
error(str(e))
|
||||
|
||||
return response['GroupId']
|
||||
|
||||
@@ -172,8 +172,7 @@ class RdsProvider(AbsProvider):
|
||||
})
|
||||
try:
|
||||
output({'Adding': 'Adding ingress rule for: {}...'.format(ip)})
|
||||
debug(args,
|
||||
'Adding ingress rule for: {}...'.format(ip))
|
||||
debug('Adding ingress rule for: {}...'.format(ip))
|
||||
ec2.authorize_security_group_ingress(
|
||||
GroupId=security_group,
|
||||
IpPermissions=[
|
||||
@@ -186,7 +185,7 @@ class RdsProvider(AbsProvider):
|
||||
]
|
||||
)
|
||||
except Exception as e:
|
||||
error(args, e)
|
||||
error(e)
|
||||
|
||||
def _create_rds_instance(self, args, security_group):
|
||||
""" Create an RDS instance """
|
||||
@@ -197,7 +196,7 @@ class RdsProvider(AbsProvider):
|
||||
else args.db_password
|
||||
|
||||
try:
|
||||
debug(args, 'Creating RDS instance: {}...'.format(args.name))
|
||||
debug('Creating RDS instance: {}...'.format(args.name))
|
||||
rds.create_db_instance(DBInstanceIdentifier=args.name,
|
||||
AllocatedStorage=args.storage_size,
|
||||
DBName=args.db_name,
|
||||
@@ -218,18 +217,18 @@ class RdsProvider(AbsProvider):
|
||||
|
||||
except rds.exceptions.DBInstanceAlreadyExistsFault as e:
|
||||
try:
|
||||
debug(args, DEL_SEC_GROUP_MSG.format(security_group))
|
||||
debug(DEL_SEC_GROUP_MSG.format(security_group))
|
||||
ec2.delete_security_group(GroupId=security_group)
|
||||
except Exception:
|
||||
pass
|
||||
error(args, 'RDS instance {} already exists.'.format(args.name))
|
||||
error('RDS instance {} already exists.'.format(args.name))
|
||||
except Exception as e:
|
||||
try:
|
||||
debug(args, DEL_SEC_GROUP_MSG.format(security_group))
|
||||
debug(DEL_SEC_GROUP_MSG.format(security_group))
|
||||
ec2.delete_security_group(GroupId=security_group)
|
||||
except Exception:
|
||||
pass
|
||||
error(args, str(e))
|
||||
error(str(e))
|
||||
|
||||
# Wait for completion
|
||||
running = True
|
||||
@@ -252,7 +251,7 @@ class RdsProvider(AbsProvider):
|
||||
""" Delete an RDS instance """
|
||||
rds = self._get_aws_client('rds', args)
|
||||
|
||||
debug(args, 'Deleting RDS instance: {}...'.format(name))
|
||||
debug('Deleting RDS instance: {}...'.format(name))
|
||||
try:
|
||||
rds.delete_db_instance(
|
||||
DBInstanceIdentifier=name,
|
||||
@@ -260,7 +259,7 @@ class RdsProvider(AbsProvider):
|
||||
DeleteAutomatedBackups=True
|
||||
)
|
||||
except Exception as e:
|
||||
error(args, str(e))
|
||||
error(str(e))
|
||||
|
||||
# Wait for completion
|
||||
while True:
|
||||
@@ -269,7 +268,7 @@ class RdsProvider(AbsProvider):
|
||||
except rds.exceptions.DBInstanceNotFoundFault:
|
||||
return
|
||||
except Exception as e:
|
||||
error(args, str(e))
|
||||
error(str(e))
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
@@ -277,13 +276,13 @@ class RdsProvider(AbsProvider):
|
||||
""" Delete a security group """
|
||||
ec2 = self._get_aws_client('ec2', args)
|
||||
|
||||
debug(args, 'Deleting security group: {}...'.format(id))
|
||||
debug('Deleting security group: {}...'.format(id))
|
||||
try:
|
||||
ec2.delete_security_group(
|
||||
GroupId=id
|
||||
)
|
||||
except Exception as e:
|
||||
error(args, str(e))
|
||||
error(str(e))
|
||||
|
||||
##########################################################################
|
||||
# User commands
|
||||
|
||||
@@ -13,20 +13,17 @@ import sys
|
||||
import time
|
||||
|
||||
|
||||
def debug(args, message):
|
||||
def debug(message):
|
||||
""" Print a debug message """
|
||||
if not args.debug:
|
||||
return
|
||||
|
||||
now = datetime.datetime.now()
|
||||
|
||||
print('[{}]: {}'.format(now.strftime("%H:%M:%S"), message),
|
||||
file=sys.stderr, flush=True)
|
||||
file=sys.stderr)
|
||||
|
||||
|
||||
def error(args, message):
|
||||
def error(message):
|
||||
""" Print an error message and exit """
|
||||
debug(args, message)
|
||||
debug(message)
|
||||
|
||||
output({'error': message})
|
||||
|
||||
@@ -35,4 +32,4 @@ def error(args, message):
|
||||
|
||||
def output(data):
|
||||
""" Dump JSON output from a dict """
|
||||
print(json.dumps(data), flush=True)
|
||||
print(json.dumps(data))
|
||||
|
||||
Reference in New Issue
Block a user